fix(devx): anchor the self-test floor probe on the real definition, not the first text that reads like one - #15580
Conversation
`injectEarlyReturn` took the FIRST match of `function <name>(` against RAW
source, so a docblock sentence or a fixture string won a definition. In
`scripts/pm/dispatch-gates.mjs` both stand ahead of the real one, and the
injection landed inside a template literal: the copy is then a SyntaxError,
which exits non-zero AND prints a stack, so the verdict reads HELD — a hold
awarded to a gate for the probe having broken its own copy of it. That was
recorded as an `ENTRY_BY_HAND` null describing the FILE, and read for months as
a property of it.
Two rules now make the anchor the definition, and the control fixture carries
one decoy of each kind ahead of its real definition so neither rule can be
dropped silently:
MASKED the match is taken over `maskCommentsAndLiterals(src)` — the
existing `scanSource` flags, comments and literals blanked,
offsets and line numbers preserved.
LINE-START the match must begin a line (`export` / `async` prefixes kept).
⛔ The POPULATION criterion keeps reading `maskComments`: every `--self-test`
dispatch names the flag with a string literal, so masking literals there would
empty the census rather than shrink it. A control pins the two masks to their
opposite answers on the same fixture.
Measured: the census is byte-identical to main (178 files, `--json` diff empty),
and over all 171 mechanically anchored rows the injection offset does not move —
the new rules pick the same byte in every one.
Ledger: the `scripts/pm/dispatch-gates.mjs` null becomes `'selfTest'`. Its copy
now parses and runs (measured by hand: exit 1, `selfTest() returned without
reaching its verdict`), so the row's remaining NOT MEASURED is the BASELINE
precondition — the probe writes its copy under `scripts/`, where that gate's own
single-site sweep finds the near-duplicate and refuses (#15515, a separate card,
not worked around here) — and the row now states that reason instead of a false
one about this file. Two stale counts in the same ledger corrected:
`check-platform-checklist` dispatches SIX self-test functions, and the
docblock's "nine files" is TEN rows.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
The docblock above `SELF_TEST_VERDICT` told readers the mechanical probe in `scripts/measure-self-test-floor.mjs` cannot read this file. Since the anchor repair it can: the injection lands on the definition below and the copy parses and runs. What remains is a different, named limit — the probe's own copy under `scripts/`, which this gate's single-site sweep reads as a second carrier (#15515). Comment only; the line count is unchanged, so nothing below it moves. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…rection moves to #15553 PR #15570 rewrites the same `SELF_TEST_VERDICT` region (it inserts the `selfTestCaseLines` docblock directly after the sentence this branch edited), and `git merge-tree` reports a conflict between the two, so this PR would go dirty the moment that one lands. It is ahead in the queue and the serial-file rule allows one live PR per hot file, so the hunk is dropped here rather than raced. `scripts/pm/dispatch-gates.mjs` is restored from `origin/main`; its blob is byte-identical to this branch's merge base (f9dbdd6 at cf6b671, at origin/main 0c5e973 and in the tree now), so no content from the newer main comes in with it. Nothing else on this branch changes: the anchor repair, the DECOY fixture and the `ENTRY_BY_HAND` row all live in `scripts/measure-self-test-floor.mjs`. The docblock there still tells readers the mechanical probe cannot read that file, which this branch makes false. That correction is deferred to #15553 rather than dropped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
|
ACCEPT — PM seat Reviewed against the three-dot diff What I checked in the diff:
Dev's verification quoted (on Out-of-scope findings filed by the dev: #15573 (probe spawn timeout 120 s < the 6m40s dispatch-gates self-test) and #15574 ( Flipping ready + enabling auto-merge. Fixes #14963. Instrument Generated by Claude Code |
Fixes #14963
injectEarlyReturntook the FIRST match offunction NAME(against RAWsource. In
scripts/pm/dispatch-gates.mjsa docblock sentence and then aFIXTURE STRING stand ahead of the real definition, so the ledger carried
— a limit of the INSTRUMENT recorded as a property of the FILE, and read that
way for months.
worse and is now written down: a copy that dies in the parser exits non-zero
and prints a stack, so
mutatedSpokeis true and the three-valued verdictscores it HELD — a hold awarded to a gate for the probe having broken its
own copy of it. Only the hand-written
nullkept that off the sweep.The repair — TWO rules, neither of them redundant
maskCommentsAndLiterals(src)—scanSource's existingcommentandliteralflags OR-ed and handed to the module's ownblank(). Offsets and line numbers survive, so the index found in masked text slices the ORIGINAL.export/export default/asyncare the only prefixes a definition in this tree carries.scripts/js-comment-mask.mjsis not touched. Itsorigin/mainstate alreadyexports everything this needs (
scanSourcereturns{ comment, literal, interpolation }and flags interpolation bytes as literal at the end of itspass;
blanktakes a flag array). Nothing was missing, so #15532 is left toextend that module on its own.
⛔ The POPULATION criterion keeps reading
maskComments. Every--self-testdispatch names the flag with a string literal, so the code-only mask would not
classify a file generously — it would empty the census, taking this
instrument's own control fixtures (deliberately strings) with it. A control now
pins the two masks to their OPPOSITE answers on the same fixture.
The controls, inline on every invocation, in the file's convention
(1) the anchor — one fixture carrying three decoys ahead of its real
definition, one per way the anchor can take the wrong text, in the order they
occur in
dispatch-gates.mjs: a docblock sentence (COMMENT), a fixture the gatefeeds its own scanner (TEMPLATE LITERAL), and a named function expression held
as a value (real CODE, MID-LINE). Six fixture-validity assertions pin that each
decoy is the kind it claims to be (
scanSourceflags read directly), that anunmasked first match lands on a decoy, and that masking ALONE still lands on the
mid-line one — so neither rule can be carrying the other. Then the injection
position, and the fixture is SPAWNED: the real definition is holed, so the one
reading that can only come from anchoring on it is
DEFEATEDwith zero bytesprinted — which also says the copy parsed and ran.
(2) the two masks stay apart —
DISPATCH.test(maskComments(HOLED_GATE))holds (unchanged) and
DISPATCH.test(maskCommentsAndLiterals(HOLED_GATE))mustbe false. Census count unchanged: 178, and the whole non-probe census
output is byte-identical to main.
(3) the existing controls — sentinel
HELD, helper-handshakeHELD/DEFEATEDon the one-line pair,ACCIDENT, and the baseline precondition — allstill read what they read, unmodified. The three-valued verdict, the #15455
precondition, the #15491 helper control and the #15517 DISPATCH regex are
untouched.
The ledger row
'scripts/pm/dispatch-gates.mjs': nullbecomes'scripts/pm/dispatch-gates.mjs': 'selfTest'. It stays hand-read — raw source holds four self-test-shaped names —but for THIS FILE's reason, not the instrument's, and the comment now says the
measured thing.
The
check-platform-checklist.mjsrow stays as anull; only its prose iscorrected. The card says FIVE self-test functions and the comment said FOUR;
both are stale — the dispatch calls SIX (
selfTestTrapVocabulary,ProvisioningUse,UnreferencedRecipes,MetaCallSpelling,SourceLineCitations,SymbolAnchors, each with its ownrequireReachedVerdicton both legs). The same "four" in theHELPER_HANDSHAKE_GATEdocblock is corrected too, and the ledger docblock's"nine files" is ten rows.
The dispatch-gates probe reading — MEASURED, and it is still NOT MEASURED
Narrowed to that one row through
main()'s own selection (ENTRY_BY_HANDfirst). A full
--proberuns 178 self-tests twice and does not fit thiscontainer's ~10-minute foreground cap: NOT MEASURED, said as such.
Two different limits, each masking the other, and neither is this card's:
(
time node scripts/pm/dispatch-gates.mjs --self-test→ real 6m39.690s,exit 0,
✓ dispatch-gates self-test: 1415 cases pass.). Filed as measure-self-test-floor's probe spawns with a 120s timeout, shorter than the self-tests it measures — dispatch-gates.mjs reads NOT MEASURED, killed by SIGTERM #15573.under
scripts/, and this gate's own single-site sweep reads it. Measureddirectly rather than assumed, by placing the same copy by hand:
⛔ Not folded in, and not worked around. #15515 is a separate queued card on this
same file.
What this card's repair does deliver, measured by hand the way #14960 did —
the anchor now lands on the definition and the copy is RUNNABLE, which is
exactly what the
nullrow denied:That is #14960's expectation reproduced on today's 1415-case count: before the
handshake landed, an early return took this gate from
1288 cases passto zerobytes and exit 0; after it, exit 1 with the named diagnostic. The probe would
score this HELD the moment #15573 and #15515 are out of its way.
Verification — every exit code captured before any pipe
Final commit
08cf96d57, tree clean. The census,--json,runControlsandcheck:nul-bytesreadings below were re-taken on that head after the revert; therest were taken on
b366d16a8, whose tree is this one plus the reverted docblock.A static differential the census cannot show
For all 171 rows the probe anchors mechanically, the injection OFFSET was
recorded before and after the change, using
main()'s own entry selection:The new rules pick the same byte in every row that was already measurable —
the repair is inert on the measured population and only reaches the rows the
ledger had to carry by hand.
Ablation, on the COMMITTED implementation
Each leg mutates one rule, proves the mutation reached disk with anchored counts
in BOTH directions, reports where the anchor then lands on the real carrier, runs
the instrument, restores with
git checkout HEAD -- ABSOLUTE_PATHand provesthe restore by blob hash AND an empty
git diff HEAD, all undertrap ... EXIT INT TERM. Nodist/is involved: the instrument is resolved bypath, not through a package
exports, so there is no rebuild leg.dispatch-gates.mjsline 11507function selfTest() {^dropped from both patterns (LINE-START removed)ANCHOR CONTROL FAILEDandread as NOT MEASURED (mutation had no observable effect)maskCommentsAndLiterals(src)→src(MASKED removed)Restore proved on both legs:
blob == HEAD (18088a5e42c248e91a5e82772870f6cb8d125aa2)andgit diff HEADempty.to red as a false HELD (the SyntaxError direction). It reds as
mutation had no observable effect: injecting into the fixture's PLAIN templateliteral keeps the file parseable — what breaks parsing in the real carrier is
injecting the
/*...*/marker into a BLOCK COMMENT, which closes it early. Andon the real
dispatch-gates.mjs, either rule alone already lands on line11507 today (its decoys are mid-line inside a block comment, and its fixture
lines begin with a quote). So the carrier does not currently discriminate
between the two rules — the FIXTURE does, which is the whole reason the controls
are the pair and not the file. The triage's "a line-anchored match may be
sufficient on its own — measure before building the mask" is answered: on
today's carrier, yes; the mask is what keeps that true of the next one.
Derived families
derived 24 commands while this branch carried 2 paths. All 24 ran and all 24
exited 0 — nothing reported exit 3,
PREREQUISITE NOT METorERR_MODULE_NOT_FOUND, so there is no NOT MEASURED row to record here. Longest:check-comment-mask-corpus61s (5937 files, 0 disagree),check:pm-dispatch-gates~7m.Re-derived on
08cf96d57(1 path now): 22 commands, a strict subset ofthose 24 —
commreports 0 added and 2 dropped(
check:declared-population-live,check:pm-dispatch-gates). All 22 were re-runon the new head and all 22 exited 0.
cf6b67164andorigin/mainhas since moved to0c5e97368, across which.github/workflows/lint.yml,package.jsonandscripts/check-registry-log-declared.mjschanged — none of them paths this PRtouches. CI derives the families again against the merge result.
skip-changeset:scripts/**publishes nothing from any package. The label isset on this PR.
Out of scope, filed separately — never in this PR
measures;
dispatch-gates.mjs(6m40s) readskilled by SIGTERMunder a plain--probe. Measured above.[finding]selfTestDefs()still reads RAW source, so a name ina fixture string counts as a definition. Swept over the whole census: 1 row
differs (
fixtureSelfTestatdispatch-gates.mjs:12715), 0 verdicts changetoday. Left out of this PR because
defsis published in--json, and thiscard required a byte-identical
--jsondiff.The
dispatch-gates.mjsdocblock correction is DEFERRED, not droppedA second commit here corrected the docblock above
SELF_TEST_VERDICT, whichtells readers the mechanical probe cannot read this file — false after this
repair. It is reverted in
08cf96d57: PR #15570 rewrites that exact region(it inserts the
selfTestCaseLinesdocblock right after the sentence editedhere),
git merge-treereports a conflict between the two, and it is ahead inthe queue — so this PR would go
dirtythe moment it lands, and the serial-filerule allows one live PR per hot file. The correction moves to #15553.
The revert takes
scripts/pm/dispatch-gates.mjsfromorigin/main, whose blobis byte-identical to this branch's merge base
(
f9dbdd690a755a972a23005c4efffebe4dc13303atcf6b67164, atorigin/main0c5e97368and in the tree now), so no content from the newer main rides in withit. This PR now changes exactly one file.
🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code