Skip to main content

SOLID audit charter

Tier: Internal
Status: Active charter for milestone SOLID Architecture Audit (2026-06-04)
Audience: core team, module authors, AI agents executing architectural work
Owners: project lead
Related: solid-decoupling-audit.md, solid-audit-inventory.md, application-surfaces-vs-platform-backbone.md, DATA-ACCESS-BOUNDARIES.md, RFC-0002


1. Purpose

This charter defines how we score, slice, and document exceptions for the system-wide SOLID audit in umbraculum-dev. SOLID is mapped to repo-native boundaries (contracts packages, module-sdk, data-access rules) — not generic Java OOP dogma.

Guiding sentence: Follow dependency direction and single ownership; extend via registration and contracts; when coupling is intentional, say why in code.


2. SOLID mapping (Umbraculum-native)

LetterPrincipleUmbraculum expression
SSingle ResponsibilityOne owner per URL segment; one Prisma schema per module; routes thin / services own logic; contracts wire-only
OOpen/ClosedregisterModule() extension points; versioned *V1 DTOs; new modules via registration
LLiskov SubstitutionZod response schemas; ValidatedSchema<T> in ai-tool-sdk
IInterface SegregationSeparate *-contracts; clients use api-client; UI layering in CODING-STANDARDS
DDependency InversionApps → HTTP + contracts; modules → platform SDK; no sibling module service imports

Full table and examples: solid-decoupling-audit.md §2.


3. Violation severity rubric

SeverityDefinitionExamplesDefault action
P0Boundary break — violates documented module or data-access ruleCanonical module imports sibling module service; app imports services/api or PrismaFix in Tier A or block merge
P1God unit — multiple unrelated reasons to change in one fileRoute >800 LoC with inline business logic; service >800 LoC; page >1500 LoC mixing auth + domain + UITier A/B refactor or split epic
P2Transitional pattern — known RFC-0002 gap, not yet migratedBrewery routes in modules/brewery/ calling horizontal services/; fat platform route without service classDocument + backlog; align with β-layout over time
P3Style / future debt — size or packaging hygiene without wrong dependency directionLarge cohesive domain file; barrel export sizeNote in inventory; no urgent action

4. Audit slices and owners

SlicePrimary pathsReview focusDefault owner
API canonical modulesservices/api/src/modules/{pim,mrp,crp,automation}/Route/service split; no sibling importsModule code owner
Brewery verticalmodules/brewery/routes/, services/api/src/services/*, domain/Transitional layout; service surfaceBrewery vertical
Platform routes/servicesservices/api/src/routes/, services/api/src/services/Auth, AI, integrations thicknessPlatform API
Packagespackages/*-contracts/, module-sdk, brewery-core, beerjsonPackage boundaries; contract purityPackage maintainer
Appsapps/web/app/, apps/native/src/modules/Page decomposition; api-client onlyPlatform shared layout
Cross-module edgesMRP↔CRP↔brewery projections, AI toolsDependency direction; shared contractsPlanning modules + platform

5. Per-slice pass criteria

For each file or cluster, answer:

  1. S — How many distinct reasons to change?
  2. O — Can behavior extend via registerModule / new package without editing stable core?
  3. L — Are wire shapes contract-safe (Zod parse at boundary)?
  4. I — Do consumers depend on a narrow surface?
  5. D — Does dependency flow backbone → module → app (never reverse)?

Pass: no P0; P1 items have Tier A/B plan or @arch-boundary; P2 documented as transitional.


6. @arch-boundary exception convention

When we choose not to decouple (cost > benefit, α scope, intentional platform pattern), document at the coupling site:

/** @arch-boundary — intentional coupling
* Reason: <why this coupling exists and what it enables>
* Revisit: <trigger to reconsider — epic id, RFC, roadmap milestone>
* Owner: <module or team>
*/

Rules:

  • Place immediately above the import, call, or cross-schema read.
  • Mirror one line in the module README under § Known couplings (create section if missing).
  • No @arch-boundary without all three fields (Reason, Revisit, Owner).
  • Prefer fixing P0 over annotating; annotation is for P1–P3 accepted debt.

7. Automated inventory

Regenerate with:

npx tsx scripts/audit/solid-inventory.ts

Output: solid-audit-inventory.md (committed snapshot; script is source of regeneration).

CI: P0 sibling imports on canonical modules are merge-blocking via eslint-plugin-boundaries (boundaries/element-types at error on services/api/src/modules/** — SOLID B5, 2026-06-04). The inventory script remains report-only (fat-file and drift signal).


8. Milestone success criteria

  • Inventory complete for all six slices with severity scores
  • Tier A/B/C desirability matrix in solid-decoupling-audit.md
  • Six skeptical tests answered with evidence
  • Verdict: SOUND / NOT SOUND / NEEDS BROADER ANALYSIS
  • If SOUND: CODING-STANDARDS § Architectural coupling + toolset rule 03-layering-and-coupling-discipline.mdc

9. What this audit explicitly avoids

  • Mandating interfaces or DI containers on every service class
  • Blocking CI on raw LoC thresholds without human triage
  • Re-litigating RFC-0001/0002/0003 decisions
  • Applying identical size rules to cohesive domain math and HTTP routes

10. Post–Wave 17 program closure

Mechanical Waves 11–17 and the Tier A/B implementation epic are complete. Inventory automation is green (P2/P3=0) except generated OpenAPI (P1 tooling).

Canonical status for agents and maintainers: solid-post-wave17-closure.md — includes when to reopen hygiene waves, Tier C wont-fix list, and verification gates for SOLID PRs.


11. App boundaries (WS5)

Apps (apps/web, apps/native) enforce Dependency (D) direction via eslint-plugin-boundaries at error: no services/api/** source imports, no sibling feature-segment imports (water mash↔boil, native module↔module), and no cross-locale-vertical imports ((pim)(mrp) etc.). Belt-and-suspenders WS6 no-restricted-imports blocks @prisma/* and deep API paths by pattern.

Agent gate: AGENTS.md § SOLID and dependency direction (D).

Contributor docs: LINTING.md § App layer boundaries (WS5), solid-boundaries-eslint-apps-spike.md, DATA-ACCESS-BOUNDARIES.md §6.


Charter frozen 2026-06-04. Amend only when the scoring rubric or slice model needs revision — not for individual inventory rows (those live in solid-audit-inventory.md).