blocked is invisible to decide_state — a frozen PR reaches state:needs-human and tells the maintainer to merge it
#122
Replies: 4 comments
|
This thread was opened without the block it needs, and that is my miss. The Options 1 and 2 in the body are two mechanisms for one decision — demote a Three things the block compressesWhy A's demotion target is imperfect and I recommend it anyway. Why the mechanism inside A does not need your ruling. Body option 1 Lifting the freeze does not retract this. If the ceremony#111 hold No If the default fires I mint A, cite this thread for the reasoning, and stay |
|
Premise update, not a new outcome and not a change to the ladder. The ceremony#111 freeze is lifted as of 12:00Z — my pick, recorded So: the bug is unchanged, and the urgency has dropped. Nothing else moves. The options, the recommendation (A), and the default — |
|
Decision — the default fired. 2026-07-25T09:00Z passed with no ruling; per the ladder's past-24h rung (LABELS.md, #50 D13–D14) triage picks and stays accountable: Option A — One ladder note against myself: the 12:01Z comment above was a premise update at 2.5 hours, not the 12h re-read, and the 21:27Z rung got no comment of its own. The re-read ran now, before firing: at main ( Recorded as #180 — minted @danmt — you overturn this at the merge gate, not before it: the change lands as an ordinary PR you can decline, and if the answer was C — leave the machine — closing #180 with that one word undoes everything. Resolving this thread; the decision record is this comment plus the ladder above it. |
|
Correction to one sentence in the decision above, from the read I owed before writing it. "No open PR in ceremony, rig or incubator carries What survives: no board is displaying the lie today — |
Uh oh!
There was an error while loading. Please reload this page.
state:needs-humanmeans exactly one thing — LABELS.md says it "requires zero blockers", andmerge-nextis cleared the moment a PR is "no longer the thing a human should merge next". Right now, during the freeze, it is lying on two PRs at once.What the board is currently saying
Both of these carry
blocked, applied by triage at 08:19 under the ceremony#111 directive, and both carrystate:needs-humanwith @danmt as the requested reviewer:state:needs-humanat 08:50:49, 31 minutes after triage setblocked.In both cases the review round genuinely finished with a full panel approving the current head. The state label is a correct statement about the round and a false statement about the merge, which is the one thing the doctrine promises it is about.
Why
decide_state()is the only place the two axes join, and it demotes tostate:addressingwhenblockers()returns anything (L288–L292). Butblockers()only ever emitsblocker:conflict,blocker:ci-redandblocker:unrequested(L252–L274) — all three derived from branch facts. The cross-cuttingblockedlabel is never consulted on this axis, so it cannot demote anything.This is not a read problem. The same script already reads the label:
has_label blockedgates the staleness skip at L499, and themerge-nextclause deliberately declines to setblockedbecause it is intent. It is legible and correctly hand-owned — it is simply not consulted on the one axis where it decides whether a maintainer is being invited to merge.Why this is worth a decision now rather than filed as debt
Before the freeze,
blockedon a PR was rare enough that the gap never fired. Under the freeze it is the normal state of every open PR in five repos, and every one of them whose round completes will land onstate:needs-human— the two above did so within seventeen minutes of each other, without either being touched by a builder. The failure mode is the expensive one: a maintainer merging a PR the directive parked, on a board that told them to.The decision
The immediate correctness fix is small; what it should say is not obvious, and that is the part I do not own.
blockers()emitsblockedwhen the label is present. One line, reuses the existing demotion. Against:blockers()documents itself as branch facts computed from GitHub, and its return value is also the set written back asblocker:*labels — a non-blocker:*member would need special-casing at the write, which is exactly the kind of precedence tangle the two-axis split was minted to remove.decide_state()consultshas_label blockeddirectly, beside its existingblockers()check. Keepsblockers()purely branch-derived; one extra condition on the only function that already joins the axes. This is the cleaner mechanism — but its demotion target isstate:addressing, which means the builder owes work, and under a directive hold the builder owes nothing. ceremony#113 is currently writing the doctrine that says exactly that. So this fix stops the lie about merging by telling a smaller one about whose ball it is.Leave the machine; change the doctrine — redefine
state:needs-humanas "the panel is done" and treat merge-gating as a separate concern a human reads offblocked. Against: it retracts LABELS.md's own one-thing sentence, and needs-human going stale is the precise bug that split the axes in the first place.A fifth state. None of the four says reviewed, correct, and waiting on something that is nobody-here's ball — which is what a frozen PR with a finished round actually is. The freeze made that state common; before it, it barely existed.
My recommendation: 2 now, 4 as the question worth answering properly. The needs-human promise is the one that must not lie, and imprecision about whose ball it is costs less than a wrong merge — but if the answer is 4, doing 2 first means writing the demotion twice.
Decider: @danmt. The freeze is yours and so is the taxonomy; I have not touched the state labels on either PR (states are machine-owned, and hand-stripping one fires
unlabeled→ the reconciler recomputes it back within seconds). Both PRs now carry a triage comment sayingblockedoutranks the state label and neither is to be merged, so the hazard is papered over on those two — but it is papered over in prose, on a board whose whole value is that its labels are true without reading the comments.All reactions