Skip to content

fix(checks): the gates read a comment the way code writes one - #61

Merged
Tradebaas merged 1 commit into
mainfrom
fix/comment-aware-gates
Aug 1, 2026
Merged

fix(checks): the gates read a comment the way code writes one#61
Tradebaas merged 1 commit into
mainfrom
fix/comment-aware-gates

Conversation

@Tradebaas

Copy link
Copy Markdown
Owner

What changed and why

Intake rows 56 and 62 were one defect with two faces. 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. 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-length grants 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.mjs at 629 lines, exempt through a
fixture string, and check.mjs itself at 678, exempt through the prose comment that documents
the marker. Neither exemption was ever decided by anyone, and checks/ may not be excluded from
the 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, including checkCommitMessage.
  • check.mjs - the runner, the document and rulebook gates, and the published surface. The
    commit-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.
  • The suites mirror the source split, sharing one fixture harness. Both new suites are wired into
    CI and into the evidence drill's suite list.

Verified by

  • Red first, per comment style. The C, Python, block, block-continuation and markup forms
    each got a failing test before the helper existed: 5 reds on defer-markers and 2 on
    code-file-cap, then green after the helper. The cases that keep it honest went in beside
    them: 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.
  • 154 self-tests across the three suites (73 runner and document, 34 code, 47 trace), up from
    144, all green. progress, links, cockpit-path and cockpit green as well.
  • The armed gate on this repo. node checks/check.mjs reported the two oversized files, and
    passes now that both are split. Every file in checks/ is under the cap without an exemption.
  • The evidence drill, end to end. node checks/drill.mjs: a fresh copy reaches a governed
    first commit in 2.2s, now running 7 suites instead of 5. drill.test.mjs: 11 of 11.
  • The commit itself went through the real hooks, so check.mjs and the commit-message gate
    both ran on this change.
  • Not verified here: the helper is line-scoped by design, so a comment marker inside a
    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.

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
Tradebaas merged commit 6014041 into main Aug 1, 2026
6 checks passed
@Tradebaas
Tradebaas deleted the fix/comment-aware-gates branch August 1, 2026 11:47
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant