fix(checks): the gates read a comment the way code writes one - #61
Merged
Conversation
Two gates asked the same question and got opposite wrong answers. code-file-cap looked for its escape marker anywhere in the file, so any line that merely mentioned checks:allow-length granted the exemption. defer-markers read a comment only when the line opened with one, so "x = 1; // for now" walked past every entry in commentBans while the same words on their own line failed. One honest answer to "what on this line is a comment?" closes both, and building either alone writes half of it. commentOn() steps over quoted text before it looks for an opener, so a "//" inside a string stays a string, and it takes the first real opener wherever it sits on the line. Its two limits are named at the site: unbalanced quotes (a Rust lifetime, an apostrophe) end the search and return null, which keeps the gate quiet rather than wrong, and Python's floor division reads as a comment whose text is an operand that no ban matches. The exemption also had to become a declaration rather than a mention, so only a comment that opens with checks:allow-length grants it. That is the defect intake row 56 named, a test or a doc snippet exempting itself, and it cost more than the row knew. With the gate armed, two files were over the 500-line cap and not one: check.test.mjs at 629 lines through a fixture string, and check.mjs itself at 678 through the prose comment that documents the marker. Neither exemption was ever decided by anyone. So both were split by responsibility, and each gate family now sits beside the tests that prove it. check-code.mjs holds the gates that read source code (secrets, defer-markers, zombie-code, code-file-cap) with the shared helper; check-trace.mjs holds the chain BRIEF to spec to ticket to commit; check.mjs keeps the runner, the document and rulebook gates, and its published surface, so the commit-msg hook and every importer are untouched. The registry is still composed into one object, which is what keeps the gate count derived: the explainer still reads 21. Red first, per comment style: the C, Python, block, block-continuation and markup forms each got a failing test before the helper existed, beside the cases that keep it honest, a marker inside a string is not a comment and a mention of the length marker is not a grant. The helper is also pinned directly, because the gates reach it only through a fixture repo. 154 self-tests across three suites, up from 144, all green, and CI and the evidence drill run all three. Traces-to: SC-5, SC-12
Tradebaas
added a commit
that referenced
this pull request
Aug 1, 2026
…irst one Cutting v0.2.0 by following the release runbook literally is what found this. Smoke check 1 told the releaser to run node checks/check.test.mjs in the unpacked tarball and to expect "the self-tests green". That sentence was true when it was written, at v0.1.0: one file held the whole gate suite. PR #61 split it in three, and nothing came back to the runbook, so the command proves 78 of the 236 tests the gate job runs while the sentence still claims all of them. A check that overstates its own coverage is worse than no check, because it is the one a releaser trusts at the moment they stop looking. The fix is not to type the two missing filenames. That would restore the same sentence with the same clock running: the next split breaks it again, silently, and the next person to notice will also be someone who happened not to follow the instruction. So the step names no suite file at all now. It points at the gate job in .github/workflows/ci.yml, which is the list CI already keeps and the only copy that cannot drift from what actually runs, and it says out loud why the list is not repeated here, so a later editor does not helpfully inline it again. The README carried the same stale shape one sentence long, naming that single file as the way the checks test themselves. Same cause, same change: it now says every gate has to prove it fails on a violation and points at the workflow that runs those suites. Verified by following the new step in the published v0.2.0 tarball: it resolves to seven suites, 236 tests, all green, against 78 under the old wording. Traces-to: SC-6, SC-9
Tradebaas
added a commit
that referenced
this pull request
Aug 1, 2026
…rmed Nothing in checks/ knows anything about a product's code. The gates next door prove documents, budgets, traces and secrets, and every one of them stays green on a repo whose TypeScript does not compile. The tools that do know are the ecosystem's own, and the skill stack wires them into CI at the moment the stack is chosen: typecheck, lint, tests, build, audit, replacing the commented placeholder stage the workflow ships with. Until that happens there is a window nobody was watching. enforcement.mjs reports CI as armed the moment any workflow file exists, so a project can choose a stack, write its standards file, pass all 21 Groundwork gates, read "CI armed" in its own report, and have not one line of its own code checked by anything. That is the same family as the two defects fixed on 2026-08-01: a gate that is off without anyone deciding it should be. The new gate fires on exactly that window: a standards file that is not the cross-stack floor, plus a workflow that still carries commented-out stack gates. It asserts one mechanical thing, that the placeholders were dealt with, and deliberately not which tools were wired. Naming the blessed commands per ecosystem is an allowance list, and an allowance list is the thing that rots; proving the wired gates actually bite stays where stack section 3 already puts it, at introduce a violation and watch it fail. Three cases stay quiet on purpose and each has a test: a fresh copy that has not picked a stack, GLOBAL.md on its own, and a project whose CI lives on another host, which stack section 3 explicitly allows and enforcement.mjs already reports on. It lands in its own file because adding it to check.mjs took that file to 527 lines against its own 500-line cap. Splitting by responsibility is what PR #61 did for the same reason, so the suite mirrors the source here too: check-stack.mjs and check-stack.test.mjs, composed into the one registry like the code and trace families beside them. The registry is the source of the explainer's gate count, which moves 21 to 22 and was caught by its own gate rather than by me. Traces-to: SC-5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed and why
Intake rows 56 and 62 were one defect with two faces.
code-file-caplooked for its escapemarker anywhere in the file, so any line that merely mentioned
checks:allow-lengthgrantedthe exemption.
defer-markersread a comment only when the line opened with one, sox = 1; // for nowwalked past every entry incommentBanswhile the same words on their ownline failed. Both now ask one shared helper what, on this line, is actually a comment: it steps
over quoted text before looking for an opener, and takes the first real opener wherever it sits.
Its two limits are named at the site rather than left to be discovered.
The escape marker also had to become a declaration instead of a mention, which is what row 56
named as the defect: only a comment that opens with
checks:allow-lengthgrants the exemption.That is where this ticket got more expensive than the triage knew. Arming the gate honestly put
two files over the 500-line cap, not one:
check.test.mjsat 629 lines, exempt through afixture string, and
check.mjsitself at 678, exempt through the prose comment that documentsthe marker. Neither exemption was ever decided by anyone, and
checks/may not be excluded fromthe cap, so both files were split by responsibility in the same change:
check-code.mjs- the gates that read source code (secrets,defer-markers,zombie-code,code-file-cap), with the shared helper that two of them need.check-trace.mjs- the chain BRIEF to spec to ticket to commit, includingcheckCommitMessage.check.mjs- the runner, the document and rulebook gates, and the published surface. Thecommit-msg hook and every importer are untouched, and the registry is still composed into one
object, which is what keeps the gate count derived: the explainer still reads 21.
CI and into the evidence drill's suite list.
Verified by
each got a failing test before the helper existed: 5 reds on
defer-markersand 2 oncode-file-cap, then green after the helper. The cases that keep it honest went in besidethem: a marker inside a string is not a comment, and a mention of the length marker is not a
grant. The helper is also pinned directly, since the gates reach it only through a fixture repo.
144, all green.
progress,links,cockpit-pathandcockpitgreen as well.node checks/check.mjsreported the two oversized files, andpasses now that both are split. Every file in
checks/is under the cap without an exemption.node checks/drill.mjs: a fresh copy reaches a governedfirst commit in 2.2s, now running 7 suites instead of 5.
drill.test.mjs: 11 of 11.check.mjsand the commit-message gateboth ran on this change.
multi-line string in a language this repo holds no code in (a Python docstring, a heredoc)
would read as a comment. That is the same exposure the previous anchored regex had, and no
code in this repo exercises it.