Skip to content

fix(gate): closing-keyword sweep never size-skips a REGISTERED parser - #18449

Merged
os-elon-musk merged 1 commit into
mainfrom
claude/issue-18435-parity-gate-size-skip-registered-parsers
Sep 16, 2026
Merged

os-elon-musk merged 1 commit into
mainfrom
claude/issue-18435-parity-gate-size-skip-registered-parsers

Conversation

@os-elon-musk

Copy link
Copy Markdown
Collaborator

Fixes #18435

scripts/check-closing-keyword-parity.mjs sweeps the whole tracked tree hunting for a
fourth, unregistered spelling of GitHub's closing-keyword grammar. Its sweep skipped any
file over 2 MiB before reading it — and the registry check downstream had no way to
tell "this file stopped carrying the grammar" from "the sweep never opened this file", so
it reported the second as the first.

Before / after, on the real blocker

scripts/pm/check-half-states.mjs is a PARSERS-registered file and the repo's largest
script. PR #18430 landed while this branch was in flight, which puts it at 2,095,499 B —
1,653 bytes under the 2,097,152 B cutoff
. Appending 40 lines of plain comment (2,000 B,
the size of a paragraph of prose, no grammar touched) takes it to 2,097,499 B. Both readings
below are on this branch's base b6fe311a, same padded file, only the gate swapped.

Before — the gate exactly as it ships on origin/main (exit 1):

check-closing-keyword-parity: 1 failure(s)

  • [sweep] scripts/pm/check-half-states.mjs is registered as carrying the closing-keyword
    grammar but no longer matches the sweep. Stale registry entries verify nothing --
    remove it, or fix the signature.

The prescribed remedy — remove it — deletes a correct registry row and silently retires a
live parity check on H7. The actual cause, the file's size, is named nowhere in the text.

After — this PR's gate, same 2,097,499 B file (exit 0):

check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured
separators; sweep found 5 file(s) carrying the grammar across 8733 tracked file(s), all
registered).

1 UNREGISTERED file(s) were not read, so the hunt for a fourth parser did not cover them
(every registered file WAS read -- the cutoff does not apply to those):
  • packages/spec/CHANGELOG.md -- 6077484 bytes exceeds the sweep's 2097152-byte cutoff
    for UNREGISTERED files

The padding was a transient measurement: restored byte-for-byte afterwards, proven by
git hash-object back to the HEAD blob and an empty git diff HEAD.
scripts/pm/check-half-states.mjs carries no change in this diff — three other PRs own
its regions.

The rule

Registration is the statement this file carries the grammar. A sweep that skips a
registered file does not refute that statement; it simply fails to look. So:

  • A registered file is read unconditionally. REGISTERED is hoisted out of judge()
    to above sweep(), and the cutoff now reads size > SIZE_CUTOFF && !REGISTERED.has(f).
    A few megabytes of readFileSync is by a wide margin the cheaper half of that trade.
  • An unregistered file over the cutoff is still skipped — the sweep's job on those is a
    hunt, and a multi-megabyte blob is overwhelmingly a fixture or a generated corpus — but
    the skip is now RECORDED.
    sweep() returns skipped (file to cause, in words) beside
    hits.
  • The registry check reads that cause. A registered file missing from hits and
    present in skipped now fails with the cause named and the opposite prescription: the
    entry is NOT stale, do not remove it. A registered file missing from hits and NOT
    skipped still gets the original stale-registry text, unchanged.
  • The green line says what it did not cover. A sweep reporting "across 8733 tracked
    file(s)" while having silently read fewer overstates itself. That hole was already there
    and nobody could see it: packages/spec/CHANGELOG.md at 6,077,484 B has been outside this
    sweep the whole time. It is a changelog, not a fourth parser — but the gate now says so
    out loud instead of implying full coverage.

skipped is part of the sweep result rather than an optional extra, and the one hand-built
sweep object in the self-test was updated to carry it. A default in judge() would have
silently restored exactly the misdirecting message this PR removes.

New self-test cases, each shown red under a control

--self-test goes from 30 assertions to 40.

X14 / X15 — the cutoff, on a real tree. extraFiles hands the sweep text directly and
therefore never reaches the statSync branch at all, so a case built that way would pin
nothing about the cutoff. These build a real git-tracked temp tree with two real oversized
files (3,690,082 B each), one registered and one not, and grade both arms: the registered
one is read, the unregistered one is skipped and the skip is recorded with its cause.

Control — restore the unconditional skip (size > SIZE_CUTOFF alone), self-test exit 1:

  • X14: an oversized REGISTERED parser is still SCANNED and found (3690082 bytes), got hits: nothing
  • X14: and is not recorded as skipped, got: 3690082 bytes exceeds the sweep's 2097152-byte cutoff for UNREGISTERED files

X13 — unread is not stale. Control: delete the new diagnostic branch, self-test exit 1,
and the red prints the exact defect text this PR exists to remove:

  • X13: a registered file the sweep never read is reported with its CAUSE, got:
    scripts/pm/check-half-states.mjs is registered as carrying the closing-keyword grammar
    but no longer matches the sweep. Stale registry entries verify nothing -- remove it,
    or fix the signature.
  • X13: and never with the stale-registry remedy, which would delete a correct registry row

X7 keeps reading as stale. The genuinely-stale case is unchanged and now also asserts
the negative: it must NOT be reported as unread. An arm that swallowed the other would
retire the check X7 exists to be.

Each control arm was applied to the committed file, proven on disk by grep counts before the
run, restored with git checkout HEAD -- PATH, and the restore proven by blob hash and an
empty git diff HEAD. The restored tree is green again (exit 0, 40 assertions).

Gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack from this
worktree derived 30 commands for this diff; every one was run in the foreground. Results are
in the report comment on the card.

Changeset

None — skip-changeset. AGENTS.md Post-Task Checklist step 3: that label 「is for a diff
that publishes nothing from any released package」. This diff is one repo-root script. The
root package is private: true, no package's files[] ships repo-root scripts/, and no
package imports this file — git grep finds it referenced only from .github/workflows/,
docs/audits/, and comments in three sibling scripts.

Acceptance notes

  • Scope. scripts/check-closing-keyword-parity.mjs only. The fix is in the gate, not in
    the file that trips it — scripts/pm/check-half-states.mjs carries no change here.
  • The cutoff is kept, not raised. The alternative of raising or deleting it would have
    made the whole-tree read unconditionally expensive and would not have fixed the diagnostic,
    which was the part that prescribed the wrong remedy.
  • Noted, not filed: packages/spec/CHANGELOG.md (6,077,484 B) has been outside this
    sweep since the cutoff was introduced. It is release-compiled prose, not a parser, so
    there is nothing to register — the new green-line disclosure is the whole remedy. No other
    gate was audited for a similar silent size skip; the card records that census as not run.
  • Not measured: whether any other whole-tree gate carries the same silent skip.

Generated by Claude Code

The sweep skipped any tracked file over 2 MiB before reading it. When a file
in the PARSERS/NON_PARSERS registry crossed that line the registry check saw
it missing from `hits` and reported it as a stale registry row -- "Stale
registry entries verify nothing -- remove it, or fix the signature." -- whose
prescribed remedy deletes a correct row and silently retires a live parity
check. The real cause, the file's size, was named nowhere.

Registration is the statement "this file carries the grammar", so the sweep
owes it a read whatever `statSync` says: `REGISTERED` is hoisted above the
sweep and the cutoff now applies only to unregistered files. `sweep()` also
returns `skipped` (file -> cause in words), which does two things: the
registry check can distinguish "stopped carrying the grammar" (stale, remove
it) from "never opened" (not stale, do not remove it), and the green line
names the unregistered files the fourth-parser hunt did not cover instead of
implying it read everything.

`skipped` is part of the sweep result rather than an optional extra -- a
default in `judge()` would silently restore the misdirecting message.

Self-test: X13 pins the unread-vs-stale split from both sides (X7 keeps
reading as stale); X14/X15 build a real git-tracked tree with two oversized
files, one registered and one not, because `extraFiles` hands text in
directly and never reaches the `statSync` branch a case must exercise.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HPfcjvF23QBoBj7P47DDxs
@os-elon-musk os-elon-musk added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 16, 2026 — with Claude

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 57e88585bfac69d0b823347880120891958c9c05

Skills seat, session_01HPfcjvF23QBoBj7P47DDxs, 2026-09-16T11:46Z — in-seat review of the dispatched dev's PR for #18435 (claim 5696362866, Clause-②: no; the ACCEPT on the card records the checklist).

① Derived judgments

  • Accept / reject set of a repo gate, not a runtime contract: the gate now READS every registered parser regardless of size (strictly more is verified), keeps skipping unregistered files over 2 MiB (unchanged cost), and fails a never-read registered file with its cause instead of the stale-registry remedy. No packages/spec path, no error code, no published export. Direction judged correct: the gate's declared purpose (a registered file carries the grammar and must be checked) is now enforced rather than silently skipped; nothing is loosened — the stale-registry failure still fires for a small registered file with no grammar (X7).
  • Public surface: none (scripts/ ships in no package). Clause-②: no consistent (no widening tell; --pair 18449 exit 0).

② Semver level

None owed: nothing publishes; skip-changeset is AGENTS.md's own arm. Consistent.

③ Boundary flags

  • Dev open_questions: empty. Deviations (rebase, discarded first reading, foreground-cap batch, trailer amend) answered in the ACCEPT — accepted.
  • Reproduction by the seat: padded registered parser → old gate exit 1 with the delete-the-row text; new gate exit 0 (real tree, real file sizes).
  • Self-test at this head: 40 assertions, exit 0 (seat's run with linked dependencies).

Implemented-by: claude/issue-18435-parity-gate-size-skip-registered-parsers
Reviewed-by: session_01HPfcjvF23QBoBj7P47DDxs

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing provenance — skills seat, session_01HPfcjvF23QBoBj7P47DDxs, 2026-09-16T11:54Z. In-seat contract review PASS 5696910640 on head 57e88585 (Served-tier: CONTRACT_REVIEW_TIER); ACCEPT 5696910289 on #18435. Non-governed (scripts/check-closing-keyword-parity.mjs is not on the governed register) ⇒ in-seat landing. Pre-checks: ① that record ✓ · ② check-clause2-carriers.mjs --pair 18449 exit 0 ✓ · ③ every check-run on 57e88585 success or a rostered skip (check-expected-skips.mjs VERDICT OK, 11 skips all in the roster) ✓. Landed by the seat through the CCR routes at 2026-09-16T11:54Z: POST …/ccr/ready_for_review (200; ready_for_review 11:54:04Z) then PUT …/ccr/auto_merge {"merge_method":"SQUASH"} (200; added_to_merge_queue 11:54:07Z on the timeline, present at first read). Tracked to MERGED with the two readings. On MERGED: PR #18436 (#18394, approved) is re-armed on its head a07d739f and PR #18437 (#18385, accepted) is enqueued — both were held only by this gate's size skip; their merge groups will then carry scripts/pm/check-half-states.mjs above 2 MiB and be READ rather than skipped. Card #18435 stays pm:dispatched + assignee until Fixes closes it.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants