Skip to content

test(scripts): pin a battery roster floor on 40 more scripts/** self-tests - #14851

Merged
baozhoutao merged 6 commits into
mainfrom
claude/issue-13799-self-test-roster-floor-b1
Sep 3, 2026
Merged

test(scripts): pin a battery roster floor on 40 more scripts/** self-tests#14851
baozhoutao merged 6 commits into
mainfrom
claude/issue-13799-self-test-roster-floor-b1

Conversation

@claude

@claude claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1, Tier B)

failures.length === 0 was the only success condition in these gates' self-tests, so "every case held" and "the cases never ran" printed the same line. This lands the PR #13487 shape — the one validated on check-doc-authoring.mjs and carried to check-self-test-wired.mjs / check-self-test-workflow-commands.mjs in PR #13797 — on 40 more scripts/** self-tests.

Per file: every existing section opens with battery('NAME'), one registerCase() line goes into the self-test's own assertion helper so every assertion is attributed to the battery most recently opened, and a floor evaluated before the verdict requires the OPENED set to equal the DECLARED set with each battery at or above its own case count. The roster's own size is pinned too, so deleting an entry cannot silence a floor. An assertion that fires with no battery open is filed under (no battery open), which is not a declared name and reds by the same set difference.

⛔ What is pinned is registered NAMES, never a total: a set difference says WHICH battery stopped, a count says only that something did. ⛔ No case was rewritten — the only edits inside each self-test are the roster wiring.

Census, measured not recalled

node scripts/measure-self-test-floor.mjs --json, on the merge base 2d40f9146 and again on this branch:

ROSTER COUNT NONE
before 3 4 158
after 43 4 118

Three set-equality assertions, all holding (computed, not eyeballed):

  1. every one of the 40 worklist files reads ROSTER after;
  2. the set of files whose classification CHANGED equals the worklist exactly (symmetric difference empty) — nothing outside the worklist moved;
  3. inside PR Verdict handshake for 134 scripts/** self-tests that exit 0 on an early return #14479's 134 handshake-carrying files, the files still reading NONE are exactly the declared remainder below plus the honoured exclusions — symmetric difference empty, 91 = 86 + 5.

The 40 files floored

file batteries pinned cases
scripts/check-adr-anchors.mjs 6 104
scripts/check-agent-test-spelling.mjs 12 67
scripts/check-aggregator-roster.mjs 9 45
scripts/check-bash32-floor.mjs 17 153
scripts/check-changeset-no-major.mjs 13 116
scripts/check-corpus-claim-drift.mjs 16 94
scripts/check-declared-population-live.mjs 2 16
scripts/check-doc-route-spelling.mjs 11 50
scripts/check-docs-image-tag.mjs 11 94
scripts/check-docs-nav-label.mjs 9 25
scripts/check-docs-redirects.mjs 5 48
scripts/check-driver-conformance.mjs 11 98
scripts/check-driver-memory-census.mjs 6 41
scripts/check-empty-changeset.mjs 21 118
scripts/check-engine-double-contract.mjs 31 186
scripts/check-nul-bytes.mjs 6 75
scripts/check-position-name-fold-loaders.mjs 4 15
scripts/check-prerelease-pin-watch.mjs 8 50
scripts/check-published-readme-exports.mjs 23 82
scripts/check-published-readme-links.mjs 10 114
scripts/check-query-options-erasure-ratchet.mjs 4 102
scripts/check-react-page-adapter-contract.mjs 8 51
scripts/check-release-page-status.mjs 7 35
scripts/check-release-section-coverage.mjs 9 50
scripts/check-resume-authority-declared.mjs 7 18
scripts/check-role-word.mjs 11 87
scripts/check-runtime-services-index.mjs 8 51
scripts/check-sdui-lockstep.mjs 5 35
scripts/check-shard-attestation.mjs 15 129
scripts/check-single-authz-resolver.mjs 8 59
scripts/check-slot-lookup-ratchet.mjs 5 49
scripts/check-test-source-alias.mjs 9 98
scripts/check-test-typecheck.mts 5 26
scripts/check-verify-stand-in-erasure.mjs 3 24
scripts/check-where-matcher-conformance.mjs 3 39
scripts/check-workflow-status-functions.mjs 8 34
scripts/objectui-changeset-digest.mjs 16 130
scripts/objectui-range.mjs 7 23
scripts/pr-labels.mjs 6 50
scripts/sync-docs-image-tags.mjs 8 35

383 batteries, 2716 pinned cases.

Two floors are NOT today's count, deliberately

Per the triage ruling: where a battery is one-case-per-row of a list that only ever loses rows, a floor at today's count reddens every legitimate shrink and trains the next author to edit the floor. Both are said in place, over the roster entry:

  • check-adr-anchors.mjsLive tree: green as shipped, red under ablation runs one case per KNOWN_NUMBER_COLLISIONS row (3 today) on top of 20 structural cases; a resolved collision is meant to be deleted and the gate already fails a stale entry. Pinned 21 (20 structural + at least one row audited), not 23.
  • check-single-authz-resolver.mjsEvery exemption carries its reason. runs one case per exemption row (2 today) plus one structural case. Pinned 2, not 3.

Deferred, and why

Tier C — a decision, not a transplant (triage 5478879048; untouched here, returns as its own batch with the reshaping decision made explicitly): the inline top-level blocks scripts/check-regen-pending.mjs, scripts/git-merge-regen.mjs, scripts/setup-git-hooks.mjs; the multi-entry dispatches scripts/check-platform-checklist.mjs, scripts/check-durability-degradation-log-level.mjs; and table-driven self-tests whose natural roster is the table's own rows.

Exclusions honoured (⛔ not edited): the #13798 batch-2 worklist in flight on claude/issue-13798-self-test-handshake-b2 (24 files); scripts/check-i18n-coverage.mjs (PR #14799); scripts/check-i18n-bundles.mjs (reserved for #14008); scripts/pm/dispatch-gates.mjs, scripts/pm/bare-root-worklist.mjs, scripts/check-type-check-coverage.mjs (open PRs #14765 / #14800 / #14805); and every file without the #13798 handshake.

Carve-out, named rather than silent: three handshake-carrying files classify COUNT rather than NONE, so they were never in this card's 155 — scripts/check-refd-timer-probe.mjs, scripts/docs-audit/check-audit-scope.mjs, scripts/measure-stall-guard-headroom.mjs. The census header records the COUNT hits it knew about as hand-checked false positives; these three are unaudited, and are left for a later batch rather than quietly counted as floored.

Remainder of the Tier B surface — 86 files, each with the measured reason it is not in this batch. These are honest transplant blockers, not skips: the transform here is a verbatim one, and each of these needs a decision (name the block-scoped groups, or reshape the report) that belongs in a batch of its own rather than at the end of a long PR.

B1 — no assertion helper inside the self-test body. Every case is written inline, or the helper lives at module scope. A roster here cannot attribute cases without rewriting the cases, which this card forbids. (48 files)

  • scripts/ablation-dist-preflight.mjs
  • scripts/check-adr-links.mjs
  • scripts/check-agent-model-declared.mjs
  • scripts/check-auth-mount-ledger.mjs
  • scripts/check-cli-command-ids.mjs
  • scripts/check-cli-test-child-env.mjs
  • scripts/check-comment-mask-corpus.mjs
  • scripts/check-console-intercept-disarm.mjs
  • scripts/check-cross-package-test-inputs.mjs
  • scripts/check-declaration-mirrors.mjs
  • scripts/check-dispatcher-error-vocabulary.mjs
  • scripts/check-docs-section-name.mjs
  • scripts/check-docs-single-h1.mjs
  • scripts/check-dual-build-cjs-loads.mjs
  • scripts/check-durability-degradation-log-level.mjs
  • scripts/check-entry-guard.mjs
  • scripts/check-examples-live-imports.mjs
  • scripts/check-init-service-contract.mjs
  • scripts/check-kernel-hook-pairs.mjs
  • scripts/check-live-db-isolation.mjs
  • scripts/check-optional-error-sink-contract.mjs
  • scripts/check-org-identifier.mjs
  • scripts/check-override-consistency.mjs
  • scripts/check-parse-guard.mjs
  • scripts/check-partof-closing-keyword.mjs
  • scripts/check-plugin-teardown-shape.mjs
  • scripts/check-published-files.mjs
  • scripts/check-published-list-mirrors.mjs
  • scripts/check-route-envelope.mjs
  • scripts/check-runner-env-posture.mjs
  • scripts/check-sdui-manifest.mjs
  • scripts/check-single-claim-paths.mjs
  • scripts/check-skill-compatibility-version.mjs
  • scripts/check-skill-frame-freshness.mjs
  • scripts/check-skill-frame-sync.mjs
  • scripts/check-tenant-audit-census.mjs
  • scripts/check-watch-hint-literal.mjs
  • scripts/check-widget-option-census.mjs
  • scripts/check-wildcard-fallthrough.mjs
  • scripts/import-prerequisite.mjs
  • scripts/invoked-as.mjs
  • scripts/measure-position-name-fold-census.mjs
  • scripts/measure-test-shard-timings.mjs
  • scripts/partition-test-shards.mjs
  • scripts/publish-smoke-pack.mjs
  • scripts/qa/qa-rollup.mjs
  • scripts/render-release-coverage-anchor.mjs
  • scripts/ts-parse.mjs

B2 — fewer than two named sections. The groups are block-scoped with no banner, so naming them is a judgement per block rather than a transplant. (15 files)

  • scripts/check-console-injection.mjs
  • scripts/check-cross-repo-closer-outcome.mjs
  • scripts/check-docs-locale-catch-all.mjs
  • scripts/check-dts-emitted.mjs
  • scripts/check-error-status-conformance.mjs
  • scripts/check-logger-receiver-detach.mjs
  • scripts/check-merge-queue-triage-outcome.mjs
  • scripts/check-objectql-double-limit.mjs
  • scripts/check-quick-reference-counts.mjs
  • scripts/check-ratchet-remedy-authority.mjs
  • scripts/check-spec-parsed-alias.mjs
  • scripts/check-stack-collection-maps.mjs
  • scripts/check-startup-registry-verdict.mjs
  • scripts/check-tenant-chokepoint.mjs
  • scripts/check-whole-set-label-write.mjs

B3 — the verdict is not reached through a body-level if (SINK…), so there is no single point at which a floor can be evaluated before the verdict without reshaping the report. (11 files)

  • scripts/check-adr-0087-registration.mjs
  • scripts/check-comment-mask-adoption.mjs
  • scripts/check-doc-frontmatter.mjs
  • scripts/check-engine-split-ratio.mjs
  • scripts/check-i18n-stale-fill.mjs
  • scripts/check-required-contexts.mjs
  • scripts/check-section-landing-index.mjs
  • scripts/check-stall-guard-budget.mjs
  • scripts/check-system-context-census.mjs
  • scripts/check-turbo-task-graph.mjs
  • scripts/check-undeclared-dep-imports.mjs

B4 — the self-test entry the census names has no function body this transform can resolve (an aliased or re-exported entry). (9 files)

  • scripts/check-ci-filter-parity.mjs
  • scripts/check-doc-anchors.mjs
  • scripts/check-error-code-casing.mjs
  • scripts/check-keyed-text-bounds.mjs
  • scripts/check-overlay-whitelist-table.mjs
  • scripts/check-pnpm-acquisition.mjs
  • scripts/check-vendor-version-stamps.mjs
  • scripts/js-comment-mask.mjs
  • scripts/release-github-releases.mjs

B5 — the only named sections sit after the verdict site. (2 files)

  • scripts/check-dev-prereqs.mjs
  • scripts/sync-template-versions.mjs

B3 — same as above (failed sink). (1 files)

  • scripts/docs-audit/affected-docs.mjs

Verification

  • Per file: --self-test run before and after; exit codes identical on all 40 (all 0). Floors are the per-battery counts measured by instrumenting the wiring on this tree, not guessed.
  • Ablations, from a COMMITTED implementation, mutation proved on disk and restore proved by an empty git diff HEAD:
    • a battery's cases deleted, array sink (check-sdui-lockstep.mjs) → exit 1;
    • a roster ENTRY deleted (check-sdui-lockstep.mjs) → exit 1, and the run names both halves: "declares 4 batteries, below the pinned 5" and "registered 7 case(s) but is not declared in SELF_TEST_BATTERIES";
    • a battery's cases deleted, counter sink (check-bash32-floor.mjs) → exit 1, "battery 'the table itself' DID NOT RUN — 0 cases registered, 2 pinned".
  • Gates: the 41 families derived by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack on this branch, all run, exit codes captured before any pipe. All 41 exit 0 on the merged head 3da3d92.
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0.
  • Two real findings this caught and fixed, both worth recording:
    • check:ratchet-remedy-authority went red because the first spelling of the ALLOW note put that registry's name and the words "shrink-only" inside anchorFor's window, flipping check-single-authz-resolver.mjs from excluded to unmarked and then MISCLASSIFIED against its CONTROL row. Reworded to state the same fact without the token; green.
    • check:type-check-debt went red because scripts/check-test-typecheck.mts is reached by the root tsc --noEmit and the untyped wiring added 7 raw errors (26 → 33, drifting a frozen DEBT entry upward). Typed; back to the recorded 26.

skip-changeset: scripts/** only, nothing is published from any package.


Generated by Claude Code

`failures.length === 0` was the only success condition in these gates'
self-tests, so "every case held" and "the cases never ran" printed the
same line. Transplants the PR #13487 shape validated on
check-doc-authoring and carried to check-self-test-wired /
check-self-test-workflow-commands in PR #13797: every section opens with
`battery('<name>')`, every assertion is attributed to the battery most
recently opened, and a floor evaluated before the verdict requires the
OPENED set to equal the DECLARED set with each battery at or above its
own case count. The roster's own size is pinned too, so deleting an
entry cannot silence a floor.

Registered NAMES are what is pinned, never a total: a set difference
says WHICH battery stopped, a count says only that something did.

No case was rewritten — the only edits inside each self-test are the
roster wiring (a `battery()` opener per existing section banner, one
`registerCase()` line in the assertion helper, and the floor block
before the verdict). Floors are the per-battery counts measured on this
tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Swfxm6gT9ESmjSS7pqfVw
…o ledger-driven batteries

Two batteries register one case per row of a list that is meant to
shrink: `KNOWN_NUMBER_COLLISIONS` in check-adr-anchors (a resolved
collision is deleted, and a stale entry already fails) and `ALLOW` in
check-single-authz-resolver (an exemption that exempts nothing is dead
weight). A floor at today's count would redden every legitimate removal
and train the next author to edit the floor — the one habit these floors
exist to prevent. Pinned instead is the part that does not move with the
list: the structural cases ran AND at least one row was audited, said in
place over each entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Swfxm6gT9ESmjSS7pqfVw
…het-remedy offer

The first spelling put `ALLOW` and the words "shrink-only" inside
`anchorFor`'s window in check-single-authz-resolver, which flipped that
file from `excluded` to `unmarked` under check:ratchet-remedy-authority
(and then MISCLASSIFIED against its CONTROL row). Measured, both
findings; the gate is green again with the same fact stated without the
token. The adr-anchors note is reflowed for readability only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Swfxm6gT9ESmjSS7pqfVw
scripts/check-test-typecheck.mts is the one .mts file in the batch and it
is reached by the root `tsc --noEmit`, so the untyped `battery()` /
`registerCase()` / `floorFailure()` insertions added 7 raw errors and
drifted the frozen DEBT entry for @objectstack/spec-monorepo upward
(26 -> 33, measured). Typing the wiring and widening the roster to
`Readonly<Record<string, number>>` puts the count back at its recorded
26 with check:type-check-debt green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Swfxm6gT9ESmjSS7pqfVw
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): PR-side CI on 3da3d9225 is red only on Test Core (1/6)packages/cli pnpm run test exit 1 (job 100534211626; the log download is refused from this container, the annotations carry the exit code only). This diff is 40 scripts/** self-tests and touches nothing under packages/cli; the shard/package is the #14648 signature, and this head already contains the fix accb9231c7, so it is the post-fix recurrence recorded on #14648. Single permitted non-own re-trigger, spent now: update-branch (a real merge of the current origin/main, no rewrite). If the same shard reds again, the PR parks under the #14648 reading; any other red is real. Review of the content is in progress on #13799.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 3, 2026 06:15
@baozhoutao
baozhoutao enabled auto-merge September 3, 2026 06:15
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 3, 2026
baozhoutao pushed a commit that referenced this pull request Sep 3, 2026
Batch 1b of #13799: the PR #13487 roster-floor shape transplanted onto the
`scripts/**` self-tests whose assertion sink is NOT a block-bodied helper
inside the self-test body -- a concise arrow, or a module-scope function --
so batch 1 (PR #14851) could not transplant it verbatim.

Per the batch-1 review ruling, the roster machinery lives at MODULE scope
(SELF_TEST_BATTERIES / SELF_TEST_BATTERY_FLOOR / UNATTRIBUTED_BATTERY /
battery() / registerCase() / selfTestFloorFailures()), and each file's
existing assertion sink is given a minimal block body that calls
registerCase() and returns the original expression unchanged. No case is
rewritten, none is reordered, and no assertion changes meaning: all 18
self-tests exit with the same code and byte-identical output before and
after.

What is pinned is the registered NAMES, not a total: every existing section
banner opens a battery, every assertion is attributed to the battery most
recently opened, the floor requires the OPENED set to equal the DECLARED set
with each battery at or above its own count, and the roster's own size is
pinned so deleting an entry cannot silence a floor quietly.

One battery is pinned at its structural invariant rather than at today's
count, with the reason written over the entry: check-plugin-teardown-shape's
exclusions battery runs exactly one case per DELIBERATELY_EXCLUDED row, and
promoting a name onto the teardown roster is a legitimate edit that shrinks
that list.

Census (`node scripts/measure-self-test-floor.mjs --json`): ROSTER 3 -> 21,
NONE 158 -> 140; the set of files whose class changed equals this worklist
exactly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015k1DVWthZyPS7xi1Q72YpK
baozhoutao pushed a commit that referenced this pull request Sep 3, 2026
Batch 1b of #13799: the PR #13487 roster-floor shape transplanted onto the
`scripts/**` self-tests whose assertion sink is NOT a block-bodied helper
inside the self-test body -- a concise arrow, or a module-scope function --
so batch 1 (PR #14851) could not transplant it verbatim.

Per the batch-1 review ruling, the roster machinery lives at MODULE scope
(SELF_TEST_BATTERIES / SELF_TEST_BATTERY_FLOOR / UNATTRIBUTED_BATTERY /
battery() / registerCase() / batteryFloorFailures()), and each file's
existing assertion sink is given a minimal block body that calls
registerCase() and returns the original expression unchanged. No case is
rewritten, none is reordered, and no assertion changes meaning: all 18
self-tests exit with the same code and byte-identical output before and
after.

What is pinned is the registered NAMES, not a total: every existing section
banner opens a battery, every assertion is attributed to the battery most
recently opened, the floor requires the OPENED set to equal the DECLARED set
with each battery at or above its own count, and the roster's own size is
pinned so deleting an entry cannot silence a floor quietly.

None of the introduced helpers is named with a self-test spelling. That is
deliberate and recorded beside them: `check:pm-dispatch-gates` anchors on a
top-level declaration whose NAME spells self-test, and every such name owes a
row in that gate's COMPOUND_ANCHOR_LEDGER. These helpers hold no fixtures to
mask and read no path literal, so the accurate name is the one that says
`battery`.

One battery is pinned at its structural invariant rather than at today's
count, with the reason written over the entry: check-plugin-teardown-shape's
exclusions battery runs exactly one case per DELIBERATELY_EXCLUDED row, and
promoting a name onto the teardown roster is a legitimate edit that shrinks
that list.

Census (`node scripts/measure-self-test-floor.mjs --json`): ROSTER 3 -> 21,
NONE 158 -> 140; the set of files whose class changed equals this worklist
exactly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015k1DVWthZyPS7xi1Q72YpK
Merged via the queue into main with commit abc10b2 Sep 3, 2026
34 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13799-self-test-roster-floor-b1 branch September 3, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl 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