Census the self-test name anchor's accidental matches and pin their cost - #14284

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision
Sep 2, 2026
Merged

Census the self-test name anchor's accidental matches and pin their cost#14284
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13941

Triage fixed the dispatch order into the card and it binds: census first, remedy second — the census result SELECTS the remedy, so no shape was chosen before the population was known. This PR reports the census, then ships what the census selected.

1. Premise re-check, on today's origin/main

#14004 (81256734) added ~412 lines to scripts/pm/dispatch-gates.mjs after the card's measurement, so everything below is re-measured rather than recalled. Base of this branch: 812567346.

The card's reading reproduces, and it understates the surface. When the module masks itself, six spans are blanked, not one:

spanbytes blankedwhy
maskSelfTests649name matches the anchor — the card's specimen
selfTestOnlyCallables1,646name matches the anchor — added by #14004, new since the card
bracedBodyEnd823collateral: reachable only from the two above, which the mask treats as self-tests
valueDeclEnd375collateral, same
topLevelDecls721collateral, same
selfTest421,024the genuine entry point — correct

Controls that make this a reading rather than an artifact: extractWatchHints (7,884 non-whitespace chars) and isNonPathNamespace (1,992) survive the mask untouched. The card's named control indexRefusalAccumulators no longer exists on this tree.

"Costs nothing today" still holds, and is now measured rather than asserted: no slash-bearing string literal appears anywhere in the five collision spans (the first one is at L9158, inside the genuine selfTest), and this file's hint set is identical with and without the collision.

2. The census — the ruled first step

209 code-position SELF_TEST_DECL matches over the tracked JS/TS corpus (5,659 files). 187 are the bare selfTest. The remaining 22 carry compound names over 21 distinct spellings — the population the card put a floor of 19/18 on. Classified:

Genuine self-test batteries — 15 (the anchor working)

filename
packages/lint/scripts/check-doc-formula-expressions.mjsspecSelfTest
packages/lint/scripts/check-doc-formula-expressions.mjsfieldRuleSelfTest
scripts/check-comment-mask-corpus.mjsrunSelfTestCases
scripts/check-doc-authoring.mjsselfTestRule3
scripts/check-doc-authoring.mjsselfTestPackagesProse
scripts/check-durability-degradation-log-level.mjsselfTestReadSeams
scripts/check-platform-checklist.mjsselfTestTrapVocabulary
scripts/check-platform-checklist.mjsselfTestProvisioningUse
scripts/check-platform-checklist.mjsselfTestUnreferencedRecipes
scripts/check-platform-checklist.mjsselfTestMetaCallSpelling
scripts/check-platform-checklist.mjsselfTestSourceLineCitations
scripts/check-regen-pending.mjsfixtureSelfTest
scripts/check-regen-pending.mjsprePushIsArmedSelfTest
scripts/check-regen-pending.mjsdecisionTableSelfTest
scripts/check-turbo-task-graph.mjsrunSelfTest

Accidental — production code the anchor caught by its name — 7, over 4 files

filenamerolespells a path today?
scripts/check-self-test-wired.mjscarriesSelfTestexported predicate: does this file carry a self-test?no
scripts/check-self-test-workflow-commands.mjsrunSelfTestexported: spawns another script's --self-testno
scripts/check-step-collectors.mjsselfTestTargetsexported extractor over a workflow run: bodyno
scripts/check-step-collectors.mjsselfTestDiscoveriesexported extractor, sameno
scripts/measure-self-test-floor.mjsselfTestDefsexported extractor over a module sourceno
scripts/pm/dispatch-gates.mjsselfTestOnlyCallablesinternal reachability helperno
scripts/pm/dispatch-gates.mjsmaskSelfTeststhe masker itself — the card's specimenno

The class has SEVEN members, not one. They cluster because every one of them is a gate that reasons about self-tests: a tool that finds, spawns, counts or masks other scripts' self-tests names its functions after the thing it handles, and a name-based anchor cannot tell "runs a self-test" from "is one".

Cost, measured per declaration

Each of the 22 was neutralised one at a time (the self-test token replaced inside the identifier, word-anchored) and the file's hints re-extracted, refusing unless the anchored-declaration count dropped by exactly one:

  • All 7 accidental members: zero hint delta. The card's "costs nothing today" holds tree-wide, over a surface 7x the one it measured.
  • 2 genuine members do move hints: fixtureSelfTest drops packages/spec/spec-changes.json, prePushIsArmedSelfTest drops .githooks/pre-push — both fixture paths in scripts/check-regen-pending.mjs, both correctly refused by the mask.

That asymmetry is load-bearing: "no compound-name match may contribute a hint" is false as a blanket invariant. The invariant holds only over the accidental half, and only a classification can name that half.

3. Remedy — and why both of triage's candidates are refuted by the census

Triage's decision table sent a MULTIPLE-member census to "narrowing SELF_TEST_DECL, or a self-scan special case — pick with the measurement in hand". The measurement refutes both:

  • Narrowing the name pattern is impossible, not merely risky.runSelfTest is a genuine entry point in scripts/check-turbo-task-graph.mjs (reached only from that file's --self-test guard) and accidental in scripts/check-self-test-workflow-commands.mjs (exported, called from the gate body at line 264). One spelling, both classes. No predicate over the name separates them, so any narrowing that excludes the accidental one also unmasks a real self-test battery and readmits its fixture paths as hints — trading a silence that costs nothing for the fabricated-lead family this masker exists to refuse.
  • A self-scan special case fixes 2 rows of 7. The other five live in three other files, so the card's own "fixes one instance and leaves the class" objection applies to it too, one file wider — plus the hazard the card names, that the tool's self-scan would differ from every other scan.

What ships is neither shape. The anchor is unchanged, the mask still blanks all 22, no path is special-cased, and nothing is renamed. What changes is that the seven accidental members' cost is measured on every run instead of asserted in a docblock. Silence was the defect; the remedy is noise on the day it stops being free.

This is deliberately remedy-neutral — it forecloses none of the three shapes if triage still wants one. The fork is reported on the card.

4. What this PR contains

One file, scripts/pm/dispatch-gates.mjs (+277 / -7):

  • COMPOUND_ANCHOR_LEDGER — the census above as data: 22 rows of file, name, accidental. Keyed by file plus name because runSelfTest proves a name-keyed ledger cannot hold both verdicts at once.
  • compoundAnchorDecls(source) — every compound-name anchor match at code positions only (comments masked first, string literals excluded via scanSource, so a fixture that builds a module source cannot enter the census).
  • withoutAnchor(source, name) — the counterfactual rename, word-anchored so runSelfTest does not rewrite runSelfTestCases.
  • Three self-test cases, live over the tracked tree (~3.3s):
    1. every compound-name match is classified — an unlisted or vanished one reds;
    2. masking an accidental row still costs no hint — reds and names the hidden hint;
    3. control: at least one genuine row is seen to lose a fixture hint, so a counterfactual that silently failed to rename cannot report "zero everywhere" and read as a pass.
  • Two identity pins that maskSelfTests and selfTestOnlyCallables are in the anchor's own population, so a rename that "fixes" either has to move the ledger row rather than the problem.
  • The stale claim in SELF_TEST_DECL's docblock corrected — it said the collision was one declaration; it is seven.

5. Verification

All at head 169cfb977.

pnpm check:pm-dispatch-gates — the module's own battery, run under the shared verify lock: dispatch-gates self-test: 1179 cases pass., VERDICT command-exit 0 (held 676s). The five new cases are in it, all green.

Ablation, both directions — mutation proved on disk before each reading, restored against HEAD and proved byte-exact by blob hash afterwards:

  • Injected a path literal into maskSelfTests — assertion 2 goes red: scripts/pm/dispatch-gates.mjs::maskSelfTests hides ["packages/ablation-only/src"]. This is the card's exact scenario, made loud.
  • Appended function ablationSelfTestNewcomer() to scripts/check-turbo-task-graph.mjs — assertion 1 goes red: unlisted: scripts/check-turbo-task-graph.mjs::ablationSelfTestNewcomer.
  • Restores verified: git hash-object equals the HEAD blob in both cases, zero residue, git status --porcelain empty.

Derived families (node scripts/pm/dispatch-gates.mjs, 15 matched) all green — exit codes captured before any pipe, verdict lines quoted in the terminal report. check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET locally (it grades a saved turbo log CI tees); recorded as NOT MEASURED, not as a red.

Changeset route: skip-changeset. The diff is one file under scripts/pm/** — PM tooling, publishing nothing from any package — which is the label's closed list. Applied on open per the label's read-back discipline.


Generated by Claude Code

…n their cost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Census the self-test name anchor's accidental matches and pin their cost - #14284

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision
Sep 2, 2026
Merged

Census the self-test name anchor's accidental matches and pin their cost#14284
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13941

Triage fixed the dispatch order into the card and it binds: census first, remedy second — the census result SELECTS the remedy, so no shape was chosen before the population was known. This PR reports the census, then ships what the census selected.

1. Premise re-check, on today's origin/main

#14004 (81256734) added ~412 lines to scripts/pm/dispatch-gates.mjs after the card's measurement, so everything below is re-measured rather than recalled. Base of this branch: 812567346.

The card's reading reproduces, and it understates the surface. When the module masks itself, six spans are blanked, not one:

spanbytes blankedwhy
maskSelfTests649name matches the anchor — the card's specimen
selfTestOnlyCallables1,646name matches the anchor — added by #14004, new since the card
bracedBodyEnd823collateral: reachable only from the two above, which the mask treats as self-tests
valueDeclEnd375collateral, same
topLevelDecls721collateral, same
selfTest421,024the genuine entry point — correct

Controls that make this a reading rather than an artifact: extractWatchHints (7,884 non-whitespace chars) and isNonPathNamespace (1,992) survive the mask untouched. The card's named control indexRefusalAccumulators no longer exists on this tree.

"Costs nothing today" still holds, and is now measured rather than asserted: no slash-bearing string literal appears anywhere in the five collision spans (the first one is at L9158, inside the genuine selfTest), and this file's hint set is identical with and without the collision.

2. The census — the ruled first step

209 code-position SELF_TEST_DECL matches over the tracked JS/TS corpus (5,659 files). 187 are the bare selfTest. The remaining 22 carry compound names over 21 distinct spellings — the population the card put a floor of 19/18 on. Classified:

Genuine self-test batteries — 15 (the anchor working)

filename
packages/lint/scripts/check-doc-formula-expressions.mjsspecSelfTest
packages/lint/scripts/check-doc-formula-expressions.mjsfieldRuleSelfTest
scripts/check-comment-mask-corpus.mjsrunSelfTestCases
scripts/check-doc-authoring.mjsselfTestRule3
scripts/check-doc-authoring.mjsselfTestPackagesProse
scripts/check-durability-degradation-log-level.mjsselfTestReadSeams
scripts/check-platform-checklist.mjsselfTestTrapVocabulary
scripts/check-platform-checklist.mjsselfTestProvisioningUse
scripts/check-platform-checklist.mjsselfTestUnreferencedRecipes
scripts/check-platform-checklist.mjsselfTestMetaCallSpelling
scripts/check-platform-checklist.mjsselfTestSourceLineCitations
scripts/check-regen-pending.mjsfixtureSelfTest
scripts/check-regen-pending.mjsprePushIsArmedSelfTest
scripts/check-regen-pending.mjsdecisionTableSelfTest
scripts/check-turbo-task-graph.mjsrunSelfTest

Accidental — production code the anchor caught by its name — 7, over 4 files

filenamerolespells a path today?
scripts/check-self-test-wired.mjscarriesSelfTestexported predicate: does this file carry a self-test?no
scripts/check-self-test-workflow-commands.mjsrunSelfTestexported: spawns another script's --self-testno
scripts/check-step-collectors.mjsselfTestTargetsexported extractor over a workflow run: bodyno
scripts/check-step-collectors.mjsselfTestDiscoveriesexported extractor, sameno
scripts/measure-self-test-floor.mjsselfTestDefsexported extractor over a module sourceno
scripts/pm/dispatch-gates.mjsselfTestOnlyCallablesinternal reachability helperno
scripts/pm/dispatch-gates.mjsmaskSelfTeststhe masker itself — the card's specimenno

The class has SEVEN members, not one. They cluster because every one of them is a gate that reasons about self-tests: a tool that finds, spawns, counts or masks other scripts' self-tests names its functions after the thing it handles, and a name-based anchor cannot tell "runs a self-test" from "is one".

Cost, measured per declaration

Each of the 22 was neutralised one at a time (the self-test token replaced inside the identifier, word-anchored) and the file's hints re-extracted, refusing unless the anchored-declaration count dropped by exactly one:

  • All 7 accidental members: zero hint delta. The card's "costs nothing today" holds tree-wide, over a surface 7x the one it measured.
  • 2 genuine members do move hints: fixtureSelfTest drops packages/spec/spec-changes.json, prePushIsArmedSelfTest drops .githooks/pre-push — both fixture paths in scripts/check-regen-pending.mjs, both correctly refused by the mask.

That asymmetry is load-bearing: "no compound-name match may contribute a hint" is false as a blanket invariant. The invariant holds only over the accidental half, and only a classification can name that half.

3. Remedy — and why both of triage's candidates are refuted by the census

Triage's decision table sent a MULTIPLE-member census to "narrowing SELF_TEST_DECL, or a self-scan special case — pick with the measurement in hand". The measurement refutes both:

  • Narrowing the name pattern is impossible, not merely risky.runSelfTest is a genuine entry point in scripts/check-turbo-task-graph.mjs (reached only from that file's --self-test guard) and accidental in scripts/check-self-test-workflow-commands.mjs (exported, called from the gate body at line 264). One spelling, both classes. No predicate over the name separates them, so any narrowing that excludes the accidental one also unmasks a real self-test battery and readmits its fixture paths as hints — trading a silence that costs nothing for the fabricated-lead family this masker exists to refuse.
  • A self-scan special case fixes 2 rows of 7. The other five live in three other files, so the card's own "fixes one instance and leaves the class" objection applies to it too, one file wider — plus the hazard the card names, that the tool's self-scan would differ from every other scan.

What ships is neither shape. The anchor is unchanged, the mask still blanks all 22, no path is special-cased, and nothing is renamed. What changes is that the seven accidental members' cost is measured on every run instead of asserted in a docblock. Silence was the defect; the remedy is noise on the day it stops being free.

This is deliberately remedy-neutral — it forecloses none of the three shapes if triage still wants one. The fork is reported on the card.

4. What this PR contains

One file, scripts/pm/dispatch-gates.mjs (+277 / -7):

  • COMPOUND_ANCHOR_LEDGER — the census above as data: 22 rows of file, name, accidental. Keyed by file plus name because runSelfTest proves a name-keyed ledger cannot hold both verdicts at once.
  • compoundAnchorDecls(source) — every compound-name anchor match at code positions only (comments masked first, string literals excluded via scanSource, so a fixture that builds a module source cannot enter the census).
  • withoutAnchor(source, name) — the counterfactual rename, word-anchored so runSelfTest does not rewrite runSelfTestCases.
  • Three self-test cases, live over the tracked tree (~3.3s):
    1. every compound-name match is classified — an unlisted or vanished one reds;
    2. masking an accidental row still costs no hint — reds and names the hidden hint;
    3. control: at least one genuine row is seen to lose a fixture hint, so a counterfactual that silently failed to rename cannot report "zero everywhere" and read as a pass.
  • Two identity pins that maskSelfTests and selfTestOnlyCallables are in the anchor's own population, so a rename that "fixes" either has to move the ledger row rather than the problem.
  • The stale claim in SELF_TEST_DECL's docblock corrected — it said the collision was one declaration; it is seven.

5. Verification

All at head 169cfb977.

pnpm check:pm-dispatch-gates — the module's own battery, run under the shared verify lock: dispatch-gates self-test: 1179 cases pass., VERDICT command-exit 0 (held 676s). The five new cases are in it, all green.

Ablation, both directions — mutation proved on disk before each reading, restored against HEAD and proved byte-exact by blob hash afterwards:

  • Injected a path literal into maskSelfTests — assertion 2 goes red: scripts/pm/dispatch-gates.mjs::maskSelfTests hides ["packages/ablation-only/src"]. This is the card's exact scenario, made loud.
  • Appended function ablationSelfTestNewcomer() to scripts/check-turbo-task-graph.mjs — assertion 1 goes red: unlisted: scripts/check-turbo-task-graph.mjs::ablationSelfTestNewcomer.
  • Restores verified: git hash-object equals the HEAD blob in both cases, zero residue, git status --porcelain empty.

Derived families (node scripts/pm/dispatch-gates.mjs, 15 matched) all green — exit codes captured before any pipe, verdict lines quoted in the terminal report. check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET locally (it grades a saved turbo log CI tees); recorded as NOT MEASURED, not as a red.

Changeset route: skip-changeset. The diff is one file under scripts/pm/** — PM tooling, publishing nothing from any package — which is the label's closed list. Applied on open per the label's read-back discipline.


Generated by Claude Code

…n their cost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Census the self-test name anchor's accidental matches and pin their cost - #14284

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision
Sep 2, 2026
Merged

Census the self-test name anchor's accidental matches and pin their cost#14284
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13941

Triage fixed the dispatch order into the card and it binds: census first, remedy second — the census result SELECTS the remedy, so no shape was chosen before the population was known. This PR reports the census, then ships what the census selected.

1. Premise re-check, on today's origin/main

#14004 (81256734) added ~412 lines to scripts/pm/dispatch-gates.mjs after the card's measurement, so everything below is re-measured rather than recalled. Base of this branch: 812567346.

The card's reading reproduces, and it understates the surface. When the module masks itself, six spans are blanked, not one:

spanbytes blankedwhy
maskSelfTests649name matches the anchor — the card's specimen
selfTestOnlyCallables1,646name matches the anchor — added by #14004, new since the card
bracedBodyEnd823collateral: reachable only from the two above, which the mask treats as self-tests
valueDeclEnd375collateral, same
topLevelDecls721collateral, same
selfTest421,024the genuine entry point — correct

Controls that make this a reading rather than an artifact: extractWatchHints (7,884 non-whitespace chars) and isNonPathNamespace (1,992) survive the mask untouched. The card's named control indexRefusalAccumulators no longer exists on this tree.

"Costs nothing today" still holds, and is now measured rather than asserted: no slash-bearing string literal appears anywhere in the five collision spans (the first one is at L9158, inside the genuine selfTest), and this file's hint set is identical with and without the collision.

2. The census — the ruled first step

209 code-position SELF_TEST_DECL matches over the tracked JS/TS corpus (5,659 files). 187 are the bare selfTest. The remaining 22 carry compound names over 21 distinct spellings — the population the card put a floor of 19/18 on. Classified:

Genuine self-test batteries — 15 (the anchor working)

filename
packages/lint/scripts/check-doc-formula-expressions.mjsspecSelfTest
packages/lint/scripts/check-doc-formula-expressions.mjsfieldRuleSelfTest
scripts/check-comment-mask-corpus.mjsrunSelfTestCases
scripts/check-doc-authoring.mjsselfTestRule3
scripts/check-doc-authoring.mjsselfTestPackagesProse
scripts/check-durability-degradation-log-level.mjsselfTestReadSeams
scripts/check-platform-checklist.mjsselfTestTrapVocabulary
scripts/check-platform-checklist.mjsselfTestProvisioningUse
scripts/check-platform-checklist.mjsselfTestUnreferencedRecipes
scripts/check-platform-checklist.mjsselfTestMetaCallSpelling
scripts/check-platform-checklist.mjsselfTestSourceLineCitations
scripts/check-regen-pending.mjsfixtureSelfTest
scripts/check-regen-pending.mjsprePushIsArmedSelfTest
scripts/check-regen-pending.mjsdecisionTableSelfTest
scripts/check-turbo-task-graph.mjsrunSelfTest

Accidental — production code the anchor caught by its name — 7, over 4 files

filenamerolespells a path today?
scripts/check-self-test-wired.mjscarriesSelfTestexported predicate: does this file carry a self-test?no
scripts/check-self-test-workflow-commands.mjsrunSelfTestexported: spawns another script's --self-testno
scripts/check-step-collectors.mjsselfTestTargetsexported extractor over a workflow run: bodyno
scripts/check-step-collectors.mjsselfTestDiscoveriesexported extractor, sameno
scripts/measure-self-test-floor.mjsselfTestDefsexported extractor over a module sourceno
scripts/pm/dispatch-gates.mjsselfTestOnlyCallablesinternal reachability helperno
scripts/pm/dispatch-gates.mjsmaskSelfTeststhe masker itself — the card's specimenno

The class has SEVEN members, not one. They cluster because every one of them is a gate that reasons about self-tests: a tool that finds, spawns, counts or masks other scripts' self-tests names its functions after the thing it handles, and a name-based anchor cannot tell "runs a self-test" from "is one".

Cost, measured per declaration

Each of the 22 was neutralised one at a time (the self-test token replaced inside the identifier, word-anchored) and the file's hints re-extracted, refusing unless the anchored-declaration count dropped by exactly one:

  • All 7 accidental members: zero hint delta. The card's "costs nothing today" holds tree-wide, over a surface 7x the one it measured.
  • 2 genuine members do move hints: fixtureSelfTest drops packages/spec/spec-changes.json, prePushIsArmedSelfTest drops .githooks/pre-push — both fixture paths in scripts/check-regen-pending.mjs, both correctly refused by the mask.

That asymmetry is load-bearing: "no compound-name match may contribute a hint" is false as a blanket invariant. The invariant holds only over the accidental half, and only a classification can name that half.

3. Remedy — and why both of triage's candidates are refuted by the census

Triage's decision table sent a MULTIPLE-member census to "narrowing SELF_TEST_DECL, or a self-scan special case — pick with the measurement in hand". The measurement refutes both:

  • Narrowing the name pattern is impossible, not merely risky.runSelfTest is a genuine entry point in scripts/check-turbo-task-graph.mjs (reached only from that file's --self-test guard) and accidental in scripts/check-self-test-workflow-commands.mjs (exported, called from the gate body at line 264). One spelling, both classes. No predicate over the name separates them, so any narrowing that excludes the accidental one also unmasks a real self-test battery and readmits its fixture paths as hints — trading a silence that costs nothing for the fabricated-lead family this masker exists to refuse.
  • A self-scan special case fixes 2 rows of 7. The other five live in three other files, so the card's own "fixes one instance and leaves the class" objection applies to it too, one file wider — plus the hazard the card names, that the tool's self-scan would differ from every other scan.

What ships is neither shape. The anchor is unchanged, the mask still blanks all 22, no path is special-cased, and nothing is renamed. What changes is that the seven accidental members' cost is measured on every run instead of asserted in a docblock. Silence was the defect; the remedy is noise on the day it stops being free.

This is deliberately remedy-neutral — it forecloses none of the three shapes if triage still wants one. The fork is reported on the card.

4. What this PR contains

One file, scripts/pm/dispatch-gates.mjs (+277 / -7):

  • COMPOUND_ANCHOR_LEDGER — the census above as data: 22 rows of file, name, accidental. Keyed by file plus name because runSelfTest proves a name-keyed ledger cannot hold both verdicts at once.
  • compoundAnchorDecls(source) — every compound-name anchor match at code positions only (comments masked first, string literals excluded via scanSource, so a fixture that builds a module source cannot enter the census).
  • withoutAnchor(source, name) — the counterfactual rename, word-anchored so runSelfTest does not rewrite runSelfTestCases.
  • Three self-test cases, live over the tracked tree (~3.3s):
    1. every compound-name match is classified — an unlisted or vanished one reds;
    2. masking an accidental row still costs no hint — reds and names the hidden hint;
    3. control: at least one genuine row is seen to lose a fixture hint, so a counterfactual that silently failed to rename cannot report "zero everywhere" and read as a pass.
  • Two identity pins that maskSelfTests and selfTestOnlyCallables are in the anchor's own population, so a rename that "fixes" either has to move the ledger row rather than the problem.
  • The stale claim in SELF_TEST_DECL's docblock corrected — it said the collision was one declaration; it is seven.

5. Verification

All at head 169cfb977.

pnpm check:pm-dispatch-gates — the module's own battery, run under the shared verify lock: dispatch-gates self-test: 1179 cases pass., VERDICT command-exit 0 (held 676s). The five new cases are in it, all green.

Ablation, both directions — mutation proved on disk before each reading, restored against HEAD and proved byte-exact by blob hash afterwards:

  • Injected a path literal into maskSelfTests — assertion 2 goes red: scripts/pm/dispatch-gates.mjs::maskSelfTests hides ["packages/ablation-only/src"]. This is the card's exact scenario, made loud.
  • Appended function ablationSelfTestNewcomer() to scripts/check-turbo-task-graph.mjs — assertion 1 goes red: unlisted: scripts/check-turbo-task-graph.mjs::ablationSelfTestNewcomer.
  • Restores verified: git hash-object equals the HEAD blob in both cases, zero residue, git status --porcelain empty.

Derived families (node scripts/pm/dispatch-gates.mjs, 15 matched) all green — exit codes captured before any pipe, verdict lines quoted in the terminal report. check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET locally (it grades a saved turbo log CI tees); recorded as NOT MEASURED, not as a red.

Changeset route: skip-changeset. The diff is one file under scripts/pm/** — PM tooling, publishing nothing from any package — which is the label's closed list. Applied on open per the label's read-back discipline.


Generated by Claude Code

…n their cost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Census the self-test name anchor's accidental matches and pin their cost - #14284

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision
Sep 2, 2026
Merged

Census the self-test name anchor's accidental matches and pin their cost#14284
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13941

Triage fixed the dispatch order into the card and it binds: census first, remedy second — the census result SELECTS the remedy, so no shape was chosen before the population was known. This PR reports the census, then ships what the census selected.

1. Premise re-check, on today's origin/main

#14004 (81256734) added ~412 lines to scripts/pm/dispatch-gates.mjs after the card's measurement, so everything below is re-measured rather than recalled. Base of this branch: 812567346.

The card's reading reproduces, and it understates the surface. When the module masks itself, six spans are blanked, not one:

spanbytes blankedwhy
maskSelfTests649name matches the anchor — the card's specimen
selfTestOnlyCallables1,646name matches the anchor — added by #14004, new since the card
bracedBodyEnd823collateral: reachable only from the two above, which the mask treats as self-tests
valueDeclEnd375collateral, same
topLevelDecls721collateral, same
selfTest421,024the genuine entry point — correct

Controls that make this a reading rather than an artifact: extractWatchHints (7,884 non-whitespace chars) and isNonPathNamespace (1,992) survive the mask untouched. The card's named control indexRefusalAccumulators no longer exists on this tree.

"Costs nothing today" still holds, and is now measured rather than asserted: no slash-bearing string literal appears anywhere in the five collision spans (the first one is at L9158, inside the genuine selfTest), and this file's hint set is identical with and without the collision.

2. The census — the ruled first step

209 code-position SELF_TEST_DECL matches over the tracked JS/TS corpus (5,659 files). 187 are the bare selfTest. The remaining 22 carry compound names over 21 distinct spellings — the population the card put a floor of 19/18 on. Classified:

Genuine self-test batteries — 15 (the anchor working)

filename
packages/lint/scripts/check-doc-formula-expressions.mjsspecSelfTest
packages/lint/scripts/check-doc-formula-expressions.mjsfieldRuleSelfTest
scripts/check-comment-mask-corpus.mjsrunSelfTestCases
scripts/check-doc-authoring.mjsselfTestRule3
scripts/check-doc-authoring.mjsselfTestPackagesProse
scripts/check-durability-degradation-log-level.mjsselfTestReadSeams
scripts/check-platform-checklist.mjsselfTestTrapVocabulary
scripts/check-platform-checklist.mjsselfTestProvisioningUse
scripts/check-platform-checklist.mjsselfTestUnreferencedRecipes
scripts/check-platform-checklist.mjsselfTestMetaCallSpelling
scripts/check-platform-checklist.mjsselfTestSourceLineCitations
scripts/check-regen-pending.mjsfixtureSelfTest
scripts/check-regen-pending.mjsprePushIsArmedSelfTest
scripts/check-regen-pending.mjsdecisionTableSelfTest
scripts/check-turbo-task-graph.mjsrunSelfTest

Accidental — production code the anchor caught by its name — 7, over 4 files

filenamerolespells a path today?
scripts/check-self-test-wired.mjscarriesSelfTestexported predicate: does this file carry a self-test?no
scripts/check-self-test-workflow-commands.mjsrunSelfTestexported: spawns another script's --self-testno
scripts/check-step-collectors.mjsselfTestTargetsexported extractor over a workflow run: bodyno
scripts/check-step-collectors.mjsselfTestDiscoveriesexported extractor, sameno
scripts/measure-self-test-floor.mjsselfTestDefsexported extractor over a module sourceno
scripts/pm/dispatch-gates.mjsselfTestOnlyCallablesinternal reachability helperno
scripts/pm/dispatch-gates.mjsmaskSelfTeststhe masker itself — the card's specimenno

The class has SEVEN members, not one. They cluster because every one of them is a gate that reasons about self-tests: a tool that finds, spawns, counts or masks other scripts' self-tests names its functions after the thing it handles, and a name-based anchor cannot tell "runs a self-test" from "is one".

Cost, measured per declaration

Each of the 22 was neutralised one at a time (the self-test token replaced inside the identifier, word-anchored) and the file's hints re-extracted, refusing unless the anchored-declaration count dropped by exactly one:

  • All 7 accidental members: zero hint delta. The card's "costs nothing today" holds tree-wide, over a surface 7x the one it measured.
  • 2 genuine members do move hints: fixtureSelfTest drops packages/spec/spec-changes.json, prePushIsArmedSelfTest drops .githooks/pre-push — both fixture paths in scripts/check-regen-pending.mjs, both correctly refused by the mask.

That asymmetry is load-bearing: "no compound-name match may contribute a hint" is false as a blanket invariant. The invariant holds only over the accidental half, and only a classification can name that half.

3. Remedy — and why both of triage's candidates are refuted by the census

Triage's decision table sent a MULTIPLE-member census to "narrowing SELF_TEST_DECL, or a self-scan special case — pick with the measurement in hand". The measurement refutes both:

  • Narrowing the name pattern is impossible, not merely risky.runSelfTest is a genuine entry point in scripts/check-turbo-task-graph.mjs (reached only from that file's --self-test guard) and accidental in scripts/check-self-test-workflow-commands.mjs (exported, called from the gate body at line 264). One spelling, both classes. No predicate over the name separates them, so any narrowing that excludes the accidental one also unmasks a real self-test battery and readmits its fixture paths as hints — trading a silence that costs nothing for the fabricated-lead family this masker exists to refuse.
  • A self-scan special case fixes 2 rows of 7. The other five live in three other files, so the card's own "fixes one instance and leaves the class" objection applies to it too, one file wider — plus the hazard the card names, that the tool's self-scan would differ from every other scan.

What ships is neither shape. The anchor is unchanged, the mask still blanks all 22, no path is special-cased, and nothing is renamed. What changes is that the seven accidental members' cost is measured on every run instead of asserted in a docblock. Silence was the defect; the remedy is noise on the day it stops being free.

This is deliberately remedy-neutral — it forecloses none of the three shapes if triage still wants one. The fork is reported on the card.

4. What this PR contains

One file, scripts/pm/dispatch-gates.mjs (+277 / -7):

  • COMPOUND_ANCHOR_LEDGER — the census above as data: 22 rows of file, name, accidental. Keyed by file plus name because runSelfTest proves a name-keyed ledger cannot hold both verdicts at once.
  • compoundAnchorDecls(source) — every compound-name anchor match at code positions only (comments masked first, string literals excluded via scanSource, so a fixture that builds a module source cannot enter the census).
  • withoutAnchor(source, name) — the counterfactual rename, word-anchored so runSelfTest does not rewrite runSelfTestCases.
  • Three self-test cases, live over the tracked tree (~3.3s):
    1. every compound-name match is classified — an unlisted or vanished one reds;
    2. masking an accidental row still costs no hint — reds and names the hidden hint;
    3. control: at least one genuine row is seen to lose a fixture hint, so a counterfactual that silently failed to rename cannot report "zero everywhere" and read as a pass.
  • Two identity pins that maskSelfTests and selfTestOnlyCallables are in the anchor's own population, so a rename that "fixes" either has to move the ledger row rather than the problem.
  • The stale claim in SELF_TEST_DECL's docblock corrected — it said the collision was one declaration; it is seven.

5. Verification

All at head 169cfb977.

pnpm check:pm-dispatch-gates — the module's own battery, run under the shared verify lock: dispatch-gates self-test: 1179 cases pass., VERDICT command-exit 0 (held 676s). The five new cases are in it, all green.

Ablation, both directions — mutation proved on disk before each reading, restored against HEAD and proved byte-exact by blob hash afterwards:

  • Injected a path literal into maskSelfTests — assertion 2 goes red: scripts/pm/dispatch-gates.mjs::maskSelfTests hides ["packages/ablation-only/src"]. This is the card's exact scenario, made loud.
  • Appended function ablationSelfTestNewcomer() to scripts/check-turbo-task-graph.mjs — assertion 1 goes red: unlisted: scripts/check-turbo-task-graph.mjs::ablationSelfTestNewcomer.
  • Restores verified: git hash-object equals the HEAD blob in both cases, zero residue, git status --porcelain empty.

Derived families (node scripts/pm/dispatch-gates.mjs, 15 matched) all green — exit codes captured before any pipe, verdict lines quoted in the terminal report. check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET locally (it grades a saved turbo log CI tees); recorded as NOT MEASURED, not as a red.

Changeset route: skip-changeset. The diff is one file under scripts/pm/** — PM tooling, publishing nothing from any package — which is the label's closed list. Applied on open per the label's read-back discipline.


Generated by Claude Code

…n their cost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Census the self-test name anchor's accidental matches and pin their cost - #14284

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision
Sep 2, 2026
Merged

Census the self-test name anchor's accidental matches and pin their cost#14284
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13941

Triage fixed the dispatch order into the card and it binds: census first, remedy second — the census result SELECTS the remedy, so no shape was chosen before the population was known. This PR reports the census, then ships what the census selected.

1. Premise re-check, on today's origin/main

#14004 (81256734) added ~412 lines to scripts/pm/dispatch-gates.mjs after the card's measurement, so everything below is re-measured rather than recalled. Base of this branch: 812567346.

The card's reading reproduces, and it understates the surface. When the module masks itself, six spans are blanked, not one:

spanbytes blankedwhy
maskSelfTests649name matches the anchor — the card's specimen
selfTestOnlyCallables1,646name matches the anchor — added by #14004, new since the card
bracedBodyEnd823collateral: reachable only from the two above, which the mask treats as self-tests
valueDeclEnd375collateral, same
topLevelDecls721collateral, same
selfTest421,024the genuine entry point — correct

Controls that make this a reading rather than an artifact: extractWatchHints (7,884 non-whitespace chars) and isNonPathNamespace (1,992) survive the mask untouched. The card's named control indexRefusalAccumulators no longer exists on this tree.

"Costs nothing today" still holds, and is now measured rather than asserted: no slash-bearing string literal appears anywhere in the five collision spans (the first one is at L9158, inside the genuine selfTest), and this file's hint set is identical with and without the collision.

2. The census — the ruled first step

209 code-position SELF_TEST_DECL matches over the tracked JS/TS corpus (5,659 files). 187 are the bare selfTest. The remaining 22 carry compound names over 21 distinct spellings — the population the card put a floor of 19/18 on. Classified:

Genuine self-test batteries — 15 (the anchor working)

filename
packages/lint/scripts/check-doc-formula-expressions.mjsspecSelfTest
packages/lint/scripts/check-doc-formula-expressions.mjsfieldRuleSelfTest
scripts/check-comment-mask-corpus.mjsrunSelfTestCases
scripts/check-doc-authoring.mjsselfTestRule3
scripts/check-doc-authoring.mjsselfTestPackagesProse
scripts/check-durability-degradation-log-level.mjsselfTestReadSeams
scripts/check-platform-checklist.mjsselfTestTrapVocabulary
scripts/check-platform-checklist.mjsselfTestProvisioningUse
scripts/check-platform-checklist.mjsselfTestUnreferencedRecipes
scripts/check-platform-checklist.mjsselfTestMetaCallSpelling
scripts/check-platform-checklist.mjsselfTestSourceLineCitations
scripts/check-regen-pending.mjsfixtureSelfTest
scripts/check-regen-pending.mjsprePushIsArmedSelfTest
scripts/check-regen-pending.mjsdecisionTableSelfTest
scripts/check-turbo-task-graph.mjsrunSelfTest

Accidental — production code the anchor caught by its name — 7, over 4 files

filenamerolespells a path today?
scripts/check-self-test-wired.mjscarriesSelfTestexported predicate: does this file carry a self-test?no
scripts/check-self-test-workflow-commands.mjsrunSelfTestexported: spawns another script's --self-testno
scripts/check-step-collectors.mjsselfTestTargetsexported extractor over a workflow run: bodyno
scripts/check-step-collectors.mjsselfTestDiscoveriesexported extractor, sameno
scripts/measure-self-test-floor.mjsselfTestDefsexported extractor over a module sourceno
scripts/pm/dispatch-gates.mjsselfTestOnlyCallablesinternal reachability helperno
scripts/pm/dispatch-gates.mjsmaskSelfTeststhe masker itself — the card's specimenno

The class has SEVEN members, not one. They cluster because every one of them is a gate that reasons about self-tests: a tool that finds, spawns, counts or masks other scripts' self-tests names its functions after the thing it handles, and a name-based anchor cannot tell "runs a self-test" from "is one".

Cost, measured per declaration

Each of the 22 was neutralised one at a time (the self-test token replaced inside the identifier, word-anchored) and the file's hints re-extracted, refusing unless the anchored-declaration count dropped by exactly one:

  • All 7 accidental members: zero hint delta. The card's "costs nothing today" holds tree-wide, over a surface 7x the one it measured.
  • 2 genuine members do move hints: fixtureSelfTest drops packages/spec/spec-changes.json, prePushIsArmedSelfTest drops .githooks/pre-push — both fixture paths in scripts/check-regen-pending.mjs, both correctly refused by the mask.

That asymmetry is load-bearing: "no compound-name match may contribute a hint" is false as a blanket invariant. The invariant holds only over the accidental half, and only a classification can name that half.

3. Remedy — and why both of triage's candidates are refuted by the census

Triage's decision table sent a MULTIPLE-member census to "narrowing SELF_TEST_DECL, or a self-scan special case — pick with the measurement in hand". The measurement refutes both:

  • Narrowing the name pattern is impossible, not merely risky.runSelfTest is a genuine entry point in scripts/check-turbo-task-graph.mjs (reached only from that file's --self-test guard) and accidental in scripts/check-self-test-workflow-commands.mjs (exported, called from the gate body at line 264). One spelling, both classes. No predicate over the name separates them, so any narrowing that excludes the accidental one also unmasks a real self-test battery and readmits its fixture paths as hints — trading a silence that costs nothing for the fabricated-lead family this masker exists to refuse.
  • A self-scan special case fixes 2 rows of 7. The other five live in three other files, so the card's own "fixes one instance and leaves the class" objection applies to it too, one file wider — plus the hazard the card names, that the tool's self-scan would differ from every other scan.

What ships is neither shape. The anchor is unchanged, the mask still blanks all 22, no path is special-cased, and nothing is renamed. What changes is that the seven accidental members' cost is measured on every run instead of asserted in a docblock. Silence was the defect; the remedy is noise on the day it stops being free.

This is deliberately remedy-neutral — it forecloses none of the three shapes if triage still wants one. The fork is reported on the card.

4. What this PR contains

One file, scripts/pm/dispatch-gates.mjs (+277 / -7):

  • COMPOUND_ANCHOR_LEDGER — the census above as data: 22 rows of file, name, accidental. Keyed by file plus name because runSelfTest proves a name-keyed ledger cannot hold both verdicts at once.
  • compoundAnchorDecls(source) — every compound-name anchor match at code positions only (comments masked first, string literals excluded via scanSource, so a fixture that builds a module source cannot enter the census).
  • withoutAnchor(source, name) — the counterfactual rename, word-anchored so runSelfTest does not rewrite runSelfTestCases.
  • Three self-test cases, live over the tracked tree (~3.3s):
    1. every compound-name match is classified — an unlisted or vanished one reds;
    2. masking an accidental row still costs no hint — reds and names the hidden hint;
    3. control: at least one genuine row is seen to lose a fixture hint, so a counterfactual that silently failed to rename cannot report "zero everywhere" and read as a pass.
  • Two identity pins that maskSelfTests and selfTestOnlyCallables are in the anchor's own population, so a rename that "fixes" either has to move the ledger row rather than the problem.
  • The stale claim in SELF_TEST_DECL's docblock corrected — it said the collision was one declaration; it is seven.

5. Verification

All at head 169cfb977.

pnpm check:pm-dispatch-gates — the module's own battery, run under the shared verify lock: dispatch-gates self-test: 1179 cases pass., VERDICT command-exit 0 (held 676s). The five new cases are in it, all green.

Ablation, both directions — mutation proved on disk before each reading, restored against HEAD and proved byte-exact by blob hash afterwards:

  • Injected a path literal into maskSelfTests — assertion 2 goes red: scripts/pm/dispatch-gates.mjs::maskSelfTests hides ["packages/ablation-only/src"]. This is the card's exact scenario, made loud.
  • Appended function ablationSelfTestNewcomer() to scripts/check-turbo-task-graph.mjs — assertion 1 goes red: unlisted: scripts/check-turbo-task-graph.mjs::ablationSelfTestNewcomer.
  • Restores verified: git hash-object equals the HEAD blob in both cases, zero residue, git status --porcelain empty.

Derived families (node scripts/pm/dispatch-gates.mjs, 15 matched) all green — exit codes captured before any pipe, verdict lines quoted in the terminal report. check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET locally (it grades a saved turbo log CI tees); recorded as NOT MEASURED, not as a red.

Changeset route: skip-changeset. The diff is one file under scripts/pm/** — PM tooling, publishing nothing from any package — which is the label's closed list. Applied on open per the label's read-back discipline.


Generated by Claude Code

…n their cost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Census the self-test name anchor's accidental matches and pin their cost - #14284

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision
Sep 2, 2026
Merged

Census the self-test name anchor's accidental matches and pin their cost#14284
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13941

Triage fixed the dispatch order into the card and it binds: census first, remedy second — the census result SELECTS the remedy, so no shape was chosen before the population was known. This PR reports the census, then ships what the census selected.

1. Premise re-check, on today's origin/main

#14004 (81256734) added ~412 lines to scripts/pm/dispatch-gates.mjs after the card's measurement, so everything below is re-measured rather than recalled. Base of this branch: 812567346.

The card's reading reproduces, and it understates the surface. When the module masks itself, six spans are blanked, not one:

spanbytes blankedwhy
maskSelfTests649name matches the anchor — the card's specimen
selfTestOnlyCallables1,646name matches the anchor — added by #14004, new since the card
bracedBodyEnd823collateral: reachable only from the two above, which the mask treats as self-tests
valueDeclEnd375collateral, same
topLevelDecls721collateral, same
selfTest421,024the genuine entry point — correct

Controls that make this a reading rather than an artifact: extractWatchHints (7,884 non-whitespace chars) and isNonPathNamespace (1,992) survive the mask untouched. The card's named control indexRefusalAccumulators no longer exists on this tree.

"Costs nothing today" still holds, and is now measured rather than asserted: no slash-bearing string literal appears anywhere in the five collision spans (the first one is at L9158, inside the genuine selfTest), and this file's hint set is identical with and without the collision.

2. The census — the ruled first step

209 code-position SELF_TEST_DECL matches over the tracked JS/TS corpus (5,659 files). 187 are the bare selfTest. The remaining 22 carry compound names over 21 distinct spellings — the population the card put a floor of 19/18 on. Classified:

Genuine self-test batteries — 15 (the anchor working)

filename
packages/lint/scripts/check-doc-formula-expressions.mjsspecSelfTest
packages/lint/scripts/check-doc-formula-expressions.mjsfieldRuleSelfTest
scripts/check-comment-mask-corpus.mjsrunSelfTestCases
scripts/check-doc-authoring.mjsselfTestRule3
scripts/check-doc-authoring.mjsselfTestPackagesProse
scripts/check-durability-degradation-log-level.mjsselfTestReadSeams
scripts/check-platform-checklist.mjsselfTestTrapVocabulary
scripts/check-platform-checklist.mjsselfTestProvisioningUse
scripts/check-platform-checklist.mjsselfTestUnreferencedRecipes
scripts/check-platform-checklist.mjsselfTestMetaCallSpelling
scripts/check-platform-checklist.mjsselfTestSourceLineCitations
scripts/check-regen-pending.mjsfixtureSelfTest
scripts/check-regen-pending.mjsprePushIsArmedSelfTest
scripts/check-regen-pending.mjsdecisionTableSelfTest
scripts/check-turbo-task-graph.mjsrunSelfTest

Accidental — production code the anchor caught by its name — 7, over 4 files

filenamerolespells a path today?
scripts/check-self-test-wired.mjscarriesSelfTestexported predicate: does this file carry a self-test?no
scripts/check-self-test-workflow-commands.mjsrunSelfTestexported: spawns another script's --self-testno
scripts/check-step-collectors.mjsselfTestTargetsexported extractor over a workflow run: bodyno
scripts/check-step-collectors.mjsselfTestDiscoveriesexported extractor, sameno
scripts/measure-self-test-floor.mjsselfTestDefsexported extractor over a module sourceno
scripts/pm/dispatch-gates.mjsselfTestOnlyCallablesinternal reachability helperno
scripts/pm/dispatch-gates.mjsmaskSelfTeststhe masker itself — the card's specimenno

The class has SEVEN members, not one. They cluster because every one of them is a gate that reasons about self-tests: a tool that finds, spawns, counts or masks other scripts' self-tests names its functions after the thing it handles, and a name-based anchor cannot tell "runs a self-test" from "is one".

Cost, measured per declaration

Each of the 22 was neutralised one at a time (the self-test token replaced inside the identifier, word-anchored) and the file's hints re-extracted, refusing unless the anchored-declaration count dropped by exactly one:

  • All 7 accidental members: zero hint delta. The card's "costs nothing today" holds tree-wide, over a surface 7x the one it measured.
  • 2 genuine members do move hints: fixtureSelfTest drops packages/spec/spec-changes.json, prePushIsArmedSelfTest drops .githooks/pre-push — both fixture paths in scripts/check-regen-pending.mjs, both correctly refused by the mask.

That asymmetry is load-bearing: "no compound-name match may contribute a hint" is false as a blanket invariant. The invariant holds only over the accidental half, and only a classification can name that half.

3. Remedy — and why both of triage's candidates are refuted by the census

Triage's decision table sent a MULTIPLE-member census to "narrowing SELF_TEST_DECL, or a self-scan special case — pick with the measurement in hand". The measurement refutes both:

  • Narrowing the name pattern is impossible, not merely risky.runSelfTest is a genuine entry point in scripts/check-turbo-task-graph.mjs (reached only from that file's --self-test guard) and accidental in scripts/check-self-test-workflow-commands.mjs (exported, called from the gate body at line 264). One spelling, both classes. No predicate over the name separates them, so any narrowing that excludes the accidental one also unmasks a real self-test battery and readmits its fixture paths as hints — trading a silence that costs nothing for the fabricated-lead family this masker exists to refuse.
  • A self-scan special case fixes 2 rows of 7. The other five live in three other files, so the card's own "fixes one instance and leaves the class" objection applies to it too, one file wider — plus the hazard the card names, that the tool's self-scan would differ from every other scan.

What ships is neither shape. The anchor is unchanged, the mask still blanks all 22, no path is special-cased, and nothing is renamed. What changes is that the seven accidental members' cost is measured on every run instead of asserted in a docblock. Silence was the defect; the remedy is noise on the day it stops being free.

This is deliberately remedy-neutral — it forecloses none of the three shapes if triage still wants one. The fork is reported on the card.

4. What this PR contains

One file, scripts/pm/dispatch-gates.mjs (+277 / -7):

  • COMPOUND_ANCHOR_LEDGER — the census above as data: 22 rows of file, name, accidental. Keyed by file plus name because runSelfTest proves a name-keyed ledger cannot hold both verdicts at once.
  • compoundAnchorDecls(source) — every compound-name anchor match at code positions only (comments masked first, string literals excluded via scanSource, so a fixture that builds a module source cannot enter the census).
  • withoutAnchor(source, name) — the counterfactual rename, word-anchored so runSelfTest does not rewrite runSelfTestCases.
  • Three self-test cases, live over the tracked tree (~3.3s):
    1. every compound-name match is classified — an unlisted or vanished one reds;
    2. masking an accidental row still costs no hint — reds and names the hidden hint;
    3. control: at least one genuine row is seen to lose a fixture hint, so a counterfactual that silently failed to rename cannot report "zero everywhere" and read as a pass.
  • Two identity pins that maskSelfTests and selfTestOnlyCallables are in the anchor's own population, so a rename that "fixes" either has to move the ledger row rather than the problem.
  • The stale claim in SELF_TEST_DECL's docblock corrected — it said the collision was one declaration; it is seven.

5. Verification

All at head 169cfb977.

pnpm check:pm-dispatch-gates — the module's own battery, run under the shared verify lock: dispatch-gates self-test: 1179 cases pass., VERDICT command-exit 0 (held 676s). The five new cases are in it, all green.

Ablation, both directions — mutation proved on disk before each reading, restored against HEAD and proved byte-exact by blob hash afterwards:

  • Injected a path literal into maskSelfTests — assertion 2 goes red: scripts/pm/dispatch-gates.mjs::maskSelfTests hides ["packages/ablation-only/src"]. This is the card's exact scenario, made loud.
  • Appended function ablationSelfTestNewcomer() to scripts/check-turbo-task-graph.mjs — assertion 1 goes red: unlisted: scripts/check-turbo-task-graph.mjs::ablationSelfTestNewcomer.
  • Restores verified: git hash-object equals the HEAD blob in both cases, zero residue, git status --porcelain empty.

Derived families (node scripts/pm/dispatch-gates.mjs, 15 matched) all green — exit codes captured before any pipe, verdict lines quoted in the terminal report. check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET locally (it grades a saved turbo log CI tees); recorded as NOT MEASURED, not as a red.

Changeset route: skip-changeset. The diff is one file under scripts/pm/** — PM tooling, publishing nothing from any package — which is the label's closed list. Applied on open per the label's read-back discipline.


Generated by Claude Code

…n their cost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Census the self-test name anchor's accidental matches and pin their cost - #14284

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision
Sep 2, 2026
Merged

Census the self-test name anchor's accidental matches and pin their cost#14284
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13941

Triage fixed the dispatch order into the card and it binds: census first, remedy second — the census result SELECTS the remedy, so no shape was chosen before the population was known. This PR reports the census, then ships what the census selected.

1. Premise re-check, on today's origin/main

#14004 (81256734) added ~412 lines to scripts/pm/dispatch-gates.mjs after the card's measurement, so everything below is re-measured rather than recalled. Base of this branch: 812567346.

The card's reading reproduces, and it understates the surface. When the module masks itself, six spans are blanked, not one:

spanbytes blankedwhy
maskSelfTests649name matches the anchor — the card's specimen
selfTestOnlyCallables1,646name matches the anchor — added by #14004, new since the card
bracedBodyEnd823collateral: reachable only from the two above, which the mask treats as self-tests
valueDeclEnd375collateral, same
topLevelDecls721collateral, same
selfTest421,024the genuine entry point — correct

Controls that make this a reading rather than an artifact: extractWatchHints (7,884 non-whitespace chars) and isNonPathNamespace (1,992) survive the mask untouched. The card's named control indexRefusalAccumulators no longer exists on this tree.

"Costs nothing today" still holds, and is now measured rather than asserted: no slash-bearing string literal appears anywhere in the five collision spans (the first one is at L9158, inside the genuine selfTest), and this file's hint set is identical with and without the collision.

2. The census — the ruled first step

209 code-position SELF_TEST_DECL matches over the tracked JS/TS corpus (5,659 files). 187 are the bare selfTest. The remaining 22 carry compound names over 21 distinct spellings — the population the card put a floor of 19/18 on. Classified:

Genuine self-test batteries — 15 (the anchor working)

filename
packages/lint/scripts/check-doc-formula-expressions.mjsspecSelfTest
packages/lint/scripts/check-doc-formula-expressions.mjsfieldRuleSelfTest
scripts/check-comment-mask-corpus.mjsrunSelfTestCases
scripts/check-doc-authoring.mjsselfTestRule3
scripts/check-doc-authoring.mjsselfTestPackagesProse
scripts/check-durability-degradation-log-level.mjsselfTestReadSeams
scripts/check-platform-checklist.mjsselfTestTrapVocabulary
scripts/check-platform-checklist.mjsselfTestProvisioningUse
scripts/check-platform-checklist.mjsselfTestUnreferencedRecipes
scripts/check-platform-checklist.mjsselfTestMetaCallSpelling
scripts/check-platform-checklist.mjsselfTestSourceLineCitations
scripts/check-regen-pending.mjsfixtureSelfTest
scripts/check-regen-pending.mjsprePushIsArmedSelfTest
scripts/check-regen-pending.mjsdecisionTableSelfTest
scripts/check-turbo-task-graph.mjsrunSelfTest

Accidental — production code the anchor caught by its name — 7, over 4 files

filenamerolespells a path today?
scripts/check-self-test-wired.mjscarriesSelfTestexported predicate: does this file carry a self-test?no
scripts/check-self-test-workflow-commands.mjsrunSelfTestexported: spawns another script's --self-testno
scripts/check-step-collectors.mjsselfTestTargetsexported extractor over a workflow run: bodyno
scripts/check-step-collectors.mjsselfTestDiscoveriesexported extractor, sameno
scripts/measure-self-test-floor.mjsselfTestDefsexported extractor over a module sourceno
scripts/pm/dispatch-gates.mjsselfTestOnlyCallablesinternal reachability helperno
scripts/pm/dispatch-gates.mjsmaskSelfTeststhe masker itself — the card's specimenno

The class has SEVEN members, not one. They cluster because every one of them is a gate that reasons about self-tests: a tool that finds, spawns, counts or masks other scripts' self-tests names its functions after the thing it handles, and a name-based anchor cannot tell "runs a self-test" from "is one".

Cost, measured per declaration

Each of the 22 was neutralised one at a time (the self-test token replaced inside the identifier, word-anchored) and the file's hints re-extracted, refusing unless the anchored-declaration count dropped by exactly one:

  • All 7 accidental members: zero hint delta. The card's "costs nothing today" holds tree-wide, over a surface 7x the one it measured.
  • 2 genuine members do move hints: fixtureSelfTest drops packages/spec/spec-changes.json, prePushIsArmedSelfTest drops .githooks/pre-push — both fixture paths in scripts/check-regen-pending.mjs, both correctly refused by the mask.

That asymmetry is load-bearing: "no compound-name match may contribute a hint" is false as a blanket invariant. The invariant holds only over the accidental half, and only a classification can name that half.

3. Remedy — and why both of triage's candidates are refuted by the census

Triage's decision table sent a MULTIPLE-member census to "narrowing SELF_TEST_DECL, or a self-scan special case — pick with the measurement in hand". The measurement refutes both:

  • Narrowing the name pattern is impossible, not merely risky.runSelfTest is a genuine entry point in scripts/check-turbo-task-graph.mjs (reached only from that file's --self-test guard) and accidental in scripts/check-self-test-workflow-commands.mjs (exported, called from the gate body at line 264). One spelling, both classes. No predicate over the name separates them, so any narrowing that excludes the accidental one also unmasks a real self-test battery and readmits its fixture paths as hints — trading a silence that costs nothing for the fabricated-lead family this masker exists to refuse.
  • A self-scan special case fixes 2 rows of 7. The other five live in three other files, so the card's own "fixes one instance and leaves the class" objection applies to it too, one file wider — plus the hazard the card names, that the tool's self-scan would differ from every other scan.

What ships is neither shape. The anchor is unchanged, the mask still blanks all 22, no path is special-cased, and nothing is renamed. What changes is that the seven accidental members' cost is measured on every run instead of asserted in a docblock. Silence was the defect; the remedy is noise on the day it stops being free.

This is deliberately remedy-neutral — it forecloses none of the three shapes if triage still wants one. The fork is reported on the card.

4. What this PR contains

One file, scripts/pm/dispatch-gates.mjs (+277 / -7):

  • COMPOUND_ANCHOR_LEDGER — the census above as data: 22 rows of file, name, accidental. Keyed by file plus name because runSelfTest proves a name-keyed ledger cannot hold both verdicts at once.
  • compoundAnchorDecls(source) — every compound-name anchor match at code positions only (comments masked first, string literals excluded via scanSource, so a fixture that builds a module source cannot enter the census).
  • withoutAnchor(source, name) — the counterfactual rename, word-anchored so runSelfTest does not rewrite runSelfTestCases.
  • Three self-test cases, live over the tracked tree (~3.3s):
    1. every compound-name match is classified — an unlisted or vanished one reds;
    2. masking an accidental row still costs no hint — reds and names the hidden hint;
    3. control: at least one genuine row is seen to lose a fixture hint, so a counterfactual that silently failed to rename cannot report "zero everywhere" and read as a pass.
  • Two identity pins that maskSelfTests and selfTestOnlyCallables are in the anchor's own population, so a rename that "fixes" either has to move the ledger row rather than the problem.
  • The stale claim in SELF_TEST_DECL's docblock corrected — it said the collision was one declaration; it is seven.

5. Verification

All at head 169cfb977.

pnpm check:pm-dispatch-gates — the module's own battery, run under the shared verify lock: dispatch-gates self-test: 1179 cases pass., VERDICT command-exit 0 (held 676s). The five new cases are in it, all green.

Ablation, both directions — mutation proved on disk before each reading, restored against HEAD and proved byte-exact by blob hash afterwards:

  • Injected a path literal into maskSelfTests — assertion 2 goes red: scripts/pm/dispatch-gates.mjs::maskSelfTests hides ["packages/ablation-only/src"]. This is the card's exact scenario, made loud.
  • Appended function ablationSelfTestNewcomer() to scripts/check-turbo-task-graph.mjs — assertion 1 goes red: unlisted: scripts/check-turbo-task-graph.mjs::ablationSelfTestNewcomer.
  • Restores verified: git hash-object equals the HEAD blob in both cases, zero residue, git status --porcelain empty.

Derived families (node scripts/pm/dispatch-gates.mjs, 15 matched) all green — exit codes captured before any pipe, verdict lines quoted in the terminal report. check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET locally (it grades a saved turbo log CI tees); recorded as NOT MEASURED, not as a red.

Changeset route: skip-changeset. The diff is one file under scripts/pm/** — PM tooling, publishing nothing from any package — which is the label's closed list. Applied on open per the label's read-back discipline.


Generated by Claude Code

…n their cost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Census the self-test name anchor's accidental matches and pin their cost - #14284

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision
Sep 2, 2026
Merged

Census the self-test name anchor's accidental matches and pin their cost#14284
baozhoutao merged 1 commit into
mainfrom
claude/issue-13941-selftest-decl-collision

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13941

Triage fixed the dispatch order into the card and it binds: census first, remedy second — the census result SELECTS the remedy, so no shape was chosen before the population was known. This PR reports the census, then ships what the census selected.

1. Premise re-check, on today's origin/main

#14004 (81256734) added ~412 lines to scripts/pm/dispatch-gates.mjs after the card's measurement, so everything below is re-measured rather than recalled. Base of this branch: 812567346.

The card's reading reproduces, and it understates the surface. When the module masks itself, six spans are blanked, not one:

spanbytes blankedwhy
maskSelfTests649name matches the anchor — the card's specimen
selfTestOnlyCallables1,646name matches the anchor — added by #14004, new since the card
bracedBodyEnd823collateral: reachable only from the two above, which the mask treats as self-tests
valueDeclEnd375collateral, same
topLevelDecls721collateral, same
selfTest421,024the genuine entry point — correct

Controls that make this a reading rather than an artifact: extractWatchHints (7,884 non-whitespace chars) and isNonPathNamespace (1,992) survive the mask untouched. The card's named control indexRefusalAccumulators no longer exists on this tree.

"Costs nothing today" still holds, and is now measured rather than asserted: no slash-bearing string literal appears anywhere in the five collision spans (the first one is at L9158, inside the genuine selfTest), and this file's hint set is identical with and without the collision.

2. The census — the ruled first step

209 code-position SELF_TEST_DECL matches over the tracked JS/TS corpus (5,659 files). 187 are the bare selfTest. The remaining 22 carry compound names over 21 distinct spellings — the population the card put a floor of 19/18 on. Classified:

Genuine self-test batteries — 15 (the anchor working)

filename
packages/lint/scripts/check-doc-formula-expressions.mjsspecSelfTest
packages/lint/scripts/check-doc-formula-expressions.mjsfieldRuleSelfTest
scripts/check-comment-mask-corpus.mjsrunSelfTestCases
scripts/check-doc-authoring.mjsselfTestRule3
scripts/check-doc-authoring.mjsselfTestPackagesProse
scripts/check-durability-degradation-log-level.mjsselfTestReadSeams
scripts/check-platform-checklist.mjsselfTestTrapVocabulary
scripts/check-platform-checklist.mjsselfTestProvisioningUse
scripts/check-platform-checklist.mjsselfTestUnreferencedRecipes
scripts/check-platform-checklist.mjsselfTestMetaCallSpelling
scripts/check-platform-checklist.mjsselfTestSourceLineCitations
scripts/check-regen-pending.mjsfixtureSelfTest
scripts/check-regen-pending.mjsprePushIsArmedSelfTest
scripts/check-regen-pending.mjsdecisionTableSelfTest
scripts/check-turbo-task-graph.mjsrunSelfTest

Accidental — production code the anchor caught by its name — 7, over 4 files

filenamerolespells a path today?
scripts/check-self-test-wired.mjscarriesSelfTestexported predicate: does this file carry a self-test?no
scripts/check-self-test-workflow-commands.mjsrunSelfTestexported: spawns another script's --self-testno
scripts/check-step-collectors.mjsselfTestTargetsexported extractor over a workflow run: bodyno
scripts/check-step-collectors.mjsselfTestDiscoveriesexported extractor, sameno
scripts/measure-self-test-floor.mjsselfTestDefsexported extractor over a module sourceno
scripts/pm/dispatch-gates.mjsselfTestOnlyCallablesinternal reachability helperno
scripts/pm/dispatch-gates.mjsmaskSelfTeststhe masker itself — the card's specimenno

The class has SEVEN members, not one. They cluster because every one of them is a gate that reasons about self-tests: a tool that finds, spawns, counts or masks other scripts' self-tests names its functions after the thing it handles, and a name-based anchor cannot tell "runs a self-test" from "is one".

Cost, measured per declaration

Each of the 22 was neutralised one at a time (the self-test token replaced inside the identifier, word-anchored) and the file's hints re-extracted, refusing unless the anchored-declaration count dropped by exactly one:

  • All 7 accidental members: zero hint delta. The card's "costs nothing today" holds tree-wide, over a surface 7x the one it measured.
  • 2 genuine members do move hints: fixtureSelfTest drops packages/spec/spec-changes.json, prePushIsArmedSelfTest drops .githooks/pre-push — both fixture paths in scripts/check-regen-pending.mjs, both correctly refused by the mask.

That asymmetry is load-bearing: "no compound-name match may contribute a hint" is false as a blanket invariant. The invariant holds only over the accidental half, and only a classification can name that half.

3. Remedy — and why both of triage's candidates are refuted by the census

Triage's decision table sent a MULTIPLE-member census to "narrowing SELF_TEST_DECL, or a self-scan special case — pick with the measurement in hand". The measurement refutes both:

  • Narrowing the name pattern is impossible, not merely risky.runSelfTest is a genuine entry point in scripts/check-turbo-task-graph.mjs (reached only from that file's --self-test guard) and accidental in scripts/check-self-test-workflow-commands.mjs (exported, called from the gate body at line 264). One spelling, both classes. No predicate over the name separates them, so any narrowing that excludes the accidental one also unmasks a real self-test battery and readmits its fixture paths as hints — trading a silence that costs nothing for the fabricated-lead family this masker exists to refuse.
  • A self-scan special case fixes 2 rows of 7. The other five live in three other files, so the card's own "fixes one instance and leaves the class" objection applies to it too, one file wider — plus the hazard the card names, that the tool's self-scan would differ from every other scan.

What ships is neither shape. The anchor is unchanged, the mask still blanks all 22, no path is special-cased, and nothing is renamed. What changes is that the seven accidental members' cost is measured on every run instead of asserted in a docblock. Silence was the defect; the remedy is noise on the day it stops being free.

This is deliberately remedy-neutral — it forecloses none of the three shapes if triage still wants one. The fork is reported on the card.

4. What this PR contains

One file, scripts/pm/dispatch-gates.mjs (+277 / -7):

  • COMPOUND_ANCHOR_LEDGER — the census above as data: 22 rows of file, name, accidental. Keyed by file plus name because runSelfTest proves a name-keyed ledger cannot hold both verdicts at once.
  • compoundAnchorDecls(source) — every compound-name anchor match at code positions only (comments masked first, string literals excluded via scanSource, so a fixture that builds a module source cannot enter the census).
  • withoutAnchor(source, name) — the counterfactual rename, word-anchored so runSelfTest does not rewrite runSelfTestCases.
  • Three self-test cases, live over the tracked tree (~3.3s):
    1. every compound-name match is classified — an unlisted or vanished one reds;
    2. masking an accidental row still costs no hint — reds and names the hidden hint;
    3. control: at least one genuine row is seen to lose a fixture hint, so a counterfactual that silently failed to rename cannot report "zero everywhere" and read as a pass.
  • Two identity pins that maskSelfTests and selfTestOnlyCallables are in the anchor's own population, so a rename that "fixes" either has to move the ledger row rather than the problem.
  • The stale claim in SELF_TEST_DECL's docblock corrected — it said the collision was one declaration; it is seven.

5. Verification

All at head 169cfb977.

pnpm check:pm-dispatch-gates — the module's own battery, run under the shared verify lock: dispatch-gates self-test: 1179 cases pass., VERDICT command-exit 0 (held 676s). The five new cases are in it, all green.

Ablation, both directions — mutation proved on disk before each reading, restored against HEAD and proved byte-exact by blob hash afterwards:

  • Injected a path literal into maskSelfTests — assertion 2 goes red: scripts/pm/dispatch-gates.mjs::maskSelfTests hides ["packages/ablation-only/src"]. This is the card's exact scenario, made loud.
  • Appended function ablationSelfTestNewcomer() to scripts/check-turbo-task-graph.mjs — assertion 1 goes red: unlisted: scripts/check-turbo-task-graph.mjs::ablationSelfTestNewcomer.
  • Restores verified: git hash-object equals the HEAD blob in both cases, zero residue, git status --porcelain empty.

Derived families (node scripts/pm/dispatch-gates.mjs, 15 matched) all green — exit codes captured before any pipe, verdict lines quoted in the terminal report. check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET locally (it grades a saved turbo log CI tees); recorded as NOT MEASURED, not as a red.

Changeset route: skip-changeset. The diff is one file under scripts/pm/** — PM tooling, publishing nothing from any package — which is the label's closed list. Applied on open per the label's read-back discipline.


Generated by Claude Code

…n their cost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude