Skip to content
SHC Docs

ADR-0005 — Architectural fitness functions

  • Status: Accepted + landed (2026-06-22). scripts/arch-fitness.sh, wired into make lint.

A codebase grown organically (and ported module-by-module from Python) accumulates architectural drift: the same concept implemented many ways, patterns that diverge because no single contributor holds the whole model. Multi-agent development amplifies this — each agent sees a slice and can reinvent or contradict an existing pattern (“agent myopia”). This codebase was already a patient: 30 service-locator copies, 5–6 Scope types, the 4-surface state model. The from-scratch convergence (ADR-0001..0004) collapses each to one canonical form — but nothing stopped a future change from silently regressing it. The only safeguard was a human in the loop.

Codify the canonical patterns (docs/contributing/engineering-standards.md, the ADRs, the rewritten docs/architecture/*) and enforce them in CI with scripts/arch-fitness.sh — “architectural fitness functions”: cheap, greppable checks that fail the build when a converged pattern regresses. Three kinds:

  • ratchet — a count that may only decrease; the ceiling is lowered as a migration removes the legacy pattern, never raised. (di-singletons 30→0, di-test-fixtures 57→0, uds-dialer-copies 21→0.)
  • hard — the count must be zero. (one-scope-type, no-cgo-sqlite, layer-boundary, core-imports-modules, subsystem-loop-start-ctx, error-template-style, cli-daemon-routing, no-connection-in-render-ctx.)
  • floor — an inverted ratchet: a count that may only grow or hold (fails when it drops). (example-floor, floor 20 — cobra Example: blocks are contract and must not be dropped in a refactor.)

The script now enforces ~20 checks; the roster above lists representative members of each kind. See section (C) of DRIFT-REGISTER.md for the full enforced-vs-pending cross-check.

The remaining audit findings (every deviation + every stale doc, ~240 items) live as a prioritized backlog in docs/architecture/DRIFT-REGISTER.md, with the pending fitness rules listed for promotion into the script as each class is normalized.

  • A new package singleton, a parallel Scope type, a CGO-sqlite import, an impl/modules → daemon import, a new global-swap test fixture, or a new inline UDS dialer fails make lint — for humans and agents alike.
  • The ratchets double as the live progress tracker for the in-flight sweeps.
  • New canonical patterns are added by: write the rule into arch-fitness.sh, set the baseline, document it here + in the standards, then normalize the backlog.