Commit d76facf
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:
> 「19491 接受你的建议,并立刻派发处理相关任务。」
R3 of that ruling, verbatim:
> **R3** the half-state patrol: ⛔ not frozen, ⛔ not trimmed; its
**schedule is retired** (workflow_dispatch only, on demand for the
triage seat's daily reconciliation; **#19497**) and the charter's 「锚行未处置
⛔ 不开新派发」 precondition is deleted (#19496). No line of the 36,717 is
edited.
and the maintainer's question it answers: 「巡查仪留着却不继续开发,会不会反而不停的报错误的信息」
## What changed — two files, +89 / −45
**1. `.github/workflows/half-state-patrol.yml`** — the card's change:
1. `on:` drops the `schedule:` block (`37 1,7,13,19 * * *`) and its
cadence comment. `workflow_dispatch: {}` and the whole `pull_request:`
paths trigger are kept byte-identical, so a PR editing the patrol still
proves it on a real runner.
2. The header prose that promised the cadence is rewritten, in seven
places, to describe on-demand use and to carry the ⛔ against
re-scheduling it on the old argument: the 「why a workflow」 section (kept
as history, marked half-superseded), the #13544 correction paragraph,
the `Swept` heartbeat paragraph, the new `on:` block prose, the
`concurrency` note, the anchor-issue fallback note, and the citation
census's HOW OFTEN / WHAT IT COSTS note.
3. ⛔ Not touched, as ruled:
`.github/actions/half-state-patrol/action.yml` and
`scripts/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/workflows`
tree and assert that the specimen workflow declares `schedule`. 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-gates`
is a required-context gate (`Lint & Repo Gates`) and
`scripts/ci/select-gate-families.sh` runs the `pm_dispatch_gates` family
for a modified path of class `workflow` (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:
```
top keys: ['name', True, 'permissions', 'concurrency', 'jobs']
on: {'workflow_dispatch': {}, 'pull_request': {'paths': ['scripts/pm/check-half-states.mjs', 'scripts/invoked-as.mjs', 'scripts/pm/sweep-closed-cards.mjs', 'scripts/check-issue-citations.mjs', '.github/actions/half-state-patrol/**', '.github/workflows/half-state-patrol.yml']}}
jobs: ['patrol']
steps: ['Checkout repository', 'Setup Node.js', 'Sweep the board and update the anchor', 'Sweep the closed cards', "Census the repo's issue citations"]
```
## 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:
```
.github/workflows/board-snapshot.yml:176: - 'scripts/pm/check-half-states.mjs'
.github/workflows/lint.yml:1189: - name: PM half-state sweeper self-test
.github/workflows/lint.yml:1190: run: pnpm check:pm-half-states
.github/workflows/test-nightly-tiers.yml:442: // scripts/pm/check-half-states.mjs. ⛔ Do not re-derive 65,536 from
```
and exactly one `uses:` of the action anywhere in the tree
(`half-state-patrol.yml:236`, the local path).
| file | runs the patrol? | scheduled? | verdict |
|---|---|---|---|
| `.github/workflows/half-state-patrol.yml` | **yes** — `uses:
./.github/actions/half-state-patrol` | was `37 1,7,13,19 * * *` |
**schedule removed here**; the call stays, `workflow_dispatch` +
`pull_request` stay |
| `.github/actions/half-state-patrol/action.yml` | it *is* the patrol
body | composite action — declares no trigger of its own | untouched by
ruling |
| `.github/workflows/board-snapshot.yml` | **no** — it runs
`scripts/pm/board-snapshot.mjs`; its one non-comment reference is a
`pull_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). | yes, `7
2,8,14,20 * * *` — its own archiver cadence | **keeps its schedule and
its call**: it is not a path into the patrol |
| `.github/workflows/lint.yml` | **no** — `pnpm check:pm-half-states` is
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 | yes (hourly full run) | **keeps both**; the
self-test's PR-time scoping is the sibling card's, not this one's |
| `.github/workflows/test-nightly-tiers.yml` | **no** — one `//` comment
citing where a constant comes from | yes | untouched |
| `.github/workflows/checklist-status.yml`,
`.github/workflows/merged-branch-reaper.yml` | **no** — 0 hits for
`half.state`; they were hits on a wider grep only for their own `:37`
cron minute | yes (`37 3 * * 1`, `37 4 * * 1`) | untouched |
| `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.yml` | **no** — comment text only
(`cut-rc.yml` is a false positive: "the doc half states below") | varies
| untouched |
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.mjs`
at 2cac363, that is not what happens:
- `h57Population()` holds a workflow **out** of H57's population when it
declares any of `H57_PR_GATING_EVENTS` = `pull_request` /
`pull_request_target` / `merge_group`. `half-state-patrol.yml` declares
`pull_request` and already did before this PR, so H57 has never judged
it (`kind: 'pr-gating'`) and could not report it stale.
- After this PR the same workflow is held out one branch earlier, as
`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()` stamps `expected every 6h (...) · next by
…` beside `Swept` in the anchor body on **every** run, computed from the
sweeper's own `PATROL_CRON` / `PATROL_CADENCE_HOURS` constants — 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
```
$ git grep -n 'cron' .github/workflows/half-state-patrol.yml ; echo "exit=$?"
exit=1 # 0 hits (was 1 line on 2cac363)
$ git grep -c 'workflow_dispatch' .github/workflows/half-state-patrol.yml
3 # was 2 on 2cac363
```
The card states the control as `= 1`; measured on `origin/main` the 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 parsed `on:` mapping above contains `workflow_dispatch`
and no `schedule`.
## Gates
Every family `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derives for this diff was run, each exit
code captured before any pipe (`cmd > log 2>&1; code=$?`), then
reconciled against a fresh derivation:
```
$ node scripts/pm/dispatch-gates.mjs --ran RANFILE --repo objectstack-ai/objectstack
Run reconciliation — 48 derived, 48 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 48 derived famil(ies) accounted for — 48 run, 0 NOT-MEASURED
(a DERIVED zero — all 48 recorded an exit code and none of them is 3).
```
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:
```
before (51a65a3, the workflow-only commit):
✗ dispatch-gates self-test: 2 of 1883 case(s) failed.
✗ ⭐ the card's own specimen declares a pull_request trigger beside its schedule — …
✗ the card's specimen is still discovered … — not withheld for being scheduled
after (dbeb16d, with the two cases re-pointed):
✓ dispatch-gates self-test: 1883 cases pass.
check:pm-dispatch-gates: the battery took 1031.6s on this box. :: exit 0
```
Also run, as the dispatch asks:
```
$ bash scripts/ci/select-gate-families.selftest.sh :: exit 0
all 40 cases passed (192 checks)
```
`actionlint` is not wired in this repo (0 hits over
`.github/workflows/*.yml` and `package.json`); the workflow lint here is
the `check: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 `main` at
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:
```
$ node scripts/pm/check-governed-merges.mjs --branch claude/issue-19497-patrol-schedule-retired
derived from `git diff --name-only --no-renames 48c39e0 dbeb16d` (three-dot): 2 path(s).
governed-surface predicate: 0 of 2 path(s) hit the register (6 surfaces, repo-agnostic).
✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list.
size: 134 changed line(s) (+89 / −45) ≤ 5000 — under the human-merge threshold
```
`skip-changeset`: one `.github/workflows/**` file and one
`scripts/pm/**` file. Nothing under `packages/**`, nothing in any
package's `files[]`, 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 in
`half-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,140` and
`.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.mjs` header prose ("a scheduled caller",
four runs a day) — under the same ⛔ as its constants; not touched.
---
_Generated by [Claude
Code](https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent c736eaa commit d76facf
2 files changed
Lines changed: 89 additions & 45 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
12 | 18 | | |
13 | 19 | | |
14 | 20 | | |
| |||
17 | 23 | | |
18 | 24 | | |
19 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
20 | 30 | | |
21 | 31 | | |
22 | 32 | | |
| |||
37 | 47 | | |
38 | 48 | | |
39 | 49 | | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
46 | 57 | | |
47 | 58 | | |
48 | 59 | | |
| |||
52 | 63 | | |
53 | 64 | | |
54 | 65 | | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
60 | 81 | | |
61 | 82 | | |
62 | 83 | | |
| |||
119 | 140 | | |
120 | 141 | | |
121 | 142 | | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
139 | 176 | | |
140 | 177 | | |
141 | 178 | | |
| |||
191 | 228 | | |
192 | 229 | | |
193 | 230 | | |
194 | | - | |
195 | | - | |
196 | | - | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
197 | 235 | | |
198 | 236 | | |
199 | 237 | | |
| |||
223 | 261 | | |
224 | 262 | | |
225 | 263 | | |
226 | | - | |
227 | | - | |
| 264 | + | |
| 265 | + | |
228 | 266 | | |
229 | 267 | | |
230 | 268 | | |
| |||
246 | 284 | | |
247 | 285 | | |
248 | 286 | | |
249 | | - | |
| 287 | + | |
250 | 288 | | |
251 | 289 | | |
252 | 290 | | |
| |||
418 | 456 | | |
419 | 457 | | |
420 | 458 | | |
421 | | - | |
422 | | - | |
423 | | - | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
424 | 462 | | |
425 | 463 | | |
426 | 464 | | |
427 | 465 | | |
428 | | - | |
429 | | - | |
430 | | - | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
431 | 470 | | |
432 | 471 | | |
433 | 472 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20667 | 20667 | | |
20668 | 20668 | | |
20669 | 20669 | | |
| 20670 | + | |
| 20671 | + | |
| 20672 | + | |
| 20673 | + | |
| 20674 | + | |
| 20675 | + | |
20670 | 20676 | | |
20671 | | - | |
20672 | | - | |
| 20677 | + | |
| 20678 | + | |
20673 | 20679 | | |
20674 | 20680 | | |
20675 | 20681 | | |
| |||
20722 | 20728 | | |
20723 | 20729 | | |
20724 | 20730 | | |
20725 | | - | |
| 20731 | + | |
20726 | 20732 | | |
20727 | 20733 | | |
20728 | | - | |
20729 | 20734 | | |
20730 | 20735 | | |
20731 | 20736 | | |
| |||
0 commit comments