ADR-0005 — Architectural fitness functions
- Status: Accepted + landed (2026-06-22).
scripts/arch-fitness.sh, wired intomake lint.
Context
Section titled “Context”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.
Decision
Section titled “Decision”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-singletons30→0,di-test-fixtures57→0,uds-dialer-copies21→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 — cobraExample: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.
Consequences
Section titled “Consequences”- A new package singleton, a parallel
Scopetype, a CGO-sqlite import, animpl/modules → daemonimport, a new global-swap test fixture, or a new inline UDS dialer failsmake 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.