Skip to content

Commit 4073030

Browse files
os-elon-muskclaude
andauthored
fix(scripts): read the self-test handshake at two injection points, so a flag above the returned block is no longer scored HELD (#19026)
Fixes #18987 Clause-②: no The early-return probe in `scripts/measure-self-test-floor.mjs` read ONE injection point — `return;` as the first statement of the dispatched self-test function — and published a three-valued verdict from it. A handshake set ABOVE the block that function RETURNS stays unset under that injection, the dispatch refuses, and the row reads HELD, while the same `return;` one statement lower, inside the block, leaves the handshake SET and the run silent at exit 0. The 2026-09 census cleared `scripts/check-single-claim-paths.mjs` HELD exactly that way, on the revision that carried that ordering defect. A file whose dispatched function returns a block is now read at BOTH points, and HELD requires both. `HANDSHAKE-ABOVE-BLOCK` is the new verdict for "point 1 refused, point 2 exited 0", counted apart from HELD as ACCIDENT already is. ## The prerequisite: the instrument refused to run at all `node scripts/measure-self-test-floor.mjs` exits 1 on `main` — census, `--probe` and `--probe --only` alike — because `classifyHandshake` reads three landed spellings as `none` while `LANDED_HANDSHAKE_NAMES` sees them, and the live completeness check refuses rather than printing the smaller of two disagreeing populations. That is the defect #18329 records, and nothing about this card could be measured without it, so the first commit widens the recogniser exactly as far as running the probe needs. #18329 remains open and this PR does not address it beyond that prerequisite; the seat decides what is left of it. The three spellings, all of them the return value bound before it is compared: | file | spelling | |:--|:--| | `scripts/check-workflow-step-name-quoting.mjs` | `const verdict = selfTest();` then `if (verdict !== SELF_TEST_VERDICT)` | | `scripts/pm/check-expected-skips.mjs` | `const r = selfTest();` then `if (r.verdict !== SELF_TEST_VERDICT)` | | `scripts/pm/check-prior-rulings.mjs` | same record shape | **`LANDED_HANDSHAKE_NAMES` needed no addition** — its `*VERDICT` comparison pattern already matches all three, which is why the disagreement was one-sided. The refusal text prescribes widening both "with a control on each"; the measurement says only one of the two was short, and adding a spelling to the second opinion that it already reads would weaken it, not widen it. **The GUARD half is a boundary the measurement forced.** Admitted as "the binding compared against a named operand anywhere", a FOURTH row moved: `scripts/check-plugin-teardown-shape.mjs` binds `const selfTestCode = selfTest();`, refuses on its FLAG one line later, and then compares that binding against `EXIT_PREREQUISITE_NOT_MET` inside `process.exit(cmp ? a : b)` to choose which code to exit with. Choosing a value is not asking whether the self-test finished, so the bound spelling reads a comparison that heads an `if (...)` and nothing else. Its control is a fixture reduced from that file. Delta over the 223-row census: exactly the three rows move `none -> sentinel`. Before: 101 sentinel, 103 flag, 1 helper, 18 none. After: 104 sentinel, 103 flag, 1 helper, 15 none. ## The two controls, with exit codes **Negative control — `main`'s own `scripts/check-single-claim-paths.mjs`** (PR #18986 has not landed, so the flag still sits above the returned block). Through the shipped path, `node scripts/measure-self-test-floor.mjs --probe --only scripts/check-single-claim-paths.mjs`: ```text before (this branch's first commit, one injection point) -- exit 0 0 DEFEATED, 1 HELD, 0 ACCIDENT, 0 NOT MEASURED. HELD scripts/check-single-claim-paths.mjs -- ✗ check-single-claim-paths self-test: selfTest() returned without reaching its verdict, after (two injection points) -- exit 0 0 DEFEATED, 0 HELD, 1 HANDSHAKE-ABOVE-BLOCK, 0 ACCIDENT, 0 NOT MEASURED. 1 of the 1 probed return a block and were read at BOTH injection points; HAB scripts/check-single-claim-paths.mjs -- point 1 refused (✗ check-single-claim-paths self-test: selfTest() returned wi) point 2, inside the block it returns: exited 0 printing 0 byte(s) ``` **Positive control — PR #18986's head `03310bc914`**, the same file with the handshake moved inside the block as its last statement, probed in a second worktree at that revision (fetched into a ref of this session's own, never merged into this branch): ```text { "verdict": "HELD", "points": 2, "first": { "verdict": "HELD", "baselineExit": 0, "mutatedExit": 1, "mutatedBytes": 210 }, "second": { "verdict": "HELD", "baselineExit": 0, "mutatedExit": 1, "mutatedBytes": 210, "mutatedHead": "✗ check-single-claim-paths self-test: selfTest() returned without reaching its verdict," } } ``` Both legs' baselines exit 0, so both readings are available rather than inherited from a red tree. ## The anchor, and what it deliberately does not read The second point is the returned `async () => {` IIFE at the dispatched function's own statement level: read over the comment-and-literal mask, line-anchored, and at brace depth 0. Measured over the census: 217 rows resolve an entry and exactly ONE carries a second point, so no other row's reading moves; every one-point row publishes `points: 1` and the reason it has one, rather than an absent field. Unadmitted, each with its reason in the docblock rather than for want of a regex: - an `await (async () => {` in the MIDDLE of the body (`check-half-states.mjs`, `check-governed-queue-guard.mjs`): an early return there skips a battery while the self-test still reaches its own verdict, which is HOLE 1, and the two holes are never summed; - a returned `.then(callback)` and an `async function` expression: no carrier in this tree. The header also records what this point CANNOT read, because a reader will otherwise take a HELD for more than it is: work after the handshake whose failure is SILENT. Measured on `scripts/check-system-context-census.mjs`, whose handshake sits 48 lines above its own battery floor — a `return;` immediately after the flag leaves both runs printing the identical 18566 bytes at exit 0, so a mutation reports "no observable effect" and no probe can stand in for reading the ordering. That file is in the acceptance notes below. ## Tests - `node scripts/measure-self-test-floor.mjs` — exit 0, 223 rows (exit 1 on `main`). Its controls run inline on every invocation: **119 -> 154 control assertions**. - Ablations, each from the committed state through `scripts/ablation-replace.mjs`, each restored with the blob hash equal to HEAD's and `git diff HEAD` empty: - compose the second point's DEFEATED as HELD -> 2 controls red (`a handshake set ABOVE the block the self-test returns read HELD`); - drop the anchor's LINE-START rule -> anchor control red (anchor moved 627 -> 442); - drop the anchor's DEPTH rule -> anchor control red (anchor moved 627 -> 512); - drop the `if (...)` guard from the bound sentinel -> boundary control red (`read sentinel, not flag`). The first ablation of LINE-START came back GREEN and that is in the history: the only mid-line decoy was also nested, so DEPTH covered for it. Two decoys now isolate the rules, one each, and the re-run reds. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `e39670e33`: 26 families derived, all 26 run, **26 exit 0**; `--ran` reconciliation `✓ 26 derived famil(ies) accounted for — 26 run, 0 NOT-MEASURED`. - `pnpm lint` (the whole repo, `eslint . --no-inline-config`): exit 0 in 1m46s — the full run, so no narrowing to declare. - No package is touched, so there is no build closure and no package test to run. ## Changeset `skip-changeset`. Nothing published moves: the only changed path is `scripts/measure-self-test-floor.mjs`, which lives in the private root package (`@objectstack/spec-monorepo`, `private: true`), and no published package's `files[]` carries an entry escaping its own directory (measured: 0 of them). Symbol grep over `packages/`: `returnedBlockAnchor`, `probeVerdictPoints`, `measure-self-test-floor` -> 0 files; positive control `defineStack` -> 395. ## The census document is deliberately untouched `docs/audits/2026-09-self-test-shape-census.md` is the instrument's own output and is NOT regenerated here. Its population is 179 rows at `d30ccb9bd`; the census is 223 rows today, and its probe column is a full sweep this container cannot run in one foreground turn. Regenerating it would move rows for drift and for the recogniser widening as well as for this point, which makes "which rows moved" unreadable — the one thing a regeneration owes. A regeneration is its own card, and this PR's readings above name the row that changes: `check-single-claim-paths.mjs`, HELD -> HANDSHAKE-ABOVE-BLOCK. ## Reader test Run `node scripts/measure-self-test-floor.mjs --probe --only scripts/check-single-claim-paths.mjs`: on `main` it exits 1 printing no census at all, and on this branch it prints `1 HANDSHAKE-ABOVE-BLOCK` with `point 2 ... exited 0 printing 0 byte(s)` — the reading the census's HELD did not take. ## Acceptance notes To file (class b, a declared-contract violation; dedupe words: `check-system-context-census handshake above floor` · `selfTestReachedVerdict before battery floor` · `success line printed before the floor evaluates` · `self-test verdict ordering static` · `handshake not the last statement`): `scripts/check-system-context-census.mjs` sets `selfTestReachedVerdict = true` at its line 2849 and then runs 48 lines of battery floor, with its success line printed ABOVE the flag. AGENTS.md states 「Set the flag as the self-test's last statement, after its success line prints」, and the file's own comment claims the floor is 「Evaluated after every battery has had its chance and BEFORE the verdict」, which its own text contradicts. An early exit anywhere in that floor region leaves a run that printed `all cases passed`, exited 0, and never evaluated its floor — with the handshake reading "reached". Measured here: the mutation is invisible to this probe (identical 18566 bytes, both exit 0), so this is a static ordering finding and the instrument correctly reports that it observed nothing. noted, not filed: the record spelling of the bound sentinel (`const r = selfTest(); if (r.verdict !== ...)`) refuses with a TypeError rather than with the sentence its author wrote — after an early return the binding is `undefined` and the property read throws. It is a real, loud non-zero exit and the probe publishes whatever the run said, so the column's answer (`sentinel`) is correct; the wording is a property of the shape, recorded in the recogniser's docblock. Next toucher: whoever widens `classifyHandshake` again. noted, not filed: this instrument's docblock still quotes "181 rows" and "158 differently shaped self-tests" from an older base against today's 223. Prose drift in a file this PR already touches; correcting it here would bury the diff that matters. Next toucher: whoever regenerates the census document. --- _Generated by [Claude Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9bdc384 commit 4073030

1 file changed

Lines changed: 700 additions & 13 deletions

File tree

0 commit comments

Comments
 (0)