test(scripts): give 30 more self-tests a battery roster and floor - #14896
Merged
Conversation
Batch 1c of #13799: PR #13487's roster-and-floor shape, exactly as batch 1 landed it, transplanted onto the `scripts/**` self-tests that already carry its shape -- a block-bodied assertion helper inside the self-test body, at least two named section banners, and a body-level verdict site. Per file: a module-level frozen `SELF_TEST_BATTERIES` roster of NAMES with a per-battery floor, `SELF_TEST_BATTERY_FLOOR` pinning the roster's own size, `UNATTRIBUTED_BATTERY`, a `battery('<banner text>')` opener at each existing section banner, one `registerCase()` at the head of the existing assertion helper, and the floor evaluated immediately before the existing verdict so its messages enter the file's own failure sink. 432 batteries, 2591 cases. No case is rewritten, none reordered and no assertion changes meaning: every file's `--self-test` exits 0 with byte-identical stdout AND stderr before and after. The roster block goes after the import block in every file, which is TDZ-safe by construction. 15 of the 45 files the dispatch named do not meet that criterion when measured against the tree and are left for a later batch rather than improvised; the reason per file is in the PR body. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YWBXxmS9sXHoqnozSNwHnE
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This was referenced Sep 3, 2026
baozhoutao
marked this pull request as ready for review
September 3, 2026 11:01
baozhoutao
enabled auto-merge
September 3, 2026 11:02
os-litant
pushed a commit
that referenced
this pull request
Sep 3, 2026
Two conflicts, both against main commits that landed after this branch's merge base 5ff5f95: - .claude/skills/pm-dispatch/references/lanes/services.md: main's 365141f (#14856) edited the very bullet this branch deletes (the bullet moved to references/platform-readings.md). Settled to OUR side: the bullet stays deleted and the move stands. The FACT main corrected in that bullet (the i18n gates answer PREREQUISITE NOT MET with exit 3, not 1) is carried into the moved text by the follow-up commit, so this merge commit's non-conflict content stays a pure merge of the two parents. - scripts/check-ratchet-remedy-authority.mjs: main's a610775 (#14896) added the self-test battery roster and floor at the spot where this branch's section (21) sits. Settled by keeping BOTH: section (21) opens with battery('(21) ...'), is declared in SELF_TEST_BATTERIES at its case count (2), and sits before the floor-evaluation block; the roster's own size pin SELF_TEST_BATTERY_FLOOR moves 20 -> 21 by the header's own convention (the roster size is pinned at the declared count). The exception phrase stays assembled, never spelled, in author-facing text, and the gate's run line is byte-identical to main's. scripts/pm/check-skill-line-ratchet.mjs auto-merged with main's c47a5ac (#14853) verdict handshake; the two run-path verdict lines this branch adds are outside that handshake, which covers selfTest()'s sentinel return only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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 1c, Tier B — batch-1 shape)
PR #13487's roster-and-floor shape, exactly as batch 1 landed it in PR #14851, transplanted onto the
scripts/**self-tests that already carry that shape. 432 batteries / 2591 cases pinned across 30 files.Per file, and nothing else:
SELF_TEST_BATTERIESroster of names with a per-battery floor,SELF_TEST_BATTERY_FLOORpinning the roster's own size, andUNATTRIBUTED_BATTERY, placed after the import block (batch 1b's uniform placement — TDZ-safe by construction);battery('BANNER TEXT')at each existing section banner, named verbatim from the banner;registerCase()at the head of the existing in-body assertion helper;⛔ No case is rewritten, none reordered, no assertion changes meaning. ⛔ No TOTAL is pinned.
The file surface: 30 floored, 15 dropped with the measured reason
The dispatch named 45 files (the batch-1b report's "HAS a block-bodied in-body assertion helper" list). That list was derived on the helper half of the batch-1 criterion only. Re-measured against the tree on all three halves — a block-bodied in-body assertion helper and at least two named section banners and a body-level verdict site — 30 qualify.
The banner criterion, as measured
A named section banner is a comment block at statement position inside the self-test body (innermost open bracket is a brace, never a square bracket or a paren) whose head carries a section marker: a horizontal rule, a box (a bare rule line, then the title, then a rule), or an enumerator (
// 1. name,// 3 — name,// G1: name,// TSO-6048: name). A comment with no marker is a paragraph explaining the next statement, not a section head; reading one as a section head is a judgement per comment, which a verbatim transplant does not make.The bracket half of that rule is load-bearing and was measured: in
scripts/docs-audit/affected-docs.mjsa brace-only depth count put four banners inside an array literal, where abattery()statement is a syntax error — the Tier-C shape the batch-1b report names. The probe pass caught it; the renderer now refuses that position.Floored (30)
scripts/check-adr-0087-registration.mjsscripts/check-auth-mount-ledger.mjsscripts/check-ci-filter-parity.mjsscripts/check-comment-mask-adoption.mjsscripts/check-console-injection.mjsscripts/check-cross-repo-closer-outcome.mjsscripts/check-dev-prereqs.mjsscripts/check-dispatcher-error-vocabulary.mjsscripts/check-doc-frontmatter.mjsscripts/check-docs-locale-catch-all.mjsscripts/check-docs-section-name.mjsscripts/check-engine-split-ratio.mjsscripts/check-error-status-conformance.mjsscripts/check-i18n-stale-fill.mjsscripts/check-init-service-contract.mjsscripts/check-keyed-text-bounds.mjsscripts/check-merge-queue-triage-outcome.mjsscripts/check-overlay-whitelist-table.mjsscripts/check-quick-reference-counts.mjsscripts/check-ratchet-remedy-authority.mjsscripts/check-required-contexts.mjsscripts/check-route-envelope.mjsscripts/check-section-landing-index.mjsscripts/check-stall-guard-budget.mjsscripts/check-system-context-census.mjsscripts/check-undeclared-dep-imports.mjsscripts/check-vendor-version-stamps.mjsscripts/docs-audit/affected-docs.mjsscripts/release-github-releases.mjsscripts/sync-template-versions.mjsFive files run cases before their first banner (
check-dispatcher-error-vocabulary,check-i18n-stale-fill,check-route-envelope,check-vendor-version-stamps,docs-audit/affected-docs); their first battery is opened at the top of the body and that banner then carries no second opener — the PR #13487 precedent's own shape, as batch 1b did it. Banners that registered no case are not declared as batteries and get no opener: a battery is a section that actually runs cases.Dropped (15) — left for a later batch rather than improvised
scripts/check-declaration-mirrors.mjsconst ok = (label, cond) => cases.push({ label, cond });(L411) — with no block body to hold registerCase(). That is the class-1 blocker, not the batch-1 shape.scripts/check-dts-emitted.mjsscripts/check-logger-receiver-detach.mjsscripts/check-objectql-double-limit.mjsscripts/check-skill-compatibility-version.mjsscripts/check-skill-frame-freshness.mjscases.push({ ... })sites, so there is no single block body for registerCase(). The only block-bodied helper,linear(), is a fixture builder.scripts/check-spec-parsed-alias.mjs// GOOD:,// BAD (rule 1)), not sections.scripts/check-stack-collection-maps.mjsscripts/check-startup-registry-verdict.mjsscripts/check-tenant-chokepoint.mjsscripts/check-turbo-task-graph.mjsselfTest()returns a failures array and the verdict is printed by a SEPARATErunSelfTest()(L892). That is the class-3 shape.scripts/check-whole-set-label-write.mjsscripts/check-wildcard-fallthrough.mjsscripts/js-comment-mask.mjsscripts/publish-smoke-pack.mjsShrink-only ledgers: scanned, none found in this batch
Every battery that iterates a module-level constant was enumerated (28 battery/ledger pairs across 13 files) and each underlying list read:
CATEGORIES,SHAPES,MUTATIONS,ROOTS,TEXT_STAMPS,EXPECTED_TEXT_FAMILY,FLOORS,REQUIRED_CONTEXTS,INSTRUCTION_SURFACES,RETIRED_CONTEXT_NAMES,EXPRESS_RESPONSE_MODULES,DECLARED_COUNTS,UNENFORCED_TEXT_COUNTS,ROOT_DIR_WATCH_HINTS,YAML_CENSUS_ANCHORS. None is shrink-only, and the two that read closest to it are not one-case-per-row:UNENFORCED_TEXT_COUNTSis asserted structurally (two cases, with the row count inside the case rather than as case multiplicity), andRETIRED_CONTEXT_NAMESonly ever grows. So no floor in this batch moves with a list a legitimate edit shortens.Evidence
Behaviour is unchanged, per file
For all 30:
node FILE --self-testbefore and after, exit code captured by redirect before any pipe. 30/30 exit 0 both sides, andcmp -sreports stdout and stderr byte-identical on every one. (Both readings are from the tree afterpnpm install; without it seven files exit 3 on a missing-distprerequisite and two onERR_MODULE_NOT_FOUND, which is a container state, not a verdict.)Census
node scripts/measure-self-test-floor.mjs --json:2263ca4d6f248bc953Three set-equality assertions, computed in python, all
True:NONEbefore andROSTERafter;git diff --name-onlyequals the worklist exactly.And the 128 still
NONEdecompose exactly — a disjoint partition whose union is the set, asserted in python, not read by eye:The 30 floored here intersect PR #14851's 40, PR #14872's 18, PR #14853's 24, the i18n pair and the open-PR exclusions in zero files.
Ablations — four, each from the committed implementation
Each leg carries a
traprestore on absolute paths; the mutation is proved on disk by an anchored grep count plusgit hash-objectagainst the HEAD blob, and the restore by an emptygit diff HEAD— never by an exit code. Nodistis on the resolution path of any of the four, so no rebuild leg applies to them.scripts/check-auth-mount-ledger.mjs, the 2ok(deriveBasePath(cases (grep 2 → 0;cd7b24ab→b8493fe3). Exit 0 → 1: battery "The base path is DERIVED, and its absence is not an empty population." DID NOT RUN — 0 cases registered, 2 pinned. The verdict below would have claimed those cases hold.scripts/check-engine-split-ratio.mjs(grep 1 → 0;d92d2186→06deaef8). Exit 1 naming both halves: SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2 and battery "pure rendering" registered 5 case(s) but is not declared in SELF_TEST_BATTERIES.registerCase()removed from the sink helper —scripts/check-quick-reference-counts.mjs(grep 1 → 0;ce2b2de3→a29feeba). Exit 1 with every original case still passing: all 22 batteries report DID NOT RUN.battery()opener deleted —scripts/docs-audit/affected-docs.mjs(grep 1 → 0;965c95f0→255698df). Exit 1: battery "anchor PROVENANCE ([decision] docs-audit: a data-property anchor is both the noisiest and the most valuable anchor the tool mints — 70 of 402 rows, and no cheap discriminator survives measurement #12824)" DID NOT RUN — 0 cases registered, 12 pinned.A fifth attempt aborted before running — the anchor matched zero lines and the guard refused rather than reporting a clean measurement, which is the failure shape the ablation contract exists to catch.
Gates
Derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths passed; the script reads the change set from git — 30 paths vs merge base2263ca4d6). 55 commands derived +check:declared-population-live+check:pm-dispatch-gates+check:nul-bytes= 58 run. Exit codes captured by redirect before any pipe.check:ratchet-remedy-authority0,check:declared-population-live0 (158 of 202 famil(ies) declare a path population, and every one of them reaches this tree's 8079 tracked file(s)).check:pm-dispatch-gates— exit 0, dispatch-gates self-test: 1241 cases pass. No compound-anchor red: this batch introduces no top-levelfunctiondeclaration at all, so theSELF_TEST_DECLanchor sees nothing new. Thebattery/registerCaseclosures are function-scoped and the three new module-level names areconsts, which the anchor does not match.check-test-completeness— exit 3, NOT MEASURED by the gate's own instruction, not a red.check-dev-prereqsexit 1 (The workspace is not built — 1 unmet precondition, not a list of problems) → 0 afterpnpm exec turbo run build --filter=./packages/* --filter=./packages/*/*underscripts/pm/os-verify-lock.sh(VERDICT command-exit 0, held 6m32s);check-engine-split-ratioexit 2 (this clone is shallow … a ratio derived here would be real, plausible and WRONG) → 0 aftergit fetch --shallow-since=2026-05-29 origin main. Neither is a verdict about this diff, and neither was read as a pass before it was re-run.pnpm lint(whole repo,eslint . --no-inline-config) — exit 0 onf248bc953. No narrowing claimed.grep -naP) clean;check:nul-bytesexit 0.The build left the tracked tree unchanged (
git status --porcelainempty).scripts/**only ⇒skip-changeset.Generated by Claude Code