Skip to content

test(scripts): batch 9 pilot (class-3) — floor check-turbo-task-graph at its verdict site - #15309

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-13799-batch9-pilot-class3-verdict-site
Sep 4, 2026
Merged

test(scripts): batch 9 pilot (class-3) — floor check-turbo-task-graph at its verdict site#15309
baozhoutao merged 2 commits into
mainfrom
claude/issue-13799-batch9-pilot-class3-verdict-site

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

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 comment 5538595066 and dispatched by PM claim 5538603533.

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.

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-bodied t(label, ok), 49 sites, 0 named banners) and returns its failures; a separate runSelfTest() decides — it prints the FAIL:/OK: lines and owns process.exit. So the floor check is evaluated at the verdict site, inside runSelfTest(), after the existing failures branch and immediately before the green line, on the path reached only from the dispatch's --self-test branch (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 batterySeen map. 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_BATTERY and registerCase() were already module-level in the landed recipes, so nothing else moves — attribution and scope are untouched, no assertion condition is touched, the selfTest()/runSelfTest() split is not restructured, and the SELF_TEST_VERDICT handshake 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:

  • the floor function's own JSDoc — "Guards the registrations made by selfTest() — the body whose assertion sink t() routes through registerCase(). It is called from runSelfTest() immediately before the success line…";
  • the call site's comment — "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, why batterySeen is 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 HEAD blob and an empty git diff HEAD; the harness carries a trap … EXIT INT TERM restore with absolute paths.

# ablation expected measured
a battery opener renamed in selfTest() runSelfTest() non-zero, naming DID NOT RUN exit 1, 3 problems: the renamed key registered 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 pinned
b registerCase() removed from t() non-zero, every original case still passing exit 1, DID NOT RUN — 0 cases registered, 49 pinned; no case failure printed — all 49 assertions still evaluated and held
c floor check deleted from runSelfTest() (and the sink stopped registering) the self-test goes green over 0 registrations exit 0, 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.
d early return failures; injected into selfTest() after the first case the verdict-site floor still catches it, below floor exit 1, registered 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

  • Floor 49, measured, not transcribed. The roster was probed with a deliberately unreachable pin (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 static t( 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.
  • Behaviour unchanged. --self-test and normal mode, stdout and stderr, byte-identical (cmp) against an installed base worktree at the pin 5b09356b7; exit 0 on both sides, both modes.
  • extractWatchHints identical on both trees for this file — 5 hints, same set: apps/*/package.json, examples/*/package.json, package.json/**, packages/**/package.json, turbo.json/**.
  • The card's instrument moves by exactly one row. node scripts/measure-self-test-floor.mjs --json: ROSTER 133 + NONE 34 + COUNT 2ROSTER 134 + NONE 33 + COUNT 2; the single mover is scripts/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 pipe

Derived 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, a lint.yml gate: OK: check-turbo-task-graph --self-test — all cases passed. then OK: 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 no COMPOUND_ANCHOR_LEDGER row is owed and dispatch-gates.mjs is 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-authorityOK check-ratchet-remedy-authority: 202 scripts swept …
  • check:nul-bytescheck-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 lint was run whole, not narrowedeslint . --no-inline-config over 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.mjs prints a STALE TREE notice: this branch is off the dispatch pin 5b09356b7 and origin/main has 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

claude Bot and others added 2 commits September 4, 2026 09:45
…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
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 4, 2026
@github-actions github-actions Bot added the size/m label Sep 4, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 4, 2026 10:09
@baozhoutao
baozhoutao enabled auto-merge September 4, 2026 10:09
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 8b54e9d Sep 4, 2026
39 checks passed
@baozhoutao
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant