Trovella Wiki

ADR-013: Architecture Enforcement

Automated quality system for AI-generated code -- dependency-cruiser, Knip, jscpd, sonarjs, fitness tests, and AI-assisted review skills.

Status: Accepted Date: 2026-03-29 (enforcement audit TRO-94), matured 2026-04-03 (skills and scheduled audits TRO-97) Deciders: Kyle Olson (Solo Founder)

Decision

Audit every rule in root CLAUDE.md and all 14 package-level CLAUDE.md files. Classify each as machine-enforced, convertible, or semantic-only. Convert all convertible rules to machine enforcement. Add static analysis CI gates: dependency-cruiser (package boundaries), Knip (dead code), jscpd (duplication). Add hand-picked sonarjs complexity rules. Add Vitest architecture fitness tests. Create /arch-review and /dep-check Claude Code skills for qualitative assessment. Schedule monthly automated audits.

Context

Solo founder building a multi-tenant SaaS where AI coding agents write the majority of the code. AI agents produce code that compiles, passes type checks, and appears correct, but may silently violate architectural boundaries, duplicate existing patterns, or bypass security conventions.

The problem is not hypothetical. A Claude Code session hit a CI failure from dependency-cruiser and removed the check rather than fixing the root cause. This established the foundational principle: every rule that is not machine-enforced is effectively optional when AI agents are the primary developers.

A research phase evaluated 40+ tools. Tools were assessed on three criteria: (1) instant editor feedback so AI agents see violations while writing, (2) maturity for use as a CI gate, (3) low false-positive rate to avoid training agents to suppress warnings.

Decision Drivers

  1. Machine enforcement over documentation -- CI gates cannot be ignored
  2. Editor-time feedback -- violations caught while writing are cheaper than violations caught in CI
  3. Low false-positive rate -- false positives train AI agents to add eslint-disable comments
  4. Maturity and adoption -- solo-maintainer projects are too risky for core enforcement
  5. Ratcheting over big-bang -- new rules as warnings first, promoted to errors after fixing violations
  6. Defense-in-depth -- overlapping tools catch different violation types

Alternatives Considered

SonarQube

Enterprise code quality platform with dashboards, security analysis, and trend tracking. Rejected because the marginal benefit over ESLint strict + sonarjs does not justify the operational overhead for a solo developer. Reconsidered if the team grows.

CodeScene

Commercial temporal coupling analysis (EUR 18/month). Rejected because social complexity metrics are meaningless for a solo developer. Replaced by scripts/hotspot-analysis.sh that cross-references git churn with ESLint complexity violations at zero cost.

ArchUnitTS

TypeScript architecture testing library (Java ArchUnit inspired). Solo maintainer, zero npm dependents at evaluation time. Rejected -- the planned fitness tests were recreated as dependency-cruiser forbidden rules with better tooling support (978K weekly npm downloads).

FTA (Fast TypeScript Analyzer)

Rust-based composite complexity scorer. ~295 GitHub stars. Rejected because the composite score is opaque with no actionable thresholds. ESLint complexity + sonarjs cognitive-complexity provide the same coverage with instant editor feedback and granular per-metric configuration.

150+ error-level rules. Rejected due to high volume of false positives. 9 rules were hand-picked targeting the specific code smells AI agents produce most frequently.

Implementation

The implementation spans six tools across three enforcement layers:

LayerToolWhat It Catches
Editor-timeESLint + sonarjs + naming-conventionComplexity, naming, banned imports, code smells
Editor-timeno-restricted-imports via restrictions.jsDirect SDK imports bypassing wrapper packages
CIdependency-cruiserPackage layer violations, circular dependencies
CIKnipDead code: unused exports, files, dependencies
CIjscpdCode duplication above 15% threshold
CIVitest fitness testsauthorizedProcedure in routers, MCP tool registration
Qualitative/arch-review skill6-dimension quality audit with semantic rules
Qualitative/dep-check skillDependency health, stale packages, CVEs

For detailed configuration of each tool, see the reference pages in this topic:

Consequences

Positive

  • Zero advisory-only rules -- every CLAUDE.md rule is either machine-enforced or explicitly marked for AI-assisted review
  • Editor-time feedback -- complexity, naming, and import rules give AI agents instant red squiggles while writing
  • Defense-in-depth -- ESLint catches single-file issues, dependency-cruiser catches cross-package issues, Knip catches dead code, jscpd catches duplication, fitness tests catch structural invariants
  • Self-monitoring -- the arch-review-checklist includes rules about the enforcement system itself

Negative

  • Tooling maintenance burden -- 5 enforcement tools each require configuration and version updates
  • Configuration duplication -- the 5-layer hierarchy is encoded in both .dependency-cruiserrc.mjs and restrictions.js
  • Complexity thresholds are opinionated -- cyclomatic 20, cognitive 20, max-lines 400 are more permissive than common recommendations

Risks

  • Enforcement config tampering -- mitigated by CODEOWNERS + arch-review-checklist Section 7
  • Allowlist creep -- mitigated by /arch-review skill checking allowlist justifications
  • Tool ecosystem changes -- mitigated by all tools having strong download numbers and active maintenance
  • Skill drift from codebase -- mitigated by the convention "when a PR changes architecture, update docs in the same PR"

Validation

RuleEnforcement
Package layer hierarchydependency-cruiser 6 forbidden rules
External SDKs through wrappersESLint no-restricted-imports via restrictions.js
Feature routers use authorizedProcedureVitest architecture test with allowlist
No console.logESLint no-console: error
Naming conventions@typescript-eslint/naming-convention
Cyclomatic/cognitive complexity max 20ESLint complexity + sonarjs/cognitive-complexity
File max 400 lines, function max 100ESLint max-lines + max-lines-per-function
No dead codeKnip in CI
Duplication max 15%jscpd in CI
Semantic rules (RLS, CASL, audit logs)/arch-review skill
Dependency health/dep-check skill + monthly audit
Enforcement configs not weakenedCODEOWNERS + arch-review-checklist Section 7

References

On this page