AGPL-3.0-or-later · forever.

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

0

ADR-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-macro
  • lookup-table — large compile-time static data table
  • enum-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