test(scripts): batch 9 pilot (class-3) — floor check-turbo-task-graph at its verdict site - #15309
Merged
baozhoutao merged 2 commits intoSep 4, 2026
Merged
Conversation
…rdict site (class-3) `failures.length === 0` inside `runSelfTest()` was this self-test's only success condition, so "every case held" and "the cases never ran" printed the same line. This is the class-3 pilot: the registering body has no verdict site. `selfTest()` registers through a block-bodied `t(label, ok)` (49 sites, 0 named banners) and returns its failures; a separate `runSelfTest()` decides and prints. So the floor CHECK moves to the verdict site — inside `runSelfTest()`, immediately before the green line, on the `--self-test`-only path — while the `SELF_TEST_BATTERIES` roster, the sink's `registerCase()` and the ledger stay module-level as in every landed recipe. Only the check's location moves; attribution and scope are untouched, no assertion condition is touched, and the `SELF_TEST_VERDICT` handshake is kept exactly as landed. The floor is deliberately NOT placed before `selfTest()`'s own `return`: an early return above that line would skip it — the defect the #13798 handshake exists to catch — coupling hole 1 to hole 2 after the card ruled them orthogonal. Floor 49, measured rather than transcribed: the roster was probed with an unreachable pin (9999) and the floor's own report named the registered count. Behaviour is unchanged — normal mode and `--self-test` stdout and stderr are byte-identical against the pin, exit 0 both sides. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
This was referenced Sep 4, 2026
baozhoutao
marked this pull request as ready for review
September 4, 2026 10:09
baozhoutao
enabled auto-merge
September 4, 2026 10:09
baozhoutao
deleted the
claude/issue-13799-batch9-pilot-class3-verdict-site
branch
September 4, 2026 10:30
baozhoutao
pushed a commit
that referenced
this pull request
Sep 4, 2026
…self-tests The class-3 shape: the body that REGISTERS cases is not the body that prints the verdict, so the floor is evaluated at the verdict site — the place that prints the green line — with roster, sink and ledger at module scope. Copies the placement PR #15309 settled. - check-osv-exemptions: recipe A over the 17 literal table rows; floor at the verdict site in main()'s --self-test branch. Boolean `passed` converted to a `failures` counter so the floor shares the counted sink (sink/verdict lines only). - typecheck-configs: recipe A over BOTH literal tables under one roster (11 keys, size pin 11); check in the --self-test dispatch block, so the two importers that fold this self-test in never reach it. - check-comment-mask-corpus: concise arrow sink converted to a block-bodied helper calling registerCase(); one hoisted battery, floor 12; check at the verdict site in selfTest(), so the production sweep in main() — which calls runSelfTestCases() too — never reaches it. SELF_TEST_CASE_COUNT and its production read are untouched. - check-workspace-manifest-cycles: Tier B multi-battery, one battery per named section banner (7), floors measured per battery on a run; check at the verdict site in runSelfTest(). Every floor is read off a run with the roster pinned at 9999, never counted from source. No assertion condition, message or argument is changed. Also adds the one ENTRY_BY_HAND row the instrument needs to probe check-workspace-manifest-cycles at all: it defines two self-test-shaped functions and reads NOT MEASURED without it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
This was referenced Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #13799
Batch 9 pilot for the assertion-floor card: the first of the five class-3
scripts/**self-tests — the class whose registering body contains no verdict site — implemented under class-3 decision A, recorded in comment5538595066and dispatched by PM claim5538603533.failures.length === 0insiderunSelfTest()was this self-test's only success condition, so "every case held" and "the cases never ran" printed the same line.One file in the diff:
scripts/check-turbo-task-graph.mjs.The class-3 spec this pilot settles
The other four class-3 files (
check-osv-exemptions,typecheck-configs,check-workspace-manifest-cycles(#14842),check-comment-mask-corpus) copy this shape. Everything below is the shape; the recipe for the battery itself is unchanged — b5's single hoisted battery here, recipe A rows where the file is table-driven.1. Where the floor check sits, and how it reads the registrations
The split, as it stands on
main:selfTest()registers (block-bodiedt(label, ok), 49 sites, 0 named banners) and returns itsfailures; a separaterunSelfTest()decides — it prints theFAIL:/OK:lines and ownsprocess.exit. So the floor check is evaluated at the verdict site, insiderunSelfTest(), after the existing failures branch and immediately before the green line, on the path reached only from the dispatch's--self-testbranch (so it can never fire on a production run):@@ function runSelfTest() { for (const f of failures) console.error(` - ${f}`); process.exit(1); } + + // ── The assertion floor, at the verdict site ─────────────────────── + // `selfTest()` registers but does not decide, so the floor over ITS + // registrations is evaluated here, after every battery has had its chance and + // immediately before the success line — the only place a run that registered + // nothing can still be stopped from reporting that every case held. + const floorBreaches = batteryFloorFailures(); + if (floorBreaches.length) { + console.error( + `FAIL: check-turbo-task-graph --self-test — the assertion floor over selfTest()'s ` + + `registrations was breached (${floorBreaches.length} problem(s)); every case that DID run passed.`, + ); + for (const b of floorBreaches) console.error(` - ${b}`); + process.exit(1); + } + console.log('OK: check-turbo-task-graph --self-test — all cases passed.'); return SELF_TEST_VERDICT;It reads the registrations from the module-level
batterySeenmap. That is the one thing class-3 forces: the ledger has to outlive the registering function's frame, because the reader is a different function. The roster,SELF_TEST_BATTERY_FLOOR,UNATTRIBUTED_BATTERYandregisterCase()were already module-level in the landed recipes, so nothing else moves — attribution and scope are untouched, no assertion condition is touched, theselfTest()/runSelfTest()split is not restructured, and theSELF_TEST_VERDICThandshake is kept exactly as landed. The handshake and the floor stay orthogonal: neither is expressed in terms of the other.The registering side is the landed b5 shape, unchanged:
export function selfTest() { + // The single hoisted battery this body's assertions are attributed to. The + // floor that reads them is evaluated at the verdict site in `runSelfTest()`. + battery('check-turbo-task-graph self-test'); const failures = []; const t = (label, ok) => { + registerCase(); if (!ok) failures.push(label); };⛔ Option B — a check at the end of the registering body, before its
return— is refused. An early return above that line skips the check entirely, which is the defect the #13798 verdict handshake exists to catch; adopting B would couple hole 1 to hole 2 after the card ruled them orthogonal. Ablation (d) below shows the same early return caught at the verdict site.2. How the explaining comment names the guarded body
A reader arriving at the verdict site has to be told whose registrations are being floored, because they were not made here. Two places say it, both naming
selfTest()explicitly:selfTest()— the body whose assertion sinkt()routes throughregisterCase(). It is called fromrunSelfTest()immediately before the success line…";selfTest()registers but does not decide, so the floor over ITS registrations is evaluated here…".The module-level roster block carries a dedicated section,
── Why the LEDGER is module-level and the CHECK sits at the verdict site ──, which states the split, whybatterySeenis module-scope rather than a local map, and the refusal of option B. The red line itself names the body too:the assertion floor over selfTest()'s registrations was breached.3. What the ablations read across the split
Every mutation was proven on disk before it was run (anchored single-site match plus the blob hash moving), and every restore was proven by hash equality against the
HEADblob and an emptygit diff HEAD; the harness carries atrap … EXIT INT TERMrestore with absolute paths.selfTest()runSelfTest()non-zero, naming DID NOT RUNregistered 49 case(s) but is not declared in SELF_TEST_BATTERIES, and"check-turbo-task-graph self-test" DID NOT RUN — 0 cases registered, 49 pinnedregisterCase()removed fromt()DID NOT RUN — 0 cases registered, 49 pinned; no case failure printed — all 49 assertions still evaluated and heldrunSelfTest()(and the sink stopped registering)OK: check-turbo-task-graph --self-test — all cases passed.on stdout with nothing registered — the vacuous green, i.e. the check is load-bearing exactly where it sits. Restored.return failures;injected intoselfTest()after the first caseregistered 1 case(s), below its pinned floor of 49— the case option B would have missed(a) and (b) also demonstrate that the two arms of the floor read correctly across the split: the attribution arm sees a key
runSelfTest()never opened, and the count arm sees a zero the registering body produced.Verification record
9999) and the floor's own report named the registered count:registered 49 case(s), below its pinned floor of 9999. It agrees with the 49 statict(sites. The size pin is unreachable in the ordinary sense (one battery,SELF_TEST_BATTERY_FLOOR = 1), so its breach line names N:SELF_TEST_BATTERIES declares N batteries, below the pinned 1.--self-testand normal mode, stdout and stderr, byte-identical (cmp) against an installed base worktree at the pin5b09356b7; exit 0 on both sides, both modes.extractWatchHintsidentical on both trees for this file — 5 hints, same set:apps/*/package.json,examples/*/package.json,package.json/**,packages/**/package.json,turbo.json/**.node scripts/measure-self-test-floor.mjs --json:ROSTER 133 + NONE 34 + COUNT 2→ROSTER 134 + NONE 33 + COUNT 2; the single mover isscripts/check-turbo-task-graph.mjs, NONE → ROSTER. No row added or removed.skip-changeset: no package publishes anything from this diff.Gates — all green on head
5af480866, exit codes captured by redirect before any pipeDerived family,
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(22 commands, all run, all exit 0):check-ci-filter-parity,check-closing-keyword-parity(+--self-test),check-comment-mask-corpus,check-self-test-wired(+--self-test),check-whole-set-label-write(+--self-test),pm/bare-root-worklist --self-test,check:agent-test-spelling,check:bash32-floor,check:cli-command-ids,check:cross-package-test-inputs,check:entry-guard,check:nul-bytes,check:parse-guard,check:pm-dispatch-gates,check:pnpm-filter-targets,check:ratchet-remedy-authority,check:refd-timer-probe,check:turbo-task-graph,check:watch-hint-literal. Plus the always-runs tail:check-self-test-workflow-commands(+--self-test),check:declared-population-live.Verdict lines, quoted from the gates themselves rather than from a bare
$?:check:turbo-task-graph— this script's own suite, alint.ymlgate:OK: check-turbo-task-graph --self-test — all cases passed.thenOK: 28 package-scoped and 8 generic turbo task(s) judged against 79 workspace package(s) …check:pm-dispatch-gates—✓ dispatch-gates self-test: 1381 cases pass.(the new module-level names are SCREAMING_SNAKE and the helpers are named for the battery role, so noCOMPOUND_ANCHOR_LEDGERrow is owed anddispatch-gates.mjsis untouched)check-self-test-wired—✓ check-self-test-wired: every one of the 168 script(s) CI runs that ship a --self-test has that self-test run by CI.check-self-test-workflow-commands—✓ … no self-test CI runs prints a line the Actions runner would parse as a workflow command.(17 mentioning scripts actually RUN, stdout+stderr scanned)check:declared-population-live—✓ … 201 of 252 famil(ies) declare a path population, and every one of them reaches this tree's 8300 tracked file(s).check:watch-hint-literal—✓ … 49 declaration(s) across 4 rostered name(s) …check:ratchet-remedy-authority—OK check-ratchet-remedy-authority: 202 scripts swept …check:nul-bytes—check-nul-bytes: OK (scanned 8293 text file(s) … no raw ASCII control bytes).A direct control-byte scan of the edited file and of every body posted for this PR is clean too.pnpm lintwas run whole, not narrowed —eslint . --no-inline-configover the repo, exit 0, so no narrowing claim is needed. The one-file targeted run reports 0 errors / 0 warnings over exactly 1 linted file (--format json).node scripts/pm/dispatch-gates.mjsprints a STALE TREE notice: this branch is off the dispatch pin5b09356b7andorigin/mainhas since moved (batches 7a/7b). The files it names as stale are those PRs' — disjoint from this one — so the branch is deliberately not rebased.Generated by Claude Code