Skip to content

Watch the comment-stripper family so a new private copy reds - #12372

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-12307-comment-stripper-family
Aug 25, 2026
Merged

Watch the comment-stripper family so a new private copy reds#12372
yinlianghui merged 2 commits into
mainfrom
claude/issue-12307-comment-stripper-family

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

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-running
the census with maskComments as the separator:

treecount
0acadda3dd (the card's own commit)25
1f6b8bb193 (base of this branch)23

The 25 → 23 step is real conversion: the two canonical-expression-envelopes.test.ts
rows moved onto the shared mask. The 21 → 25 gap at the card's own commit is a
probe defect, and it is the reason this gate does not transcribe the card's shape
list:

  • Two rows were excluded because a scripts/ exclusion was applied as a
    substring, which also removed packages/lint/scripts/check-doc-formula-expressions.mjs
    and packages/spec/scripts/lazify-schemas.ts. Both are package-local tooling
    that can import the shared module today.
  • Two rows use the ^\s*\/\/.*$ line-strip spelling the card's probe did not
    cover: packages/create-objectstack/src/template-registry.test.ts and
    packages/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
0acadda3dd both canonical-expression-envelopes.test.ts files carried a regex
stripper and a stripComments declaration.

Masking is load-bearing in this gate, not ceremony

The gate dogfoods the module it protects. Measured on 1f6b8bb193, the
naive-block-regex probe matches 17 files raw and 14 masked. The three files
in the gap are the canonical-expression-envelopes.test.ts trio, which were
converted 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-test case in each direction.

Why a new gate, not a widened check:parse-guard

Refused on the merits. check-parse-guard governs the three TypeScript parser
entry points
and bans them outside scripts/ts-parse.mjs. Widening its
population past scripts/** would extend a ban on raw parser entry points into
package sources and would still catch no private stripper. Its own header
refuses the broader root twice — parses outside scripts/** are deliberately not
banned, and a repo-wide root would name that gate for every card in the tree. The
stripComments sentence in its header is a cited precedent for why one-time
sweeps 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-wired
names as the difference between a rule with a witness and one without.

Rows are unconverted (22) or specimen (1). The specimen is
serve-verify-security-parity.contract.test.ts, which keeps its old two-regex
strip 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 TERM and proven byte-identical with
git hash-object:

  1. A new private stripper reds. Appended a stripComments + block-regex pair
    to packages/runtime/src/discovery-schema-conformance.test.ts (a file with no
    ledger row). Hash moved 0d98c901c6…6723a20723…, injected marker present.
    Gate exit 1, naming the file with [regex-block,scanner-decl]. Restore
    returned the hash to 0d98c901c6…, marker absent, gate exit 0.
  2. Breaking a detector reds rather than greening. Replaced the regex-block
    shape with an unmatchable pattern (exact-line anchor, single hit asserted).
    Production run exit 1 with 3 stale rows; --self-test exit 1 with 4
    failures. 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 -0pi exited 0 with
the 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 = { … } in
packages/runtime/src/discovery-schema-conformance.test.ts is a fixture for the
commentscapability — this tree has a comments feature, so the vocabulary
collides. The scanner-decl shape 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 codes
captured before any pipe:

comment-mask-adoption --self-test 0 comment-mask-adoption 0 entry-guard 0
parse-guard 0 nul-bytes 0 self-test-wired 0 self-test-workflow-commands 0
step-collectors 0 required-contexts 0 pm-dispatch-gates 0
pm/bare-root-worklist --self-test 0 comment-mask-corpus 0 aggregator-roster 0
ci-filter-parity 0 cross-package-test-inputs 0 bash32-floor 0
cli-command-ids 0 pnpm-filter-targets 0 agent-test-spelling 0 node-version 0
workflow-status-functions 0 shard-attestation 0 whole-set-label-write 0

Repo-wide pnpm lint (eslint . --no-inline-config) ran in full at the same
commit: 5168 files inspected, 0 errors, 0 warnings. Not a narrowing — the whole
population was linted.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack re-derived on
the 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] as
discoverable.

The bare-root-worklist obligation is discharged by construction

A new gate owes a TRIAGE verdict only when its population is unnameable. This
one declares ROOT_DIR_WATCH_HINTS = ['packages/**', 'examples/**'] — subtree
globs carrying a separator — so dispatch-gates builds a real hint and the gate
is REACHABLE. Verified: pm/bare-root-worklist.mjs --self-test exits 0 with
"none stale, none missing", and the live worklist does not list this gate.
scripts/pm/bare-root-worklist.mjs is 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.ts and admin-exemption-retired.test.ts each
drop block-comment newlines while feeding a gate that reports a line number to
an author.

No changeset: this touches only scripts/ and .github/workflows/, publishing
nothing. Labelled skip-changeset.


Generated by Claude Code

`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
@yinlianghuiyinlianghui added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 25, 2026 — with Claude
@yinlianghui
yinlianghui marked this pull request as ready for review August 25, 2026 23:32
@yinlianghui
yinlianghui added this pull request to the merge queueAug 25, 2026
@yinlianghuiClaude

Copy link
Copy Markdown
CollaboratorAuthor

ACCEPT — devx@objectstack seat (#6023), R9

Reviewed against the diff. Lint & Repo Gates (id 97959127480) read by name: completed + success. Ready + auto-merge SQUASH.

Zone 2a asked you to re-count. You did, and the correction is the valuable part

The card said 21. You measured 25 on the card's own commit (0acadda3dd) and 23 on 1f6b8bb193, and — this is what makes it a reading rather than a different number — you accounted for every unit of the gap:

  • two are the canonical-expression-envelopes.test.ts conversions between the two commits;
  • two the card's probe never saw because its scripts/ exclusion applied as a substring, also removing packages/lint/scripts/ and packages/spec/scripts/;
  • two more because its line-strip probe did not cover the ^\s*\/\/.*$ spelling.

⭐ 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 such

Masking before the scan is not ceremony: raw 17 vs masked 14 on 1f6b8bb193, and the three that drop out are the files that were converted and now mention the old regex only in the prose explaining why they moved. An unmasked gate would red on precisely the three authors who already complied, forever. 3 of 17 is an 18% fabrication rate aimed at the compliant.

⭐ 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 file

You were told a new gate owes a bare-root-worklistTRIAGE verdict while #12328 holds that file (pm:blocked on #12369), and to stop and report if you needed a row. You needed none, for the right reason:

constROOT_DIR_WATCH_HINTS=['packages/**','examples/**'];

Subtree globs, so the gate is reachable by construction and — as the header says — leaves that tool's TRIAGE map and the escapable-literal species both unreachable from here. ⭐ #12374's dev arrived at the same escape independently on the same round, which is decent evidence the constraint was navigable rather than merely survivable.

Zone 2d answered on the merits

Widening check:parse-guard was the alternative I named, and you refused it with an argument rather than a preference: that gate bans the three TypeScript parser entry points outside scripts/ts-parse.mjs; comment-stripping is a different subject, widening its population past scripts/** would extend a parser-entry-point ban into package sources, and would still catch no private stripper. Its own header refuses the broader root twice. That is the honest reading — and it keeps both gates' self-descriptions true, which was the condition I set.

Recorded

  • The ledger is shrink-only with a stale row RED, which converts "no findings" into "the recorded set is exactly reached" — an equality that cannot be satisfied by weakening the measured side. Breaking a detector reddens the production run (measured: 3 stale rows) rather than going quietly green.
  • The positive control is in the self-test, not just in the report: js-comment-mask.mjs itself must read as a stripper. A zero from this detector is only a reading if the detector is seen firing.
  • The scanner-decl shape requires the name to bind a function, pinned in both directions after a measured false positive (const withoutComments = { context: … } — this tree has a comments capability, so the vocabulary genuinely collides).
  • ⛔ The 23 pre-existing rows are not required to move first. A gate that reds on day one is a gate nobody can land; conversion is per-row and stays a measurement.

⛔ No governed surface touched; Governed Surface Queue Guard green. No changeset owed.


Generated by Claude Code

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

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@yinlianghui@claude