Install
$ agentstack add skill-tairitsua-monica-monica-development ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Monica Development Guide
This skill provides essential guidance for developing modules and services in the Monica framework.
Architecture Overview
Monica is a modular .NET infrastructure library designed for flexibility and performance. Each module can be used independently without requiring the entire framework.
Module Pattern
Every module follows a consistent pattern with four components in one Module{Name}.cs file, which is located in the Modules folder of each project.
| Component | Purpose | Example | |-----------|---------|---------| | Module{Name} | Core module implementation inheriting from ModuleBase or WebModuleBase | ModuleSignalR | | Module{Name}Option | Configuration options for the module | ModuleSignalROption | | Module{Name}Guide | Configuration guide/builder for fluent API | ModuleSignalRGuide | | Module{Name}BuilderExtensions | Extension methods for Mo registration entry points | ModuleSignalRBuilderExtensions |
ModuleKey Rules
- For Monica first-party modules in this repository, add new keys to
Monica.Core/Modularity/Models/BuiltInModuleKey.csand use[ModuleKey(BuiltInModuleKey.YourModule)]. - Do not introduce ad-hoc string keys such as
BuildingBlocksPlatform.*for Monica-owned modules unless the module is intentionally external to Monica's built-in key set.
Module Runtime Kinds
Choose the module runtime kind before writing registration code:
- Use
ModuleBasewithModuleGuidewhen the module only participates in host-builder, service-registration, post-service, and dependency phases. - Use
WebModuleBasewithWebModuleGuideonly when the module actually configures ASP.NET Core middleware or endpoint phases. - Do not equate UI module with web module. A UI module that only registers pages, components, dialogs, or shell contributions should usually remain a non-web
ModuleBase. - Use
MinimalApiModuleOptionswhen the module exposes minimal APIs and needs API-group or API-disable controls. - If a web module still provides useful non-web behavior in a generic host, override
CanDowngradeToNonWebModule()and returntrue. In downgrade mode only the non-web phases execute;ConfigureApplicationBuilderandConfigureEndpointsare skipped. - If you surface module-system diagnostics or dashboard data, keep module capability and runtime mode separate:
IsWebModuleanswers what the module can do,IsDowngradedFromWebModuleanswers how it is currently running.
Localization Registration Rules
- If a module uses
IStringLocalizerdirectly or indirectly, some participating module in that dependency chain must declare:
DependsOnModule().Register()
.AddResource();
- For Monica project-local resources, keep the marker class and JSON files under the project root
Localization/folder so resource namespace, embedded resource path, and validation tooling stay aligned. - Prefer constructor-injected
IStringLocalizerin modules' DI-created services, support classes, state classes, Razor components, pages, and dialogs. UseLocalizationManager.Get/Foronly when dependency injection is not available, such as static helpers, view-model computed properties created outside DI, or module registration/endpoint metadata that is built outside a service instance. - Preferred layout:
Monica.{Project}/
└── Localization/
├── {Resource}.cs
└── {Resource}/
├── zh-CN.json
└── en-US.json
Core Dependencies
Monica.Core is the foundation for all other modules, containing:
ModuleBase/WebModuleBasebase classes- Module registration system
- Automatic middleware ordering
- Core utilities and extensions
Module Registration
Modules use a unified registration pattern:
// Basic registration with options
Mo.Add{ModuleName}(options =>
{
options.Property1 = value1;
options.Property2 = value2;
});
// With guide for fluent configuration
Mo.Add{ModuleName}()
.GuideMethod1()
.GuideMethod2();
Module Dependencies
When a module depends on other modules:
public override void ClaimDependencies()
{
DependsOnModule().Register();
DependsOnModule().Register();
}
Modules declare dependencies by overriding ClaimDependencies() on ModuleBase or WebModuleBase.
Dependencies are automatically registered when a module is added.
Module State Ownership Rules
- Keep
Module{Name}Optionfocused on developer configuration. Do not use options objects as mutable runtime registries for discovered types, generated endpoints, caches, or other cross-phase state. - Do not put required shared runtime state in builder-extension local variables or closures inside
Mo.Add{ModuleName}(...). A module can be registered directly or transitively throughDependsOnModule(...).Register(), and both paths must behave identically. - If a module needs mutable state across registration phases such as
ConfigureServices,IterateBusinessTypes,PostConfigureServices, MVC configuration, or endpoint mapping, create and own that state inside the module and expose the same instance through a module-owned singleton or internal registry service. - Do not hide required default services, middleware, endpoint mapping, or post-configuration in
Mo.Add{ModuleName}()convenience methods by chaining extra guide calls afterRegister(...). Direct registration and transitive dependency registration must share the same module-owned baseline behavior. - If a module's built-in behavior needs a non-default phase order, model that order as module-owned lifecycle behavior instead of keeping the behavior in builder-entry-only guide methods.
- When reviewing an existing module, treat builder-entry-only state as a design bug even if the direct registration path currently works.
IBusinessTypeIteratorshould almost always preserve the full incoming type stream. Use it for discovery side effects and enrichment, not for accidentally filtering later modules out of the host's business types. Only drop types when the module is explicitly intended to transform the downstream scan set.
Required Configuration Methods (GetRequestedConfigMethodKeys)
When a module's services depend on registrations that must come from Guide methods (e.g., choosing a store implementation), the Guide class must override GetRequestedConfigMethodKeys to declare those requirements. The module system validates at startup that all required keys have been satisfied, throwing a clear error if any are missing.
Pattern: Define private const string keys, return them from GetRequestedConfigMethodKeys, and pass key: to ConfigureServices/ConfigureEmpty in the guide methods that satisfy each requirement.
public class ModuleExampleGuide
: ModuleGuide
{
// 1. Define constants for required configuration keys
private const string CONFIG_STORE = nameof(CONFIG_STORE);
private const string CONFIG_PROVIDER = nameof(CONFIG_PROVIDER);
// 2. Declare which keys are required
protected override string[] GetRequestedConfigMethodKeys()
{
return [CONFIG_STORE, CONFIG_PROVIDER];
}
// 3. Guide methods satisfy requirements by passing key:
// Multiple methods can share the same key (alternatives)
public ModuleExampleGuide UseInMemoryStore()
{
ConfigureServices(ctx =>
{
ctx.Services.AddSingleton();
}, key: CONFIG_STORE); // Satisfies CONFIG_STORE
return this;
}
public ModuleExampleGuide UseCustomStore() where T : class, IStore
{
ConfigureServices(ctx =>
{
ctx.Services.AddSingleton();
}, key: CONFIG_STORE); // Also satisfies CONFIG_STORE
return this;
}
// Use ConfigureEmpty when the method doesn't register services
// but still needs to mark the requirement as satisfied
public ModuleExampleGuide UseDefaultProvider()
{
ConfigureEmpty(CONFIG_PROVIDER);
return this;
}
}
Key rules:
- If a service registered in
Module.ConfigureServicesdepends on a DI registration that only comes from a Guide method, that Guide method's key must be inGetRequestedConfigMethodKeys - Alternative methods (e.g.,
UseInMemoryvsUseCustom) share the same key constant - Use
ConfigureEmpty(KEY)when a method satisfies a requirement without registering services
Key Architectural Decisions
- Modular Independence: Each module has minimal dependencies and can function standalone
- Automatic Middleware Registration: Modules automatically register required middleware in correct order
- Prevention of Duplicate Registration: Module system prevents accidental multiple registrations
- Strong Typing: Leverages C# type system for compile-time safety
- Performance Optimization: Reduces reflection usage through cached metadata
Unified Result Model (Res)
Scope: Res/Res is the lightweight result-envelope model used in Monica entry points that intentionally follow the IsFailed consumption pattern. In the current repository guidance, prefer Res for UI-facing flows and keep internal infrastructure services on standard .NET returns plus exceptions.
Use Res or Res only at the boundary that is meant to expose Monica's result-envelope pattern. Do not wrap every internal service in Res just for uniformity.
Quick Reference
// Returning success with data
return data; // Implicit conversion: T => Res
// Returning error
return "Error message"; // Implicit conversion: string => Res
// Explicit methods
return Res.Ok(data);
return Res.Fail("Error message");
// Handling responses
if ((await service.GetDataAsync(id)).IsFailed(out var error, out var data))
{
// Handle error
return error;
}
// Use data
Important Rules
- Result-envelope entry points must return
ResorRes— never return null - Internal services (in
Services/) must use standard return types and throw exceptions — do not useRes - Use implicit conversions for cleaner code when returning success or error from result-envelope entry points
- Handle responses using the
IsFailedpattern to extract error and data - Required using: Include
using Monica.Tool.Results;whereResis used - Typed error details: Use
AppendMetadata("error", payload)rather than introducing a separateResErrormodel - Caught exceptions to
Res.Fail: When a UI service, Facade, or other result-envelope entry point converts a caught exception intoRes.Fail(...), return the full recursive message withex.GetMessageRecursively()instead of onlyex.Message, so nested exception details are preserved for diagnostics. This usually also requiresusing Monica.Core.Extensions;.
For detailed Res type documentation, see references/res-type-guide.md.
For service layer patterns (UI vs infrastructure), see references/module-patterns.md.
Hosted Service Development
Monica provides MoBackgroundService as a base class for background services with built-in observability.
Key Principle: Use RecordState, Not Logger
Use RecordState instead of direct Logger calls for observability. The base class already configures a Logger internally, so direct logging would be redundant.
// CORRECT: Use RecordState with explicit LogLevel
RecordState("Operation started", logLevel: LogLevel.Information);
RecordState("Error occurred", logLevel: LogLevel.Error, exception: ex);
// AVOID: Don't use Logger directly (redundant)
// Logger.LogInformation("..."); // Already handled by RecordState
Quick Reference
public class MyMonitorService(
IObservableInstanceManager observableManager,
IOptions hostedServiceOptions,
ILogger logger,
IMyDependency dependency
) : MoBackgroundService(observableManager, hostedServiceOptions, logger)
{
public override string ServiceName => nameof(MyMonitorService);
protected override async Task ExecuteBackgroundAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
RecordState("Starting work cycle", logLevel: LogLevel.Information);
await DoWorkAsync(stoppingToken);
}
catch (OperationCanceledException) { break; }
catch (Exception ex)
{
RecordState("Work cycle failed", logLevel: LogLevel.Error, exception: ex);
}
}
}
}
Required Dependencies
| Dependency | Purpose | |------------|---------| | IObservableInstanceManager | Manages observable state tracking | | IOptions | Service configuration options | | ILogger | Optional, passed to base for internal use |
For detailed hosted service patterns including CoordinatedLeaderService for leader-aware services, see references/hosted-service-guide.md.
Additional Resources
Reference Files
references/res-type-guide.md- Complete Res result-envelope documentation with implicit conversions and best practicesreferences/module-patterns.md- Module naming conventions, file structure, and implementation patternsreferences/hosted-service-guide.md- MoBackgroundService patterns, RecordState usage, and CoordinatedLeaderService
Source Code Reference
- Res type definition:
Monica.Tool/Results/Res.cs - Module base classes:
Monica.Core/Modularity/Abstractions/ModuleBase.cs,Monica.Core/Modularity/Abstractions/WebModuleBase.cs - Module guides:
Monica.Core/Modularity/Abstractions/ModuleGuide.cs,Monica.Core/Modularity/Abstractions/WebModuleGuide.cs - Web module contract:
Monica.Core/Modularity/Abstractions/IWebModule.cs - MoBackgroundService:
Monica.Core/Features/HostedServices/MoBackgroundService.cs - CoordinatedLeaderService:
Monica.RegisterCentre/Core/CoordinatedLeaderService.cs
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Tairitsua
- Source: Tairitsua/Monica
- License: MIT
- Homepage: https://monica.dpdns.org/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.