test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b) - #14872

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b
Draft

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b)#14872
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1b, Tier B class 1)

Batch 1b of the assertion-floor card: the PR #13487 roster-floor shape transplanted onto the scripts/** self-tests that batch 1 (PR #14851) could not take verbatim because their assertion sink is not a block-bodied helper inside the self-test body — it is a concise arrow (const t = (name, ok, detail) => cases.push({ ... });) or a module-scope function, so there was no { ... } to thread a per-run ledger through.

Per the batch-1 review ruling (comment 5521234092, class 1): the roster machinery lives at module scope, and each file's existing assertion sink is given a minimal block body that calls registerCase() and then returns the original expression unchanged.

  • ⛔ no case rewritten, ⛔ none reordered, ⛔ no assertion changed meaning
  • all 18 --self-test runs exit with the same code (0) and byte-identical stdout and stderr before and after
  • what is pinned is the registered NAMES, never a total; the roster's own size is pinned too

The shape, identical in all 18 files

Inserted after the import block (uniformly, so no dispatch can read a const before its initialiser — the TDZ trap batch 1 measured on check-adr-anchors):

  • SELF_TEST_BATTERIES — a frozen roster of battery NAMES, each with its own case floor
  • SELF_TEST_BATTERY_FLOOR — the roster's own size, so deleting an entry cannot silence a floor quietly
  • UNATTRIBUTED_BATTERY — the key an assertion is filed under with no battery open; undeclared on purpose, so it reds by the same set difference
  • battery() / registerCase() / batteryFloorFailures()

⚠️None of these helpers is named with a self-test spelling, and that is on the record rather than a dodge.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. The first draft called the floor evaluator selfTestFloorFailures() and reddened that gate on all 18 files. The ledger row is the right remedy for a pre-existing accidental name — the gate says so — but scripts/pm/dispatch-gates.mjs is on this dispatch's do-not-edit list (PR #14765 in flight), and this is a new helper whose name was being chosen for the first time. These helpers hold no fixtures to mask and read no path literal, so battery… is simply the accurate name for what they are. Flagged for the PM in the report in case a ledger row is preferred instead.

In the body: one battery('NAME') at each existing section banner, named verbatim from the banner text; one registerCase() in the sink; and the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the existing report prints them.

Files, and the sink each one grew a helper at

185 batteries, 968 cases pinned across 18 files.

filebatteriescasesthe sink registerCase() was added to
scripts/check-cli-command-ids.mjs839const t = (name, ok, detail = '') => cases.push({ ... })
scripts/check-cli-test-child-env.mjs24152const t = (name, ok, detail) => cases.push({ ... })
scripts/check-cross-package-test-inputs.mjs7117const ok = (label, cond) => cases.push({ label, cond })
scripts/check-dual-build-cjs-loads.mjs1593const t = (name, ok, detail) => cases.push({ ... })
scripts/check-entry-guard.mjs1054const t = (name, ok, detail) => cases.push({ ... })
scripts/check-live-db-isolation.mjs912const check = (label, ok) => cases.push({ label, ok })
scripts/check-parse-guard.mjs1346const t = (name, ok, detail) => cases.push({ ... })
scripts/check-partof-closing-keyword.mjs928const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-plugin-teardown-shape.mjs1247const t = (name, ok, detail) => cases.push({ ... })
scripts/check-published-list-mirrors.mjs837const ok = (label, cond) => cases.push({ label, cond })
scripts/check-runner-env-posture.mjs834const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-single-claim-paths.mjs754const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-tenant-audit-census.mjs419const t = (name, ok, detail) => cases.push({ ... })
scripts/check-watch-hint-literal.mjs957const t = (name, ok, detail) => cases.push({ ... })
scripts/import-prerequisite.mjs1760const t = (name, ok, detail) => cases.push({ ... })
scripts/invoked-as.mjs211const t = (name, ok, detail) => cases.push({ ... })
scripts/qa/qa-rollup.mjs1178module-scope function assert(cond, msg, failures) — called only from selfTest(), verified
scripts/ts-parse.mjs1230const t = (name, ok, detail) => cases.push({ ... })

Every concise arrow became:

constt=(name,ok,detail)=>{registerCase();returncases.push({ name,ok: Boolean(ok), detail });};

The return is load-bearing: it preserves the sink's value exactly, so the conversion is a wrap and not a rewrite.

Two files (check-cross-package-test-inputs, check-tenant-audit-census) run cases before their first section banner, so their first battery is opened at the top of the body — the PR #13487 precedent's own shape — and that banner then carries no second opener.

The shrink-only ruling, applied

Three batteries in this batch run one case per row of a list. Each was read by hand:

  • qa-rollup / every RETIRED phrasing is rejected, by the right reason (16 = RETIRED_TITLES) — pinned at 16. That list only grows; removing a row means un-retiring a phrasing, which is the regression it exists to catch.
  • check-plugin-teardown-shape / every roster name reds (4 = TEARDOWN_ALIASES) — pinned at 4. A move onto the roster grows it.
  • check-plugin-teardown-shape / the exclusions, pinned as cases rather than asserted in prose (18 = DELIBERATELY_EXCLUDED) — ⛔ NOT pinned at 18. It has no structural case of its own, and the roster it reads is complementary to TEARDOWN_ALIASES: promoting a name onto the teardown roster is a legitimate edit that shrinks this list while growing the sibling battery. Pinned instead at the part that does not move with the list — this battery ran and at least one excluded name was audited — with the reason written over the entry.

The two genuinely shrink-only ledgers this batch touches (KNOWN_TEARDOWN_UNREACHED, TYPED_EXEMPTIONS and the dual-build baseline) are asserted structurallyevery shipped X …, one case regardless of row count — so no floor here moves with them.

Census

node scripts/measure-self-test-floor.mjs --json, merge base 2263ca4d6 → this branch:

ROSTERCOUNTNONE
before34158
after214140

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

  1. every worklist file reads NONE before and ROSTER after;
  2. the set of files whose class changed equals this worklist exactly (symmetric difference empty);
  3. inside the handshake-carrying population, the files still NONE decompose exactly: 113 = 68 + 40 + 2 + 3 — 68 of the batch-1 remainder this batch does not take, 40 in flight on PR test(scripts): pin a battery roster floor on 40 more scripts/** self-tests #14851, check-i18n-coverage and check-i18n-bundles (honoured exclusions), and 3 handshake-carrying files outside batch 1's declared Tier-B population of 126.

⚠️ The class-1 set is 18, not 48 — the batch-1 class labels do not survive re-derivation

The dispatch put the class-1 surface at 48 and asked for the class to be re-derived from the tree. It was, and the number does not reproduce. 45 of the batch-1 report's 86 remainder_declared files do have a block-bodied assertion helper inside the self-test body — for example check-adr-0087-registration.mjs:3236 is const assert = (cond, msg) => { checked++; if (!cond) failures.push(msg); };, three lines below function selfTest(). Those files are not class 1 under the stated criterion, whatever kept them out of batch 1.

Applying the dispatch's criterion strictly — a resolvable entry body, no block-bodied in-body assertion helper, ≥2 named section banners all before the verdict, and a body-level if (SINK) verdict site — yields 23, of which 5 are excluded for shapes the class-1 ruling does not cover, leaving 18.

The direction of the difference is the safe one: this batch is a strict subset of any wider reading, so it cannot collide with batch 1c (classes 2 and 3). Under batch 1's own class ordering a file with no in-body helper is class 1, never 2 or 3, so nothing 1c owns is taken here.

The 68 batch-1 remainder files this batch does NOT take, with the measured reason

  • 45 — has a block-bodied in-body assertion helper, so not class 1: check-adr-0087-registration, check-auth-mount-ledger, check-ci-filter-parity, check-comment-mask-adoption, check-console-injection, check-cross-repo-closer-outcome, check-declaration-mirrors, check-dev-prereqs, check-dispatcher-error-vocabulary, check-doc-frontmatter, check-docs-locale-catch-all, check-docs-section-name, check-dts-emitted, check-engine-split-ratio, check-error-status-conformance, check-i18n-stale-fill, check-init-service-contract, check-keyed-text-bounds, check-logger-receiver-detach, check-merge-queue-triage-outcome, check-objectql-double-limit, check-overlay-whitelist-table, check-quick-reference-counts, check-ratchet-remedy-authority, check-required-contexts, check-route-envelope, check-section-landing-index, check-skill-compatibility-version, check-skill-frame-freshness, check-spec-parsed-alias, check-stack-collection-maps, check-stall-guard-budget, check-startup-registry-verdict, check-system-context-census, check-tenant-chokepoint, check-turbo-task-graph, check-undeclared-dep-imports, check-vendor-version-stamps, check-whole-set-label-write, check-wildcard-fallthrough, docs-audit/affected-docs, js-comment-mask, publish-smoke-pack, release-github-releases, sync-template-versions.
  • 13 — fewer than two named section banners (class 2, batch 1c): check-agent-model-declared, check-comment-mask-corpus, check-console-intercept-disarm, check-docs-single-h1, check-error-code-casing, check-optional-error-sink-contract, check-pnpm-acquisition, check-sdui-manifest, check-skill-frame-sync, check-widget-option-census, measure-position-name-fold-census, measure-test-shard-timings, render-release-coverage-anchor.
  • 5 — no body-level if (SINK) verdict site (class 3, batch 1c): check-adr-links, check-doc-anchors, check-kernel-hook-pairs, check-override-consistency, partition-test-shards.
  • 5 — class 1 by the criterion, but the class-1 remedy does not reach them:
    • ablation-dist-preflight and check-published-files — no single assertion sink; the sink is repeated inline in about six separate for loops, so "a minimal helper at the existing sink" would mean rewriting the reporting of every case.
    • check-examples-live-imports and check-org-identifier — table-driven, with every section banner inside the cases array literal, where no battery() statement can go. The card names this as Tier C.
    • check-durability-degradation-log-level — multi-entry dispatch, named as Tier C in the batch-1 report.

Verification

  • Per file, before and after:node GATE --self-test — 18/18 identical exit code (0) and byte-identical stdout and stderr, captured by redirect before any pipe. (check-plugin-teardown-shape needed the clone deepened to reach its pinned fixture commit; both readings are from the deepened tree.)
  • Three ablations, each from the committed tree, each with a trap restore, mutation proved on disk by anchored grep -c plus a git hash-object comparison against the HEAD blob, and restore proved by an empty git diff HEAD — never by an exit code:
    1. a battery's cases deleted (invoked-as, one t(...) line removed): exit 0 → 1, battery "the predicate, directly" registered 4 case(s), below its pinned floor of 5.
    2. a roster ENTRY deleted (invoked-as): exit 1 naming both halves — SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2andbattery "the fixture: a probe reached three ways" registered 6 case(s) but is not declared.
    3. the new registerCase() line removed from the sink (check-runner-env-posture) — the class-1-specific leg, proving the introduced helper is load-bearing: exit 1, every battery DID NOT RUN — 0 cases registered, N pinned, with every original case still passing.
  • No dist is on the resolution path of any of the three, so no rebuild leg applies to them; the dist-dependent gates were re-run after a real build.
  • scripts/check-nul-bytes.mjs exit 0; grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the 18 changed files: no hits.

Gates, on ca1e17bf

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — the script derives the change set itself) reconciles this card at 34 families: 32 by path + 2 by change kind. All 34 were run, plus check:declared-population-live, which the dispatch names as mandatory and which the derivation does not include — 35 commands. Exit codes captured by redirect before any pipe; every result below quotes the gate's own verdict line, never a bare $?.

  • 32 RAN-PASS (exit 0), the two mandatory ones included: pnpm check:ratchet-remedy-authority = 0 and pnpm check:declared-population-live = 0.
  • pnpm check:pm-dispatch-gates = 0✓ dispatch-gates self-test: 1241 cases pass. It was red on the first head for the compound-anchor reason above (✗ every compound self-test NAME the anchor matches is classified in COMPOUND_ANCHOR_LEDGER — unlisted: … ×18), and green after the helpers were named battery….
  • 2 = PREREQUISITE NOT MET, not a verdict.node scripts/check-partof-closing-keyword.mjs and node scripts/check-single-claim-paths.mjs exit 2 printing NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or usage failure, NOT a verdict. Their pnpm check:* aliases — what CI runs — both exit 0.
  • 1 = NOT MEASURED, by the gate's own instruction.node scripts/check-test-completeness.mjs exits 3: "That list names this script with NO argument… There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red."
  • Four gates first read exit 3 = NOT MEASURED for want of dist. ⛔ Never read as a pass: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* ran under scripts/pm/os-verify-lock.sh — VERDICT command-exit 0, 7m15s — and three of the four then exit 0 (check:dual-build-cjs-loads, and the two @objectstack/lint doc gates); the fourth is the NOT MEASURED one above. The build left the tracked tree unchanged (git status empty).
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0. No narrowing claimed.

Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label 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
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed on #13799 — content PASS. Test Core (1/6) on ca1e17bfd is the packages/cli shard signature tracked on #14822 / #14832 (job 100550425448; this diff is 18 scripts/** self-tests, nothing under packages/cli). Single non-own re-trigger spent now via update-branch (real merge of current origin/main); same shard red again ⇒ parked under the anchor, any other red is real.


Generated by Claude Code

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

Correction to the note above (same seat): the update-branch call did NOT happen — GitHub answered 422 "There are no new commits on the base branch." — because origin/main has not moved since this branch was cut (2263ca4d6, the merge queue has landed nothing since 03:48Z). So the PR's single non-own re-trigger is still unspent; it will be taken (merge of origin/main) once main moves, ideally carrying the #14832 fix. Until then the red stands as recorded: not this PR's, parked.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlskip-changesetPR 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

@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b) - #14872

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b
Draft

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b)#14872
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1b, Tier B class 1)

Batch 1b of the assertion-floor card: the PR #13487 roster-floor shape transplanted onto the scripts/** self-tests that batch 1 (PR #14851) could not take verbatim because their assertion sink is not a block-bodied helper inside the self-test body — it is a concise arrow (const t = (name, ok, detail) => cases.push({ ... });) or a module-scope function, so there was no { ... } to thread a per-run ledger through.

Per the batch-1 review ruling (comment 5521234092, class 1): the roster machinery lives at module scope, and each file's existing assertion sink is given a minimal block body that calls registerCase() and then returns the original expression unchanged.

  • ⛔ no case rewritten, ⛔ none reordered, ⛔ no assertion changed meaning
  • all 18 --self-test runs exit with the same code (0) and byte-identical stdout and stderr before and after
  • what is pinned is the registered NAMES, never a total; the roster's own size is pinned too

The shape, identical in all 18 files

Inserted after the import block (uniformly, so no dispatch can read a const before its initialiser — the TDZ trap batch 1 measured on check-adr-anchors):

  • SELF_TEST_BATTERIES — a frozen roster of battery NAMES, each with its own case floor
  • SELF_TEST_BATTERY_FLOOR — the roster's own size, so deleting an entry cannot silence a floor quietly
  • UNATTRIBUTED_BATTERY — the key an assertion is filed under with no battery open; undeclared on purpose, so it reds by the same set difference
  • battery() / registerCase() / batteryFloorFailures()

⚠️None of these helpers is named with a self-test spelling, and that is on the record rather than a dodge.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. The first draft called the floor evaluator selfTestFloorFailures() and reddened that gate on all 18 files. The ledger row is the right remedy for a pre-existing accidental name — the gate says so — but scripts/pm/dispatch-gates.mjs is on this dispatch's do-not-edit list (PR #14765 in flight), and this is a new helper whose name was being chosen for the first time. These helpers hold no fixtures to mask and read no path literal, so battery… is simply the accurate name for what they are. Flagged for the PM in the report in case a ledger row is preferred instead.

In the body: one battery('NAME') at each existing section banner, named verbatim from the banner text; one registerCase() in the sink; and the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the existing report prints them.

Files, and the sink each one grew a helper at

185 batteries, 968 cases pinned across 18 files.

filebatteriescasesthe sink registerCase() was added to
scripts/check-cli-command-ids.mjs839const t = (name, ok, detail = '') => cases.push({ ... })
scripts/check-cli-test-child-env.mjs24152const t = (name, ok, detail) => cases.push({ ... })
scripts/check-cross-package-test-inputs.mjs7117const ok = (label, cond) => cases.push({ label, cond })
scripts/check-dual-build-cjs-loads.mjs1593const t = (name, ok, detail) => cases.push({ ... })
scripts/check-entry-guard.mjs1054const t = (name, ok, detail) => cases.push({ ... })
scripts/check-live-db-isolation.mjs912const check = (label, ok) => cases.push({ label, ok })
scripts/check-parse-guard.mjs1346const t = (name, ok, detail) => cases.push({ ... })
scripts/check-partof-closing-keyword.mjs928const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-plugin-teardown-shape.mjs1247const t = (name, ok, detail) => cases.push({ ... })
scripts/check-published-list-mirrors.mjs837const ok = (label, cond) => cases.push({ label, cond })
scripts/check-runner-env-posture.mjs834const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-single-claim-paths.mjs754const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-tenant-audit-census.mjs419const t = (name, ok, detail) => cases.push({ ... })
scripts/check-watch-hint-literal.mjs957const t = (name, ok, detail) => cases.push({ ... })
scripts/import-prerequisite.mjs1760const t = (name, ok, detail) => cases.push({ ... })
scripts/invoked-as.mjs211const t = (name, ok, detail) => cases.push({ ... })
scripts/qa/qa-rollup.mjs1178module-scope function assert(cond, msg, failures) — called only from selfTest(), verified
scripts/ts-parse.mjs1230const t = (name, ok, detail) => cases.push({ ... })

Every concise arrow became:

constt=(name,ok,detail)=>{registerCase();returncases.push({ name,ok: Boolean(ok), detail });};

The return is load-bearing: it preserves the sink's value exactly, so the conversion is a wrap and not a rewrite.

Two files (check-cross-package-test-inputs, check-tenant-audit-census) run cases before their first section banner, so their first battery is opened at the top of the body — the PR #13487 precedent's own shape — and that banner then carries no second opener.

The shrink-only ruling, applied

Three batteries in this batch run one case per row of a list. Each was read by hand:

  • qa-rollup / every RETIRED phrasing is rejected, by the right reason (16 = RETIRED_TITLES) — pinned at 16. That list only grows; removing a row means un-retiring a phrasing, which is the regression it exists to catch.
  • check-plugin-teardown-shape / every roster name reds (4 = TEARDOWN_ALIASES) — pinned at 4. A move onto the roster grows it.
  • check-plugin-teardown-shape / the exclusions, pinned as cases rather than asserted in prose (18 = DELIBERATELY_EXCLUDED) — ⛔ NOT pinned at 18. It has no structural case of its own, and the roster it reads is complementary to TEARDOWN_ALIASES: promoting a name onto the teardown roster is a legitimate edit that shrinks this list while growing the sibling battery. Pinned instead at the part that does not move with the list — this battery ran and at least one excluded name was audited — with the reason written over the entry.

The two genuinely shrink-only ledgers this batch touches (KNOWN_TEARDOWN_UNREACHED, TYPED_EXEMPTIONS and the dual-build baseline) are asserted structurallyevery shipped X …, one case regardless of row count — so no floor here moves with them.

Census

node scripts/measure-self-test-floor.mjs --json, merge base 2263ca4d6 → this branch:

ROSTERCOUNTNONE
before34158
after214140

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

  1. every worklist file reads NONE before and ROSTER after;
  2. the set of files whose class changed equals this worklist exactly (symmetric difference empty);
  3. inside the handshake-carrying population, the files still NONE decompose exactly: 113 = 68 + 40 + 2 + 3 — 68 of the batch-1 remainder this batch does not take, 40 in flight on PR test(scripts): pin a battery roster floor on 40 more scripts/** self-tests #14851, check-i18n-coverage and check-i18n-bundles (honoured exclusions), and 3 handshake-carrying files outside batch 1's declared Tier-B population of 126.

⚠️ The class-1 set is 18, not 48 — the batch-1 class labels do not survive re-derivation

The dispatch put the class-1 surface at 48 and asked for the class to be re-derived from the tree. It was, and the number does not reproduce. 45 of the batch-1 report's 86 remainder_declared files do have a block-bodied assertion helper inside the self-test body — for example check-adr-0087-registration.mjs:3236 is const assert = (cond, msg) => { checked++; if (!cond) failures.push(msg); };, three lines below function selfTest(). Those files are not class 1 under the stated criterion, whatever kept them out of batch 1.

Applying the dispatch's criterion strictly — a resolvable entry body, no block-bodied in-body assertion helper, ≥2 named section banners all before the verdict, and a body-level if (SINK) verdict site — yields 23, of which 5 are excluded for shapes the class-1 ruling does not cover, leaving 18.

The direction of the difference is the safe one: this batch is a strict subset of any wider reading, so it cannot collide with batch 1c (classes 2 and 3). Under batch 1's own class ordering a file with no in-body helper is class 1, never 2 or 3, so nothing 1c owns is taken here.

The 68 batch-1 remainder files this batch does NOT take, with the measured reason

  • 45 — has a block-bodied in-body assertion helper, so not class 1: check-adr-0087-registration, check-auth-mount-ledger, check-ci-filter-parity, check-comment-mask-adoption, check-console-injection, check-cross-repo-closer-outcome, check-declaration-mirrors, check-dev-prereqs, check-dispatcher-error-vocabulary, check-doc-frontmatter, check-docs-locale-catch-all, check-docs-section-name, check-dts-emitted, check-engine-split-ratio, check-error-status-conformance, check-i18n-stale-fill, check-init-service-contract, check-keyed-text-bounds, check-logger-receiver-detach, check-merge-queue-triage-outcome, check-objectql-double-limit, check-overlay-whitelist-table, check-quick-reference-counts, check-ratchet-remedy-authority, check-required-contexts, check-route-envelope, check-section-landing-index, check-skill-compatibility-version, check-skill-frame-freshness, check-spec-parsed-alias, check-stack-collection-maps, check-stall-guard-budget, check-startup-registry-verdict, check-system-context-census, check-tenant-chokepoint, check-turbo-task-graph, check-undeclared-dep-imports, check-vendor-version-stamps, check-whole-set-label-write, check-wildcard-fallthrough, docs-audit/affected-docs, js-comment-mask, publish-smoke-pack, release-github-releases, sync-template-versions.
  • 13 — fewer than two named section banners (class 2, batch 1c): check-agent-model-declared, check-comment-mask-corpus, check-console-intercept-disarm, check-docs-single-h1, check-error-code-casing, check-optional-error-sink-contract, check-pnpm-acquisition, check-sdui-manifest, check-skill-frame-sync, check-widget-option-census, measure-position-name-fold-census, measure-test-shard-timings, render-release-coverage-anchor.
  • 5 — no body-level if (SINK) verdict site (class 3, batch 1c): check-adr-links, check-doc-anchors, check-kernel-hook-pairs, check-override-consistency, partition-test-shards.
  • 5 — class 1 by the criterion, but the class-1 remedy does not reach them:
    • ablation-dist-preflight and check-published-files — no single assertion sink; the sink is repeated inline in about six separate for loops, so "a minimal helper at the existing sink" would mean rewriting the reporting of every case.
    • check-examples-live-imports and check-org-identifier — table-driven, with every section banner inside the cases array literal, where no battery() statement can go. The card names this as Tier C.
    • check-durability-degradation-log-level — multi-entry dispatch, named as Tier C in the batch-1 report.

Verification

  • Per file, before and after:node GATE --self-test — 18/18 identical exit code (0) and byte-identical stdout and stderr, captured by redirect before any pipe. (check-plugin-teardown-shape needed the clone deepened to reach its pinned fixture commit; both readings are from the deepened tree.)
  • Three ablations, each from the committed tree, each with a trap restore, mutation proved on disk by anchored grep -c plus a git hash-object comparison against the HEAD blob, and restore proved by an empty git diff HEAD — never by an exit code:
    1. a battery's cases deleted (invoked-as, one t(...) line removed): exit 0 → 1, battery "the predicate, directly" registered 4 case(s), below its pinned floor of 5.
    2. a roster ENTRY deleted (invoked-as): exit 1 naming both halves — SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2andbattery "the fixture: a probe reached three ways" registered 6 case(s) but is not declared.
    3. the new registerCase() line removed from the sink (check-runner-env-posture) — the class-1-specific leg, proving the introduced helper is load-bearing: exit 1, every battery DID NOT RUN — 0 cases registered, N pinned, with every original case still passing.
  • No dist is on the resolution path of any of the three, so no rebuild leg applies to them; the dist-dependent gates were re-run after a real build.
  • scripts/check-nul-bytes.mjs exit 0; grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the 18 changed files: no hits.

Gates, on ca1e17bf

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — the script derives the change set itself) reconciles this card at 34 families: 32 by path + 2 by change kind. All 34 were run, plus check:declared-population-live, which the dispatch names as mandatory and which the derivation does not include — 35 commands. Exit codes captured by redirect before any pipe; every result below quotes the gate's own verdict line, never a bare $?.

  • 32 RAN-PASS (exit 0), the two mandatory ones included: pnpm check:ratchet-remedy-authority = 0 and pnpm check:declared-population-live = 0.
  • pnpm check:pm-dispatch-gates = 0✓ dispatch-gates self-test: 1241 cases pass. It was red on the first head for the compound-anchor reason above (✗ every compound self-test NAME the anchor matches is classified in COMPOUND_ANCHOR_LEDGER — unlisted: … ×18), and green after the helpers were named battery….
  • 2 = PREREQUISITE NOT MET, not a verdict.node scripts/check-partof-closing-keyword.mjs and node scripts/check-single-claim-paths.mjs exit 2 printing NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or usage failure, NOT a verdict. Their pnpm check:* aliases — what CI runs — both exit 0.
  • 1 = NOT MEASURED, by the gate's own instruction.node scripts/check-test-completeness.mjs exits 3: "That list names this script with NO argument… There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red."
  • Four gates first read exit 3 = NOT MEASURED for want of dist. ⛔ Never read as a pass: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* ran under scripts/pm/os-verify-lock.sh — VERDICT command-exit 0, 7m15s — and three of the four then exit 0 (check:dual-build-cjs-loads, and the two @objectstack/lint doc gates); the fourth is the NOT MEASURED one above. The build left the tracked tree unchanged (git status empty).
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0. No narrowing claimed.

Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label 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
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed on #13799 — content PASS. Test Core (1/6) on ca1e17bfd is the packages/cli shard signature tracked on #14822 / #14832 (job 100550425448; this diff is 18 scripts/** self-tests, nothing under packages/cli). Single non-own re-trigger spent now via update-branch (real merge of current origin/main); same shard red again ⇒ parked under the anchor, any other red is real.


Generated by Claude Code

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

Correction to the note above (same seat): the update-branch call did NOT happen — GitHub answered 422 "There are no new commits on the base branch." — because origin/main has not moved since this branch was cut (2263ca4d6, the merge queue has landed nothing since 03:48Z). So the PR's single non-own re-trigger is still unspent; it will be taken (merge of origin/main) once main moves, ideally carrying the #14832 fix. Until then the red stands as recorded: not this PR's, parked.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlskip-changesetPR 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

@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b) - #14872

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b
Draft

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b)#14872
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1b, Tier B class 1)

Batch 1b of the assertion-floor card: the PR #13487 roster-floor shape transplanted onto the scripts/** self-tests that batch 1 (PR #14851) could not take verbatim because their assertion sink is not a block-bodied helper inside the self-test body — it is a concise arrow (const t = (name, ok, detail) => cases.push({ ... });) or a module-scope function, so there was no { ... } to thread a per-run ledger through.

Per the batch-1 review ruling (comment 5521234092, class 1): the roster machinery lives at module scope, and each file's existing assertion sink is given a minimal block body that calls registerCase() and then returns the original expression unchanged.

  • ⛔ no case rewritten, ⛔ none reordered, ⛔ no assertion changed meaning
  • all 18 --self-test runs exit with the same code (0) and byte-identical stdout and stderr before and after
  • what is pinned is the registered NAMES, never a total; the roster's own size is pinned too

The shape, identical in all 18 files

Inserted after the import block (uniformly, so no dispatch can read a const before its initialiser — the TDZ trap batch 1 measured on check-adr-anchors):

  • SELF_TEST_BATTERIES — a frozen roster of battery NAMES, each with its own case floor
  • SELF_TEST_BATTERY_FLOOR — the roster's own size, so deleting an entry cannot silence a floor quietly
  • UNATTRIBUTED_BATTERY — the key an assertion is filed under with no battery open; undeclared on purpose, so it reds by the same set difference
  • battery() / registerCase() / batteryFloorFailures()

⚠️None of these helpers is named with a self-test spelling, and that is on the record rather than a dodge.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. The first draft called the floor evaluator selfTestFloorFailures() and reddened that gate on all 18 files. The ledger row is the right remedy for a pre-existing accidental name — the gate says so — but scripts/pm/dispatch-gates.mjs is on this dispatch's do-not-edit list (PR #14765 in flight), and this is a new helper whose name was being chosen for the first time. These helpers hold no fixtures to mask and read no path literal, so battery… is simply the accurate name for what they are. Flagged for the PM in the report in case a ledger row is preferred instead.

In the body: one battery('NAME') at each existing section banner, named verbatim from the banner text; one registerCase() in the sink; and the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the existing report prints them.

Files, and the sink each one grew a helper at

185 batteries, 968 cases pinned across 18 files.

filebatteriescasesthe sink registerCase() was added to
scripts/check-cli-command-ids.mjs839const t = (name, ok, detail = '') => cases.push({ ... })
scripts/check-cli-test-child-env.mjs24152const t = (name, ok, detail) => cases.push({ ... })
scripts/check-cross-package-test-inputs.mjs7117const ok = (label, cond) => cases.push({ label, cond })
scripts/check-dual-build-cjs-loads.mjs1593const t = (name, ok, detail) => cases.push({ ... })
scripts/check-entry-guard.mjs1054const t = (name, ok, detail) => cases.push({ ... })
scripts/check-live-db-isolation.mjs912const check = (label, ok) => cases.push({ label, ok })
scripts/check-parse-guard.mjs1346const t = (name, ok, detail) => cases.push({ ... })
scripts/check-partof-closing-keyword.mjs928const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-plugin-teardown-shape.mjs1247const t = (name, ok, detail) => cases.push({ ... })
scripts/check-published-list-mirrors.mjs837const ok = (label, cond) => cases.push({ label, cond })
scripts/check-runner-env-posture.mjs834const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-single-claim-paths.mjs754const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-tenant-audit-census.mjs419const t = (name, ok, detail) => cases.push({ ... })
scripts/check-watch-hint-literal.mjs957const t = (name, ok, detail) => cases.push({ ... })
scripts/import-prerequisite.mjs1760const t = (name, ok, detail) => cases.push({ ... })
scripts/invoked-as.mjs211const t = (name, ok, detail) => cases.push({ ... })
scripts/qa/qa-rollup.mjs1178module-scope function assert(cond, msg, failures) — called only from selfTest(), verified
scripts/ts-parse.mjs1230const t = (name, ok, detail) => cases.push({ ... })

Every concise arrow became:

constt=(name,ok,detail)=>{registerCase();returncases.push({ name,ok: Boolean(ok), detail });};

The return is load-bearing: it preserves the sink's value exactly, so the conversion is a wrap and not a rewrite.

Two files (check-cross-package-test-inputs, check-tenant-audit-census) run cases before their first section banner, so their first battery is opened at the top of the body — the PR #13487 precedent's own shape — and that banner then carries no second opener.

The shrink-only ruling, applied

Three batteries in this batch run one case per row of a list. Each was read by hand:

  • qa-rollup / every RETIRED phrasing is rejected, by the right reason (16 = RETIRED_TITLES) — pinned at 16. That list only grows; removing a row means un-retiring a phrasing, which is the regression it exists to catch.
  • check-plugin-teardown-shape / every roster name reds (4 = TEARDOWN_ALIASES) — pinned at 4. A move onto the roster grows it.
  • check-plugin-teardown-shape / the exclusions, pinned as cases rather than asserted in prose (18 = DELIBERATELY_EXCLUDED) — ⛔ NOT pinned at 18. It has no structural case of its own, and the roster it reads is complementary to TEARDOWN_ALIASES: promoting a name onto the teardown roster is a legitimate edit that shrinks this list while growing the sibling battery. Pinned instead at the part that does not move with the list — this battery ran and at least one excluded name was audited — with the reason written over the entry.

The two genuinely shrink-only ledgers this batch touches (KNOWN_TEARDOWN_UNREACHED, TYPED_EXEMPTIONS and the dual-build baseline) are asserted structurallyevery shipped X …, one case regardless of row count — so no floor here moves with them.

Census

node scripts/measure-self-test-floor.mjs --json, merge base 2263ca4d6 → this branch:

ROSTERCOUNTNONE
before34158
after214140

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

  1. every worklist file reads NONE before and ROSTER after;
  2. the set of files whose class changed equals this worklist exactly (symmetric difference empty);
  3. inside the handshake-carrying population, the files still NONE decompose exactly: 113 = 68 + 40 + 2 + 3 — 68 of the batch-1 remainder this batch does not take, 40 in flight on PR test(scripts): pin a battery roster floor on 40 more scripts/** self-tests #14851, check-i18n-coverage and check-i18n-bundles (honoured exclusions), and 3 handshake-carrying files outside batch 1's declared Tier-B population of 126.

⚠️ The class-1 set is 18, not 48 — the batch-1 class labels do not survive re-derivation

The dispatch put the class-1 surface at 48 and asked for the class to be re-derived from the tree. It was, and the number does not reproduce. 45 of the batch-1 report's 86 remainder_declared files do have a block-bodied assertion helper inside the self-test body — for example check-adr-0087-registration.mjs:3236 is const assert = (cond, msg) => { checked++; if (!cond) failures.push(msg); };, three lines below function selfTest(). Those files are not class 1 under the stated criterion, whatever kept them out of batch 1.

Applying the dispatch's criterion strictly — a resolvable entry body, no block-bodied in-body assertion helper, ≥2 named section banners all before the verdict, and a body-level if (SINK) verdict site — yields 23, of which 5 are excluded for shapes the class-1 ruling does not cover, leaving 18.

The direction of the difference is the safe one: this batch is a strict subset of any wider reading, so it cannot collide with batch 1c (classes 2 and 3). Under batch 1's own class ordering a file with no in-body helper is class 1, never 2 or 3, so nothing 1c owns is taken here.

The 68 batch-1 remainder files this batch does NOT take, with the measured reason

  • 45 — has a block-bodied in-body assertion helper, so not class 1: check-adr-0087-registration, check-auth-mount-ledger, check-ci-filter-parity, check-comment-mask-adoption, check-console-injection, check-cross-repo-closer-outcome, check-declaration-mirrors, check-dev-prereqs, check-dispatcher-error-vocabulary, check-doc-frontmatter, check-docs-locale-catch-all, check-docs-section-name, check-dts-emitted, check-engine-split-ratio, check-error-status-conformance, check-i18n-stale-fill, check-init-service-contract, check-keyed-text-bounds, check-logger-receiver-detach, check-merge-queue-triage-outcome, check-objectql-double-limit, check-overlay-whitelist-table, check-quick-reference-counts, check-ratchet-remedy-authority, check-required-contexts, check-route-envelope, check-section-landing-index, check-skill-compatibility-version, check-skill-frame-freshness, check-spec-parsed-alias, check-stack-collection-maps, check-stall-guard-budget, check-startup-registry-verdict, check-system-context-census, check-tenant-chokepoint, check-turbo-task-graph, check-undeclared-dep-imports, check-vendor-version-stamps, check-whole-set-label-write, check-wildcard-fallthrough, docs-audit/affected-docs, js-comment-mask, publish-smoke-pack, release-github-releases, sync-template-versions.
  • 13 — fewer than two named section banners (class 2, batch 1c): check-agent-model-declared, check-comment-mask-corpus, check-console-intercept-disarm, check-docs-single-h1, check-error-code-casing, check-optional-error-sink-contract, check-pnpm-acquisition, check-sdui-manifest, check-skill-frame-sync, check-widget-option-census, measure-position-name-fold-census, measure-test-shard-timings, render-release-coverage-anchor.
  • 5 — no body-level if (SINK) verdict site (class 3, batch 1c): check-adr-links, check-doc-anchors, check-kernel-hook-pairs, check-override-consistency, partition-test-shards.
  • 5 — class 1 by the criterion, but the class-1 remedy does not reach them:
    • ablation-dist-preflight and check-published-files — no single assertion sink; the sink is repeated inline in about six separate for loops, so "a minimal helper at the existing sink" would mean rewriting the reporting of every case.
    • check-examples-live-imports and check-org-identifier — table-driven, with every section banner inside the cases array literal, where no battery() statement can go. The card names this as Tier C.
    • check-durability-degradation-log-level — multi-entry dispatch, named as Tier C in the batch-1 report.

Verification

  • Per file, before and after:node GATE --self-test — 18/18 identical exit code (0) and byte-identical stdout and stderr, captured by redirect before any pipe. (check-plugin-teardown-shape needed the clone deepened to reach its pinned fixture commit; both readings are from the deepened tree.)
  • Three ablations, each from the committed tree, each with a trap restore, mutation proved on disk by anchored grep -c plus a git hash-object comparison against the HEAD blob, and restore proved by an empty git diff HEAD — never by an exit code:
    1. a battery's cases deleted (invoked-as, one t(...) line removed): exit 0 → 1, battery "the predicate, directly" registered 4 case(s), below its pinned floor of 5.
    2. a roster ENTRY deleted (invoked-as): exit 1 naming both halves — SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2andbattery "the fixture: a probe reached three ways" registered 6 case(s) but is not declared.
    3. the new registerCase() line removed from the sink (check-runner-env-posture) — the class-1-specific leg, proving the introduced helper is load-bearing: exit 1, every battery DID NOT RUN — 0 cases registered, N pinned, with every original case still passing.
  • No dist is on the resolution path of any of the three, so no rebuild leg applies to them; the dist-dependent gates were re-run after a real build.
  • scripts/check-nul-bytes.mjs exit 0; grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the 18 changed files: no hits.

Gates, on ca1e17bf

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — the script derives the change set itself) reconciles this card at 34 families: 32 by path + 2 by change kind. All 34 were run, plus check:declared-population-live, which the dispatch names as mandatory and which the derivation does not include — 35 commands. Exit codes captured by redirect before any pipe; every result below quotes the gate's own verdict line, never a bare $?.

  • 32 RAN-PASS (exit 0), the two mandatory ones included: pnpm check:ratchet-remedy-authority = 0 and pnpm check:declared-population-live = 0.
  • pnpm check:pm-dispatch-gates = 0✓ dispatch-gates self-test: 1241 cases pass. It was red on the first head for the compound-anchor reason above (✗ every compound self-test NAME the anchor matches is classified in COMPOUND_ANCHOR_LEDGER — unlisted: … ×18), and green after the helpers were named battery….
  • 2 = PREREQUISITE NOT MET, not a verdict.node scripts/check-partof-closing-keyword.mjs and node scripts/check-single-claim-paths.mjs exit 2 printing NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or usage failure, NOT a verdict. Their pnpm check:* aliases — what CI runs — both exit 0.
  • 1 = NOT MEASURED, by the gate's own instruction.node scripts/check-test-completeness.mjs exits 3: "That list names this script with NO argument… There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red."
  • Four gates first read exit 3 = NOT MEASURED for want of dist. ⛔ Never read as a pass: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* ran under scripts/pm/os-verify-lock.sh — VERDICT command-exit 0, 7m15s — and three of the four then exit 0 (check:dual-build-cjs-loads, and the two @objectstack/lint doc gates); the fourth is the NOT MEASURED one above. The build left the tracked tree unchanged (git status empty).
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0. No narrowing claimed.

Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label 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
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed on #13799 — content PASS. Test Core (1/6) on ca1e17bfd is the packages/cli shard signature tracked on #14822 / #14832 (job 100550425448; this diff is 18 scripts/** self-tests, nothing under packages/cli). Single non-own re-trigger spent now via update-branch (real merge of current origin/main); same shard red again ⇒ parked under the anchor, any other red is real.


Generated by Claude Code

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

Correction to the note above (same seat): the update-branch call did NOT happen — GitHub answered 422 "There are no new commits on the base branch." — because origin/main has not moved since this branch was cut (2263ca4d6, the merge queue has landed nothing since 03:48Z). So the PR's single non-own re-trigger is still unspent; it will be taken (merge of origin/main) once main moves, ideally carrying the #14832 fix. Until then the red stands as recorded: not this PR's, parked.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlskip-changesetPR 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

@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b) - #14872

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b
Draft

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b)#14872
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1b, Tier B class 1)

Batch 1b of the assertion-floor card: the PR #13487 roster-floor shape transplanted onto the scripts/** self-tests that batch 1 (PR #14851) could not take verbatim because their assertion sink is not a block-bodied helper inside the self-test body — it is a concise arrow (const t = (name, ok, detail) => cases.push({ ... });) or a module-scope function, so there was no { ... } to thread a per-run ledger through.

Per the batch-1 review ruling (comment 5521234092, class 1): the roster machinery lives at module scope, and each file's existing assertion sink is given a minimal block body that calls registerCase() and then returns the original expression unchanged.

  • ⛔ no case rewritten, ⛔ none reordered, ⛔ no assertion changed meaning
  • all 18 --self-test runs exit with the same code (0) and byte-identical stdout and stderr before and after
  • what is pinned is the registered NAMES, never a total; the roster's own size is pinned too

The shape, identical in all 18 files

Inserted after the import block (uniformly, so no dispatch can read a const before its initialiser — the TDZ trap batch 1 measured on check-adr-anchors):

  • SELF_TEST_BATTERIES — a frozen roster of battery NAMES, each with its own case floor
  • SELF_TEST_BATTERY_FLOOR — the roster's own size, so deleting an entry cannot silence a floor quietly
  • UNATTRIBUTED_BATTERY — the key an assertion is filed under with no battery open; undeclared on purpose, so it reds by the same set difference
  • battery() / registerCase() / batteryFloorFailures()

⚠️None of these helpers is named with a self-test spelling, and that is on the record rather than a dodge.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. The first draft called the floor evaluator selfTestFloorFailures() and reddened that gate on all 18 files. The ledger row is the right remedy for a pre-existing accidental name — the gate says so — but scripts/pm/dispatch-gates.mjs is on this dispatch's do-not-edit list (PR #14765 in flight), and this is a new helper whose name was being chosen for the first time. These helpers hold no fixtures to mask and read no path literal, so battery… is simply the accurate name for what they are. Flagged for the PM in the report in case a ledger row is preferred instead.

In the body: one battery('NAME') at each existing section banner, named verbatim from the banner text; one registerCase() in the sink; and the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the existing report prints them.

Files, and the sink each one grew a helper at

185 batteries, 968 cases pinned across 18 files.

filebatteriescasesthe sink registerCase() was added to
scripts/check-cli-command-ids.mjs839const t = (name, ok, detail = '') => cases.push({ ... })
scripts/check-cli-test-child-env.mjs24152const t = (name, ok, detail) => cases.push({ ... })
scripts/check-cross-package-test-inputs.mjs7117const ok = (label, cond) => cases.push({ label, cond })
scripts/check-dual-build-cjs-loads.mjs1593const t = (name, ok, detail) => cases.push({ ... })
scripts/check-entry-guard.mjs1054const t = (name, ok, detail) => cases.push({ ... })
scripts/check-live-db-isolation.mjs912const check = (label, ok) => cases.push({ label, ok })
scripts/check-parse-guard.mjs1346const t = (name, ok, detail) => cases.push({ ... })
scripts/check-partof-closing-keyword.mjs928const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-plugin-teardown-shape.mjs1247const t = (name, ok, detail) => cases.push({ ... })
scripts/check-published-list-mirrors.mjs837const ok = (label, cond) => cases.push({ label, cond })
scripts/check-runner-env-posture.mjs834const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-single-claim-paths.mjs754const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-tenant-audit-census.mjs419const t = (name, ok, detail) => cases.push({ ... })
scripts/check-watch-hint-literal.mjs957const t = (name, ok, detail) => cases.push({ ... })
scripts/import-prerequisite.mjs1760const t = (name, ok, detail) => cases.push({ ... })
scripts/invoked-as.mjs211const t = (name, ok, detail) => cases.push({ ... })
scripts/qa/qa-rollup.mjs1178module-scope function assert(cond, msg, failures) — called only from selfTest(), verified
scripts/ts-parse.mjs1230const t = (name, ok, detail) => cases.push({ ... })

Every concise arrow became:

constt=(name,ok,detail)=>{registerCase();returncases.push({ name,ok: Boolean(ok), detail });};

The return is load-bearing: it preserves the sink's value exactly, so the conversion is a wrap and not a rewrite.

Two files (check-cross-package-test-inputs, check-tenant-audit-census) run cases before their first section banner, so their first battery is opened at the top of the body — the PR #13487 precedent's own shape — and that banner then carries no second opener.

The shrink-only ruling, applied

Three batteries in this batch run one case per row of a list. Each was read by hand:

  • qa-rollup / every RETIRED phrasing is rejected, by the right reason (16 = RETIRED_TITLES) — pinned at 16. That list only grows; removing a row means un-retiring a phrasing, which is the regression it exists to catch.
  • check-plugin-teardown-shape / every roster name reds (4 = TEARDOWN_ALIASES) — pinned at 4. A move onto the roster grows it.
  • check-plugin-teardown-shape / the exclusions, pinned as cases rather than asserted in prose (18 = DELIBERATELY_EXCLUDED) — ⛔ NOT pinned at 18. It has no structural case of its own, and the roster it reads is complementary to TEARDOWN_ALIASES: promoting a name onto the teardown roster is a legitimate edit that shrinks this list while growing the sibling battery. Pinned instead at the part that does not move with the list — this battery ran and at least one excluded name was audited — with the reason written over the entry.

The two genuinely shrink-only ledgers this batch touches (KNOWN_TEARDOWN_UNREACHED, TYPED_EXEMPTIONS and the dual-build baseline) are asserted structurallyevery shipped X …, one case regardless of row count — so no floor here moves with them.

Census

node scripts/measure-self-test-floor.mjs --json, merge base 2263ca4d6 → this branch:

ROSTERCOUNTNONE
before34158
after214140

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

  1. every worklist file reads NONE before and ROSTER after;
  2. the set of files whose class changed equals this worklist exactly (symmetric difference empty);
  3. inside the handshake-carrying population, the files still NONE decompose exactly: 113 = 68 + 40 + 2 + 3 — 68 of the batch-1 remainder this batch does not take, 40 in flight on PR test(scripts): pin a battery roster floor on 40 more scripts/** self-tests #14851, check-i18n-coverage and check-i18n-bundles (honoured exclusions), and 3 handshake-carrying files outside batch 1's declared Tier-B population of 126.

⚠️ The class-1 set is 18, not 48 — the batch-1 class labels do not survive re-derivation

The dispatch put the class-1 surface at 48 and asked for the class to be re-derived from the tree. It was, and the number does not reproduce. 45 of the batch-1 report's 86 remainder_declared files do have a block-bodied assertion helper inside the self-test body — for example check-adr-0087-registration.mjs:3236 is const assert = (cond, msg) => { checked++; if (!cond) failures.push(msg); };, three lines below function selfTest(). Those files are not class 1 under the stated criterion, whatever kept them out of batch 1.

Applying the dispatch's criterion strictly — a resolvable entry body, no block-bodied in-body assertion helper, ≥2 named section banners all before the verdict, and a body-level if (SINK) verdict site — yields 23, of which 5 are excluded for shapes the class-1 ruling does not cover, leaving 18.

The direction of the difference is the safe one: this batch is a strict subset of any wider reading, so it cannot collide with batch 1c (classes 2 and 3). Under batch 1's own class ordering a file with no in-body helper is class 1, never 2 or 3, so nothing 1c owns is taken here.

The 68 batch-1 remainder files this batch does NOT take, with the measured reason

  • 45 — has a block-bodied in-body assertion helper, so not class 1: check-adr-0087-registration, check-auth-mount-ledger, check-ci-filter-parity, check-comment-mask-adoption, check-console-injection, check-cross-repo-closer-outcome, check-declaration-mirrors, check-dev-prereqs, check-dispatcher-error-vocabulary, check-doc-frontmatter, check-docs-locale-catch-all, check-docs-section-name, check-dts-emitted, check-engine-split-ratio, check-error-status-conformance, check-i18n-stale-fill, check-init-service-contract, check-keyed-text-bounds, check-logger-receiver-detach, check-merge-queue-triage-outcome, check-objectql-double-limit, check-overlay-whitelist-table, check-quick-reference-counts, check-ratchet-remedy-authority, check-required-contexts, check-route-envelope, check-section-landing-index, check-skill-compatibility-version, check-skill-frame-freshness, check-spec-parsed-alias, check-stack-collection-maps, check-stall-guard-budget, check-startup-registry-verdict, check-system-context-census, check-tenant-chokepoint, check-turbo-task-graph, check-undeclared-dep-imports, check-vendor-version-stamps, check-whole-set-label-write, check-wildcard-fallthrough, docs-audit/affected-docs, js-comment-mask, publish-smoke-pack, release-github-releases, sync-template-versions.
  • 13 — fewer than two named section banners (class 2, batch 1c): check-agent-model-declared, check-comment-mask-corpus, check-console-intercept-disarm, check-docs-single-h1, check-error-code-casing, check-optional-error-sink-contract, check-pnpm-acquisition, check-sdui-manifest, check-skill-frame-sync, check-widget-option-census, measure-position-name-fold-census, measure-test-shard-timings, render-release-coverage-anchor.
  • 5 — no body-level if (SINK) verdict site (class 3, batch 1c): check-adr-links, check-doc-anchors, check-kernel-hook-pairs, check-override-consistency, partition-test-shards.
  • 5 — class 1 by the criterion, but the class-1 remedy does not reach them:
    • ablation-dist-preflight and check-published-files — no single assertion sink; the sink is repeated inline in about six separate for loops, so "a minimal helper at the existing sink" would mean rewriting the reporting of every case.
    • check-examples-live-imports and check-org-identifier — table-driven, with every section banner inside the cases array literal, where no battery() statement can go. The card names this as Tier C.
    • check-durability-degradation-log-level — multi-entry dispatch, named as Tier C in the batch-1 report.

Verification

  • Per file, before and after:node GATE --self-test — 18/18 identical exit code (0) and byte-identical stdout and stderr, captured by redirect before any pipe. (check-plugin-teardown-shape needed the clone deepened to reach its pinned fixture commit; both readings are from the deepened tree.)
  • Three ablations, each from the committed tree, each with a trap restore, mutation proved on disk by anchored grep -c plus a git hash-object comparison against the HEAD blob, and restore proved by an empty git diff HEAD — never by an exit code:
    1. a battery's cases deleted (invoked-as, one t(...) line removed): exit 0 → 1, battery "the predicate, directly" registered 4 case(s), below its pinned floor of 5.
    2. a roster ENTRY deleted (invoked-as): exit 1 naming both halves — SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2andbattery "the fixture: a probe reached three ways" registered 6 case(s) but is not declared.
    3. the new registerCase() line removed from the sink (check-runner-env-posture) — the class-1-specific leg, proving the introduced helper is load-bearing: exit 1, every battery DID NOT RUN — 0 cases registered, N pinned, with every original case still passing.
  • No dist is on the resolution path of any of the three, so no rebuild leg applies to them; the dist-dependent gates were re-run after a real build.
  • scripts/check-nul-bytes.mjs exit 0; grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the 18 changed files: no hits.

Gates, on ca1e17bf

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — the script derives the change set itself) reconciles this card at 34 families: 32 by path + 2 by change kind. All 34 were run, plus check:declared-population-live, which the dispatch names as mandatory and which the derivation does not include — 35 commands. Exit codes captured by redirect before any pipe; every result below quotes the gate's own verdict line, never a bare $?.

  • 32 RAN-PASS (exit 0), the two mandatory ones included: pnpm check:ratchet-remedy-authority = 0 and pnpm check:declared-population-live = 0.
  • pnpm check:pm-dispatch-gates = 0✓ dispatch-gates self-test: 1241 cases pass. It was red on the first head for the compound-anchor reason above (✗ every compound self-test NAME the anchor matches is classified in COMPOUND_ANCHOR_LEDGER — unlisted: … ×18), and green after the helpers were named battery….
  • 2 = PREREQUISITE NOT MET, not a verdict.node scripts/check-partof-closing-keyword.mjs and node scripts/check-single-claim-paths.mjs exit 2 printing NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or usage failure, NOT a verdict. Their pnpm check:* aliases — what CI runs — both exit 0.
  • 1 = NOT MEASURED, by the gate's own instruction.node scripts/check-test-completeness.mjs exits 3: "That list names this script with NO argument… There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red."
  • Four gates first read exit 3 = NOT MEASURED for want of dist. ⛔ Never read as a pass: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* ran under scripts/pm/os-verify-lock.sh — VERDICT command-exit 0, 7m15s — and three of the four then exit 0 (check:dual-build-cjs-loads, and the two @objectstack/lint doc gates); the fourth is the NOT MEASURED one above. The build left the tracked tree unchanged (git status empty).
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0. No narrowing claimed.

Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label 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
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed on #13799 — content PASS. Test Core (1/6) on ca1e17bfd is the packages/cli shard signature tracked on #14822 / #14832 (job 100550425448; this diff is 18 scripts/** self-tests, nothing under packages/cli). Single non-own re-trigger spent now via update-branch (real merge of current origin/main); same shard red again ⇒ parked under the anchor, any other red is real.


Generated by Claude Code

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

Correction to the note above (same seat): the update-branch call did NOT happen — GitHub answered 422 "There are no new commits on the base branch." — because origin/main has not moved since this branch was cut (2263ca4d6, the merge queue has landed nothing since 03:48Z). So the PR's single non-own re-trigger is still unspent; it will be taken (merge of origin/main) once main moves, ideally carrying the #14832 fix. Until then the red stands as recorded: not this PR's, parked.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlskip-changesetPR 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

@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b) - #14872

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b
Draft

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b)#14872
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1b, Tier B class 1)

Batch 1b of the assertion-floor card: the PR #13487 roster-floor shape transplanted onto the scripts/** self-tests that batch 1 (PR #14851) could not take verbatim because their assertion sink is not a block-bodied helper inside the self-test body — it is a concise arrow (const t = (name, ok, detail) => cases.push({ ... });) or a module-scope function, so there was no { ... } to thread a per-run ledger through.

Per the batch-1 review ruling (comment 5521234092, class 1): the roster machinery lives at module scope, and each file's existing assertion sink is given a minimal block body that calls registerCase() and then returns the original expression unchanged.

  • ⛔ no case rewritten, ⛔ none reordered, ⛔ no assertion changed meaning
  • all 18 --self-test runs exit with the same code (0) and byte-identical stdout and stderr before and after
  • what is pinned is the registered NAMES, never a total; the roster's own size is pinned too

The shape, identical in all 18 files

Inserted after the import block (uniformly, so no dispatch can read a const before its initialiser — the TDZ trap batch 1 measured on check-adr-anchors):

  • SELF_TEST_BATTERIES — a frozen roster of battery NAMES, each with its own case floor
  • SELF_TEST_BATTERY_FLOOR — the roster's own size, so deleting an entry cannot silence a floor quietly
  • UNATTRIBUTED_BATTERY — the key an assertion is filed under with no battery open; undeclared on purpose, so it reds by the same set difference
  • battery() / registerCase() / batteryFloorFailures()

⚠️None of these helpers is named with a self-test spelling, and that is on the record rather than a dodge.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. The first draft called the floor evaluator selfTestFloorFailures() and reddened that gate on all 18 files. The ledger row is the right remedy for a pre-existing accidental name — the gate says so — but scripts/pm/dispatch-gates.mjs is on this dispatch's do-not-edit list (PR #14765 in flight), and this is a new helper whose name was being chosen for the first time. These helpers hold no fixtures to mask and read no path literal, so battery… is simply the accurate name for what they are. Flagged for the PM in the report in case a ledger row is preferred instead.

In the body: one battery('NAME') at each existing section banner, named verbatim from the banner text; one registerCase() in the sink; and the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the existing report prints them.

Files, and the sink each one grew a helper at

185 batteries, 968 cases pinned across 18 files.

filebatteriescasesthe sink registerCase() was added to
scripts/check-cli-command-ids.mjs839const t = (name, ok, detail = '') => cases.push({ ... })
scripts/check-cli-test-child-env.mjs24152const t = (name, ok, detail) => cases.push({ ... })
scripts/check-cross-package-test-inputs.mjs7117const ok = (label, cond) => cases.push({ label, cond })
scripts/check-dual-build-cjs-loads.mjs1593const t = (name, ok, detail) => cases.push({ ... })
scripts/check-entry-guard.mjs1054const t = (name, ok, detail) => cases.push({ ... })
scripts/check-live-db-isolation.mjs912const check = (label, ok) => cases.push({ label, ok })
scripts/check-parse-guard.mjs1346const t = (name, ok, detail) => cases.push({ ... })
scripts/check-partof-closing-keyword.mjs928const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-plugin-teardown-shape.mjs1247const t = (name, ok, detail) => cases.push({ ... })
scripts/check-published-list-mirrors.mjs837const ok = (label, cond) => cases.push({ label, cond })
scripts/check-runner-env-posture.mjs834const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-single-claim-paths.mjs754const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-tenant-audit-census.mjs419const t = (name, ok, detail) => cases.push({ ... })
scripts/check-watch-hint-literal.mjs957const t = (name, ok, detail) => cases.push({ ... })
scripts/import-prerequisite.mjs1760const t = (name, ok, detail) => cases.push({ ... })
scripts/invoked-as.mjs211const t = (name, ok, detail) => cases.push({ ... })
scripts/qa/qa-rollup.mjs1178module-scope function assert(cond, msg, failures) — called only from selfTest(), verified
scripts/ts-parse.mjs1230const t = (name, ok, detail) => cases.push({ ... })

Every concise arrow became:

constt=(name,ok,detail)=>{registerCase();returncases.push({ name,ok: Boolean(ok), detail });};

The return is load-bearing: it preserves the sink's value exactly, so the conversion is a wrap and not a rewrite.

Two files (check-cross-package-test-inputs, check-tenant-audit-census) run cases before their first section banner, so their first battery is opened at the top of the body — the PR #13487 precedent's own shape — and that banner then carries no second opener.

The shrink-only ruling, applied

Three batteries in this batch run one case per row of a list. Each was read by hand:

  • qa-rollup / every RETIRED phrasing is rejected, by the right reason (16 = RETIRED_TITLES) — pinned at 16. That list only grows; removing a row means un-retiring a phrasing, which is the regression it exists to catch.
  • check-plugin-teardown-shape / every roster name reds (4 = TEARDOWN_ALIASES) — pinned at 4. A move onto the roster grows it.
  • check-plugin-teardown-shape / the exclusions, pinned as cases rather than asserted in prose (18 = DELIBERATELY_EXCLUDED) — ⛔ NOT pinned at 18. It has no structural case of its own, and the roster it reads is complementary to TEARDOWN_ALIASES: promoting a name onto the teardown roster is a legitimate edit that shrinks this list while growing the sibling battery. Pinned instead at the part that does not move with the list — this battery ran and at least one excluded name was audited — with the reason written over the entry.

The two genuinely shrink-only ledgers this batch touches (KNOWN_TEARDOWN_UNREACHED, TYPED_EXEMPTIONS and the dual-build baseline) are asserted structurallyevery shipped X …, one case regardless of row count — so no floor here moves with them.

Census

node scripts/measure-self-test-floor.mjs --json, merge base 2263ca4d6 → this branch:

ROSTERCOUNTNONE
before34158
after214140

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

  1. every worklist file reads NONE before and ROSTER after;
  2. the set of files whose class changed equals this worklist exactly (symmetric difference empty);
  3. inside the handshake-carrying population, the files still NONE decompose exactly: 113 = 68 + 40 + 2 + 3 — 68 of the batch-1 remainder this batch does not take, 40 in flight on PR test(scripts): pin a battery roster floor on 40 more scripts/** self-tests #14851, check-i18n-coverage and check-i18n-bundles (honoured exclusions), and 3 handshake-carrying files outside batch 1's declared Tier-B population of 126.

⚠️ The class-1 set is 18, not 48 — the batch-1 class labels do not survive re-derivation

The dispatch put the class-1 surface at 48 and asked for the class to be re-derived from the tree. It was, and the number does not reproduce. 45 of the batch-1 report's 86 remainder_declared files do have a block-bodied assertion helper inside the self-test body — for example check-adr-0087-registration.mjs:3236 is const assert = (cond, msg) => { checked++; if (!cond) failures.push(msg); };, three lines below function selfTest(). Those files are not class 1 under the stated criterion, whatever kept them out of batch 1.

Applying the dispatch's criterion strictly — a resolvable entry body, no block-bodied in-body assertion helper, ≥2 named section banners all before the verdict, and a body-level if (SINK) verdict site — yields 23, of which 5 are excluded for shapes the class-1 ruling does not cover, leaving 18.

The direction of the difference is the safe one: this batch is a strict subset of any wider reading, so it cannot collide with batch 1c (classes 2 and 3). Under batch 1's own class ordering a file with no in-body helper is class 1, never 2 or 3, so nothing 1c owns is taken here.

The 68 batch-1 remainder files this batch does NOT take, with the measured reason

  • 45 — has a block-bodied in-body assertion helper, so not class 1: check-adr-0087-registration, check-auth-mount-ledger, check-ci-filter-parity, check-comment-mask-adoption, check-console-injection, check-cross-repo-closer-outcome, check-declaration-mirrors, check-dev-prereqs, check-dispatcher-error-vocabulary, check-doc-frontmatter, check-docs-locale-catch-all, check-docs-section-name, check-dts-emitted, check-engine-split-ratio, check-error-status-conformance, check-i18n-stale-fill, check-init-service-contract, check-keyed-text-bounds, check-logger-receiver-detach, check-merge-queue-triage-outcome, check-objectql-double-limit, check-overlay-whitelist-table, check-quick-reference-counts, check-ratchet-remedy-authority, check-required-contexts, check-route-envelope, check-section-landing-index, check-skill-compatibility-version, check-skill-frame-freshness, check-spec-parsed-alias, check-stack-collection-maps, check-stall-guard-budget, check-startup-registry-verdict, check-system-context-census, check-tenant-chokepoint, check-turbo-task-graph, check-undeclared-dep-imports, check-vendor-version-stamps, check-whole-set-label-write, check-wildcard-fallthrough, docs-audit/affected-docs, js-comment-mask, publish-smoke-pack, release-github-releases, sync-template-versions.
  • 13 — fewer than two named section banners (class 2, batch 1c): check-agent-model-declared, check-comment-mask-corpus, check-console-intercept-disarm, check-docs-single-h1, check-error-code-casing, check-optional-error-sink-contract, check-pnpm-acquisition, check-sdui-manifest, check-skill-frame-sync, check-widget-option-census, measure-position-name-fold-census, measure-test-shard-timings, render-release-coverage-anchor.
  • 5 — no body-level if (SINK) verdict site (class 3, batch 1c): check-adr-links, check-doc-anchors, check-kernel-hook-pairs, check-override-consistency, partition-test-shards.
  • 5 — class 1 by the criterion, but the class-1 remedy does not reach them:
    • ablation-dist-preflight and check-published-files — no single assertion sink; the sink is repeated inline in about six separate for loops, so "a minimal helper at the existing sink" would mean rewriting the reporting of every case.
    • check-examples-live-imports and check-org-identifier — table-driven, with every section banner inside the cases array literal, where no battery() statement can go. The card names this as Tier C.
    • check-durability-degradation-log-level — multi-entry dispatch, named as Tier C in the batch-1 report.

Verification

  • Per file, before and after:node GATE --self-test — 18/18 identical exit code (0) and byte-identical stdout and stderr, captured by redirect before any pipe. (check-plugin-teardown-shape needed the clone deepened to reach its pinned fixture commit; both readings are from the deepened tree.)
  • Three ablations, each from the committed tree, each with a trap restore, mutation proved on disk by anchored grep -c plus a git hash-object comparison against the HEAD blob, and restore proved by an empty git diff HEAD — never by an exit code:
    1. a battery's cases deleted (invoked-as, one t(...) line removed): exit 0 → 1, battery "the predicate, directly" registered 4 case(s), below its pinned floor of 5.
    2. a roster ENTRY deleted (invoked-as): exit 1 naming both halves — SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2andbattery "the fixture: a probe reached three ways" registered 6 case(s) but is not declared.
    3. the new registerCase() line removed from the sink (check-runner-env-posture) — the class-1-specific leg, proving the introduced helper is load-bearing: exit 1, every battery DID NOT RUN — 0 cases registered, N pinned, with every original case still passing.
  • No dist is on the resolution path of any of the three, so no rebuild leg applies to them; the dist-dependent gates were re-run after a real build.
  • scripts/check-nul-bytes.mjs exit 0; grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the 18 changed files: no hits.

Gates, on ca1e17bf

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — the script derives the change set itself) reconciles this card at 34 families: 32 by path + 2 by change kind. All 34 were run, plus check:declared-population-live, which the dispatch names as mandatory and which the derivation does not include — 35 commands. Exit codes captured by redirect before any pipe; every result below quotes the gate's own verdict line, never a bare $?.

  • 32 RAN-PASS (exit 0), the two mandatory ones included: pnpm check:ratchet-remedy-authority = 0 and pnpm check:declared-population-live = 0.
  • pnpm check:pm-dispatch-gates = 0✓ dispatch-gates self-test: 1241 cases pass. It was red on the first head for the compound-anchor reason above (✗ every compound self-test NAME the anchor matches is classified in COMPOUND_ANCHOR_LEDGER — unlisted: … ×18), and green after the helpers were named battery….
  • 2 = PREREQUISITE NOT MET, not a verdict.node scripts/check-partof-closing-keyword.mjs and node scripts/check-single-claim-paths.mjs exit 2 printing NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or usage failure, NOT a verdict. Their pnpm check:* aliases — what CI runs — both exit 0.
  • 1 = NOT MEASURED, by the gate's own instruction.node scripts/check-test-completeness.mjs exits 3: "That list names this script with NO argument… There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red."
  • Four gates first read exit 3 = NOT MEASURED for want of dist. ⛔ Never read as a pass: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* ran under scripts/pm/os-verify-lock.sh — VERDICT command-exit 0, 7m15s — and three of the four then exit 0 (check:dual-build-cjs-loads, and the two @objectstack/lint doc gates); the fourth is the NOT MEASURED one above. The build left the tracked tree unchanged (git status empty).
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0. No narrowing claimed.

Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label 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
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed on #13799 — content PASS. Test Core (1/6) on ca1e17bfd is the packages/cli shard signature tracked on #14822 / #14832 (job 100550425448; this diff is 18 scripts/** self-tests, nothing under packages/cli). Single non-own re-trigger spent now via update-branch (real merge of current origin/main); same shard red again ⇒ parked under the anchor, any other red is real.


Generated by Claude Code

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

Correction to the note above (same seat): the update-branch call did NOT happen — GitHub answered 422 "There are no new commits on the base branch." — because origin/main has not moved since this branch was cut (2263ca4d6, the merge queue has landed nothing since 03:48Z). So the PR's single non-own re-trigger is still unspent; it will be taken (merge of origin/main) once main moves, ideally carrying the #14832 fix. Until then the red stands as recorded: not this PR's, parked.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlskip-changesetPR 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

@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b) - #14872

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b
Draft

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b)#14872
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1b, Tier B class 1)

Batch 1b of the assertion-floor card: the PR #13487 roster-floor shape transplanted onto the scripts/** self-tests that batch 1 (PR #14851) could not take verbatim because their assertion sink is not a block-bodied helper inside the self-test body — it is a concise arrow (const t = (name, ok, detail) => cases.push({ ... });) or a module-scope function, so there was no { ... } to thread a per-run ledger through.

Per the batch-1 review ruling (comment 5521234092, class 1): the roster machinery lives at module scope, and each file's existing assertion sink is given a minimal block body that calls registerCase() and then returns the original expression unchanged.

  • ⛔ no case rewritten, ⛔ none reordered, ⛔ no assertion changed meaning
  • all 18 --self-test runs exit with the same code (0) and byte-identical stdout and stderr before and after
  • what is pinned is the registered NAMES, never a total; the roster's own size is pinned too

The shape, identical in all 18 files

Inserted after the import block (uniformly, so no dispatch can read a const before its initialiser — the TDZ trap batch 1 measured on check-adr-anchors):

  • SELF_TEST_BATTERIES — a frozen roster of battery NAMES, each with its own case floor
  • SELF_TEST_BATTERY_FLOOR — the roster's own size, so deleting an entry cannot silence a floor quietly
  • UNATTRIBUTED_BATTERY — the key an assertion is filed under with no battery open; undeclared on purpose, so it reds by the same set difference
  • battery() / registerCase() / batteryFloorFailures()

⚠️None of these helpers is named with a self-test spelling, and that is on the record rather than a dodge.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. The first draft called the floor evaluator selfTestFloorFailures() and reddened that gate on all 18 files. The ledger row is the right remedy for a pre-existing accidental name — the gate says so — but scripts/pm/dispatch-gates.mjs is on this dispatch's do-not-edit list (PR #14765 in flight), and this is a new helper whose name was being chosen for the first time. These helpers hold no fixtures to mask and read no path literal, so battery… is simply the accurate name for what they are. Flagged for the PM in the report in case a ledger row is preferred instead.

In the body: one battery('NAME') at each existing section banner, named verbatim from the banner text; one registerCase() in the sink; and the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the existing report prints them.

Files, and the sink each one grew a helper at

185 batteries, 968 cases pinned across 18 files.

filebatteriescasesthe sink registerCase() was added to
scripts/check-cli-command-ids.mjs839const t = (name, ok, detail = '') => cases.push({ ... })
scripts/check-cli-test-child-env.mjs24152const t = (name, ok, detail) => cases.push({ ... })
scripts/check-cross-package-test-inputs.mjs7117const ok = (label, cond) => cases.push({ label, cond })
scripts/check-dual-build-cjs-loads.mjs1593const t = (name, ok, detail) => cases.push({ ... })
scripts/check-entry-guard.mjs1054const t = (name, ok, detail) => cases.push({ ... })
scripts/check-live-db-isolation.mjs912const check = (label, ok) => cases.push({ label, ok })
scripts/check-parse-guard.mjs1346const t = (name, ok, detail) => cases.push({ ... })
scripts/check-partof-closing-keyword.mjs928const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-plugin-teardown-shape.mjs1247const t = (name, ok, detail) => cases.push({ ... })
scripts/check-published-list-mirrors.mjs837const ok = (label, cond) => cases.push({ label, cond })
scripts/check-runner-env-posture.mjs834const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-single-claim-paths.mjs754const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-tenant-audit-census.mjs419const t = (name, ok, detail) => cases.push({ ... })
scripts/check-watch-hint-literal.mjs957const t = (name, ok, detail) => cases.push({ ... })
scripts/import-prerequisite.mjs1760const t = (name, ok, detail) => cases.push({ ... })
scripts/invoked-as.mjs211const t = (name, ok, detail) => cases.push({ ... })
scripts/qa/qa-rollup.mjs1178module-scope function assert(cond, msg, failures) — called only from selfTest(), verified
scripts/ts-parse.mjs1230const t = (name, ok, detail) => cases.push({ ... })

Every concise arrow became:

constt=(name,ok,detail)=>{registerCase();returncases.push({ name,ok: Boolean(ok), detail });};

The return is load-bearing: it preserves the sink's value exactly, so the conversion is a wrap and not a rewrite.

Two files (check-cross-package-test-inputs, check-tenant-audit-census) run cases before their first section banner, so their first battery is opened at the top of the body — the PR #13487 precedent's own shape — and that banner then carries no second opener.

The shrink-only ruling, applied

Three batteries in this batch run one case per row of a list. Each was read by hand:

  • qa-rollup / every RETIRED phrasing is rejected, by the right reason (16 = RETIRED_TITLES) — pinned at 16. That list only grows; removing a row means un-retiring a phrasing, which is the regression it exists to catch.
  • check-plugin-teardown-shape / every roster name reds (4 = TEARDOWN_ALIASES) — pinned at 4. A move onto the roster grows it.
  • check-plugin-teardown-shape / the exclusions, pinned as cases rather than asserted in prose (18 = DELIBERATELY_EXCLUDED) — ⛔ NOT pinned at 18. It has no structural case of its own, and the roster it reads is complementary to TEARDOWN_ALIASES: promoting a name onto the teardown roster is a legitimate edit that shrinks this list while growing the sibling battery. Pinned instead at the part that does not move with the list — this battery ran and at least one excluded name was audited — with the reason written over the entry.

The two genuinely shrink-only ledgers this batch touches (KNOWN_TEARDOWN_UNREACHED, TYPED_EXEMPTIONS and the dual-build baseline) are asserted structurallyevery shipped X …, one case regardless of row count — so no floor here moves with them.

Census

node scripts/measure-self-test-floor.mjs --json, merge base 2263ca4d6 → this branch:

ROSTERCOUNTNONE
before34158
after214140

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

  1. every worklist file reads NONE before and ROSTER after;
  2. the set of files whose class changed equals this worklist exactly (symmetric difference empty);
  3. inside the handshake-carrying population, the files still NONE decompose exactly: 113 = 68 + 40 + 2 + 3 — 68 of the batch-1 remainder this batch does not take, 40 in flight on PR test(scripts): pin a battery roster floor on 40 more scripts/** self-tests #14851, check-i18n-coverage and check-i18n-bundles (honoured exclusions), and 3 handshake-carrying files outside batch 1's declared Tier-B population of 126.

⚠️ The class-1 set is 18, not 48 — the batch-1 class labels do not survive re-derivation

The dispatch put the class-1 surface at 48 and asked for the class to be re-derived from the tree. It was, and the number does not reproduce. 45 of the batch-1 report's 86 remainder_declared files do have a block-bodied assertion helper inside the self-test body — for example check-adr-0087-registration.mjs:3236 is const assert = (cond, msg) => { checked++; if (!cond) failures.push(msg); };, three lines below function selfTest(). Those files are not class 1 under the stated criterion, whatever kept them out of batch 1.

Applying the dispatch's criterion strictly — a resolvable entry body, no block-bodied in-body assertion helper, ≥2 named section banners all before the verdict, and a body-level if (SINK) verdict site — yields 23, of which 5 are excluded for shapes the class-1 ruling does not cover, leaving 18.

The direction of the difference is the safe one: this batch is a strict subset of any wider reading, so it cannot collide with batch 1c (classes 2 and 3). Under batch 1's own class ordering a file with no in-body helper is class 1, never 2 or 3, so nothing 1c owns is taken here.

The 68 batch-1 remainder files this batch does NOT take, with the measured reason

  • 45 — has a block-bodied in-body assertion helper, so not class 1: check-adr-0087-registration, check-auth-mount-ledger, check-ci-filter-parity, check-comment-mask-adoption, check-console-injection, check-cross-repo-closer-outcome, check-declaration-mirrors, check-dev-prereqs, check-dispatcher-error-vocabulary, check-doc-frontmatter, check-docs-locale-catch-all, check-docs-section-name, check-dts-emitted, check-engine-split-ratio, check-error-status-conformance, check-i18n-stale-fill, check-init-service-contract, check-keyed-text-bounds, check-logger-receiver-detach, check-merge-queue-triage-outcome, check-objectql-double-limit, check-overlay-whitelist-table, check-quick-reference-counts, check-ratchet-remedy-authority, check-required-contexts, check-route-envelope, check-section-landing-index, check-skill-compatibility-version, check-skill-frame-freshness, check-spec-parsed-alias, check-stack-collection-maps, check-stall-guard-budget, check-startup-registry-verdict, check-system-context-census, check-tenant-chokepoint, check-turbo-task-graph, check-undeclared-dep-imports, check-vendor-version-stamps, check-whole-set-label-write, check-wildcard-fallthrough, docs-audit/affected-docs, js-comment-mask, publish-smoke-pack, release-github-releases, sync-template-versions.
  • 13 — fewer than two named section banners (class 2, batch 1c): check-agent-model-declared, check-comment-mask-corpus, check-console-intercept-disarm, check-docs-single-h1, check-error-code-casing, check-optional-error-sink-contract, check-pnpm-acquisition, check-sdui-manifest, check-skill-frame-sync, check-widget-option-census, measure-position-name-fold-census, measure-test-shard-timings, render-release-coverage-anchor.
  • 5 — no body-level if (SINK) verdict site (class 3, batch 1c): check-adr-links, check-doc-anchors, check-kernel-hook-pairs, check-override-consistency, partition-test-shards.
  • 5 — class 1 by the criterion, but the class-1 remedy does not reach them:
    • ablation-dist-preflight and check-published-files — no single assertion sink; the sink is repeated inline in about six separate for loops, so "a minimal helper at the existing sink" would mean rewriting the reporting of every case.
    • check-examples-live-imports and check-org-identifier — table-driven, with every section banner inside the cases array literal, where no battery() statement can go. The card names this as Tier C.
    • check-durability-degradation-log-level — multi-entry dispatch, named as Tier C in the batch-1 report.

Verification

  • Per file, before and after:node GATE --self-test — 18/18 identical exit code (0) and byte-identical stdout and stderr, captured by redirect before any pipe. (check-plugin-teardown-shape needed the clone deepened to reach its pinned fixture commit; both readings are from the deepened tree.)
  • Three ablations, each from the committed tree, each with a trap restore, mutation proved on disk by anchored grep -c plus a git hash-object comparison against the HEAD blob, and restore proved by an empty git diff HEAD — never by an exit code:
    1. a battery's cases deleted (invoked-as, one t(...) line removed): exit 0 → 1, battery "the predicate, directly" registered 4 case(s), below its pinned floor of 5.
    2. a roster ENTRY deleted (invoked-as): exit 1 naming both halves — SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2andbattery "the fixture: a probe reached three ways" registered 6 case(s) but is not declared.
    3. the new registerCase() line removed from the sink (check-runner-env-posture) — the class-1-specific leg, proving the introduced helper is load-bearing: exit 1, every battery DID NOT RUN — 0 cases registered, N pinned, with every original case still passing.
  • No dist is on the resolution path of any of the three, so no rebuild leg applies to them; the dist-dependent gates were re-run after a real build.
  • scripts/check-nul-bytes.mjs exit 0; grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the 18 changed files: no hits.

Gates, on ca1e17bf

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — the script derives the change set itself) reconciles this card at 34 families: 32 by path + 2 by change kind. All 34 were run, plus check:declared-population-live, which the dispatch names as mandatory and which the derivation does not include — 35 commands. Exit codes captured by redirect before any pipe; every result below quotes the gate's own verdict line, never a bare $?.

  • 32 RAN-PASS (exit 0), the two mandatory ones included: pnpm check:ratchet-remedy-authority = 0 and pnpm check:declared-population-live = 0.
  • pnpm check:pm-dispatch-gates = 0✓ dispatch-gates self-test: 1241 cases pass. It was red on the first head for the compound-anchor reason above (✗ every compound self-test NAME the anchor matches is classified in COMPOUND_ANCHOR_LEDGER — unlisted: … ×18), and green after the helpers were named battery….
  • 2 = PREREQUISITE NOT MET, not a verdict.node scripts/check-partof-closing-keyword.mjs and node scripts/check-single-claim-paths.mjs exit 2 printing NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or usage failure, NOT a verdict. Their pnpm check:* aliases — what CI runs — both exit 0.
  • 1 = NOT MEASURED, by the gate's own instruction.node scripts/check-test-completeness.mjs exits 3: "That list names this script with NO argument… There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red."
  • Four gates first read exit 3 = NOT MEASURED for want of dist. ⛔ Never read as a pass: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* ran under scripts/pm/os-verify-lock.sh — VERDICT command-exit 0, 7m15s — and three of the four then exit 0 (check:dual-build-cjs-loads, and the two @objectstack/lint doc gates); the fourth is the NOT MEASURED one above. The build left the tracked tree unchanged (git status empty).
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0. No narrowing claimed.

Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label 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
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed on #13799 — content PASS. Test Core (1/6) on ca1e17bfd is the packages/cli shard signature tracked on #14822 / #14832 (job 100550425448; this diff is 18 scripts/** self-tests, nothing under packages/cli). Single non-own re-trigger spent now via update-branch (real merge of current origin/main); same shard red again ⇒ parked under the anchor, any other red is real.


Generated by Claude Code

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

Correction to the note above (same seat): the update-branch call did NOT happen — GitHub answered 422 "There are no new commits on the base branch." — because origin/main has not moved since this branch was cut (2263ca4d6, the merge queue has landed nothing since 03:48Z). So the PR's single non-own re-trigger is still unspent; it will be taken (merge of origin/main) once main moves, ideally carrying the #14832 fix. Until then the red stands as recorded: not this PR's, parked.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlskip-changesetPR 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

@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b) - #14872

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b
Draft

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b)#14872
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1b, Tier B class 1)

Batch 1b of the assertion-floor card: the PR #13487 roster-floor shape transplanted onto the scripts/** self-tests that batch 1 (PR #14851) could not take verbatim because their assertion sink is not a block-bodied helper inside the self-test body — it is a concise arrow (const t = (name, ok, detail) => cases.push({ ... });) or a module-scope function, so there was no { ... } to thread a per-run ledger through.

Per the batch-1 review ruling (comment 5521234092, class 1): the roster machinery lives at module scope, and each file's existing assertion sink is given a minimal block body that calls registerCase() and then returns the original expression unchanged.

  • ⛔ no case rewritten, ⛔ none reordered, ⛔ no assertion changed meaning
  • all 18 --self-test runs exit with the same code (0) and byte-identical stdout and stderr before and after
  • what is pinned is the registered NAMES, never a total; the roster's own size is pinned too

The shape, identical in all 18 files

Inserted after the import block (uniformly, so no dispatch can read a const before its initialiser — the TDZ trap batch 1 measured on check-adr-anchors):

  • SELF_TEST_BATTERIES — a frozen roster of battery NAMES, each with its own case floor
  • SELF_TEST_BATTERY_FLOOR — the roster's own size, so deleting an entry cannot silence a floor quietly
  • UNATTRIBUTED_BATTERY — the key an assertion is filed under with no battery open; undeclared on purpose, so it reds by the same set difference
  • battery() / registerCase() / batteryFloorFailures()

⚠️None of these helpers is named with a self-test spelling, and that is on the record rather than a dodge.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. The first draft called the floor evaluator selfTestFloorFailures() and reddened that gate on all 18 files. The ledger row is the right remedy for a pre-existing accidental name — the gate says so — but scripts/pm/dispatch-gates.mjs is on this dispatch's do-not-edit list (PR #14765 in flight), and this is a new helper whose name was being chosen for the first time. These helpers hold no fixtures to mask and read no path literal, so battery… is simply the accurate name for what they are. Flagged for the PM in the report in case a ledger row is preferred instead.

In the body: one battery('NAME') at each existing section banner, named verbatim from the banner text; one registerCase() in the sink; and the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the existing report prints them.

Files, and the sink each one grew a helper at

185 batteries, 968 cases pinned across 18 files.

filebatteriescasesthe sink registerCase() was added to
scripts/check-cli-command-ids.mjs839const t = (name, ok, detail = '') => cases.push({ ... })
scripts/check-cli-test-child-env.mjs24152const t = (name, ok, detail) => cases.push({ ... })
scripts/check-cross-package-test-inputs.mjs7117const ok = (label, cond) => cases.push({ label, cond })
scripts/check-dual-build-cjs-loads.mjs1593const t = (name, ok, detail) => cases.push({ ... })
scripts/check-entry-guard.mjs1054const t = (name, ok, detail) => cases.push({ ... })
scripts/check-live-db-isolation.mjs912const check = (label, ok) => cases.push({ label, ok })
scripts/check-parse-guard.mjs1346const t = (name, ok, detail) => cases.push({ ... })
scripts/check-partof-closing-keyword.mjs928const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-plugin-teardown-shape.mjs1247const t = (name, ok, detail) => cases.push({ ... })
scripts/check-published-list-mirrors.mjs837const ok = (label, cond) => cases.push({ label, cond })
scripts/check-runner-env-posture.mjs834const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-single-claim-paths.mjs754const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-tenant-audit-census.mjs419const t = (name, ok, detail) => cases.push({ ... })
scripts/check-watch-hint-literal.mjs957const t = (name, ok, detail) => cases.push({ ... })
scripts/import-prerequisite.mjs1760const t = (name, ok, detail) => cases.push({ ... })
scripts/invoked-as.mjs211const t = (name, ok, detail) => cases.push({ ... })
scripts/qa/qa-rollup.mjs1178module-scope function assert(cond, msg, failures) — called only from selfTest(), verified
scripts/ts-parse.mjs1230const t = (name, ok, detail) => cases.push({ ... })

Every concise arrow became:

constt=(name,ok,detail)=>{registerCase();returncases.push({ name,ok: Boolean(ok), detail });};

The return is load-bearing: it preserves the sink's value exactly, so the conversion is a wrap and not a rewrite.

Two files (check-cross-package-test-inputs, check-tenant-audit-census) run cases before their first section banner, so their first battery is opened at the top of the body — the PR #13487 precedent's own shape — and that banner then carries no second opener.

The shrink-only ruling, applied

Three batteries in this batch run one case per row of a list. Each was read by hand:

  • qa-rollup / every RETIRED phrasing is rejected, by the right reason (16 = RETIRED_TITLES) — pinned at 16. That list only grows; removing a row means un-retiring a phrasing, which is the regression it exists to catch.
  • check-plugin-teardown-shape / every roster name reds (4 = TEARDOWN_ALIASES) — pinned at 4. A move onto the roster grows it.
  • check-plugin-teardown-shape / the exclusions, pinned as cases rather than asserted in prose (18 = DELIBERATELY_EXCLUDED) — ⛔ NOT pinned at 18. It has no structural case of its own, and the roster it reads is complementary to TEARDOWN_ALIASES: promoting a name onto the teardown roster is a legitimate edit that shrinks this list while growing the sibling battery. Pinned instead at the part that does not move with the list — this battery ran and at least one excluded name was audited — with the reason written over the entry.

The two genuinely shrink-only ledgers this batch touches (KNOWN_TEARDOWN_UNREACHED, TYPED_EXEMPTIONS and the dual-build baseline) are asserted structurallyevery shipped X …, one case regardless of row count — so no floor here moves with them.

Census

node scripts/measure-self-test-floor.mjs --json, merge base 2263ca4d6 → this branch:

ROSTERCOUNTNONE
before34158
after214140

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

  1. every worklist file reads NONE before and ROSTER after;
  2. the set of files whose class changed equals this worklist exactly (symmetric difference empty);
  3. inside the handshake-carrying population, the files still NONE decompose exactly: 113 = 68 + 40 + 2 + 3 — 68 of the batch-1 remainder this batch does not take, 40 in flight on PR test(scripts): pin a battery roster floor on 40 more scripts/** self-tests #14851, check-i18n-coverage and check-i18n-bundles (honoured exclusions), and 3 handshake-carrying files outside batch 1's declared Tier-B population of 126.

⚠️ The class-1 set is 18, not 48 — the batch-1 class labels do not survive re-derivation

The dispatch put the class-1 surface at 48 and asked for the class to be re-derived from the tree. It was, and the number does not reproduce. 45 of the batch-1 report's 86 remainder_declared files do have a block-bodied assertion helper inside the self-test body — for example check-adr-0087-registration.mjs:3236 is const assert = (cond, msg) => { checked++; if (!cond) failures.push(msg); };, three lines below function selfTest(). Those files are not class 1 under the stated criterion, whatever kept them out of batch 1.

Applying the dispatch's criterion strictly — a resolvable entry body, no block-bodied in-body assertion helper, ≥2 named section banners all before the verdict, and a body-level if (SINK) verdict site — yields 23, of which 5 are excluded for shapes the class-1 ruling does not cover, leaving 18.

The direction of the difference is the safe one: this batch is a strict subset of any wider reading, so it cannot collide with batch 1c (classes 2 and 3). Under batch 1's own class ordering a file with no in-body helper is class 1, never 2 or 3, so nothing 1c owns is taken here.

The 68 batch-1 remainder files this batch does NOT take, with the measured reason

  • 45 — has a block-bodied in-body assertion helper, so not class 1: check-adr-0087-registration, check-auth-mount-ledger, check-ci-filter-parity, check-comment-mask-adoption, check-console-injection, check-cross-repo-closer-outcome, check-declaration-mirrors, check-dev-prereqs, check-dispatcher-error-vocabulary, check-doc-frontmatter, check-docs-locale-catch-all, check-docs-section-name, check-dts-emitted, check-engine-split-ratio, check-error-status-conformance, check-i18n-stale-fill, check-init-service-contract, check-keyed-text-bounds, check-logger-receiver-detach, check-merge-queue-triage-outcome, check-objectql-double-limit, check-overlay-whitelist-table, check-quick-reference-counts, check-ratchet-remedy-authority, check-required-contexts, check-route-envelope, check-section-landing-index, check-skill-compatibility-version, check-skill-frame-freshness, check-spec-parsed-alias, check-stack-collection-maps, check-stall-guard-budget, check-startup-registry-verdict, check-system-context-census, check-tenant-chokepoint, check-turbo-task-graph, check-undeclared-dep-imports, check-vendor-version-stamps, check-whole-set-label-write, check-wildcard-fallthrough, docs-audit/affected-docs, js-comment-mask, publish-smoke-pack, release-github-releases, sync-template-versions.
  • 13 — fewer than two named section banners (class 2, batch 1c): check-agent-model-declared, check-comment-mask-corpus, check-console-intercept-disarm, check-docs-single-h1, check-error-code-casing, check-optional-error-sink-contract, check-pnpm-acquisition, check-sdui-manifest, check-skill-frame-sync, check-widget-option-census, measure-position-name-fold-census, measure-test-shard-timings, render-release-coverage-anchor.
  • 5 — no body-level if (SINK) verdict site (class 3, batch 1c): check-adr-links, check-doc-anchors, check-kernel-hook-pairs, check-override-consistency, partition-test-shards.
  • 5 — class 1 by the criterion, but the class-1 remedy does not reach them:
    • ablation-dist-preflight and check-published-files — no single assertion sink; the sink is repeated inline in about six separate for loops, so "a minimal helper at the existing sink" would mean rewriting the reporting of every case.
    • check-examples-live-imports and check-org-identifier — table-driven, with every section banner inside the cases array literal, where no battery() statement can go. The card names this as Tier C.
    • check-durability-degradation-log-level — multi-entry dispatch, named as Tier C in the batch-1 report.

Verification

  • Per file, before and after:node GATE --self-test — 18/18 identical exit code (0) and byte-identical stdout and stderr, captured by redirect before any pipe. (check-plugin-teardown-shape needed the clone deepened to reach its pinned fixture commit; both readings are from the deepened tree.)
  • Three ablations, each from the committed tree, each with a trap restore, mutation proved on disk by anchored grep -c plus a git hash-object comparison against the HEAD blob, and restore proved by an empty git diff HEAD — never by an exit code:
    1. a battery's cases deleted (invoked-as, one t(...) line removed): exit 0 → 1, battery "the predicate, directly" registered 4 case(s), below its pinned floor of 5.
    2. a roster ENTRY deleted (invoked-as): exit 1 naming both halves — SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2andbattery "the fixture: a probe reached three ways" registered 6 case(s) but is not declared.
    3. the new registerCase() line removed from the sink (check-runner-env-posture) — the class-1-specific leg, proving the introduced helper is load-bearing: exit 1, every battery DID NOT RUN — 0 cases registered, N pinned, with every original case still passing.
  • No dist is on the resolution path of any of the three, so no rebuild leg applies to them; the dist-dependent gates were re-run after a real build.
  • scripts/check-nul-bytes.mjs exit 0; grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the 18 changed files: no hits.

Gates, on ca1e17bf

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — the script derives the change set itself) reconciles this card at 34 families: 32 by path + 2 by change kind. All 34 were run, plus check:declared-population-live, which the dispatch names as mandatory and which the derivation does not include — 35 commands. Exit codes captured by redirect before any pipe; every result below quotes the gate's own verdict line, never a bare $?.

  • 32 RAN-PASS (exit 0), the two mandatory ones included: pnpm check:ratchet-remedy-authority = 0 and pnpm check:declared-population-live = 0.
  • pnpm check:pm-dispatch-gates = 0✓ dispatch-gates self-test: 1241 cases pass. It was red on the first head for the compound-anchor reason above (✗ every compound self-test NAME the anchor matches is classified in COMPOUND_ANCHOR_LEDGER — unlisted: … ×18), and green after the helpers were named battery….
  • 2 = PREREQUISITE NOT MET, not a verdict.node scripts/check-partof-closing-keyword.mjs and node scripts/check-single-claim-paths.mjs exit 2 printing NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or usage failure, NOT a verdict. Their pnpm check:* aliases — what CI runs — both exit 0.
  • 1 = NOT MEASURED, by the gate's own instruction.node scripts/check-test-completeness.mjs exits 3: "That list names this script with NO argument… There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red."
  • Four gates first read exit 3 = NOT MEASURED for want of dist. ⛔ Never read as a pass: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* ran under scripts/pm/os-verify-lock.sh — VERDICT command-exit 0, 7m15s — and three of the four then exit 0 (check:dual-build-cjs-loads, and the two @objectstack/lint doc gates); the fourth is the NOT MEASURED one above. The build left the tracked tree unchanged (git status empty).
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0. No narrowing claimed.

Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label 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
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed on #13799 — content PASS. Test Core (1/6) on ca1e17bfd is the packages/cli shard signature tracked on #14822 / #14832 (job 100550425448; this diff is 18 scripts/** self-tests, nothing under packages/cli). Single non-own re-trigger spent now via update-branch (real merge of current origin/main); same shard red again ⇒ parked under the anchor, any other red is real.


Generated by Claude Code

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

Correction to the note above (same seat): the update-branch call did NOT happen — GitHub answered 422 "There are no new commits on the base branch." — because origin/main has not moved since this branch was cut (2263ca4d6, the merge queue has landed nothing since 03:48Z). So the PR's single non-own re-trigger is still unspent; it will be taken (merge of origin/main) once main moves, ideally carrying the #14832 fix. Until then the red stands as recorded: not this PR's, parked.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlskip-changesetPR 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

@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b) - #14872

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b
Draft

test(scripts): pin a battery roster floor on 18 class-1 scripts/** self-tests (#13799 batch 1b)#14872
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13799-self-test-roster-floor-b1b

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 1b, Tier B class 1)

Batch 1b of the assertion-floor card: the PR #13487 roster-floor shape transplanted onto the scripts/** self-tests that batch 1 (PR #14851) could not take verbatim because their assertion sink is not a block-bodied helper inside the self-test body — it is a concise arrow (const t = (name, ok, detail) => cases.push({ ... });) or a module-scope function, so there was no { ... } to thread a per-run ledger through.

Per the batch-1 review ruling (comment 5521234092, class 1): the roster machinery lives at module scope, and each file's existing assertion sink is given a minimal block body that calls registerCase() and then returns the original expression unchanged.

  • ⛔ no case rewritten, ⛔ none reordered, ⛔ no assertion changed meaning
  • all 18 --self-test runs exit with the same code (0) and byte-identical stdout and stderr before and after
  • what is pinned is the registered NAMES, never a total; the roster's own size is pinned too

The shape, identical in all 18 files

Inserted after the import block (uniformly, so no dispatch can read a const before its initialiser — the TDZ trap batch 1 measured on check-adr-anchors):

  • SELF_TEST_BATTERIES — a frozen roster of battery NAMES, each with its own case floor
  • SELF_TEST_BATTERY_FLOOR — the roster's own size, so deleting an entry cannot silence a floor quietly
  • UNATTRIBUTED_BATTERY — the key an assertion is filed under with no battery open; undeclared on purpose, so it reds by the same set difference
  • battery() / registerCase() / batteryFloorFailures()

⚠️None of these helpers is named with a self-test spelling, and that is on the record rather than a dodge.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. The first draft called the floor evaluator selfTestFloorFailures() and reddened that gate on all 18 files. The ledger row is the right remedy for a pre-existing accidental name — the gate says so — but scripts/pm/dispatch-gates.mjs is on this dispatch's do-not-edit list (PR #14765 in flight), and this is a new helper whose name was being chosen for the first time. These helpers hold no fixtures to mask and read no path literal, so battery… is simply the accurate name for what they are. Flagged for the PM in the report in case a ledger row is preferred instead.

In the body: one battery('NAME') at each existing section banner, named verbatim from the banner text; one registerCase() in the sink; and the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the existing report prints them.

Files, and the sink each one grew a helper at

185 batteries, 968 cases pinned across 18 files.

filebatteriescasesthe sink registerCase() was added to
scripts/check-cli-command-ids.mjs839const t = (name, ok, detail = '') => cases.push({ ... })
scripts/check-cli-test-child-env.mjs24152const t = (name, ok, detail) => cases.push({ ... })
scripts/check-cross-package-test-inputs.mjs7117const ok = (label, cond) => cases.push({ label, cond })
scripts/check-dual-build-cjs-loads.mjs1593const t = (name, ok, detail) => cases.push({ ... })
scripts/check-entry-guard.mjs1054const t = (name, ok, detail) => cases.push({ ... })
scripts/check-live-db-isolation.mjs912const check = (label, ok) => cases.push({ label, ok })
scripts/check-parse-guard.mjs1346const t = (name, ok, detail) => cases.push({ ... })
scripts/check-partof-closing-keyword.mjs928const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-plugin-teardown-shape.mjs1247const t = (name, ok, detail) => cases.push({ ... })
scripts/check-published-list-mirrors.mjs837const ok = (label, cond) => cases.push({ label, cond })
scripts/check-runner-env-posture.mjs834const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-single-claim-paths.mjs754const t = (name, actual, expected) => cases.push([ ... ])
scripts/check-tenant-audit-census.mjs419const t = (name, ok, detail) => cases.push({ ... })
scripts/check-watch-hint-literal.mjs957const t = (name, ok, detail) => cases.push({ ... })
scripts/import-prerequisite.mjs1760const t = (name, ok, detail) => cases.push({ ... })
scripts/invoked-as.mjs211const t = (name, ok, detail) => cases.push({ ... })
scripts/qa/qa-rollup.mjs1178module-scope function assert(cond, msg, failures) — called only from selfTest(), verified
scripts/ts-parse.mjs1230const t = (name, ok, detail) => cases.push({ ... })

Every concise arrow became:

constt=(name,ok,detail)=>{registerCase();returncases.push({ name,ok: Boolean(ok), detail });};

The return is load-bearing: it preserves the sink's value exactly, so the conversion is a wrap and not a rewrite.

Two files (check-cross-package-test-inputs, check-tenant-audit-census) run cases before their first section banner, so their first battery is opened at the top of the body — the PR #13487 precedent's own shape — and that banner then carries no second opener.

The shrink-only ruling, applied

Three batteries in this batch run one case per row of a list. Each was read by hand:

  • qa-rollup / every RETIRED phrasing is rejected, by the right reason (16 = RETIRED_TITLES) — pinned at 16. That list only grows; removing a row means un-retiring a phrasing, which is the regression it exists to catch.
  • check-plugin-teardown-shape / every roster name reds (4 = TEARDOWN_ALIASES) — pinned at 4. A move onto the roster grows it.
  • check-plugin-teardown-shape / the exclusions, pinned as cases rather than asserted in prose (18 = DELIBERATELY_EXCLUDED) — ⛔ NOT pinned at 18. It has no structural case of its own, and the roster it reads is complementary to TEARDOWN_ALIASES: promoting a name onto the teardown roster is a legitimate edit that shrinks this list while growing the sibling battery. Pinned instead at the part that does not move with the list — this battery ran and at least one excluded name was audited — with the reason written over the entry.

The two genuinely shrink-only ledgers this batch touches (KNOWN_TEARDOWN_UNREACHED, TYPED_EXEMPTIONS and the dual-build baseline) are asserted structurallyevery shipped X …, one case regardless of row count — so no floor here moves with them.

Census

node scripts/measure-self-test-floor.mjs --json, merge base 2263ca4d6 → this branch:

ROSTERCOUNTNONE
before34158
after214140

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

  1. every worklist file reads NONE before and ROSTER after;
  2. the set of files whose class changed equals this worklist exactly (symmetric difference empty);
  3. inside the handshake-carrying population, the files still NONE decompose exactly: 113 = 68 + 40 + 2 + 3 — 68 of the batch-1 remainder this batch does not take, 40 in flight on PR test(scripts): pin a battery roster floor on 40 more scripts/** self-tests #14851, check-i18n-coverage and check-i18n-bundles (honoured exclusions), and 3 handshake-carrying files outside batch 1's declared Tier-B population of 126.

⚠️ The class-1 set is 18, not 48 — the batch-1 class labels do not survive re-derivation

The dispatch put the class-1 surface at 48 and asked for the class to be re-derived from the tree. It was, and the number does not reproduce. 45 of the batch-1 report's 86 remainder_declared files do have a block-bodied assertion helper inside the self-test body — for example check-adr-0087-registration.mjs:3236 is const assert = (cond, msg) => { checked++; if (!cond) failures.push(msg); };, three lines below function selfTest(). Those files are not class 1 under the stated criterion, whatever kept them out of batch 1.

Applying the dispatch's criterion strictly — a resolvable entry body, no block-bodied in-body assertion helper, ≥2 named section banners all before the verdict, and a body-level if (SINK) verdict site — yields 23, of which 5 are excluded for shapes the class-1 ruling does not cover, leaving 18.

The direction of the difference is the safe one: this batch is a strict subset of any wider reading, so it cannot collide with batch 1c (classes 2 and 3). Under batch 1's own class ordering a file with no in-body helper is class 1, never 2 or 3, so nothing 1c owns is taken here.

The 68 batch-1 remainder files this batch does NOT take, with the measured reason

  • 45 — has a block-bodied in-body assertion helper, so not class 1: check-adr-0087-registration, check-auth-mount-ledger, check-ci-filter-parity, check-comment-mask-adoption, check-console-injection, check-cross-repo-closer-outcome, check-declaration-mirrors, check-dev-prereqs, check-dispatcher-error-vocabulary, check-doc-frontmatter, check-docs-locale-catch-all, check-docs-section-name, check-dts-emitted, check-engine-split-ratio, check-error-status-conformance, check-i18n-stale-fill, check-init-service-contract, check-keyed-text-bounds, check-logger-receiver-detach, check-merge-queue-triage-outcome, check-objectql-double-limit, check-overlay-whitelist-table, check-quick-reference-counts, check-ratchet-remedy-authority, check-required-contexts, check-route-envelope, check-section-landing-index, check-skill-compatibility-version, check-skill-frame-freshness, check-spec-parsed-alias, check-stack-collection-maps, check-stall-guard-budget, check-startup-registry-verdict, check-system-context-census, check-tenant-chokepoint, check-turbo-task-graph, check-undeclared-dep-imports, check-vendor-version-stamps, check-whole-set-label-write, check-wildcard-fallthrough, docs-audit/affected-docs, js-comment-mask, publish-smoke-pack, release-github-releases, sync-template-versions.
  • 13 — fewer than two named section banners (class 2, batch 1c): check-agent-model-declared, check-comment-mask-corpus, check-console-intercept-disarm, check-docs-single-h1, check-error-code-casing, check-optional-error-sink-contract, check-pnpm-acquisition, check-sdui-manifest, check-skill-frame-sync, check-widget-option-census, measure-position-name-fold-census, measure-test-shard-timings, render-release-coverage-anchor.
  • 5 — no body-level if (SINK) verdict site (class 3, batch 1c): check-adr-links, check-doc-anchors, check-kernel-hook-pairs, check-override-consistency, partition-test-shards.
  • 5 — class 1 by the criterion, but the class-1 remedy does not reach them:
    • ablation-dist-preflight and check-published-files — no single assertion sink; the sink is repeated inline in about six separate for loops, so "a minimal helper at the existing sink" would mean rewriting the reporting of every case.
    • check-examples-live-imports and check-org-identifier — table-driven, with every section banner inside the cases array literal, where no battery() statement can go. The card names this as Tier C.
    • check-durability-degradation-log-level — multi-entry dispatch, named as Tier C in the batch-1 report.

Verification

  • Per file, before and after:node GATE --self-test — 18/18 identical exit code (0) and byte-identical stdout and stderr, captured by redirect before any pipe. (check-plugin-teardown-shape needed the clone deepened to reach its pinned fixture commit; both readings are from the deepened tree.)
  • Three ablations, each from the committed tree, each with a trap restore, mutation proved on disk by anchored grep -c plus a git hash-object comparison against the HEAD blob, and restore proved by an empty git diff HEAD — never by an exit code:
    1. a battery's cases deleted (invoked-as, one t(...) line removed): exit 0 → 1, battery "the predicate, directly" registered 4 case(s), below its pinned floor of 5.
    2. a roster ENTRY deleted (invoked-as): exit 1 naming both halves — SELF_TEST_BATTERIES declares 1 batteries, below the pinned 2andbattery "the fixture: a probe reached three ways" registered 6 case(s) but is not declared.
    3. the new registerCase() line removed from the sink (check-runner-env-posture) — the class-1-specific leg, proving the introduced helper is load-bearing: exit 1, every battery DID NOT RUN — 0 cases registered, N pinned, with every original case still passing.
  • No dist is on the resolution path of any of the three, so no rebuild leg applies to them; the dist-dependent gates were re-run after a real build.
  • scripts/check-nul-bytes.mjs exit 0; grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the 18 changed files: no hits.

Gates, on ca1e17bf

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — the script derives the change set itself) reconciles this card at 34 families: 32 by path + 2 by change kind. All 34 were run, plus check:declared-population-live, which the dispatch names as mandatory and which the derivation does not include — 35 commands. Exit codes captured by redirect before any pipe; every result below quotes the gate's own verdict line, never a bare $?.

  • 32 RAN-PASS (exit 0), the two mandatory ones included: pnpm check:ratchet-remedy-authority = 0 and pnpm check:declared-population-live = 0.
  • pnpm check:pm-dispatch-gates = 0✓ dispatch-gates self-test: 1241 cases pass. It was red on the first head for the compound-anchor reason above (✗ every compound self-test NAME the anchor matches is classified in COMPOUND_ANCHOR_LEDGER — unlisted: … ×18), and green after the helpers were named battery….
  • 2 = PREREQUISITE NOT MET, not a verdict.node scripts/check-partof-closing-keyword.mjs and node scripts/check-single-claim-paths.mjs exit 2 printing NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or usage failure, NOT a verdict. Their pnpm check:* aliases — what CI runs — both exit 0.
  • 1 = NOT MEASURED, by the gate's own instruction.node scripts/check-test-completeness.mjs exits 3: "That list names this script with NO argument… There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red."
  • Four gates first read exit 3 = NOT MEASURED for want of dist. ⛔ Never read as a pass: pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* ran under scripts/pm/os-verify-lock.sh — VERDICT command-exit 0, 7m15s — and three of the four then exit 0 (check:dual-build-cjs-loads, and the two @objectstack/lint doc gates); the fourth is the NOT MEASURED one above. The build left the tracked tree unchanged (git status empty).
  • pnpm lint (whole repo, eslint . --no-inline-config): exit 0. No narrowing claimed.

Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label 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
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

PM note (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed on #13799 — content PASS. Test Core (1/6) on ca1e17bfd is the packages/cli shard signature tracked on #14822 / #14832 (job 100550425448; this diff is 18 scripts/** self-tests, nothing under packages/cli). Single non-own re-trigger spent now via update-branch (real merge of current origin/main); same shard red again ⇒ parked under the anchor, any other red is real.


Generated by Claude Code

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

Correction to the note above (same seat): the update-branch call did NOT happen — GitHub answered 422 "There are no new commits on the base branch." — because origin/main has not moved since this branch was cut (2263ca4d6, the merge queue has landed nothing since 03:48Z). So the PR's single non-own re-trigger is still unspent; it will be taken (merge of origin/main) once main moves, ideally carrying the #14832 fix. Until then the red stands as recorded: not this PR's, parked.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlskip-changesetPR 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

@claude