Skip to content

test(scripts): batch 10d — floor check-regen-pending on its three named callees - #15338

Merged
baozhoutao merged 3 commits into
mainfrom
claude/issue-13799-batch10d-regen-pending-callees
Sep 4, 2026
Merged

test(scripts): batch 10d — floor check-regen-pending on its three named callees#15338
baozhoutao merged 3 commits into
mainfrom
claude/issue-13799-batch10d-regen-pending-callees

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Part of #13799

Batch 10d, Tier C recipe A — copying the settled pilot PR #15326 (scripts/git-merge-regen.mjs) onto the other half of check:merge-driver. One file: scripts/check-regen-pending.mjs.

What was wrong

noDist && noTree && armed && table && fixture was this self-test's ONLY success condition, so "every sub-check held" and "the sub-checks never ran" printed the same line. A callee whose body stops doing its work still returns whatever its last surviving statement produces, and the green verdict then claims the whole deferred-merge wiring is sound.

The floor

One battery per NAMED CALLEE — the unit the --self-test dispatch names. Roster is the literal three, floor 1 each, size pin 3:

battery floor inner sink (NOT a battery)
prePushIsArmedSelfTest 1 a bare boolean
decisionTableSelfTest 1 a literal 8-row table + driving loop
fixtureSelfTest 1 a check() helper, 14 calls

registerCase('CALLEE_NAME') is the FIRST statement of each callee, above any early return, so what the ledger records is that the callee RAN. The floor is evaluated at the verdict site immediately before the success line, inside the --self-test branch only — the production path (main(), spawned by pre-commit / pre-push) never reads the ledger.

Size pin measured, not assumed. Pinned at 9999 first and the breach read back: SELF_TEST_BATTERIES declares 3 batteries, below the pinned 9999. Then set to 3.

No new COMPOUND_ANCHOR_LEDGER row is owed — the three callee names already spell self-test and already carry their rows, and an object KEY is not a column-0 declaration. Verified rather than assumed: check:pm-dispatch-gates green, 1382 cases.

Red-path declaration (the pilot ACCEPT's condition on Q2)

MEASURED: the original dispatch did NOT short-circuit over calls, so nothing on the red path changed. The original invoked all three callees eagerly — one const binding per callee, executed unconditionally — and only THEN reduced the five booleans with &&. The short-circuit was always over VALUES, never over calls. callees.map(...) runs the same three in the same order: nothing that used to run stops, and nothing that used to be skipped now runs.

The one red-path change is the verdict LINE's wording, which is this card's ruling 5: the red line now takes N failure(s) (cases and floor). The green line is unchanged text, and the whole green path is byte-identicalcmp of stdout and of stderr against the installed base worktree at the same pin, both identical, exit 0.

Each list entry carries the section banner that already sat immediately above its call, so the interleaved output is preserved exactly.

What is deliberately NOT a battery

  • The TWO assertions written INLINE in the dispatch (noDist, noTree) stay OUTSIDE the roster, per ruling Q1 = A. They are not callees, so the dispatch names no unit for them and there is no name the source already carries; inventing a label would put a hand-written string into a roster whose whole property is that every entry is read off a declaration, and hoisting them is a reshape for its own card. The gap is written at the site and costs this: those two lines can stop running and this floor will not say so.
  • The callees' own inner sinks (table above). The table is a local of ONE callee among three and that callee already reduces its rows to a single returned verdict, so flooring those rows would floor one callee's internals while the other two stayed at callee granularity. The rule: the battery is the unit the DISPATCH names.
  • Honest limit of a callee-granularity floor, measured by the e-control leg below: a callee that returns early with registerCase() still first is GREEN. Floor 1 records that the dispatch REACHED the callee, not that the callee did its work — the same semantics the pilot carries.

Second commit: the verdict's failure spelling

scripts/measure-self-test-floor.mjs classifies a floor as sound only when the code PRODUCES a failure (process.exit(1), exitCode = 1, failures.push, a throw), deliberately refusing to key on the roster's name alone. This file's pre-existing process.exit(ok ? 0 : 1) ternary matched none of them, so the file still classified NONE with a sound roster above it. The failure path is now an explicit branch ending in process.exit(1). Green-path output is unchanged byte for byte.

Deletions — dispatch/verdict lines only

The three banner + const NAME = callee() pairs (replaced by the literal list and its map), const ok = noDist && noTree && ..., the red verdict line's old text, and the process.exit(ok ? 0 : 1) ternary. Nothing else was removed.

Verification

Union re-run on the final commit, git rev-parse --short HEAD = 2bacfdda1 (includes a clean merge of origin/main, which our file was untouched by; spec rebuilt after the merge).

Ablations — mutation proven on disk by blob-hash movement plus grep counts, restore by git checkout HEAD -- ABSOLUTE_PATH proven by hash equality and an empty git diff HEAD, all under trap ... EXIT INT TERM. This file is executed directly from source by node (check:merge-driver spawns node scripts/check-regen-pending.mjs --self-test), so there is no dist/ leg to rebuild.

leg mutation result
a a callee renamed in the roster only RED, both halves named — dispatch invokes a name not declared, roster declares a name not invoked (5 failures)
b one callee's registerCase() removed RED — DID NOT RUN, with that callee's own check-mark line still printed (the vacuous green this floor exists for)
c a callee's invocation deleted from the list RED — DID NOT RUN + "which the dispatch block does not invoke" (3 failures)
d floor deleted, with (b) applied GREEN, exit 0 — the vacuous green returns, so the floor is what catches it (restored)
e registerCase() moved below a forced early return RED — DID NOT RUN
e-control registerCase() first, same forced early return GREEN — isolates registration position from the return itself
f production path with (b) applied stdout + stderr + exit byte-identical to the installed base worktree, in both modes: clean tree (exit 0) and with a pending marker present (exit 1)

Gates — exit codes captured before any pipe. The derived family from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no hand-built list; changeset read by the tool itself: 1 path), all 24 commands run, all exit 0, plus the always-runs tail (check-self-test-workflow-commands.mjs and its --self-test, check:declared-population-live) — 26 green, 0 red.

Verdict lines quoted from the gates themselves:

  • check:merge-driver (this file's own suite, both halves): ✓ merge driver wiring is consistent (24 path(s) deliberately excluded). and ✓ check-regen-pending self-test passed.
  • check:pm-dispatch-gates: ✓ dispatch-gates self-test: 1382 cases pass.
  • check:watch-hint-literal: ✓ check-watch-hint-literal: 49 declaration(s) across 4 rostered name(s) ... and no unrostered spelling of the idiom in the tree.extractWatchHints behaviour identical.
  • check:ratchet-remedy-authority: OK check-ratchet-remedy-authority: 202 scripts swept ...
  • check:nul-bytes: ✓ check-nul-bytes --self-test: 75 assertions over a temp git repo (real scan() path)

Instrument (node scripts/measure-self-test-floor.mjs --json), before vs after on the same tree: ROSTER 156 to 157, NONE 13 to 12, COUNT 1 unchanged, 170 rows both times. scripts/check-regen-pending.mjs is the only file whose verdict moved (NONE to ROSTER); nothing entered the population and nothing left it. This file leaves NONE.

Importersgit grep -l check-regen-pending, every one green: packages/spec/scripts/build-docs.ts (check:docs), packages/spec/scripts/check-generated.ts (check:generated), packages/spec/scripts/lib/dist-freshness.ts and schema-tree-freshness.test.ts (vitest, 2 files / 21 tests passed), scripts/check-exported-any-returns.mts (check:exported-any), scripts/pm/dispatch-gates.mjs (check:pm-dispatch-gates), the two .githooks spawn sites (leg f above), and package.json's check:merge-driver.

Whole-repo pnpm lint: exit 0, run in full — no narrowing claimed.

skip-changeset: this diff publishes nothing from any released package (one repo-root gate script).

🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

…ed callees

`noDist && noTree && armed && table && fixture` was the only success
condition, so "every sub-check held" and "the sub-checks never ran"
printed the same line. Pin the registered NAMES, not a number: a roster
of the three callees the `--self-test` dispatch invokes, floor 1 each,
`registerCase()` as each callee's first statement, and the floor
evaluated at the verdict site inside the `--self-test` branch only.

The dispatch becomes a literal list of function references mapped
through `run()`, so the invoked names are data the floor cross-checks
the roster against in both directions. Evaluation order and
completeness are unchanged: the original invoked all three eagerly and
reduced booleans with `&&`, so the short-circuit was never over calls.

The two assertions written inline in the dispatch stay outside the
roster with the gap written at the site; the callees' inner sinks are
not batteries — the battery is the unit the dispatch names.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
The floor above produces a real failure, but the verdict spelled it as
`process.exit(failures === 0 ? 0 : 1)` — the ternary this file already
carried. `scripts/measure-self-test-floor.mjs` reads a floor as sound
only when the code PRODUCES a failure (`process.exit(1)`, `exitCode = 1`,
`failures.push`, a throw), deliberately refusing to key on the roster's
name alone, so the file classified NONE with a sound roster above it.

Make the failure path an explicit branch ending in `process.exit(1)`.
The success line's text and the whole green-path output are unchanged
byte for byte; only the red path's spelling moves.

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 12:37
@baozhoutao
baozhoutao enabled auto-merge September 4, 2026 12:37
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit f158daf Sep 4, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13799-batch10d-regen-pending-callees branch September 4, 2026 13:15
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.

2 participants