LearnNewsExamplesServices
Frontmatter
id18278
titleCode-line size bands as a CI guard with a shrinking baseline
stateClosed
labels
enhancementaiarchitecturebuild
assigneesneo-gpt
createdAtSep 4, 2026, 3:42 PM
updatedAtSep 5, 2026, 12:22 AM
githubUrlhttps://github.com/neomjs/neo/issues/18278
authorneo-fable
commentsCount6
parentIssue18304
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 5, 2026, 12:22 AM

Code-line size bands as a CI guard with a shrinking baseline

Closed v13.2.0/archive-v13-2-0-chunk-19 enhancementaiarchitecturebuild
neo-fable
neo-fable commented on Sep 4, 2026, 3:42 PM

Context

The operator's mental model, offered as challengeable on 2026-09-04 and measured on D#18150 (OQ5 datum): files up to 1k lines green, 1.5k yellow, 2k red. Measured on dev@2ee23d7801 the model discriminates correctly only on code lines (docblocks and line comments excluded): on total lines it flags 29 engine files including component/Base.mjs at 1994; on code lines it flags exactly two — src/dashboard/dock/Workspace.mjs (1718 code of 4238) and src/main/addon/DragDrop.mjs (1267 of 1988) — and puts component/Base.mjs green at 868. The operator confirmed: "1k loc code lines is reasonable too." This repo has had no size guard since c623b2f63c; the institution repo runs WARN 900 / ERROR 1000 on apps/** and it fires (Ada's datum, DC_kwDODSospM4BFplq).

The Problem

Size is the smoke detector for the ownership defects D#18150 is about, and the file grew 11 % during the Discussion that named it (3895 → 4331, Vega's table). An absolute bar on total lines is falsified for the engine; a bar on code lines with a baseline is not. A doc-heavy file must not hide size in prose either: Workspace.mjs is 47 % doc lines.

The Architectural Reality

  • Sibling precedents in buildScripts/util/: check-ticket-archaeology.mjs (string-aware comment extraction, :44) and check-spec-retirement.mjs, wired through .github/workflows/*-lint.yml and package.json scripts; the behaviour precedent is the Institution buildScripts/checkAppFileSizes.mjs (check-substrate-size.mjs left this repository with the split). .github/workflows/pr-baseline.yml is only a pinned caller of the Skills reusable baseline, so its pr-body job cannot learn a repository-local rule. The new guard workflow fetches the current PR through github.rest.pulls.get() and writes data.body to the guard input file; context.payload.pull_request.body is an event snapshot and is never body authority.
  • Refreshed census at origin/dev on 2026-09-04 (intake, Acorn token locations, blank lines absent): 1,294 tracked .mjs files in scope, four over 1,000 code lines — apps/workstation/view/Workspace.mjs 3,388 · examples/dashboard/crossWindow/DemoBWorkspace.mjs 2,668 · src/dashboard/dock/Workspace.mjs 1,743 · src/main/addon/DragDrop.mjs 1,268; src/component/Base.mjs 869, src/collection/Base.mjs 956. The branch records its own numbers at its base.
  • Comment classification: reuse the archaeology checker's extractComment state machine or the Acorn precedent check-aiconfig-test-mutation.mjs:160–200 (#17406 refuted line-prefix inference for prose measurement — the same lesson applies to counting).
  • Baseline offenders at dev@2ee23d7801: src/dashboard/dock/Workspace.mjs 1718, src/main/addon/DragDrop.mjs 1267 (code lines); apps/workstation/view/Workspace.mjs 3387; examples/dashboard/crossWindow/DemoBWorkspace.mjs 2667.

The Fix

A buildScripts/util/check-file-sizes.mjs guard, run in CI on changed files (and on demand over the tree):

  1. counts code lines per .mjs (parser-owned comment ranges; blank lines excluded) and reports doc lines and doc % beside them — reported, never gated;
  2. three thresholds, three meanings, one gate. The target is ≤ 1,000 code lines (the operator's "1k loc code lines is reasonable"); ≥ 1,500 labels a report yellow and ≥ 2,000 red. The two upper bands are severity labels for files the baseline admits — printed, never a second gate. The gate for a file NOT in the baseline is the target: any in-scope .mjs over 1,000 code lines that the baseline does not list fails, whatever its band. (A file that is new to the tree and already over target is the case the target exists for; letting it pass with a warning until 2,000 would admit a 1,999-line file the operator's model calls red-adjacent.) Constants are inputs;
  3. the ratchet, mechanically. A committed HEAD baseline (buildScripts/util/file-size-baseline.json, repo path → code lines) must agree with the measured tree, while CI also reads the baseline at --base <ref> so a PR cannot raise its own ceiling invisibly. For a path present at the base with count B: current == B requires the HEAD entry to stay B; current < B requires the HEAD entry to equal the new count, or disappear once at target; current > B requires both the exact declaration hatch and a HEAD entry equal to the new count. A new offender absent from the base likewise requires the exact declaration plus enrollment at its measured count. A deleted or at-target path must not retain a HEAD entry. Thus equality passes, every shrink turns the ratchet, and raising the JSON alone cannot disguise growth. --fix rewrites/removes HEAD entries to match measured counts, but never substitutes for the declaration or the base comparison. Without --base, the on-demand whole-tree audit verifies current HEAD alignment and reports that historical monotonicity was not evaluated. The declaration hatch admits growth or enrollment with one PR-body line per file, grammar size-guard-growth: <repo path> — <reason>; the reason is prose for the reviewer, while line presence and exact path are what the guard reads from --pr-body-file <path>. No body file means no hatch, never a pass.
  4. scope src/**, apps/**, examples/**, buildScripts/** — a checker exempt from its own rule is not a rule.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
check-file-sizes.mjs this ticket; the Institution's checkAppFileSizes.mjs precedent code-line count per file, the target gate for un-baselined files, yellow/red labels ≥ 1,500 / ≥ 2,000 on the report, doc % beside every row unparsable file → not-measured, never clean (#17406's lesson) learn/ build guide the four offenders report yellow/red without a verdict change; component/Base.mjs and collection/Base.mjs clean; a new 1,001-line file fails
file-size-baseline.json this ticket (D#18150 OQ5 ratchet) compare base-ref and HEAD entries: equality preserves; shrink lowers/removes; growth or enrollment raises/adds only with the exact declaration; --fix aligns HEAD without bypassing base policy unreadable base baseline → CI refuses monotonicity; missing tree path → HEAD entry removed same equal / shrink-stale / shrink-lowered / raised-JSON-without-declaration / declared growth / declared enrollment fixture matrix
size-guard-growth: PR-body line this ticket; the guard's --pr-body-file input size-guard-growth: <repo path> — <reason>, one per file, admits growth or enrollment of exactly that path a line naming a path the diff did not grow → ignored and reported; no body file → no hatch same the +1 fixture passes with the line, fails with it malformed, naming another path, or absent
.github/workflows/file-size-guard.yml the live-body precedent in agent-pr-body-lint.yml; *-lint.yml changed-file runs on opened / edited / synchronize / ready_for_review: fetch the current PR with github.rest.pulls.get(), write data.body to a temp file, and run the guard with --base plus --pr-body-file; fetch-depth: 0; contents: read + pull-requests: read only REST/base read failure reds; event payload body is never used same source arm forbids the snapshot field; a body edit followed by rerun observes the edited body

Decision Record impact

none — a build guard; D#18150's OQ5 records the ratchet + hatch shape it implements.

Contract folded by the author on 2026-09-04 ~19:45Z after intake (IC_kwDODSospM8AAAABSokLgg): the bands had collapsed to two and equal-size touches had no disposition. Where the fold departs from the intake recommendation — the target, not 2,000, gates un-baselined files; equality passes instead of requiring a declaration — the reasons sit inline in The Fix and are open to counter-challenge at re-intake. Second fold ~19:55Z after the retraction (IC_kwDODSospM8AAAABSovTsw): the baseline pr-body job is a pinned caller into the Skills repo, so the hatch is checked by the guard from a body file its own workflow writes — option 1, one repository, no pin bump. Third implementation-boundary fold ~20:15Z by the assigned implementer after targeted author notice: live body means REST-at-run-time, and the shrinking baseline is compared base-vs-HEAD so raising the JSON cannot erase growth.

Acceptance Criteria

  • Red-proof at the branch's dev base: the guard reports exactly the four in-scope files over the target (two under src/**), labels them yellow or red by the bands, and reports component/Base.mjs and collection/Base.mjs clean; no label changes a verdict.
  • Doc % is printed for every reported file; it never changes the verdict.
  • The baseline lists the four offenders. Base-vs-HEAD fixtures prove: equal count preserves the entry; −1 with stale HEAD fails; −1 with lowered HEAD passes; raising only the JSON cannot hide +1; declared +1 with the HEAD entry raised passes; a new 1,001-line file requires both exact declaration and enrollment; 1,000 passes without enrollment; deleted/at-target paths cannot retain entries.
  • The hatch passes only an exact-path size-guard-growth: <path> — <reason> line from --pr-body-file; malformed, wrong-path, and absent-file controls fail. The workflow fetches the current PR with REST, never the event-snapshot body; a body-edit plus rerun receipt proves the edited body is observed.
  • not-measured is a distinct output from clean.

Out of Scope

  • Comment density and prose runs — #17406.
  • Splitting any offender — those are D#18150's cuts; this only stops regrowth.

Related

  • D#18150 OQ5 (ratchet, hatch, the code-line census) · #17406 · #18064 (baselined lint precedent) · institution checkAppFileSizes.mjs.

Live latest-open sweep: checked the latest 40 open issues (created-desc) at 2026-09-04 13:37Z; no equivalent found. A2A claim sweep (last 60 min, all read-states): no claim on this scope. Memory Core sweep: the 2026-08-19 comment-density work (#17406) is the adjacent precedent, not a duplicate. Own-assignment sweep: none.

unowned-rationale: captured at the operator's request from the D#18150 OQ5 convergence; claimable by any seat; not blocked on the vehicle decision.

Origin Session ID: ee061205-8271-4458-b73b-b132c1bec310

Retrieval Hint: query_raw_memories("file size bands code lines Workspace 1718 DragDrop 1267 component Base 868 ratchet baseline")

tobiu cross-referenced by PR #18333 on Sep 4, 2026, 10:35 PM