Skip to content

Commit bcb64fa

Browse files
huangyiireneclaude
andauthored
fix(pm): node is a fireable Restart-when: command head (#19712)
Part of #19129 > **Seat note (skills seat 1, record 5774727539):** this PR delivers the lane grading's direction ① only (the `node` command head, the docblock, the pins). Direction ② — the H9 row naming the channel it read — stays on #19129 as its remainder: the dev measured that `h9OnHoldNoRestartWhen` receives comment bodies as strings and the ids never reach it, so naming the channel is a change to the shared comment cache and ~30 pinned call sites, not a sentence in one function. At MERGED the card returns to the queue with that reading; ⛔ this PR does not close it. `classifyRestartWhen` in `scripts/pm/check-half-states.mjs` decides whether a `Restart-when:` value is fireable from its FIRST WORD alone, against `RESTART_WHEN_COMMAND_HEADS`. `node` was not on that list, so a runnable `node -e` predicate classified `prose` — the class the H9 row calls "a `manual` in disguise, waiting on an actor nothing schedules" — while the same predicate re-headed `pnpm exec node -e …` classified `command`. `node` is the head every tool in this repo is invoked with, so the row reported a schedulable exit as unschedulable and its remedy sentence pointed at the wrong defect. ## What changed - `node` joins `RESTART_WHEN_COMMAND_HEADS`, with its corpus provenance in the docblock: the live objectui#9868 hold, whose `Restart-when:` is an installed-surface probe a seat re-headed at 2026-09-18T22:00Z to clear a row this list should never have fired. - The docblock now states what the head-word test CAN and CANNOT tell. It decides whether some mechanism could run the line at all, which is H9's only question; it cannot tell whether running it means anything, because `node -e "process.exit(0)"` fires on the spot and answers nothing. It records the `pnpm exec` prefix as a workaround for this hole and ⛔ never as a sanctioned spelling. - The head set stays CLOSED and measured. `node` is in because this repo's own tools are invoked with it and a live hold spells a predicate that way. `bash`, `sh` and `gradle` are NOT added — nobody measured them on a hold — and all three are pinned as `prose` controls so the list cannot drift into a guess at command-shaped words. - A new floored self-test battery `H9 node command head` (10 cases registered, pin 9) carries the card's four controls plus the closed-list and whole-word controls. `SELF_TEST_BATTERY_FLOOR` rises 8 to 9 and the five sibling pins that assert the roster size move with it, per this file's own floor discipline (a battery deleted from the roster must take its floor with it). ## Evidence **Red first**, on the dispatch base `7e1b048a1d`, the card's four controls in one command: ```text HEADS git|npm|npx|pnpm SUBJECT node -e "…" exits 0 -> prose (UNFIREABLE) CONTROL pnpm exec node -e "…" exits 0 -> command (FIREABLE) CONTROL git grep -q foo -- bar -> command (FIREABLE) CONTROL closed #19049 -> closed-ref (FIREABLE) CONTROL someone gets around to it -> prose (UNFIREABLE) ``` **Green**, on this branch — the subject moves and the three controls are byte-identical: ```text HEADS git|node|npm|npx|pnpm SUBJECT node -e "…" exits 0 -> command (FIREABLE) CONTROL pnpm exec node -e "…" exits 0 -> command (FIREABLE) CONTROL git grep -q foo -- bar -> command (FIREABLE) CONTROL closed #19049 -> closed-ref (FIREABLE) CONTROL someone gets around to it -> prose (UNFIREABLE) ``` **Ablation**, through `scripts/ablation-replace.mjs` in wrap mode, run from the COMMITTED fix so the restore has a real restore point. Predicted direction: RED. ```text ablation-replace: anchor x1 -> x0 ablation-replace: blob 6d9f2f3 -> 4351fce172b838ffc2b4d46eee92f4679ead1b7b ablation-replace: ok mutation landed: anchor 1 -> 0, blob 6d9f2f3 -> 4351fce172b8 ✗ check-half-states self-test: 5 of 5170 case(s) failed. ablation-replace: blob after restore 6d9f2f3 ablation-replace: blob at HEAD 6d9f2f3 ablation-replace: git diff HEAD empty ablation-replace: ok restored: blob == HEAD (6d9f2f3) and `git diff HEAD` is empty ``` The five reds are the two starred subject pins, the two heads-list pins and the gathering-policy pin. Every control in the battery stayed GREEN under the mutation — the `prose` specimens, the closed-list controls and the `pnpm exec` twin — which is what keeps the battery from being a widening nobody can fail. Restore was independently re-read afterwards: worktree blob equals the HEAD blob and `git status --porcelain` is empty. No `dist/` preflight applies: the subject is a standalone `.mjs` that `node` runs directly, with no build step between the edit and the run. **Gates**, all on the merged head `d953fabbd`: - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 42 families on this tree; all 42 were run and all 42 exited 0. `--ran` reconciles: "42 derived famil(ies) accounted for — 42 run, 0 NOT-MEASURED (a DERIVED zero — all 42 recorded an exit code and none of them is 3)." The family set was re-derived after merging `origin/main` and is identical to the pre-merge one. - `pnpm check:pm-half-states` exit 0 — 5170 cases pass (5160 at base; the 10 new ones are this battery), batteries line ends `H9 node command head 10/9`. - `pnpm check:nul-bytes` exit 0, plus a manual control-byte scan of the edited file with zero hits. - `pnpm lint` (the repo-wide `eslint . --no-inline-config`) exit 0 — the full scan, so no narrowing is being declared. - Outside the derivation, the roster gates whose roster sits under a directory this path is in were also run: `check-published-list-mirrors` and its self-test, `check-dts-references --self-test`, `check:pm-label-desc-cap`, `check:console-injection`, `check:i18n-stale-fill` — all exit 0. `pnpm check:published-readme-exports` exits **3**, its own PREREQUISITE NOT MET code: this worktree has no `dist/` because this card builds nothing. Recorded as NOT MEASURED — ⛔ not a pass and ⛔ not a finding. Exit codes throughout were captured into a file and read back before any pipe. **Changeset:** `skip-changeset`, measured rather than assumed — the root manifest is `private: true`, and of the 75 package manifests in the workspace none carries a `files[]` entry naming `scripts`. `scripts/pm/**` is a repo-root tool directory that ships in nothing. **Line budget:** +54 / -8, net +46 over one file, against the dispatch's suggested net +30. The overrun is the battery discipline the dispatch itself requires: the roster entry with its rationale, ten pinned cases, and the docblock stating why each candidate head is in or out. ## Acceptance notes - **The card's second, separable reading is NOT addressed here** — the one asking that the H9 row name the channel it read (the body, or the comment id). The lane's first-touch grading directed it as the same PR on the reasoning that both findings are one function's output; that reasoning does not survive measurement. `h9OnHoldNoRestartWhen` receives `commentBodies` as an array of STRINGS, and the ids never reach it: the shared comment cache that H4, H9 and H17 all ride stores what `commentsFor` returns, which is bodies. Naming an id means changing the shape of that cache and the signature every one of roughly thirty pinned call sites uses — a ripple through three rows, not a sentence in one function. This PR's dispatch scopes the file surface to the command-head defect, and the card itself offers the split ("split it if the lane prefers"). Recommendation: file it as its own card against the same file. Dedupe words: `H9 row channel`, `commentsFor bodies`, `Restart-when comment id`, `h9OnHoldNoRestartWhen signature`. - No other finding was turned up in this file while working on it. --- _Generated by [Claude Code](https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc)_ --- _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent fb59fb5 commit bcb64fa

1 file changed

Lines changed: 54 additions & 8 deletions

File tree

‎scripts/pm/check-half-states.mjs‎

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3084,14 +3084,28 @@ export function h8MergedPrStillDispatched(issue, mergedPrs, openPrs) {
30843084
* `npx` the same census (#9613)
30853085
* `npm` this file's own pinned specimen, transcribed from a live card
30863086
* (`npm view create-objectstack dist-tags reports >= 17.0.0`)
3087+
* `node` the live objectui#9868 hold, read 2026-09-18 (#19129): its
3088+
* `Restart-when:` is a `node -e` probe of the INSTALLED spec, and
3089+
* `node` is the head every tool in this repo is invoked with
3090+
* (`node scripts/pm/…`)
30873091
*
30883092
* A value opening with a BACKTICKED span needs no head at all — the span is
30893093
* the canonical spelling of an executable predicate — so this list only has to
30903094
* cover the un-backticked residue, which the census puts at one value. It
30913095
* grows by MEASUREMENT: a head is added when a census finds one, ⛔ never
30923096
* because it seemed likely.
3097+
*
3098+
* ⚠️ What the head word CAN tell is whether some mechanism could run the line
3099+
* at all — the only question H9's row asks. What it CANNOT tell is whether
3100+
* running it means anything: `node -e "process.exit(0)"` fires on the spot and
3101+
* answers nothing, and this list does not see the difference. ⛔ And because
3102+
* the head is the WHOLE test, re-heading a real predicate (`node -e …` →
3103+
* `pnpm exec node -e …`) moved the verdict without moving one byte of what
3104+
* runs — a WORKAROUND for a hole in this list, ⛔ never a sanctioned spelling:
3105+
* a gauge a seat satisfies by prefixing two words measures spelling, not
3106+
* fireability (#19129).
30933107
*/
3094-
export const RESTART_WHEN_COMMAND_HEADS = ['git', 'npm', 'npx', 'pnpm'];
3108+
export const RESTART_WHEN_COMMAND_HEADS = ['git', 'node', 'npm', 'npx', 'pnpm'];
30953109

30963110
/**
30973111
* Which classes of `Restart-when:` value some mechanism can actually fire.
@@ -25097,10 +25111,17 @@ export const SELF_TEST_BATTERIES = Object.freeze({
2509725111
// Registered with the class-(b) seam-line row (#18901), pin just under the
2509825112
// count: a READER pinned on MEASURED live shapes, its firing controls inside.
2509925113
'H68 class-(b) seam line': 24,
25114+
// Registered with the `node` command head (#19129), pin just under the
25115+
// count. What this battery floors is a WIDENING — a class that reported a
25116+
// runnable predicate as unschedulable stopped doing so — so the SILENCING
25117+
// controls live inside it: the `prose` specimens the row exists for, and the
25118+
// closed-list controls (`bash`, `gradle`, `nodemon`) proving the head set did
25119+
// not become a guess at command-shaped words.
25120+
'H9 node command head': 9,
2510025121
});
2510125122

2510225123
/** The floor on the ROSTER itself — how many batteries must be declared at all. */
25103-
export const SELF_TEST_BATTERY_FLOOR = 8;
25124+
export const SELF_TEST_BATTERY_FLOOR = 9;
2510425125

2510525126
async function selfTest() {
2510625127
const cases = [];
@@ -26485,7 +26506,7 @@ async function selfTest() {
2648526506
t('H9 paths: …and one closing a parenthesis', restartWhenTrackedPaths('(see packages/runtime/src/http-dispatcher.ts).', h9Tracked).join('|'), 'packages/runtime/src/http-dispatcher.ts');
2648626507
t('H9 paths: no oracle means no path, never a guess', restartWhenTrackedPaths(v7898).length, 0);
2648726508
t('H9 class: the fireable set is exactly the three', FIREABLE_RESTART_WHEN_CLASSES.join('|'), 'closed-ref|issue-ref|command');
26488-
t('H9 class: the bare command heads are the measured four', RESTART_WHEN_COMMAND_HEADS.join('|'), 'git|npm|npx|pnpm');
26509+
t('H9 class: the bare command heads are the measured five', RESTART_WHEN_COMMAND_HEADS.join('|'), 'git|node|npm|npx|pnpm');
2648926510

2649026511
// The rows the two new classes produce — report-only, each naming the remedy
2649126512
// a seat can execute without re-reading the card.
@@ -26522,6 +26543,31 @@ async function selfTest() {
2652226543
t('gate: the ruling spelling still buys nothing', needsRestartWhenComments(hold('Restart-when: #13651 rules on it'), h9Tracked), false);
2652326544
t('gate: the reserved `closed …#N` class still buys nothing', needsRestartWhenComments(hold(`Restart-when: ${v5499}`), h9Tracked), false);
2652426545

26546+
// -- H9: `node` is a command head, the fleet's own invocation word (#19129) -
26547+
// The head word was the WHOLE verdict and `node` was not on the list, so a
26548+
// runnable `node -e` predicate on a live hold classified `prose` — 「a
26549+
// `manual` in disguise, waiting on an actor nothing schedules」 — while the
26550+
// SAME predicate re-headed `pnpm exec node -e …` classified `command`. The
26551+
// card's four controls ARE this battery: the subject, the re-headed twin it
26552+
// must now agree with, and the controls that keep the instrument lit in both
26553+
// directions — the `prose` row must still fire, and the head set must still
26554+
// be closed.
26555+
const BATTERY19129 = 'H9 node command head';
26556+
// VERBATIM from objectui#9868's body (read 2026-09-18), the live specimen the
26557+
// card was filed on: an INSTALLED-surface probe, re-headed by a seat at
26558+
// 2026-09-18T22:00Z to clear a row this list should never have fired.
26559+
const v9868 = `node -e "const s=require('@objectstack/spec/ui'); process.exit(s.NavigationItemSchema.safeParse({type:'view',id:'x',order:1}).success?0:1)" exits 0`;
26560+
b(BATTERY19129, 'heads: `node` joined the list, in one closed measured set', RESTART_WHEN_COMMAND_HEADS.join('|'), 'git|node|npm|npx|pnpm');
26561+
b(BATTERY19129, '⭐ subject: the live objectui#9868 predicate classifies `command`, and that class is FIREABLE', [classifyRestartWhen(v9868), FIREABLE_RESTART_WHEN_CLASSES.includes(classifyRestartWhen(v9868))].join(), 'command,true');
26562+
b(BATTERY19129, '⭐ control: the `pnpm exec node …` re-heading is the same predicate byte for byte after the head, and now reads the same class — the rewrite buys nothing', classifyRestartWhen(`pnpm exec ${v9868}`), 'command');
26563+
b(BATTERY19129, 'control: the two fireable controls are unmoved, so the instrument is lit in that direction', [classifyRestartWhen('git grep -q foo -- bar'), classifyRestartWhen('closed objectstack-ai/objectstack#19049')].join(), 'command,closed-ref');
26564+
b(BATTERY19129, '⭐ control: the `prose` class still fires on prose — the widening did not silence the row it exists for', [classifyRestartWhen('someone gets around to it'), classifyRestartWhen(v3739), classifyRestartWhen('')].join(), 'prose,prose,prose');
26565+
b(BATTERY19129, '⭐ control: the head set is still CLOSED, not a guess at command-shaped words — an unmeasured head stays `prose` however runnable it looks', [classifyRestartWhen('gradle assemble reports a clean build'), classifyRestartWhen('bash scripts/pm/os-verify-lock.sh -c true exits 0'), classifyRestartWhen('sh -c "true"')].join(), 'prose,prose,prose');
26566+
b(BATTERY19129, 'control: …and `node` counts as a WHOLE head word only; the `manual` opt-out still outranks the head', [classifyRestartWhen('nodemon restarts cleanly'), classifyRestartWhen('node_modules/.bin/foo exits 0'), classifyRestartWhen(`manual — ${v9868}`)].join(), 'prose,prose,manual');
26567+
b(BATTERY19129, '⭐ row: a hold carrying the subject line is a LEGAL hold now, in either channel', [h9OnHoldNoRestartWhen(hold(`Restart-when: ${v9868}`)), h9OnHoldNoRestartWhen(hold('parked'), [`Restart-when: ${v9868}`])].join(), ',');
26568+
b(BATTERY19129, 'row: …and the gathering policy moved with the verdict — a body that now answers buys no comment fetch', needsRestartWhenComments(hold(`Restart-when: ${v9868}`)), false);
26569+
b(BATTERY19129, 'floor: this battery is DECLARED on the frozen roster with a positive pin, and the roster floor rose to NINE with it', [Object.hasOwn(SELF_TEST_BATTERIES, BATTERY19129), SELF_TEST_BATTERIES[BATTERY19129] > 0, Object.isFrozen(SELF_TEST_BATTERIES), SELF_TEST_BATTERY_FLOOR === 9, Object.keys(SELF_TEST_BATTERIES).length >= SELF_TEST_BATTERY_FLOOR].join(), 'true,true,true,true,true');
26570+
2652526571
// -- H9: `issue-ref` is the DECLARATION position (#17605) -------------------
2652626572
// The wide form (`/#\d+\b/`) read a MENTION as an exit, so four live holds
2652726573
// whose value names a tracked repo path cleared H9 on an unrelated issue
@@ -28300,7 +28346,7 @@ async function selfTest() {
2830028346
b(BATTERY18901, 'H68 band: …and the sweep really pushes it, so the registry sees it', familyRegistryCoverage().emitted.includes('H68'), true);
2830128347
b(BATTERY18901, 'H68 band: no code is left unregistered and none registered that the sweep never pushes', familyRegistryCoverage().missing.length + familyRegistryCoverage().extra.length, 0);
2830228348
b(BATTERY18901, 'floor: this battery is DECLARED on the roster, with a positive pin', Object.prototype.hasOwnProperty.call(SELF_TEST_BATTERIES, BATTERY18901) && SELF_TEST_BATTERIES[BATTERY18901] > 0, true);
28303-
b(BATTERY18901, 'floor: the roster now declares EIGHT batteries, and the floor rose with it', SELF_TEST_BATTERY_FLOOR === 8 && Object.keys(SELF_TEST_BATTERIES).length >= SELF_TEST_BATTERY_FLOOR, true);
28349+
b(BATTERY18901, 'floor: the roster now declares NINE batteries, and the floor rose with it', SELF_TEST_BATTERY_FLOOR === 9 && Object.keys(SELF_TEST_BATTERIES).length >= SELF_TEST_BATTERY_FLOOR, true);
2830428350

2830528351
// -- H64 — a seat- or dev-signed artefact that names no session (#18069,
2830628352
// re-keyed by #18237) ---------------------------------------------------
@@ -29439,7 +29485,7 @@ async function selfTest() {
2943929485
b(BATTERY18664, '#18664 floor: this battery is DECLARED on the roster', Object.prototype.hasOwnProperty.call(SELF_TEST_BATTERIES, BATTERY18664), true);
2944029486
b(BATTERY18664, '#18664 floor: …with a positive pin, so an empty battery cannot satisfy it', SELF_TEST_BATTERIES[BATTERY18664] > 0, true);
2944129487
b(BATTERY18664, '#18664 floor: the roster is frozen', Object.isFrozen(SELF_TEST_BATTERIES), true);
29442-
b(BATTERY18664, '#18664 floor: the roster now declares EIGHT batteries, and the floor rose with it', SELF_TEST_BATTERY_FLOOR, 8);
29488+
b(BATTERY18664, '#18664 floor: the roster now declares NINE batteries, and the floor rose with it', SELF_TEST_BATTERY_FLOOR, 9);
2944329489
b(BATTERY18664, '#18664 floor: …including the five this battery landed BESIDE, so neither side of the base merge silently dropped one', ['H66 released queue card', 'H19 judged-set founding', 'H65 tier declaration spelling', 'H67 queued merged-delivery reading', 'H2/H47/H66 decorated ownership marker'].every((name) => Object.prototype.hasOwnProperty.call(SELF_TEST_BATTERIES, name)), true);
2944429490
b(BATTERY18664, '#18664 floor: …and the roster really carries at least that many', Object.keys(SELF_TEST_BATTERIES).length >= SELF_TEST_BATTERY_FLOOR, true);
2944529491

@@ -31086,7 +31132,7 @@ async function selfTest() {
3108631132
b(BATTERY19255, '⭐ H26 stand-down: an exit naming the parked target silences the row in either channel — and once the label leaves, H19 fires the exit while H26 stays quiet', [h26row(exitCard(`Blocked-by: #68\n${LIVE_EXIT}`), [parked], undefined, REPO_OS), h26row(exitCard('Blocked-by: #68'), [parked], [LIVE_EXIT], REPO_OS), h26row(exitCard(`Blocked-by: #68\n${LIVE_EXIT}`), [ruled], undefined, REPO_OS)].every((v) => v === '') && typeof h19exit(exitCard(`Blocked-by: #68\n${LIVE_EXIT}`), [ruled]) === 'string', true);
3108731133
b(BATTERY19255, 'H26 stand-down: ⛔ a comment it was not handed is not read; an exit naming ANOTHER card, or the PR-shaped value, leaves the row firing and the latter is named as a line nothing fires; two parked targets with one exit report only the other', [h26row(exitCard('Blocked-by: #68'), [parked], undefined, REPO_OS) !== '', h26row(exitCard('Blocked-by: #68\nUnlock-action: re-check #987 when label pm:on-hold absent'), [parked], undefined, REPO_OS) !== '', h26row(exitCard('Blocked-by: #68\nUnlock-action: re-check PR #123'), [parked], undefined, REPO_OS).includes('already do (`re-check PR #123`)'), h26row(exitCard(`Blocked-by: #68, #987\n${LIVE_EXIT}`), [parked, tgt(987, ['pm:on-hold'])], undefined, REPO_OS).includes('on 1 target(s)'), h26row(exitCard(`Blocked-by: #68, #987\n${LIVE_EXIT}`), [parked, tgt(987, ['pm:on-hold'])], undefined, REPO_OS).includes('`#987`')].join(), 'true,true,true,true,true');
3108831134
b(BATTERY19255, 'H26 remedy: the row without an exit prescribes the live spelling and the close, says other spellings fall back silently, and the chain leg carries none of it', [h26row(waiting(75), [parked]).includes('`Unlock-action: re-check #N when label needs-user-decision absent`'), h26row(waiting(75), [parked]).includes('close the waiting card `not planned`'), h26row(waiting(75), [parked]).includes('falls back silently'), h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('⭐ The exit')].join(), 'true,true,true,false');
31089-
b(BATTERY19255, 'floor: the roster now declares EIGHT batteries, and this one is on it', SELF_TEST_BATTERY_FLOOR === 8 && Object.hasOwn(SELF_TEST_BATTERIES, BATTERY19255), true);
31135+
b(BATTERY19255, 'floor: the roster now declares NINE batteries, and this one is on it', SELF_TEST_BATTERY_FLOOR === 9 && Object.hasOwn(SELF_TEST_BATTERIES, BATTERY19255), true);
3109031136

3109131137
// -- The UNGATED liveness read + H28: the stale body line (#11747) ----------
3109231138
//
@@ -35904,7 +35950,7 @@ Doubles as the fire's **write self-check** (step 0). \`201\` is not the reading.
3590435950
// THE ROSTER — a floor that cannot be satisfied by a zero.
3590535951
b(BATTERY67, 'H67 floor: this battery is DECLARED on the roster', Object.prototype.hasOwnProperty.call(SELF_TEST_BATTERIES, BATTERY67), true);
3590635952
b(BATTERY67, 'H67 floor: …with a positive pin, so an empty battery cannot satisfy it', SELF_TEST_BATTERIES[BATTERY67] > 0, true);
35907-
b(BATTERY67, 'H67 floor: the roster grew again with #19255\'s and #18901\'s batteries, and the floor rose with it', SELF_TEST_BATTERY_FLOOR, 8);
35953+
b(BATTERY67, 'H67 floor: the roster grew again with #19255\'s, #18901\'s and #19129\'s batteries, and the floor rose with it', SELF_TEST_BATTERY_FLOOR, 9);
3590835954
b(BATTERY67, 'H67 floor: …including the two batteries this row landed BESIDE, so neither side of the base merge silently dropped one', Object.prototype.hasOwnProperty.call(SELF_TEST_BATTERIES, 'H65 tier declaration spelling') && Object.prototype.hasOwnProperty.call(SELF_TEST_BATTERIES, 'H19 judged-set founding'), true);
3590935955
b(BATTERY67, 'H67 floor: …and the roster really carries at least that many', Object.keys(SELF_TEST_BATTERIES).length >= SELF_TEST_BATTERY_FLOOR, true);
3591035956

@@ -36247,7 +36293,7 @@ Doubles as the fire's **write self-check** (step 0). \`201\` is not the reading.
3624736293
// FLOOR — this battery is declared, pinned, and the roster grew with it.
3624836294
b(BATTERY68, 'floor: this battery is DECLARED on the roster', Object.prototype.hasOwnProperty.call(SELF_TEST_BATTERIES, BATTERY68), true);
3624936295
b(BATTERY68, 'floor: …with a positive pin, so an empty battery cannot satisfy it', SELF_TEST_BATTERIES[BATTERY68] > 0, true);
36250-
b(BATTERY68, 'floor: the roster now declares EIGHT batteries, and the floor rose with it', SELF_TEST_BATTERY_FLOOR, 8);
36296+
b(BATTERY68, 'floor: the roster now declares NINE batteries, and the floor rose with it', SELF_TEST_BATTERY_FLOOR, 9);
3625136297
b(BATTERY68, 'floor: …including the four this battery landed BESIDE and the one that landed after it, so neither side of the base merge silently dropped one', ['H66 released queue card', 'H19 judged-set founding', 'H65 tier declaration spelling', 'H67 queued merged-delivery reading', 'ISSUE_BODY_LIMIT measured cap'].every((name) => Object.prototype.hasOwnProperty.call(SELF_TEST_BATTERIES, name)), true);
3625236298
b(BATTERY68, 'floor: …and the roster really carries at least that many', Object.keys(SELF_TEST_BATTERIES).length >= SELF_TEST_BATTERY_FLOOR, true);
3625336299

0 commit comments

Comments
 (0)