ci(pm): retire the half-state patrol's schedule — on demand only (#19497) - #19516
Conversation
The patrol is report-only, and the dispatch charter made its anchor rows a
precondition for opening a dispatch, so a row misreading a now-legal state
cost every executive seat a handling step four times a day until a dev
repaired the row. Ruling 208 (batch letter 2, part R3) retires the schedule
and keeps the instrument: no line of the sweeper is edited, the workflow
stays runnable, and the triage seat dispatches it for its reconciliation.
- `on:` drops the `schedule:` block; `workflow_dispatch: {}` and the
`pull_request:` paths trigger stay exactly as they were.
- The header prose that promised a four-times-a-day cadence, the `:37`
offset, the `Swept` heartbeat reading, the concurrency note, the anchor
fallback note and the citation census's cost note are rewritten for
on-demand use, and carry the ⛔ against re-scheduling it silently.
- The generator's own cadence constants are untouched by ruling, so the
anchor body will keep stamping a deadline against a schedule nobody
fires; the header records that as a known, unread reading.
Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE
Co-authored-by: Claude <noreply@anthropic.com>
…hedule Removing the half-state patrol's schedule makes two cases of the dispatch-gates self-test false: both read the live `.github/workflows` tree and assert that the specimen workflow declares `schedule`. The gate runs on this diff's class (the selector's workflow arm falls through to run), and it is a required context, so the pin is repaired in the same change that makes it false — measured red first: 2 of 1883 cases, and no others. Each case keeps the fact it was always about: one pins that the specimen is not "a workflow no PR runs" (the PR-time trigger), the other that the live sweep is withheld by the value-bearing class. Nothing is added — pinning the absence of a schedule would be a new ratchet, which the same ruling forbids this file without the maintainer's word. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
…trol-schedule-retired
Contract reviewReviewed 2026-09-21T05:21Z · PR #19516 · card #19497 · shape per ruling #208 R5 (diff read + check-runs read; no derived gate family re-run locally, ⛔ not Served-tier: Read first: card #19497 (body, claim 5755284628, os-dev-report 5755630773), the ruling record on #19491 (comment 5755284987, R3 and R6), the PR body, and the diff ① Derived judgments1. Scope — RIGHT. 2. The workflow — RIGHT. YAML-parsed at the head: top keys 3. Census — RIGHT. 4. The dev's two findings — both confirmed from the code. (a) 5. Stale cadence prose in files this PR does not own — carriers for other cards, not this PR's FAIL. Confirmed at the head: 7. Collision — RIGHT. PR #19511 ( ② Semver levelCarriers: none hung — ③ Boundary flags6. CI on the head, read from the check-runs REST endpoint (36 check-runs, polled 2026-09-21T05:21Z) — the seven required contexts:
Tier: Governed surfaces touched: none. Release act: none. Review's own writes: REST only, no MCP GitHub tool; one write, this comment; no label, ready flip, auto-merge, body edit or issue. Implemented-by: VERDICT: PASS Generated by Claude Code |
|
Provenance — director seat, summon #25 (
Generated by Claude Code |
Fixes #19497
Clause-②: no
Retires the half-state patrol's schedule under ruling #208 on #19491 (director seat, summon #25), direct-dispatch channel, on the maintainer's verbatim instruction:
R3 of that ruling, verbatim:
and the maintainer's question it answers: 「巡查仪留着却不继续开发,会不会反而不停的报错误的信息」
What changed — two files, +89 / −45
1.
.github/workflows/half-state-patrol.yml— the card's change:on:drops theschedule:block (37 1,7,13,19 * * *) and its cadence comment.workflow_dispatch: {}and the wholepull_request:paths trigger are kept byte-identical, so a PR editing the patrol still proves it on a real runner.check-half-states --probereports PREREQUISITE NOT MET in a container that reaches the API — Node's fetch bypasses the agent proxy, and the refusal reads as the container's verdict #13544 correction paragraph, theSweptheartbeat paragraph, the newon:block prose, theconcurrencynote, the anchor-issue fallback note, and the citation census's HOW OFTEN / WHAT IT COSTS note..github/actions/half-state-patrol/action.ymlandscripts/pm/check-half-states.mjs— 0 lines of its 36,717.2.
scripts/pm/dispatch-gates.mjs— a forced repair, outside the card's named surface, reported rather than smuggled:Two cases of that tool's self-test read the live
.github/workflowstree and assert that the specimen workflow declaresschedule. Retiring the schedule makes both false. Measured before touching them, on the workflow-only commit:✗ dispatch-gates self-test: 2 of 1883 case(s) failed, and the two are exactly those.pnpm check:pm-dispatch-gatesis a required-context gate (Lint & Repo Gates) andscripts/ci/select-gate-families.shruns thepm_dispatch_gatesfamily for a modified path of classworkflow(its*)arm returns 0), so the red is this PR's, on this PR.The repair is the minimum that makes each case true again, and each keeps the fact it was always about:
'schedule'drops out of the trigger list one case asserts (it still pins that the specimen is not "a workflow no PR runs"), and&& isScheduled('half-state-patrol.yml')drops out of the other (it still pins that the live sweep is withheld by the value-bearing class and nothing else). ⛔ Nothing is added: pinning the ABSENCE of a schedule would be a new ratchet, which ruling #208's R6 does not let this file grow without the maintainer's sentence. A comment above the pair records the ruling so the next reader does not restore the pin.If the director seat would rather this file were not touched at all, the alternative is to land the sibling card first or to leave the PR red by design — flagged in the report's
open_questions, not decided here.No step, permission, job, timeout or input of the workflow changed. Parsed after the edit:
Census — every other path into the patrol, file by file
Population:
git grep -ni 'half.state' -- .github= 15 files at 2cac363. Dropping comment lines leaves four non-comment references outside the patrol's own two files:and exactly one
uses:of the action anywhere in the tree (half-state-patrol.yml:236, the local path)..github/workflows/half-state-patrol.ymluses: ./.github/actions/half-state-patrol37 1,7,13,19 * * *workflow_dispatch+pull_requeststay.github/actions/half-state-patrol/action.yml.github/workflows/board-snapshot.ymlscripts/pm/board-snapshot.mjs; its one non-comment reference is apull_request: paths:entry, because the archiver imports the sweeper's proxy-re-exec plan. Its other three hits are prose (the request budget it shares, and the cron offset it chose against the patrol).7 2,8,14,20 * * *— its own archiver cadence.github/workflows/lint.ymlpnpm check:pm-half-statesis the sweeper's OFFLINE--self-test(no network, no token, no board read, no anchor write), which lint.yml's own comment separates from the live sweep at length.github/workflows/test-nightly-tiers.yml//comment citing where a constant comes from.github/workflows/checklist-status.yml,.github/workflows/merged-branch-reaper.ymlhalf.state; they were hits on a wider grep only for their own:37cron minute37 3 * * 1,37 4 * * 1)closing-target-claim-guard.yml,cross-repo-issue-closer.yml,cut-rc.yml,merge-queue-triage.yml,platform-checklist-watchdog.yml,release-coverage-patrol.yml,required-set-patrol.yml,scheduled-full-run-card.yml,shard-timings-refresh.yml,stale.ymlcut-rc.ymlis a false positive: "the doc half states below")So: one scheduled invocation of the patrol existed, and it is the one this PR retires. No other workflow loses a call or a schedule.
H57 — the card's expectation, measured and corrected
The card asks this PR to record H57 (「the workflow's latest scheduled run is green and fresh」) as reading the retired schedule as stale on every on-demand run. Measured against
scripts/pm/check-half-states.mjsat 2cac363, that is not what happens:h57Population()holds a workflow out of H57's population when it declares any ofH57_PR_GATING_EVENTS=pull_request/pull_request_target/merge_group.half-state-patrol.ymldeclarespull_requestand already did before this PR, so H57 has never judged it (kind: 'pr-gating') and could not report it stale.kind: 'not-scheduled'. The only H57-visible change is in its unconditional population clause: one fewer workflow "declares a schedule", one fewer held out as PR-gating. No row is filed either way.The reading that IS stale, and is deliberately left unrepaired here:
renderCadenceExpectation()stampsexpected every 6h (...) · next by …besideSweptin the anchor body on every run, computed from the sweeper's ownPATROL_CRON/PATROL_CADENCE_HOURSconstants — which this PR may not touch (「No line of the 36,717 is edited」). So the next on-demand run publishes a deadline against a schedule nobody fires. The workflow header now says so in place, and the one-line repair is reported as an out-of-scope finding rather than done here.Acceptance
The card states the control as
= 1; measured onorigin/mainthe same grep answers 2 (the trigger key plus the citation census's cadence note), and this PR takes it to 3 because two rewritten comments name the dispatch path in prose. The load-bearing readings are the ones that cannot be moved by prose: the trigger key appears exactly once (line 174), and the parsedon:mapping above containsworkflow_dispatchand noschedule.Gates
Every family
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives for this diff was run, each exit code captured before any pipe (cmd > log 2>&1; code=$?), then reconciled against a fresh derivation:47 of the 48 exited 0 in one detached battery. The 48th is
pnpm check:pm-dispatch-gates— the one its own header says to run detached — and it is also the reverse-verification pair for the repair above, both legs measured from committed states:Also run, as the dispatch asks:
actionlintis not wired in this repo (0 hits over.github/workflows/*.ymlandpackage.json); the workflow lint here is thecheck:workflow-status-functions/check:workflow-step-name-quoting/check:node-version/check:required-contexts/check:ci-filter-parity/check:self-test-wired/check:stall-guard-*families, all inside the 48 and all green.Stated rather than implied: the 54 artifact-roster families, the 11 wide-population families, the 14 pending-changeset families and the always-runs tail that the same tool prints OUTSIDE the derived 48 were not run here — CI owns those. The branch carries a merge of
mainat 48c39e0 and CI judges the merged generation.Tier
Tier S in the card's words — measured on the FINAL file list, the register does not hold either path:
skip-changeset: one.github/workflows/**file and onescripts/pm/**file. Nothing underpackages/**, nothing in any package'sfiles[], nothing published.Acceptance notes
Noted, not filed — stale cadence prose this PR's change makes inaccurate in files it does not own:
.github/workflows/lint.yml:4742("runs REPORT-ONLY inhalf-state-patrol.yml, four times a day"). Successor: the sibling card's dev, already in flight in that file..github/workflows/board-snapshot.yml:38,140and.github/workflows/release-coverage-patrol.yml:102,113— both cite the patrol's retired cron as the reason their own minute/hour was chosen. The offsets they bought stay correct; only the reason is now historical. Successor: none scheduled.scripts/pm/check-half-states.mjsheader prose ("a scheduled caller", four runs a day) — under the same ⛔ as its constants; not touched.Generated by Claude Code