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):
- counts code lines per
.mjs (parser-owned comment ranges; blank lines excluded) and reports doc lines and doc % beside them — reported, never gated;
- 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;
- 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.
- 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
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")
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@2ee23d7801the model discriminates correctly only on code lines (docblocks and line comments excluded): on total lines it flags 29 engine files includingcomponent/Base.mjsat 1994; on code lines it flags exactly two —src/dashboard/dock/Workspace.mjs(1718 code of 4238) andsrc/main/addon/DragDrop.mjs(1267 of 1988) — and putscomponent/Base.mjsgreen at 868. The operator confirmed: "1k loc code lines is reasonable too." This repo has had no size guard sincec623b2f63c; the institution repo runsWARN 900 / ERROR 1000onapps/**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.mjsis 47 % doc lines.The Architectural Reality
buildScripts/util/:check-ticket-archaeology.mjs(string-aware comment extraction,:44) andcheck-spec-retirement.mjs, wired through.github/workflows/*-lint.ymlandpackage.jsonscripts; the behaviour precedent is the InstitutionbuildScripts/checkAppFileSizes.mjs(check-substrate-size.mjsleft this repository with the split)..github/workflows/pr-baseline.ymlis only a pinned caller of the Skills reusable baseline, so itspr-bodyjob cannot learn a repository-local rule. The new guard workflow fetches the current PR throughgithub.rest.pulls.get()and writesdata.bodyto the guard input file;context.payload.pull_request.bodyis an event snapshot and is never body authority.origin/devon 2026-09-04 (intake, Acorn token locations, blank lines absent): 1,294 tracked.mjsfiles in scope, four over 1,000 code lines —apps/workstation/view/Workspace.mjs3,388 ·examples/dashboard/crossWindow/DemoBWorkspace.mjs2,668 ·src/dashboard/dock/Workspace.mjs1,743 ·src/main/addon/DragDrop.mjs1,268;src/component/Base.mjs869,src/collection/Base.mjs956. The branch records its own numbers at its base.extractCommentstate machine or the Acorn precedentcheck-aiconfig-test-mutation.mjs:160–200(#17406 refuted line-prefix inference for prose measurement — the same lesson applies to counting).dev@2ee23d7801:src/dashboard/dock/Workspace.mjs1718,src/main/addon/DragDrop.mjs1267 (code lines);apps/workstation/view/Workspace.mjs3387;examples/dashboard/crossWindow/DemoBWorkspace.mjs2667.The Fix
A
buildScripts/util/check-file-sizes.mjsguard, run in CI on changed files (and on demand over the tree):.mjs(parser-owned comment ranges; blank lines excluded) and reports doc lines and doc % beside them — reported, never gated;.mjsover 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;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 countB: current== Brequires the HEAD entry to stayB; current< Brequires the HEAD entry to equal the new count, or disappear once at target; current> Brequires 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.--fixrewrites/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, grammarsize-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.src/**,apps/**,examples/**,buildScripts/**— a checker exempt from its own rule is not a rule.Contract Ledger Matrix
check-file-sizes.mjscheckAppFileSizes.mjsprecedentnot-measured, neverclean(#17406's lesson)learn/build guidecomponent/Base.mjsandcollection/Base.mjsclean; a new 1,001-line file failsfile-size-baseline.json--fixaligns HEAD without bypassing base policysize-guard-growth:PR-body line--pr-body-fileinputsize-guard-growth: <repo path> — <reason>, one per file, admits growth or enrollment of exactly that path.github/workflows/file-size-guard.ymlagent-pr-body-lint.yml;*-lint.ymlchanged-file runsopened/edited/synchronize/ready_for_review: fetch the current PR withgithub.rest.pulls.get(), writedata.bodyto a temp file, and run the guard with--baseplus--pr-body-file;fetch-depth: 0;contents: read+pull-requests: readonlyDecision 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-bodyjob 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
src/**), labels them yellow or red by the bands, and reportscomponent/Base.mjsandcollection/Base.mjsclean; no label changes a verdict.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-measuredis a distinct output fromclean.Out of Scope
Related
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")