labels-reconcile.sh decide_state() — blocked excludes state:needs-human, falling to state:addressing
#180
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#180
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Minted by the armed default on discussion 122 — A at 2026-07-25T09:00Z if no ruling — which fired with no ruling posted; the decision record is on that thread. During the ceremony#111 freeze, rig#126 and rig#128 carried
blockedandstate:needs-humanat once: each round had genuinely finished, so the state label was true about the round and false about the merge — and rig#126 was merged seven minutes after the reconciler wrote the green label.The defect:
decide_state()joins the two axes only throughblockers()(L370–L379), andblockers()emits branch facts only — conflict, ci-red, unrequested (L322–L362). The hand-setblockedlabel is never consulted on this axis, so a frozen PR whose round completes lands onstate:needs-humanand lies about the one thing that label promises (LABELS.md L22: "this PR could be merged right now"). This is a decide-time omission, not a missing input — the same script already reads the label for the staleness skip at L672.Spec
Decisions, made:
blockedbecomes the second exclusion onstate:needs-human, exactly parallel toneeds-ruling(L381–L393): whenround_state()saysstate:needs-humanandhas_label blocked,decide_state()returnsstate:addressing. One condition; nothing else in the function moves.blockers()emission.BLOCKERSis machine-owned and the converge loop strips every entry the current facts do not re-derive;blockedis hand-set intent (triage sets it; anyone may correct it), so emitting it there would strip a live hold on the next 15-minute tick — the same trap #51 names forneeds-ruling.state:addressingis the accepted imprecision: under a directed hold the builder owes nothing here, but "a human could merge this now" is the promise that must not lie. A dedicated fifth state (option B on the discussion) stays a separate question — do not mint it in this PR.blockedrow (L68): the reconciler refusesstate:needs-humanwhileblockedstands — the PR falls tostate:addressing.Tasks
decide_state(): add theblockedexclusion beside theneeds-rulingone, with the not-a-blocker rationale in its commentblockedrowtest/labels-reconcile.test.sh: mirror theneeds-rulingblock (L529–L553) forblocked— exclusion, not-a-latch, draft, and blockers()-ignorance casesAcceptance criteria
MERGEABLE=MERGEABLE,CHECKS=SUCCESS,LABELS="blocked"→decide_state()returnsstate:addressing; the same fixture withLABELS=""returnsstate:needs-human(control)blockers()output is byte-identical with and withoutblockedblockedstill decidesstate:buildingTest plan
test/run.shgreen. The cases that must fail: the exclusion test with the condition deleted; the blockers()-ignorance test ifblockedis ever emitted as ablocker:*.Dependencies
None —
ready. Blocks nothing on the board. Ruling record: discussion 122.Claiming — starting now. Plan: add the
blockedexclusion indecide_state()beside theneeds-rulingone, the one LABELS.md sentence, mirror the needs-ruling test block forblocked(exclusion / not-a-latch / draft / blockers()-ignorance), and the changelog fragment. Draft PR shortly.