fix(devx): measure-self-test-floor honours a per-row probe budget and reads definitions from masked source (#15573, #15574) - #15758
Conversation
…sked source measure-self-test-floor carries two defects of its own instrument, both in scripts/measure-self-test-floor.mjs. #15573 -- the probe spawns every run with a 120 s budget, which is ~3.3x under scripts/pm/dispatch-gates.mjs's own --self-test (1,415 cases, 6m39.690s measured). The baseline is killed, the mutated run is never spawned, and the row reads NOT MEASURED `killed by SIGTERM`: a limit of the INSTRUMENT recorded as a property of the FILE. The repair is a per-member budget, not a bigger default: probeEarlyReturn's default stays 120 s for the 180 rows that fit it, and ENTRY_BY_HAND gains a second row spelling, { entry, timeoutMs }, carrying a MEASURED budget with the reading it came from. main() passes it through. The shrink is also now a number rather than a silence: a run killed by this tool's own budget publishes `timedOut: true` (read from spawnSync's error.code === 'ETIMEDOUT', never from the signal, which a foreground wall-clock cap raises identically), and the sweep prints that population as its own line -- printed even when zero, because a zero that is printed is a reading. #15574 -- selfTestDefs read RAW source, so a name written inside a fixture string counted as a definition of the file quoting it. It now reads the same maskCommentsAndLiterals text injectEarlyReturn has read since #14963, so both halves of "where is the definition" answer from one text. Measured over the census: exactly 1 row's defs differ (dispatch-gates.mjs loses the phantom fixtureSelfTest) and 0 rows change ambiguity, so no entry, no verdict and no row moves. The --json payload change IS the fix. Both are pinned in the controls, which run inline on every invocation: a fixture whose decoys are definition-shaped text inside a string and inside a comment (collected once their delimiters are removed, so the pin reads the mask), and one slow fixture probed under two budgets -- killed under the smaller, DEFEATED under one that fits. Fixes #15573 Fixes #15574 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
|
ACCEPT — PR #15758 (head Both rulings are implemented as spelled: Measured by this seat: branch blob run against the main tree (static census, controls inline) → EXIT=0 (a failing control exits 1); Open question (the unfiled finding): A — filed by this seat as #15759 ( Flip + arm when both required jobs report success on Generated by Claude Code |
|
Flipped ready + auto-merge enabled (06:45Z, Generated by Claude Code |
Fixes #15573
Fixes #15574
Two defects of one instrument,
scripts/measure-self-test-floor.mjs, folded into one PR because they edit the same file. Both are the same shape: a limit of the INSTRUMENT recorded as a property of a FILE.#15573 — a per-row budget, not a bigger default
probeEarlyReturnspawned both runs with a 120 s budget, which is ~3.3x underscripts/pm/dispatch-gates.mjs's own--self-test. The baseline is killed, the mutated run is never spawned, and the row readsNOT MEASURED — killed by SIGTERM.The default stays 120 s — 180 of the 181 rows fit it, and raising it for all of them makes every sweep slower to serve one member. Instead
ENTRY_BY_HANDgains a second row spelling:main()passestimeoutMsthrough; nothing else may. The one carrier measured on the card carries a measured budget with the reading it came from, in the ledger's own comment convention.The shrink is now a number, not a silence. A run killed by this tool's own budget publishes
timedOut: true, and the sweep prints that population as its own line — printed even when it is zero, because a zero that is printed is a reading and a line that appears only when non-zero is not.timedOutis read fromspawnSync'serror.code === 'ETIMEDOUT', never from the signal. Measured: a timed-outspawnSyncreturns{ status: null, signal: 'SIGTERM', error: { code: 'ETIMEDOUT' } }— and the signal half of that is not exclusive to it. A foreground wall-clock cap kills children in exactly the same shape, so reading every SIGTERM as this budget would book rows against a number nobody set, in the direction that OVERSTATES how much of the survey this tool shrank. The SIGTERM verdict text itself is unchanged: it is the safe direction and stays so.The count line does NOT collide with the axis suspended on #13874
Checked rather than assumed. #13874 is closed (2026-09-04, option 2 landed); the axis suspended there is its option 3, and it belongs to a different instrument — the envelope-caller census in
packages/client/src/envelope-caller-census.test.ts, whose prohibition is that its count not move. The line added here counts rows of this census. Measured, with a control:The zero is a reading, not a dead probe. The count line stays in.
#15574 —
selfTestDefsreads masked text, and the payload change IS the fixselfTestDefsread RAW source, so a name written inside a fixture string counted as a definition of the file quoting it. It now reads the samemaskCommentsAndLiteralstextinjectEarlyReturnhas read since #14963, so both halves of "where is the definition" answer from one text.The
--jsondefsdiff, for the one row it moves:{ "file": "scripts/pm/dispatch-gates.mjs", "floor": "NONE", "defs": [ - "selfTest", "selfTestOnlyCallables", "maskSelfTests", "selfTestCaseLines", - "fixtureSelfTest" + "selfTest" ] }fixtureSelfTestis a name in a fixture array; nothing can call it. (selfTestmoves position because the first raw match was a docblock sentence; the surviving entry is the real definition.) That payload change is the repair, not a side effect of it — #14963's byte-identical constraint was that card's, not this one's.selfTestCaseLineslanded sincecf6b67164). The mechanism survived; the counts did not.Census-wide, old reading vs new, through the file's own
population():⇒ no entry, no verdict and no row moves. That row was hand-read before and stays hand-read, for the three real definitions that remain — and the static census output is byte-identical before and after (
diffexit 0).The pins, and what they are pinned in
--self-testflag — its controls run inline on every invocation, precisely so they cannot become unrun (theinlineroutecheck-self-test-wired.mjsrecords, and that gate is green here). So both pins live inrunControls(), which is this file's self-test and is stronger than a flag:node scripts/measure-self-test-floor.mjs --self-testruns them, and so does every other invocation.The definition pin carries decoys whose names DIFFER from the real ones. The existing anchor fixture spells every decoy
selfTest, which is right for an anchor (it takes ONE match; the question is WHICH) but cannot pin a collector — aSetof names collapses the decoys into the real name, and the wrong answer and the right answer become the same list. Only a decoy with its own name can be seen to be absent. Both collected shapes are covered (afunctionand an arrow inside a fixture string, plus afunctioninside a comment), and the other direction is pinned too: with the literal delimiters removed the same collector DOES collect them, so the verdict reads the MASK rather than a fixture whose decoys were never collectable.The budget pin is one fixture probed under two budgets, nothing else different — killed under the smaller,
DEFEATEDunder one that fits. It sleeps rather than spins: a control that runs on every invocation may not take a core with it on a shared box.A ledger-integrity control reads every shipped row and refuses an unknown field, because a misspelled
timeoutMsis not a smaller budget — it is NO budget, i.e. the default, i.e. the SIGTERM this field exists to end.Verification
The single-row probe went through
population()->probePlan()->probeEarlyReturn(), the same pathmain()takes (a throwaway, not committed):--probetakes no row selector, and sweeping all 181 rows twice does not fit a foreground turn.baselineExit=0 mutatedExit=1 mutatedBytes=200, head✗ dispatch-gates self-test: selfTest() returned without reaching its verdict,· 434.3 s walltimeoutMsremoved from the rowkilled by SIGTERM,timedOut: true· 120.2 s wallselfTestDefsDEFINITION CONTROL FAILED: ... got ["commentSelfTest","fixtureSelfTest","selfTest","fixtureSelfTestLater","runSelfTestTwice"], expected ["selfTest","runSelfTestTwice"]Both ablations were run after the implementation was committed, trap-guarded with absolute paths, each mutation proved on disk by counting the removed and injected text (one attempt was caught by that guard as a zero-hit edit and its reading discarded, not re-used), and each restore proved by
git hash-objectequal to the HEAD blob andgit diff HEADempty.The heavy legs ran through
scripts/pm/os-verify-lock.sh(OS_VERIFY_LOCK_SLOT=issue-15573):VERDICT command-exit 0 · held the lock 436sandVERDICT command-exit 0 · held the lock 123s. Those are shared-box seconds, as the lock itself says.baseline run failed (exit 1). That was my worktree'snode_modulessymlink, not this tree and not this change:dispatch-gates' self-test runsgit check-ignore, which refusesfatal: pathspec 'node_modules/...' is beyond a symbolic link. With a real directory the baseline exits 0. Recorded because the same shape reads exactly like #15515, which the card warns masks this one.Gates
node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack(exit 0) derived 26 commands; all 26 were run. 20 exit 0. The other six are NOT MEASURED — this box has no root install, each named:typescript—check-reference-carrier-shape.mjs(and its--self-test),check:driver-memory-census(exit 3,PREREQUISITE NOT MET)yaml—check-ci-filter-parity.mjs,check-closing-keyword-parity.mjs(and its--self-test) (exit 3,PREREQUISITE NOT MET)@typescript-eslint/parser—check-comment-mask-corpus.mjs(exit 1, a bareERR_MODULE_NOT_FOUNDrather than the tidy exit 3 — same class, different spelling)origin/main, and 15 files the families are derived FROM changed across that range. The family list above is therefore a reading about this tree; CI's is authoritative.pnpm check:nul-bytesexit 0, plus a direct control-byte scan of the changed file (zero hits).node scripts/pm/check-governed-merges.mjs --test scripts/measure-self-test-floor.mjsexit 0 —✅ NOT governed.scripts/pm/dispatch-gates.mjs --self-testitself ran green on this branch ateb52f996(exit 0, 1,415 cases) — it is the probe's own baseline above.Changeset
skip-changeset, applied. AGENTS.md: "A bug fix in a released package takes apatchchangeset — never none, and ⛔ neverskip-changeset: that label is for a diff that publishes nothing from any released package." This diff is one repo-internal instrument underscripts/; nothing is published from any package.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code