@@ -8,7 +8,13 @@ name: Half-State Patrol
88# NOT repo-agnostic — when it runs, what it may touch, the board's own anchor,
99# and the one step that writes to this repo's cards.
1010#
11- # ## Why a workflow, and not "a seat should run it"
11+ # ## Why a workflow at all — the transport, not the calendar
12+ #
13+ # ⚠️ HALF SUPERSEDED (ruling #208 on objectstack#19491, executed by
14+ # objectstack#19497): this file declares NO schedule. The argument below is kept
15+ # as the history that put the patrol on a runner, not as a promise that anything
16+ # fires it. What survives is the transport reason in the next paragraph; what the
17+ # ruling retired is the calendar.
1218#
1319# The sweeper carries thirteen predicates over the dispatch protocol's
1420# label/assignee/PR invariants, and for most of its life its documented consumer
@@ -17,6 +23,10 @@ name: Half-State Patrol
1723# claims (H2) and an unenumerated backlog sat on the board. Not one predicate had
1824# fired. A healing mechanism with no scheduled caller heals only in the
1925# counterfactual, and an alarm added to a script nobody runs is still silence.
26+ # That reading bought the schedule in #9844 and was true of its day. Ruling #208
27+ # re-measured the same instrument and found the standing caller charging more
28+ # than it healed — see the `on:` block below, which carries the reading and the
29+ # ⛔ that goes with it.
2030#
2131# "Some seat should run it" also kept not happening for a MEASURED reason, not a
2232# discipline one: in every container class measured at the time, the live sweep
@@ -37,12 +47,13 @@ name: Half-State Patrol
3747# `HTTPS_PROXY`, so the sweeper sent the proxy's placeholder token straight to
3848# GitHub, earned a 401, and reported that refusal as the container's verdict.
3949#
40- # ⛔ That does NOT retire this workflow, and the fix deliberately did not touch
41- # it. The #9844 reason stands on its own and is not a transport reason: an alarm
42- # whose only caller is "a seat should remember" is silence, whoever CAN run it.
43- # The on-demand path is restored BESIDE the schedule — a lane that needs the
44- # board read right now (the 4x/day body trims its own rows, and says so) can now
45- # get it — never instead of the schedule.
50+ # ⛔ That did NOT retire this workflow either, and the fix deliberately did not
51+ # touch it: a correction about WHO CAN run the sweep is not a ruling about
52+ # WHETHER it should run by the clock, and this file stayed exactly as it was.
53+ # What the correction bought is the path this workflow now runs on ALONE — a lane
54+ # that needs the board read right now (the body trims its own rows, and says so)
55+ # gets it from its own container or from a `workflow_dispatch` here. Since ruling
56+ # #208 that on-demand read is the only read there is.
4657#
4758# ## What lands where
4859#
@@ -52,11 +63,21 @@ name: Half-State Patrol
5263# archive this needs. The body is owned end-to-end by the generator, so no run can
5364# leave half of it stale.
5465#
55- # The `Swept` timestamp in that body is the patrol's heartbeat and is deliberately
56- # refreshed even when the findings are unchanged: a timestamp that stops advancing
57- # is how a reader learns the standing caller died. That is the whole defect class
58- # this workflow exists to close, so the run must not "optimize away" the no-op
59- # edit that proves it is alive.
66+ # The `Swept` timestamp in that body is refreshed even when the findings are
67+ # unchanged, and the run must still not "optimize away" that no-op edit — but
68+ # read it for what it now is. With no schedule behind it, the stamp dates the
69+ # last time a seat ASKED for a reconciliation; it is no longer a heartbeat, and a
70+ # `Swept` that stopped advancing means nobody has dispatched the patrol since,
71+ # ⛔ not that a standing caller died.
72+ #
73+ # ⚠️ KNOWN STALE READING, deliberately not repaired here (objectstack#19497): the
74+ # generator stamps a cadence deadline beside `Swept` from its own `PATROL_CRON` /
75+ # `PATROL_CADENCE_HOURS` constants, so the anchor body will keep stamping an
76+ # "expected every 6h · next by …" deadline computed from the schedule string
77+ # those constants still hold — a deadline against a schedule this file no longer
78+ # declares, and nothing on this board fires. The ruling forbids editing the
79+ # sweeper for it (⛔ not one of its 36,717 lines); the repair is filed as its own
80+ # finding instead.
6081#
6182# ## Report-only, and the one thing that is NOT report-only
6283#
@@ -119,23 +140,39 @@ name: Half-State Patrol
119140# a healthy block.
120141
121142on :
122- schedule :
123- # Four times a day, six hours apart, at :37 past the hour.
124- #
125- # The minute is offset ON PURPOSE. The triage Routine that heals these same
126- # states fires hourly near the top of the hour, and a patrol landing at the
127- # same minute would keep reading the board mid-heal — reporting half-states
128- # the healer is in the middle of pairing, i.e. manufacturing findings that
129- # clear themselves. :37 puts this sweep in the quiet part of the healer's
130- # cycle in both directions. Four runs/day rather than hourly: H13's own
131- # threshold is 2h and the incident it comes from sat ~26h, so six-hourly
132- # detection is two orders of magnitude better than the status quo (never)
133- # while staying cheap on the core quota this sweep shares with the loop's
134- # hot path.
135- #
136- # The cadence is per-board and stays in the caller: a sibling with a
137- # different healer cycle picks its own minute here, not in the action.
138- - cron : ' 37 1,7,13,19 * * *'
143+ # ⛔ ON DEMAND ONLY — this workflow declares NO schedule, and re-adding one
144+ # takes the maintainer's sentence, not a reading of the history above.
145+ # Ruling #208 on objectstack#19491, executed by objectstack#19497, maintainer
146+ # 「19491 接受你的建议,并立刻派发处理相关任务。」 The triage seat dispatches this
147+ # workflow when it wants the board reconciled; nothing fires it by the clock.
148+ #
149+ # WHAT THE RETIRED SCHEDULE WAS: `37 1,7,13,19 * * *` — four runs a day, six
150+ # hours apart, the minute deliberately off the top of the hour so the sweep
151+ # never read the board mid-heal while the hourly triage Routine was pairing
152+ # states, and four runs rather than hourly because H13's own threshold is 2h.
153+ # None of that was what the ruling found wrong.
154+ #
155+ # WHY IT WENT: the patrol is REPORT-ONLY (see the section above) while the
156+ # dispatch charter made its anchor rows a precondition for opening a dispatch.
157+ # A row misreading a now-legal state therefore cost every executive seat a
158+ # handling step — four times a day, until a dev repaired the row — and the
159+ # sweeper was taking eight such repairs a day in the fortnight before the
160+ # ruling. On the maintainer's question 「巡查仪留着却不继续开发,会不会反而不停的报
161+ # 错误的信息」 the answer measured yes. The ruling stops the clock, ⛔ not the
162+ # instrument: the script keeps every one of its 36,717 lines and stays runnable
163+ # from here, and the charter's precondition is deleted on the sibling card.
164+ #
165+ # ⛔ Do not re-add a `schedule:` block on the argument the history above makes
166+ # ("an alarm nobody runs is silence"). That argument is answered, not
167+ # forgotten: it was weighed against what the standing caller was charging every
168+ # seat, and it lost. If a month passes with nobody dispatching this, the ruling
169+ # is that the sweeper is deleted whole on its own card — ⛔ never quietly
170+ # re-scheduled.
171+ #
172+ # The cadence was always per-board and stays in the caller: a sibling repo that
173+ # wants a scheduled sweep of ITS board declares `schedule:` in its own copy of
174+ # this caller and picks its own minute against its own healer — never in the
175+ # action, and never by reading a cadence out of this file.
139176 workflow_dispatch : {}
140177 # Changes to the patrol itself get exercised before they merge — the same
141178 # posture as engine-split-metric.yml. On a pull_request run the sweep still
@@ -191,9 +228,10 @@ permissions:
191228 issues : write
192229 pull-requests : read
193230
194- # One patrol at a time. A scheduled run overlapping a manual dispatch would have
195- # two runs racing to rewrite the same body, and the loser's findings would vanish
196- # with no trace but an edit-history entry.
231+ # One patrol at a time. Two dispatches in flight at once — the shape that is left
232+ # now that nothing fires by the clock — would have two runs racing to rewrite the
233+ # same body, and the loser's findings would vanish with no trace but an
234+ # edit-history entry.
197235concurrency :
198236 group : half-state-patrol
199237 cancel-in-progress : false
@@ -223,8 +261,8 @@ jobs:
223261 # No `pnpm install`: the sweeper imports only `node:` builtins
224262 # (`process`, `child_process`, `fs`, `url`), global `fetch`, and one
225263 # repo-local helper — no npm dependency, so installing the workspace here
226- # would buy nothing and would give a scheduled patrol a lockfile it could
227- # fail on.
264+ # would buy nothing and would give this patrol a lockfile it could fail
265+ # on.
228266 #
229267 # `./` — the LOCAL action, deliberately, and this is the one consumer that
230268 # should not pin a sha. A sibling pins one because it wants a reviewed
@@ -246,7 +284,7 @@ jobs:
246284 # travels: the cheapest way to adopt the patrol is still to copy this
247285 # caller, and an unguarded fallback is what would let such a copy
248286 # rewrite some unrelated card in a sibling with this board's findings,
249- # silently and four times a day . A number is only ever meaningful in
287+ # silently, on every run it is given . A number is only ever meaningful
250288 # the repo it was minted in.
251289 #
252290 # TO ROTATE (here): open a new `tracking`-labeled issue, put its
@@ -418,16 +456,17 @@ jobs:
418456 # `::warning::` carrying the site count. ⛔ NOT the anchor issue — that
419457 # body is owned end-to-end by `check-half-states.mjs`'s generator, and a
420458 # second writer is how half of a generated body goes stale.
421- # HOW OFTEN: on this workflow's schedule — four times a day, six hours
422- # apart — plus any `workflow_dispatch`, plus the `pull_request` runs the
423- # paths filter above admits.
459+ # HOW OFTEN: once per `workflow_dispatch` — this workflow has carried no
460+ # schedule since ruling #208 (objectstack#19497) — plus the
461+ # `pull_request` runs the paths filter above admits.
424462 # WHAT IT COSTS AND WHO PAYS: the census enumerates the whole board once
425463 # (159 requests on this repo at the time of writing, cursor-paginated —
426464 # the alternative is one request per distinct number). It is paid by
427465 # THIS repository's own `secrets.GITHUB_TOKEN` core quota, the same
428- # 5,000/hour this job already draws the live sweep from: ~636
429- # requests/day at four runs, under half a percent of a single hour's
430- # allowance. ⛔ No PAT, no cross-repo credential — the file's own rule.
466+ # 5,000/hour this job already draws the live sweep from: ~159 requests
467+ # per run, ~3% of a single hour's allowance, and no standing daily draw
468+ # at all now that a seat has to ask for each run. ⛔ No PAT, no
469+ # cross-repo credential — the file's own rule.
431470 #
432471 # ⛔ Gated on the repository NAME, for the reason the closed-card sweep
433472 # above states: this script is objectstack-only until a sibling has a
0 commit comments