ADR-023
File modularity discipline: 800 LOC warn / 1500 fail / 3000 fail-with-LOC-EXEMPT-allowlist
accepted · 2026-04-16 · L0 L0.5 L1 L2 L3 L4 L5 · cites 6
0ADR-023: File modularity discipline #
Context #
Current rule: max 1500 LOC per file (hard cap). 4-agent audit (2026-04-16) revealed:
- Current P90 ≈ 600 LOC (well under cap)
- nika-catalog/build.rs at 1386 LOC and data/models.rs at 1248 LOC are uncomfortably close
- rust-analyzer P90 = 900 LOC, helix P90 = 700 LOC, oxc P90 = 500 LOC
- Mature workspaces routinely ship 2k+ LOC files (codegen output, generated lookup tables)
Two opposing views:
- Agent 1 (rust-architect): tighten to 800/1200 — match SOTA Rust P90
- Agent 2 (web-researcher SOTA): NO workspace enforces hard limits — relax to 1500/3000
Both have merit. Cap discipline matters; codegen reality matters.
Decision #
Three-tier file LOC policy:
- Threshold · Action · Mode
- **800 LOC** · warning · hygiene vector 27 emits YELLOW
- **1500 LOC** · hard fail · hygiene vector 27 fails RED — cannot commit
- **3000 LOC** · absolute hard fail with allow-list exemption only · hygiene vector 27 fails CRITICAL — exempt ONLY via per-file marker
`// LOC-EXEMPT: <reason> [<owner>]` marker at top of file allows bypassing the 1500 cap up to 3000 LOC. Reasons must be one of:
codegen— auto-generated by build.rs / proc-macrolookup-table— large compile-time static data tableenum-mega— single mega-enum (e.g., 200+ variants of EventKind aggregator)
Per-function cap: 100 LOC soft warn (clippy::too_many_lines = "warn").
Currently unenforced — enable in workspace lints.
Consequences #
- ✅ 800 warn discipline keeps cohesion tight (we're already there at P90 ~600)
- ✅ 1500 fail catches genuine sprawl
- ✅ 3000-with-exemption preserves codegen reality
- ✅ Marker enforces conscious decision (not silent growth)
- ❌ +1 hygiene vector (27) — implementation cost ~1h
- ❌ One-time refactor of catalog/build.rs (1386) + data/models.rs (1248) to honor 800 warn — handled in Phase B'
Implementation #
bash
# scripts/hygiene/check-file-loc.sh (vector 27)# - For each .rs file in crates/*/src/ and crates/*/build.rs:# - count LOC# - if > 3000:# check for // LOC-EXEMPT: marker; fail if absent# - elif > 1500:# fail (no exemption above 1500 except via 3000 path)# - elif > 800:# warn (yellow)Workspace Cargo.toml lint section:
toml
[workspace.lints.clippy]too_many_lines = { level = "warn", priority = -1 }Alternatives considered #
- Relax to 1500/3000 (Agent 2's pure recommendation) — loses discipline
- Tighten to 800/1200 (Agent 1's pure recommendation) — fails under codegen
- Per-crate cap instead of per-file — moves the problem one level up
- Hard 1500 with no exemptions — current rule, breaks under generated code
read at v0.107.0 · the decision record ships with the engine