Skip to content

fix(pm): node is a fireable Restart-when: command head - #19712

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-19129-half-states-h9-node-command-head
Sep 22, 2026
Merged

huangyiirene merged 2 commits into
mainfrom
claude/issue-19129-half-states-h9-node-command-head

Conversation

@huangyiirene

@huangyiirene huangyiirene commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

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:

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 objectstack-ai/objectstack#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:

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 objectstack-ai/objectstack#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.

ablation-replace: anchor   x1 -> x0
ablation-replace: blob     6d9f2f36aef9835f2a47c942528fe74b76c6eb09 -> 4351fce172b838ffc2b4d46eee92f4679ead1b7b
ablation-replace: ok mutation landed: anchor 1 -> 0, blob 6d9f2f36aef9 -> 4351fce172b8
✗ check-half-states self-test: 5 of 5170 case(s) failed.
ablation-replace:   blob after restore  6d9f2f36aef9835f2a47c942528fe74b76c6eb09
ablation-replace:   blob at HEAD        6d9f2f36aef9835f2a47c942528fe74b76c6eb09
ablation-replace:   git diff HEAD       empty
ablation-replace: ok restored: blob == HEAD (6d9f2f36aef9) 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


Generated by Claude Code

`classifyRestartWhen` decides fireability from the first word alone, and
`node` was not on `RESTART_WHEN_COMMAND_HEADS`, so a runnable `node -e`
predicate classified `prose` — the class H9 calls "a `manual` in disguise,
waiting on an actor nothing schedules" — while the same predicate re-headed
`pnpm exec node -e …` classified `command`. The head 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.

Adds `node` to the measured head list with its corpus provenance, states in
the docblock what the head-word test can and cannot tell, and records that
the `pnpm exec` prefix is a workaround for this hole rather than a sanctioned
spelling. Pins land in a new floored self-test battery carrying the card's
four controls plus the closed-list and whole-word controls.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d953fabbd38cc2e8298385a1a4919e5d8c4fa680

① Derived judgments

Rendered in-seat by the skills seat 1 session at the served tier the maintainer named on this date (the fable tier word at get_session → external_metadata.last_served_model; ruling record 5771798588; the constant on origin/main names it since PR #19684). The contract is the card's measured defect (node absent from RESTART_WHEN_COMMAND_HEADS, so a runnable node -e predicate classifies prose while its pnpm exec node -e re-heading classifies command), the lane's grading 5738993813 (direction ①: node joins the list, the docblock states what the head word can and cannot tell, the pnpm exec prefix is not sanctioned; direction ②: the H9 row names the channel it read) and the seat's dispatch, which scoped this PR to direction ①.

  • (a) The fix is the card's, measured by this seat on both trees. RESTART_WHEN_COMMAND_HEADS reads git|node|npm|npx|pnpm on the head; the seat's probe (scratchpad/seat/probe-19712.mjs, ten rows) reproduces the card's table on the base (node -e … → prose, pnpm exec node -e … → command, the three controls lit) and on the head the subject reads command with every control unchanged: git grep … → command, closed …#19049 → closed-ref, prose → prose, and the closed-list controls bash …, nodemon …, node_modules/.bin/… → prose. The head set stays closed — bash, sh, gradle are pinned as prose, each with its reason in the docblock. the head match lower-cases and trims the value before it reads the first word — measured on the BASE with PNPM …, a leading-space pnpm … and Git …, all command there already, so the NODE … / leading-space node … readings on the head are that pre-existing rule applied to the new head, ⛔ not this PR's widening.
  • (b) The docblock says what the grading asked. The node row carries its provenance (objectui#9868's live hold, read 2026-09-18); the new paragraph states that the head word can tell only whether some mechanism could run the line, not whether running it means anything (node -e "process.exit(0)" fires and answers nothing), and records the pnpm exec re-heading as a WORKAROUND for a hole in the list, ⛔ not a sanctioned spelling — the grading's direction ① verbatim in substance.
  • (c) Pins — a declared battery, floors up. H9 node command head registered on the frozen roster with pin 9 (10 cases: the heads list, the starred subject, the re-headed twin, the fireable controls, the prose control, the closed-set control, the whole-word / manual control, the row in both channels, the gathering policy, the floor); SELF_TEST_BATTERY_FLOOR 8 → 9 with the five sibling floor assertions (H18901, H18664, H19255, H67, H68) moved with it. Seat's run: check:pm-half-states 5170 cases pass (5160 on the base), H9 node command head 10/9. The dev's ablation (node removed from the list ⇒ 5 of 5170 red — the subject pins, the heads-list pins and the gathering-policy pin — every control green; blob restored 6d9f2f36aef9, the blob at this head) reads as reported.
  • (d) Direction ② is NOT in this PR — and that is the seat's doing, carried on the card. The dispatch's Zone 1 and the claim's file surface scoped the PR to the command head; the grading had written ② as 「same function, same PR」. The dev measured the premise: h9OnHoldNoRestartWhen receives comment BODIES as strings, the ids never reach it, and naming the channel changes the shared comment cache's shape and the signature at roughly thirty pinned call sites across three rows. That is a separate change, not a sentence. Under the filing door this seat files no card, so ② stays on THIS card as its remainder: the seat PATCHes the PR body's first line to Part of #19129, and at MERGED the card returns to pm:queue with a Release: line naming ② and the dev's measurement of its cost, for the lane to take or the maintainer to retire. The dev's open question is thereby answered in-lane (its option A without the card it could not file); ⛔ not a decision-box item.
  • (e) Gates — measured by this seat on a clean worktree at d953fabbd after pnpm install --frozen-lockfile --offline. check:pm-half-states, check:nul-bytes, check-self-test-wired, check-scripts-symbol-anchors — exit 0; eslint over the file 1 judged, 0 / 0; no raw control byte; check-governed-merges.mjs --pr 19712 exit 0 — NOT governed, 0 of 1 path on the register, 62 lines. The dev's 42-family derivation reconciled --ran 42 / 42 / 0 NOT-MEASURED, repo-wide pnpm lint exit 0, check:published-readme-exports exit 3 recorded NOT MEASURED (no dist; nothing built) — read as reported. One file, +54 / −8 (net +46 against Zone 3's optional ≤ +30: the battery and the docblock — declared, accepted); two commits (the fix and the origin/main merge at 1d41aa885); trailers model-free per check:commit-card-trailers. CI at this head: 31 latest-per-name check runs, four in progress at the seat's read (Test Core (1/6), Lint & Repo Gates, Type Check · consumer gates, Live half-state sweep), the rest success or skipped.

② Semver level

None — no release. scripts/pm/** is in no package's files[] (the dev measured 75 manifests, none naming scripts); skip-changeset is the correct carrier, written through label-write.mjs with read-back; size/s is the labeler's.

③ Boundary flags

Implemented-by: claude/issue-19129-half-states-h9-node-command-head
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: PASS


Generated by Claude Code

@huangyiirene
huangyiirene marked this pull request as ready for review September 22, 2026 10:49
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 22, 2026
Merged via the queue into main with commit bcb64fa Sep 22, 2026
46 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-19129-half-states-h9-node-command-head branch September 22, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants