Skip to content

fix(scripts): read the self-test handshake at two injection points, so a flag above the returned block is no longer scored HELD - #19026

Merged
os-elon-musk merged 4 commits into
mainfrom
claude/issue-18987-self-test-floor-second-injection
Sep 18, 2026
Merged

os-elon-musk merged 4 commits into
mainfrom
claude/issue-18987-self-test-floor-second-injection

Conversation

@os-elon-musk

Copy link
Copy Markdown
Collaborator

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:

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):

{ "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

`classifyHandshake` read only the DIRECT comparison of a self-test's return
value (`selfTest() !== SELF_TEST_VERDICT`). Three landed files bind the value
first and compare the binding, or one field of it, a statement later:

  const verdict = selfTest();   if (verdict !== SELF_TEST_VERDICT)      -- check-workflow-step-name-quoting
  const r = selfTest();         if (r.verdict !== SELF_TEST_VERDICT)    -- check-expected-skips, check-prior-rulings

All three read `none`, `LANDED_HANDSHAKE_NAMES` saw them, and the live
completeness check therefore refused the WHOLE census on every invocation --
the census, `--probe` and `--probe --only` alike, exit 1, nothing printed.

Measured delta over the 223-row census: exactly those three rows move
`none -> sentinel`; 103 flag, 1 helper, 104 sentinel, 15 none after.
`LANDED_HANDSHAKE_NAMES` needs no addition -- its `*VERDICT` comparison
pattern already sees all three, which is why the disagreement was one-sided.

The GUARD half is a boundary the measurement forced: admitted without it, a
fourth row moved -- `check-plugin-teardown-shape.mjs`, which binds the return
value, refuses on its FLAG, and then compares the binding against a named
constant inside `process.exit(<cmp> ? a : b)` to choose an exit code. 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.

Controls, both directions on each: both carrier shapes read `sentinel`;
neither fixture matches the direct pattern; a literal operand reads `none` in
both shapes (the ACCIDENT boundary, one property deeper); the exit-code
selection fixture reads `flag` while still carrying the binding; bound and
compared INSIDE the self-test reads `none`; the spelling written only in a
comment reads `none` and reads `sentinel` un-hidden; and both new fixtures
survive the name-independence rename.

Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
Co-authored-by: Claude <noreply@anthropic.com>
…o a flag above the returned block is no longer a HELD

The probe injected `return;` as the FIRST statement of the dispatched function
and read one answer from it. A handshake set ABOVE the block that function
RETURNS stays unset under that injection, the dispatch refuses, and the row
read 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.

Measured on this base (223 rows, 217 with a resolvable entry): ONE row carries a
second point -- `check-single-claim-paths.mjs` -- so no other row's reading
moves, and every other row publishes `points: 1` with the reason it has one.

  before  --probe --only scripts/check-single-claim-paths.mjs -> 0 DEFEATED, 1 HELD, exit 0
  after   same command -> 1 HANDSHAKE-ABOVE-BLOCK, exit 0; point 1 refused,
          point 2 exited 0 printing 0 bytes
  control PR #18986's head (03310bc, the same file with the handshake moved
          inside the block) -> HELD at BOTH points, both baselines exit 0

The anchor is the returned `async () => {` IIFE at the dispatched function's own
statement level: masked, line-anchored and at depth 0. Deliberately unadmitted,
each with its reason in the docblock: an `await (async () => {` in the middle of
the body (an early return there skips a battery while the self-test still
reaches its verdict -- hole 1, never this one), a returned `.then()` callback
and an `async function` expression (no carrier in this tree). The header also
records what the point CANNOT read: work whose failure is silent, measured on
`check-system-context-census.mjs`, whose handshake sits 48 lines above its own
battery floor and where both runs print the same bytes.

Costs: the second pair of spawns runs only for a row that HELD at the first
point AND has a second point, so a red-baseline checkout costs what it did.

Controls, both directions: the pair of fixtures differing only in WHERE the
handshake line sits (above -> HANDSHAKE-ABOVE-BLOCK, inside -> HELD at both);
five anchor decoys ahead of the real block, one per rule; an awaited mid-body
block and a block-free self-test both yielding no second point; a one-point row
reading identically through the composer and through the probe; and a row
DEFEATED at the first point probed exactly once.

Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
Co-authored-by: Claude <noreply@anthropic.com>
…with a decoy each

An ablation of the LINE-START rule came back GREEN: the only mid-line decoy in
the fixture was also nested, so the DEPTH rule covered for it and deleting a
real rule moved no verdict -- the shape of false green this instrument exists
to refuse, made by its own controls.

Two decoys now isolate the rules, each a branch a passing run never enters:
a one-line `if (...) return (async () => {` at the body's own depth (only
LINE-START excludes it) and a line-anchored return one level deep inside an
`if` block (only DEPTH does). Their validity is asserted the way the anchor
reads them -- masked text, depth counted from the definition's own brace --
and an unanchored, depth-blind reading is pinned to land on a decoy.

Re-measured after the change: ablating either rule reds the anchor control.

Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e39670e334

① Derived judgments

  • Surface: scripts/measure-self-test-floor.mjs only (+700 / −13), three commits plus one merge of origin/main (the merge carries no file of this diff). Off the governed register (check-governed-merges.mjs --test → NOT governed, seat-run); no published package moves. No landing on the file since the claim; origin heads on the stem: only this branch.
  • The second injection point. injectEarlyReturn(src, name, { point: 'inner' }) injects return; as the FIRST statement of the block the dispatched function RETURNS (returnedBlockAnchor: one admitted spelling, return (async () => { at the function's own statement level — MASKED, LINE-START, DEPTH 0, each rule with a decoy control; await (async () => { mid-body and .then(…) chains deliberately unadmitted, with reasons); HELD requires BOTH points, and 「point 1 refused, point 2 exited 0」 is the new verdict HANDSHAKE-ABOVE-BLOCK, counted apart from HELD as ACCIDENT already is. The dispatch's two anchor hypotheses (last statement of the block; right after the handshake assignment) were falsified by measurement — the first is a no-op on a repaired file (NOT MEASURED for every correct file), the second would flag 81 of 103 flag/helper rows on a trailing return 0; — and the first-statement anchor is the one giving two opposite positive readings on the two controls. Correct, and the falsification is the seat's hypothesis, as invited.
  • The prerequisite ([finding] measure-self-test-floor.mjs refuses to print any census on current main — its recogniser reads two landed handshake spellings as absent, so the instrument cannot answer the floor question anyone runs it for #18329's defect), shipped first in its own commit. classifyHandshake gains the BOUND sentinel spelling (const v = selfTest(); if (v !== SENTINEL) and the record form if (r.verdict !== SENTINEL)), bounded by CROSSES / NAMED / NO-ARGS and a measured GUARD boundary (the comparison heads an if; check-plugin-teardown-shape.mjs compares its binding inside process.exit(cmp ? a : b) to CHOOSE a code and must stay flag — a fourth row that moved without the guard, pinned back by a fixture reduced from it). LANDED_HANDSHAKE_NAMES needed no addition (the disagreement was one-sided) — the refusal text's 「widen both」 followed where measurement said it was short. Over the 223-row census exactly the three rows the refusal named move nonesentinel, and exactly one row carries a second point. The census prints again on this head (main refuses).
  • Seat re-measure on a scratch worktree at this head: the bare census exits 0 (on main: exit 1, the [finding] measure-self-test-floor.mjs refuses to print any census on current main — its recogniser reads two landed handshake spellings as absent, so the instrument cannot answer the floor question anyone runs it for #18329 refusal); --probe --only scripts/check-single-claim-paths.mjs on main's file → 0 HELD, 1 HANDSHAKE-ABOVE-BLOCK (「point 2, inside the block it returns: exited 0 printing 0 byte(s)」) — the defect diagnosed; the same probe on PR fix(scripts): check:single-claim-paths keeps its own two contracts — a failed board read exits PREREQUISITE NOT MET, and the self-test handshake is set by the verdict it certifies #18986's head of that file (03310bc914, copied into the worktree) → 1 HELD, both points refused — the positive control; the file's inline controls run on every invocation; --pair 19026 exit 0. The seat could not run eslint in its own environment (the shared checkout's flat config fails to load a module — environmental, reproduced on main); the dev's whole-repo pnpm lint exit 0 stands as the reading.
  • Ablations (dev, from the committed state through scripts/ablation-replace.mjs, restore proven by blob hash): composing point 2's DEFEATED as HELD → 2 controls red; dropping LINE-START → red (after two isolating decoys were added in their own commit — the first run came back green because the only decoy was also nested and DEPTH covered for it; declared, not silently re-run); dropping DEPTH → red; dropping the if guard → the boundary control red.

② Semver level

  • skip-changeset is correct: root scripts/ ships in no package's files[]. Clause-②: no on the claim (5729187979) and in the body; --pair 19026 at 2026-09-18T12:17Z: exit 0, no widening tell.

③ Boundary flags

Implemented-by: claude/issue-18987-self-test-floor-second-injection
Reviewed-by: session_01BTeBejoPUvRHN8WdAJC6oF

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing record — every pre-check met, blocked at the channel (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF) · 2026-09-18T12:45Z

Every check on e39670e334 completed with none red (35 runs: 24 success, 11 rostered skips; the last completed at 2026-09-18T12:43Z, read at 2026-09-18T12:45Z); the review of record is 5729976404 (PASS); --pair 19026 exit 0; ACCEPT 5729976748 on #18987. One file, scripts/measure-self-test-floor.mjs, and no other open draft on it. The landing act (POST …/pulls/19026/ccr/ready_for_review then PUT …/pulls/19026/ccr/auto_merge {"merge_method":"SQUASH"}) is not attempted here: this session's auto-mode classifier refused the same act three times today (record 5726459594 on PR #18934; again at 2026-09-18T11:39Z), the allow rules that would make it the seat's are the maintainer's hand (#19014, blocked at every write route), and a classifier refusal is 「no channel」 per references/rest-channel.md. The PR stays draft, ACCEPTED, with review requested from GOVERNED_APPROVERS — it lands by the approver's hand, as seven did today, or by this seat once the channel opens. ⛔ Nothing here is a finding against the PR.


Generated by Claude Code

@os-elon-musk
os-elon-musk marked this pull request as ready for review September 18, 2026 14:27
@os-elon-musk
os-elon-musk added this pull request to the merge queue Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

Landing record — landed on the seat's record through the CCR route (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF) · 2026-09-18T14:29Z

Channel: the two allow rules of PR #19047 (67624b78d, merged 2026-09-18T14:20Z) on disk, and the session's permission mode switched by the maintainer from auto to manual confirmation at 2026-09-18T14:25Z (the auto-mode classifier had refused the same act four times, the last at 14:24Z with the rules already on disk); the act typed in the rules' literal-prefix shape. Pre-checks re-run at 2026-09-18T14:23Z: NOT governed (scripts/measure-self-test-floor.mjs); --pair 19026 exit 0; the review of record is 5729976404 (PASS) on the head e39670e334; CI on the head 22 success · 9 rostered skips · 0 red; mergeable_state clean; ACCEPT on #18987.

Acts: POST …/pulls/19026/ccr/ready_for_review HTTP 200 at 2026-09-18T14:27Z (timeline ready_for_review, draft: false read back); PUT …/pulls/19026/ccr/auto_merge {"merge_method":"SQUASH"} HTTP 200 at 2026-09-18T14:28Z (timeline auto_merge_enabled; the repository allows squash only — allow_squash_merge true, allow_merge_commit false — so the queue's merge is a squash whatever the response's merge_method field prints); armed and waiting for the queue's admission (timeline auto_merge_enabled; added_to_merge_queue follows when the queue admits it). The queue rebuilds the PR onto the current main and re-runs the subscribing workflows; the merge is the queue's, ⛔ not this seat's click. Residue on the merge: #18987pm:done, unassigned.


Generated by Claude Code

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

Labels

size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants