Skip to content

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 .slnx or a .csproj.
  • Publish must target the server .csproj — publishing the .slnx produces no runnable output and no wwwroot/ (browser 404). See Commands · Publish.
  • Fresh Linux clone: mkdir -p Aetherium_Refactor/Aetherium_Refactor/{bin,obj}/Debug before 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 in appsettings.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-bank skill).

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 test for the web client; EF tests use the InMemory provider with a shared root.