Skip to content

test(scripts): give 30 more self-tests a battery roster and floor - #14896

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1c
Sep 3, 2026
Merged

test(scripts): give 30 more self-tests a battery roster and floor#14896
baozhoutao merged 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1c

Conversation

@claude

@claude claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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:

  • 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, and UNATTRIBUTED_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;
  • one registerCase() at the head of the existing in-body assertion helper;
  • the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the report the file already prints carries them.

⛔ 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.mjs a brace-only depth count put four banners inside an array literal, where a battery() 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)

file batteries cases
scripts/check-adr-0087-registration.mjs 48 292
scripts/check-auth-mount-ledger.mjs 18 42
scripts/check-ci-filter-parity.mjs 7 42
scripts/check-comment-mask-adoption.mjs 6 23
scripts/check-console-injection.mjs 10 23
scripts/check-cross-repo-closer-outcome.mjs 5 81
scripts/check-dev-prereqs.mjs 16 51
scripts/check-dispatcher-error-vocabulary.mjs 2 (first battery hoisted) 282
scripts/check-doc-frontmatter.mjs 11 85
scripts/check-docs-locale-catch-all.mjs 15 28
scripts/check-docs-section-name.mjs 22 85
scripts/check-engine-split-ratio.mjs 2 16
scripts/check-error-status-conformance.mjs 23 40
scripts/check-i18n-stale-fill.mjs 2 (first battery hoisted) 27
scripts/check-init-service-contract.mjs 19 26
scripts/check-keyed-text-bounds.mjs 9 67
scripts/check-merge-queue-triage-outcome.mjs 7 126
scripts/check-overlay-whitelist-table.mjs 19 70
scripts/check-quick-reference-counts.mjs 22 37
scripts/check-ratchet-remedy-authority.mjs 20 27
scripts/check-required-contexts.mjs 22 150
scripts/check-route-envelope.mjs 27 (first battery hoisted) 88
scripts/check-section-landing-index.mjs 12 31
scripts/check-stall-guard-budget.mjs 10 61
scripts/check-system-context-census.mjs 17 43
scripts/check-undeclared-dep-imports.mjs 9 50
scripts/check-vendor-version-stamps.mjs 6 (first battery hoisted) 64
scripts/docs-audit/affected-docs.mjs 28 (first battery hoisted) 545
scripts/release-github-releases.mjs 10 49
scripts/sync-template-versions.mjs 8 40

Five 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

file measured reason
scripts/check-declaration-mirrors.mjs the in-body assertion sink is a CONCISE arrow — const 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.mjs no named section banner in the self-test body (0 of the 2 the criterion needs): the body carries one prose comment and no ruled, boxed or enumerated section head.
scripts/check-logger-receiver-detach.mjs one named section banner in the self-test body, below the two the criterion needs.
scripts/check-objectql-double-limit.mjs one named section banner in the self-test body, below the two the criterion needs.
scripts/check-skill-compatibility-version.mjs one named section banner in the self-test body, below the two the criterion needs.
scripts/check-skill-frame-freshness.mjs no in-body assertion helper at all: cases are pushed inline at ~14 separate cases.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 no named section banner in the self-test body: its comments label individual cases (// GOOD:, // BAD (rule 1)), not sections.
scripts/check-stack-collection-maps.mjs no named section banner in the self-test body.
scripts/check-startup-registry-verdict.mjs one named section banner in the self-test body, below the two the criterion needs.
scripts/check-tenant-chokepoint.mjs no named section banner in the self-test body: three unmarked prose comments head its blocks, and reading one as a section head rather than an explanation of the next statement is a judgement per comment, which this transplant does not make.
scripts/check-turbo-task-graph.mjs no body-level verdict site: selfTest() returns a failures array and the verdict is printed by a SEPARATE runSelfTest() (L892). That is the class-3 shape.
scripts/check-whole-set-label-write.mjs one named section banner in the self-test body, below the two the criterion needs.
scripts/check-wildcard-fallthrough.mjs no named section banner in the self-test body: four unmarked prose comments head its blocks.
scripts/js-comment-mask.mjs no named section banner in the self-test body.
scripts/publish-smoke-pack.mjs no comment at statement position in the self-test body at all, so no section banner.

Shrink-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_COUNTS is asserted structurally (two cases, with the row count inside the case rather than as case multiplicity), and RETIRED_CONTEXT_NAMES only 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-test before and after, exit code captured by redirect before any pipe. 30/30 exit 0 both sides, and cmp -s reports stdout and stderr byte-identical on every one. (Both readings are from the tree after pnpm install; without it seven files exit 3 on a missing-dist prerequisite and two on ERR_MODULE_NOT_FOUND, which is a container state, not a verdict.)

Census

node scripts/measure-self-test-floor.mjs --json:

merge base 2263ca4d6 this branch f248bc953
ROSTER 3 33
COUNT 4 4
NONE 158 128

Three set-equality assertions, computed in python, all True:

  1. every one of the 30 worklist files reads NONE before and ROSTER after;
  2. the set of files whose class changed equals the worklist exactly (symmetric difference empty);
  3. git diff --name-only equals the worklist exactly.

And the 128 still NONE decompose exactly — a disjoint partition whose union is the set, asserted in python, not read by eye:

128 = 40 PR #14851 (batch 1, unmerged)
    + 18 PR #14872 (batch 1b, unmerged)
    + 23 PR #14853 (issue #13798 batch 2, unmerged)
    + 15 this batch's measured drops
    +  2 honoured i18n exclusions (PR #14856)
    + 13 class 2 — fewer than two banners, re-derived from the tree
    +  5 class 3 — no body-level verdict site
    +  5 Tier C / shape-blocked
    +  3 honoured open-PR exclusions (check-type-check-coverage, pm/dispatch-gates, pm/bare-root-worklist)
    +  4 outside the declared Tier-B population (check-platform-checklist, check-regen-pending, git-merge-regen, setup-git-hooks)

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 trap restore on absolute paths; the mutation is proved on disk by an anchored grep count plus git hash-object against the HEAD blob, and the restore by an empty git diff HEAD — never by an exit code. No dist is on the resolution path of any of the four, so no rebuild leg applies to them.

  1. A battery's cases deletedscripts/check-auth-mount-ledger.mjs, the 2 ok(deriveBasePath( cases (grep 2 → 0; cd7b24abb8493fe3). 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.
  2. A roster entry deletedscripts/check-engine-split-ratio.mjs (grep 1 → 0; d92d218606deaef8). 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.
  3. The introduced registerCase() removed from the sink helperscripts/check-quick-reference-counts.mjs (grep 1 → 0; ce2b2de3a29feeba). Exit 1 with every original case still passing: all 22 batteries report DID NOT RUN.
  4. One battery() opener deletedscripts/docs-audit/affected-docs.mjs (grep 1 → 0; 965c95f0255698df). 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 base 2263ca4d6). 55 commands derived + check:declared-population-live + check:pm-dispatch-gates + check:nul-bytes = 58 run. Exit codes captured by redirect before any pipe.

  • 57 RAN-PASS (exit 0) of the 58 run, including both mandatory gates: check:ratchet-remedy-authority 0, check:declared-population-live 0 (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-gatesexit 0, dispatch-gates self-test: 1241 cases pass. No compound-anchor red: this batch introduces no top-level function declaration at all, so the SELF_TEST_DECL anchor sees nothing new. The battery/registerCase closures are function-scoped and the three new module-level names are consts, which the anchor does not match.
  • check-test-completenessexit 3, NOT MEASURED by the gate's own instruction, not a red.
  • Two first-pass readings that were container state, both cleared and re-run green: check-dev-prereqs exit 1 (The workspace is not built — 1 unmet precondition, not a list of problems) → 0 after pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, held 6m32s); check-engine-split-ratio exit 2 (this clone is shallow … a ratio derived here would be real, plausible and WRONG) → 0 after git 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 on f248bc953. No narrowing claimed.
  • Control-byte scan over the 30 changed files (grep -naP) clean; check:nul-bytes exit 0.

The build left the tracked tree unchanged (git status --porcelain empty).

scripts/** only ⇒ skip-changeset.


Generated by Claude Code

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
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing 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
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f116b8f8d1b43ec2bfd64fbf0ebb0cf3c301b1c8packageMentionDocs.

@baozhoutao
baozhoutao marked this pull request as ready for review September 3, 2026 11:01
@baozhoutao
baozhoutao enabled auto-merge September 3, 2026 11:02
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit a610775 Sep 3, 2026
42 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13799-self-test-roster-floor-b1c branch September 3, 2026 12:00
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
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.

1 participant