Conventions¶
The disciplines, as a checklist. Sources: §10, §12 — Extending, §19 — Contributing, §06.
Authoring¶
Adding an MCP tool = four edits (§12). Miss one and it fails a specific way:
| # | Edit | Miss it → |
|---|---|---|
| 1 | The [McpServerTool] method |
— |
| 2 | Dispatch arm in McpToolRouter.ExecuteToolAsync |
Error: Unknown tool '<name>' at call time |
| 3 | ToolToServerGroup entry |
Tool filtered out of every catalog — unreachable |
| 4 | ToolDocs entry in PromptEnhancementService |
Engine won't boot (ToolCatalogValidator) |
| 5 (opt) | ToolSecurityLevels entry |
Tool runs as Standard (no gate) — add for Elevated/Restricted |
The fifth edit is the one that bites
Absence from ToolSecurityLevels silently defaults a tool to Standard with no approval gate, and the boot validator doesn't check that map — nothing warns. Add destructive tools explicitly. The map is the only source of truth; class-header comments lie.
New MCP server class (§12): [McpServerToolType] + a public parameterless constructor (router does new T()); register .WithTools<T>() in Program.cs before .WithHttpTransport(); add the group key to ToolToServerGroup. Third-party/out-of-process MCP servers are unsupported — security gates only apply in-process.
Template casing trap (§08): outer object camelCase (id, name, defaultTier, seedKnowledgeBase); everything in the team block snake_case (topology, is_supervisor, system_prompt, mcp_servers, model_id). Wrong casing → field silently ignored, default kept, no error.
Code patterns¶
| Rule | One line | § |
|---|---|---|
| DbContextFactory mandatory | Never inject AetheriumDbContext; inject IDbContextFactory<>, await using a context per operation (DbContext isn't thread-safe; agents run concurrently) |
§06 |
| Singleton + scoped deps | Never inject a scoped service into a singleton ctor; inject IServiceProvider, CreateScope() per operation, resolve inside the using |
§06 |
| Channel work queue | Submit LLM work via SubmitTaskAsync and await ResultSource.Task — never call the LLM client directly from a producer |
§10 |
| BackgroundService | Long-running work → a hosted service via AddHostedService<T>(); keep testable logic in a separate singleton |
§06 |
| Generated API types | Never hand-write a TypeScript type that mirrors a server DTO — regenerate from OpenAPI instead | §10 |
| SSR double-registration | A client service used during prerender must be registered in both Program.cs files (server + client). Missing server-side → InvalidOperationException at prerender |
§10 |
Build & publish¶
- Build/watch work against the
.slnxor a.csproj. - Publish must target the server
.csproj— publishing the.slnxproduces no runnable output and nowwwroot/(browser 404). See Commands · Publish. - Fresh Linux clone:
mkdir -p Aetherium_Refactor/Aetherium_Refactor/{bin,obj}/Debugbefore the first build.
Git & hygiene¶
Commit convention (§19): type(scope): summary — type ∈ {feat, fix, refactor, docs, chore}, optional scope, concise summary. No hook enforces it. Example: docs(developer): draft section 18 — roadmap.
Pre-commit hygiene (§10/§19) — all manual, no CI backstop:
- Tests pass (don't commit a red build).
- No secrets — committed files keep
REPLACE_ME_VIA_LOCAL_OVERRIDE; real values only inappsettings.Local.json. - No machine paths — no drive-letter/absolute dev paths, no LAN IPs; use repo-relative +
localhost. - Casing — the override file is
appsettings.Local.json(capital L); Linux won't load a lowercase variant. - Memory bank updated for significant work (
update-memory-bankskill).
Before you submit (§19): it builds and publishes (.csproj); tests pass; the catalog validates (OK — N tools registered, all documented); hygiene clean; architectural decisions respected (permission model, in-process-only tools, push-dispatched orchestration — §17).
Line endings (.gitattributes): LF default, CRLF held for .ps1/.bat/.cmd. No .editorconfig — match surrounding C# style.
Testing¶
- xUnit for the server,
bun testfor the web client; EF tests use the InMemory provider with a shared root.