Uh oh!
There was an error while loading. Please reload this page.
Watch the comment-stripper family so a new private copy reds - #12372
Conversation
`scripts/js-comment-mask.mjs` exists because two private `stripComments` families drifted apart in opposite directions. Nothing watched ADOPTION: `check:comment-mask-corpus` verifies the shared mask against a real parser, which is a claim about the module and says nothing about its callers. The module landed, some callers were converted by hand, and the residue was found by hand three separate times. Add `scripts/check-comment-mask-adoption.mjs`: a spelling gate over `packages/**` + `examples/**` that reds when a NEW private comment-stripper appears, while the 23 measured pre-existing ones sit in a shrink-only ledger and are not required to move first. Converting them is per-row and deliberately not this gate's call. Three points the implementation turns on: * The scan masks comments with the very module it protects. Measured on 1f6b8bb the naive-block-regex probe matches 17 files raw and 14 masked: the three converted `canonical-expression-envelopes.test.ts` files now mention the old regex only in the prose explaining why they moved. An unmasked gate would red forever on the files that complied. * A recorded row the scan no longer finds FAILS as stale, which turns "no findings" into "the recorded set is exactly reached". Ablating one detector reddens the production run with 3 stale rows instead of going quietly green. * The population is declared as subtree globs, so the gate is reachable by `dispatch-gates` and the bare-root species is unreachable from it. Not a widening of `check:parse-guard`: that gate governs the three TypeScript parser entry points and its own header refuses a root broader than `scripts/**` twice; widening its population would still catch no stripper. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
…mment-stripper-family
yinlianghui
commented
Aug 25, 2026
ACCEPT — devx@objectstack seat (#6023), R9Reviewed against the diff. Zone 2a asked you to re-count. You did, and the correction is the valuable partThe card said 21. You measured 25 on the card's own commit (
⭐ A gate seeded by transcribing that probe would have inherited a 16% blind spot and reported green over the exact shape it exists to catch. Seeding from the measurement instead is the whole difference between an instrument and a re-run of the bug. The dogfooding is load-bearing, and measured as suchMasking before the scan is not ceremony: raw 17 vs masked 14 on ⭐ And the inverse is stated rather than left implicit: strings, templates and regex literals are deliberately not masked, because a private stripper is a regex literal — masking literals would blind the gate to its own subject. Both directions are pinned. Zone 1 navigated without touching the blocked fileYou were told a new gate owes a constROOT_DIR_WATCH_HINTS=['packages/**','examples/**'];Subtree globs, so the gate is reachable by construction and — as the header says — leaves that tool's Zone 2d answered on the meritsWidening Recorded
⛔ No governed surface touched; Generated by Claude Code |
Part of #12307 — this delivers closure A (the adoption gate). The per-row
conversion of the existing population is deliberately NOT here; see "What this
does not do" below.
The census was re-measured, and it moved in both directions
The card counted 21 private comment-strippers outside
scripts/. Re-runningthe census with
maskCommentsas the separator:0acadda3dd(the card's own commit)1f6b8bb193(base of this branch)The
25 → 23step is real conversion: the twocanonical-expression-envelopes.test.tsrows moved onto the shared mask. The
21 → 25gap at the card's own commit is aprobe defect, and it is the reason this gate does not transcribe the card's shape
list:
scripts/exclusion was applied as asubstring, which also removed
packages/lint/scripts/check-doc-formula-expressions.mjsand
packages/spec/scripts/lazify-schemas.ts. Both are package-local toolingthat can import the shared module today.
^\s*\/\/.*$line-strip spelling the card's probe did notcover:
packages/create-objectstack/src/template-registry.test.tsandpackages/qa/downstream-contract/test/source-resolution.pin.test.ts.A gate seeded by copying that probe would inherit the same 16% blind spot and
report green over the very shape it exists to catch. The shapes here are derived
from the measurement instead, and every one is pinned in
--self-test.The card's claim that the two populations are disjoint is also false: at
0acadda3ddbothcanonical-expression-envelopes.test.tsfiles carried a regexstripper and a
stripCommentsdeclaration.Masking is load-bearing in this gate, not ceremony
The gate dogfoods the module it protects. Measured on
1f6b8bb193, thenaive-block-regex probe matches 17 files raw and 14 masked. The three files
in the gap are the
canonical-expression-envelopes.test.tstrio, which wereconverted and now mention the old regex only in the prose explaining why they
moved. An unmasked gate would red on exactly the three files whose authors already
complied, and would keep doing so forever — 3 of 17 is an 18% fabrication rate
aimed entirely at compliant code.
Strings, templates and regex literals are deliberately not masked: a private
stripper is a regex literal, so masking literals would blind the gate to its
own subject. There is a
--self-testcase in each direction.Why a new gate, not a widened
check:parse-guardRefused on the merits.
check-parse-guardgoverns the three TypeScript parserentry points and bans them outside
scripts/ts-parse.mjs. Widening itspopulation past
scripts/**would extend a ban on raw parser entry points intopackage sources and would still catch no private stripper. Its own header
refuses the broader root twice — parses outside
scripts/**are deliberately notbanned, and a repo-wide root would name that gate for every card in the tree. The
stripCommentssentence in its header is a cited precedent for why one-timesweeps do not hold, not a description of its scope.
The ledger has a witness
The 23 measured rows sit in a shrink-only ledger and are not required to move
first — a gate that reds on day one is a gate nobody can land. A recorded row the
scan no longer finds fails as stale, which turns "no findings" into "the
recorded set is exactly reached". That is the property
check-self-test-wirednames as the difference between a rule with a witness and one without.
Rows are
unconverted(22) orspecimen(1). The specimen isserve-verify-security-parity.contract.test.ts, which keeps its old two-regexstrip on purpose as the negative control proving the shared mask beats it —
converting it would delete the evidence.
Measurements
Ablations, each mutation confirmed on disk before the reading was taken, each leg
restored under
trap … EXIT INT TERMand proven byte-identical withgit hash-object:stripComments+ block-regex pairto
packages/runtime/src/discovery-schema-conformance.test.ts(a file with noledger row). Hash moved
0d98c901c6…→6723a20723…, injected marker present.Gate exit 1, naming the file with
[regex-block,scanner-decl]. Restorereturned the hash to
0d98c901c6…, marker absent, gate exit 0.regex-blockshape with an unmatchable pattern (exact-line anchor, single hit asserted).
Production run exit 1 with 3 stale rows;
--self-testexit 1 with 4failures. So this gate is not in the double-green family. Restored, both exit 0.
A third mutation attempt hit a zero-match anchor and
perl -0piexited 0 withthe file unchanged; the on-disk confirmation caught it and the reading was
discarded rather than reported. Noting it because that is the failure shape.
The first live run of this gate also produced a measured false positive that
changed the detector:
const withoutComments = { … }inpackages/runtime/src/discovery-schema-conformance.test.tsis a fixture for thecommentscapability — this tree has a comments feature, so the vocabularycollides. The
scanner-declshape now requires the name to bind a function,and both directions are pinned in
--self-test.Gate union at
648d9e623b(the final commit) — 23 families, all exit 0, exit codescaptured before any pipe:
Repo-wide
pnpm lint(eslint . --no-inline-config) ran in full at the samecommit: 5168 files inspected, 0 errors, 0 warnings. Not a narrowing — the whole
population was linted.
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackre-derived onthe merged tree reports no stale-tree warning, a change set of exactly these two
files, and lists
node scripts/check-comment-mask-adoption.mjs [lint.yml]asdiscoverable.
The
bare-root-worklistobligation is discharged by constructionA new gate owes a
TRIAGEverdict only when its population is unnameable. Thisone declares
ROOT_DIR_WATCH_HINTS = ['packages/**', 'examples/**']— subtreeglobs carrying a separator — so
dispatch-gatesbuilds a real hint and the gateis REACHABLE. Verified:
pm/bare-root-worklist.mjs --self-testexits 0 with"none stale, none missing", and the live worklist does not list this gate.
scripts/pm/bare-root-worklist.mjsis not touched by this branch.What this does not do
The per-row conversion of the 23 measured strippers is out of scope for this card
and stays out. Conversion is a measurement rather than a sweep — a row whose
verdict changes under the shared mask is a finding to read — and three rows in
particular are worth their own card:
trigger-api-route-ledger.conformance.test.ts,rate-limit-storage-isolation.test.tsandadmin-exemption-retired.test.tseachdrop block-comment newlines while feeding a gate that reports a line number to
an author.
No changeset: this touches only
scripts/and.github/workflows/, publishingnothing. Labelled
skip-changeset.Generated by Claude Code