Skip to content

ci(half-state-patrol): state the measured reason the two steps stay in the caller - #19718

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-19396-half-state-patrol-caller-reason
Sep 22, 2026
Merged

huangyiirene merged 1 commit into
mainfrom
claude/issue-19396-half-state-patrol-caller-reason

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #19396

Comment-only, in two files. The stated reason for keeping actions/setup-node and the closed-card sweep in the CALLING workflow was a census argument, and PR #19284 made it false. Each bullet now carries a constraint that was measured on this tree instead.

The premise, re-measured on the base (a251aaa19)

All four censuses reach composite actions. Their own scope lines, exit code captured before any pipe:

check-node-version                EXIT=0  OK (43 setup-node step(s) across 38 workflow(s) and 2 composite action(s), all on Node 22).
check-self-test-wired             EXIT=0  scope: 311 file(s) under scripts/, 243 carrying `--self-test` in code ...; 226 of those are run by 38 workflow(s) and 2 composite action(s); ...
check-workflow-step-name-quoting  EXIT=0  OK (scanned 38 workflow file(s) + 2 composite action file(s), 729 step name(s) -- 9 already quoted, 720 unquoted-and-safe).
check-step-collectors             EXIT=0  511 `run:` steps across 38 workflow(s) and 2 composite action(s); 5 step(s) run 2+ independent self-tests, all of them through a collector.

premise_still_valid: true — zero composite actions would have refuted the card; both counts read 2.

Two further readings, so the repair rests on measurement rather than on the card's guess:

  • scripts/check-node-version.mjs builds one list, [...workflowFiles, ...actionFiles], and its own header retires the constraint by name: "With both roots read, the constraint is gone: put the step wherever the composition wants it."
  • The consequence the sweep bullet drew is false too, measured with the gate's own exported reader. Read-only probe, nothing in the tree touched: the closed-card step's real bytes handed to collectInvocations twice, once as a workflow and once as a composite action file.
workflow          named=true selfTested=true  attribution=["half-state-patrol.yml"]
composite action  named=true selfTested=true  attribution=[".github/actions/half-state-patrol/action.yml"]

So a moved step stays in the population, under the composite action's own name.

The constraint that IS measurable, per step

actions/setup-node — the action holds no Node pin, and a setup-node step in it would need one. The floor asserted in Locate the patrol sources is read from the .nvmrc that shipped with the action (action.yml lines 202-217, already in the file and still true). check-node-version.mjs holds every setup-node pin in this repo — workflows and composite actions alike — equal to that same .nvmrc. So a setup-node step inside the composite would install the very version the floor is then compared against: have and floor derived from one file, equal by construction, the comparison unable to fail, and no longer a reading about the CALLER's runner at all. That holds for a sibling pinning the action too, since both the pin and the .nvmrc travel at the same sha.

This is not the card's guess ("a sibling repository's Node pin has to be the sibling's own"), and the guess is not measurable today: no sibling calls this action. objectui carries its own .github/workflows/half-state-patrol.yml that runs its own copy of the sweeper, holds no .github/actions/ directory at all, and names this action nowhere. The only caller on either tree is this repo's own uses: ./.github/actions/half-state-patrol.

No gate requires the pin to be a literal in the caller: check-node-version accepts node-version-file: .nvmrc as the ideal form, fails an unresolvable expression, and cannot reach a sibling repo at all.

The closed-card sweep — the repair is a deletion, which is the card's second exit. A true reason already sat in both files, first in the sentence: it is the one step of the old file that was never repo-agnostic, it WRITES to cards under a ruling this repo's board took, and no sibling has taken it. The false half (the check-self-test-wired population argument) is deleted rather than rewritten, so what remains is what still holds.

Proof that this is comment-only

Parsed YAML compared against the base blob with the repo's own yaml@2.9.0, plus a control leg that must differ so the comparison is not one that cannot fail:

IDENTICAL  .github/actions/half-state-patrol/action.yml
   raw bytes  before=23411 after=23573 (delta 162)
   parsed JSON length before=10752 after=10752  sha-equal=true
IDENTICAL  .github/workflows/half-state-patrol.yml
   raw bytes  before=30837 after=30419 (delta -418)
   parsed JSON length before=5245 after=5245  sha-equal=true
CONTROL LEG (one real YAML byte changed, in memory): identical=false — must be false

And every changed line is a comment line:

changed lines: 56
NON-comment changed lines: (count: 0)

No step moved; no run:, uses:, with:, if: or paths: byte changed.

Line budget: action.yml block 19 lines to 21 (+2); workflow setup-node comment 5 to 5 (0); workflow sweep comment 9 to 3 (-6). Net -4 across the two files, against the suggested ceiling of +12 / -14.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths; the script took the change set from the merge base itself), then reconciled with --ran. Exit codes captured before any pipe. Readings are quoted in the report comment on #19396; the union was re-run on the final commit.

.github/** ships in no package, so this carries skip-changeset: all 70 non-private packages declare an explicit files[] and not one entry names .github or the repo root.

Acceptance notes

  • .github/actions/setup-pnpm is NOT a stale sibling sentence — checked because the edited sentence cited it. It was already repaired (lines 50-58): it records the blind spot as CLOSED and says the separation is kept for no constraint at all. That difference is now stated in action.yml, so a reader who follows the pointer does not generalise setup-pnpm's retired reason to this action, which has a live one.
  • objectui has not adopted this composite action, and its copy of the sweeper diverges from the one the action ships — 13,194 lines against 36,717 at objectui@0cf2d6644 and a251aaa19. That is the copy drift this action exists to delete, one board along. Out of scope here and reported for the filing seat rather than touched.

Generated by Claude Code

…n the caller

The two files justified keeping `actions/setup-node` and the closed-card sweep
in the CALLING workflow with a census argument -- `check-node-version` and
`check-self-test-wired` derive their population from `.github/workflows/` and
only from there, so a step moved into the composite action would drop out and
the gate would still print OK. Both censuses read `.github/actions/**` as well
now, so the sentence is false: measured on this tree they print "2 composite
action(s)", and `collectInvocations` puts the sweep's own bytes in the
population under the composite action's name when they are presented as one.

A wrong reason on a right decision invites the change it was written to
prevent, so each bullet now carries what can be measured instead:

  - setup-node: the action holds NO Node pin of its own, and the floor it
    asserts is read from the `.nvmrc` that shipped with it. A setup-node step
    inside the composite would install the very version that floor is then
    compared against -- `check-node-version` holds every pin in this repo equal
    to that same file -- so the check could never fail and would say nothing
    about the caller's runner.
  - the closed-card sweep: the true reason was already the first half of the
    sentence (it WRITES to cards under a ruling no sibling took), so the false
    half is deleted rather than rewritten.

Comment-only. The parsed YAML of both files is byte-identical to the base, and
every one of the 56 changed lines is a comment line.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>
@huangyiirene huangyiirene added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 22, 2026 — with Claude
@github-actions github-actions Bot added the ci/cd label Sep 22, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ce16aec78c75098806d618433d22a964ec4daec5

① 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_sessionexternal_metadata.last_served_model; ruling record 5771798588; the constant on origin/main names it since PR #19684). The contract is the card's class-(b) reading and the lane's grading 5752866810: the stated reason for keeping actions/setup-node and the closed-card sweep in the CALLING workflow (a gate derives its population from .github/workflows/ only) is false since PR #19284; rewrite the justification to a constraint the taker can MEASURE, or delete it where a true reason already sits in the file; ⛔ not the composite itself, ⛔ no step moves.

  • (a) The diff is comments only — proved two ways by this seat. Two files, +26 / −30, one commit on the branch, merge base a251aaa19 = origin/main. git diff -U0 filtered for non-comment, non-blank changed lines: 0. The two files parsed with the repo's own yaml on the base blob and the head: identical on both (the control leg — one real byte changed in memory, using: compositeusing: node20 — reads unequal, so the comparison can fail). No run:, uses:, with:, if: or paths: byte moved; no step moved.
  • (b) The premise held on the base — measured. The four censuses on a251aaa19 print 「2 composite action(s)」 (check-node-version 43 steps across 38 workflows + 2 actions; check-self-test-wired 226 run by 38 + 2; check-workflow-step-name-quoting 38 + 2; check-step-collectors 511 steps across 38 + 2), so the sentence 「a step moved in here would drop out of that census」 was false. .github/actions/setup-pnpm :50–:58, which the old sentence cited, was already repaired by Six gates root at .github/workflows and none reads .github/actions/** — a composite action's run: steps are audited by nothing while every scope line claims coverage #19229 (「That blind spot is CLOSED … it is no longer a constraint」) — a measured negative, reported, not edited.
  • (c) The constraint written is the one measured, not the card's guess — read by this seat in the tree. setup-node: the action holds no Node pin of its own — its runtime floor is read from the .nvmrc that ships with it (action.yml :204–:209) and compared with the runner's major; scripts/check-node-version.mjs takes .nvmrc as the single source of truth (PIN_FILE = '.nvmrc') over BOTH roots (ACTION_DIR = '.github/actions', Six gates root at .github/workflows and none reads .github/actions/** — a composite action's run: steps are audited by nothing while every scope line claims coverage #19229). A setup-node step inside the composite would install the version the floor is then compared against: equal by construction, unable to fail, no longer a reading about the caller's runner. The card's guess (a sibling-owned pin) was measured as not testable today — objectui carries its own patrol workflow and no .github/actions/ directory; zero sibling callers — and was not copied. The closed-card sweep: its true reason already sat first in the sentence (it WRITES to cards under ruling finding(skills): a card auto-closed by a merged Fixes PR keeps its pm:dispatched label — reproduced ×18 in one objectui shift; the strip is a mechanical sweep, not a seat ritual #16005 this board took; its if: is gated on the repository), so the false half is deleted — the card's second exit; the gate's own reader, handed the step's bytes as a composite-action file, still attributes the self-test, so a moved step would stay in the population (measured read-only by the dev).
  • (d) The three comment blocks read the same reason. action.yml :48–:68 (19 → 21 lines), the workflow's setup-node comment :251–:255 (5 → 5) and its sweep comment :319–:321 (9 → 3): each names the census as gone and states the measured constraint; the workflow blocks point at the action's header for the measure. The card cited the mirror at :196–:200; on the base that range is the pull_request paths: block — the dev edited the real locations. Net −4 lines against Zone 3's optional +12 / −14.
  • (e) Gates — measured by this seat on a clean worktree at ce16aec78 after pnpm install --frozen-lockfile --offline. check:node-version, check-self-test-wired, check:workflow-step-name-quoting, check-step-collectors, check:nul-bytes, check-ci-filter-parity — all exit 0, each scope line naming the two composite actions; no raw control byte in either file; check-governed-merges.mjs --pr 19718 exit 0 — NOT governed, 0 of 2 paths, 56 lines. The dev's 38-family derivation reconciled --ran 38 / 38 / 0 NOT-MEASURED (one first-pass exit 3 prerequisite, re-run to 0 after install; the check:pm-dispatch-gates battery run in full) and eslint's own 「File ignored because no matching configuration was supplied」 for both files read as reported. CI at this head: 27 latest-per-name check runs, Lint & Repo Gates and Live half-state sweep in progress at the seat's read, the rest success or skipped. The push carrying .github/workflows/** was not refused.

② Semver level

None — no release. .github/** is in no package's files[] (the dev measured 70 declaring manifests, zero naming .github), and the diff is comment-only besides; skip-changeset is the correct carrier, written through label-write.mjs with read-back; ci/cd and size/s are the labelers'.

③ Boundary flags

  • Not governed — ordinary queue landing by the seat on all green and --pair 0.
  • Report 5775193851 on the card parses (os-dev-report first line); mcp_calls 0; three REST writes plus two pushes, as budgeted; ⛔ no body PATCH by the dev and none owed by the seat (no 席位意见 slot on a non-governed path).
  • Dispatch bookkeeping: the dispatch named check:ci-filter-parity as a pnpm script — it is a bare node gate (scripts/check-ci-filter-parity.mjs), run that way and green; the claim named 4fba5036f as origin/main and the base was a251aaa19 (main had moved by the worktree's creation; nothing to merge at PR time).
  • Out of scope, filed by the dev on the objectui board (class a): objectui has not adopted the composite action and its own copy of check-half-states.mjs has diverged from objectstack's by ~23,500 lines — the failure the action was built to delete, one board along. Not this PR's.

Implemented-by: claude/issue-19396-half-state-patrol-caller-reason
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: PASS


Generated by Claude Code

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

Labels

ci/cd 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.

[finding] the half-state patrol action keeps setup-node in the caller for a reason PR #19284 falsified — the censuses read composite actions now

2 participants