fix: blocked excludes state:needs-human in decide_state()
#181
No reviewers
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#181
Loading…
Reference in a new issue
No description provided.
Delete branch "build/180-blocked-excludes-nh"
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?
Closes #180
During the ceremony#111 freeze, rig#126 and rig#128 carried
blockedandstate:needs-humanat once — the round had finished, but the hold said the merge must not happen, and rig#126 was merged seven minutes after the reconciler wrote the green label.decide_state()joined the two axes only throughblockers(), which emits branch facts; the hand-setblockedlabel was never consulted on this axis.blockedbecomes the second exclusion onstate:needs-human, exactly parallel toneeds-ruling: whenround_state()saysstate:needs-humanandhas_label blocked, the PR falls tostate:addressing. Deliberately not ablockers()emission —BLOCKERSis machine-owned and the converge loop strips every entry the current facts do not re-derive, so emitting the hand-set label there would strip a live hold on the next 15-minute tick (the trap #51 names forneeds-ruling). Ruling record: discussion #122 (armed default A fired).Acceptance criteria
MERGEABLE=MERGEABLE,CHECKS=SUCCESS,LABELS="blocked"→decide_state()returnsstate:addressing; same fixture withLABELS=""returnsstate:needs-human(control)blockers()output is byte-identical with and withoutblockedblockedstill decidesstate:buildinga directed hold excludes needs-human, 155/1)Worklog
decide_state():blockedexclusion beside theneeds-rulingone, with the not-a-blocker rationale in its commentblockedrowtest/labels-reconcile.test.sh: mirror theneeds-rulingblock forblocked— exclusion, not-a-latch, draft, blockers()-ignorancetest/run.shgreen (18 files, 156 labels-reconcile cases), CI-style shellcheck clean🔎 reviewing head
1c9a82aaf0🔎 reviewing head
1c9a82aaf0Verdict: approve
Implements #180 exactly as specified:
blockedis a second exclusion onstate:needs-human(parallel toneeds-ruling), falling tostate:addressing, and is deliberately not ablockers()emission.Acceptance criteria
LABELS="blocked"→state:addressing; controlLABELS=""→state:needs-humanblockers()byte-identical with/withoutblockedblockedstillstate:building155 passed, 1 failed(a directed hold excludes needs-human)What I verified
decide_state()adds the exclusion immediately after theneeds-rulingblock with the #51 trap rationale (hand-set intent must not land in machine-ownedBLOCKERSor the converge loop strips a live hold). Placement and shape match the established exclusion pattern; nothing else in the function moves.needs-rulingfixtures (control, exclusion, clear-again, draft,blockers()-ignorance). Fulltest/run.sh: 18 files, 0 failed;labels-reconcile156/0.labels-reconcile.sh.1c9a82aa:test,self-guards,action-exercise,labels / reconcileall success.Could not verify / not blocking
Nothing blocking. Panel membership: on-panel (author is
claude-bot-andresmgsl; I am required, not advisory).No nits that would change the merge recommendation. Ship it.
The change satisfies #180:
blockedis a decide-time exclusion fromstate:needs-human, falls back tostate:addressing, and remains outside the machine-ownedblockers()set. The control, clearing, draft, and blockers-ignorance cases cover the required behavior, and LABELS.md plus the changelog fragment accurately document it.Verified at
1c9a82aaf066b75b5d8a4fa82951515debc8e4fe:test/run.sh: 18 test files passed;labels-reconcile156/0.No blocking findings.
🔎 reviewing head
1c9a82aaf0Approve at
1c9a82aaf0.Reviewed the whole PR against #180's spec and acceptance criteria in a detached worktree. The change is exactly the sanctioned shape: one exclusion beside the
needs-rulingone, deliberately not ablockers()emission, with the #111-freeze incident and the #51 strip-trap in the comment — the convention this repo requires.What I ran:
test/labels-reconcile.test.sh: 156/0. Fulltest/run.sh: everything green except thepackage-jsonversion-source cases, which requirenode— absent on this box (version3,changelog-armed2,drill-recorded2,facts2; same known gap as always).jqis also absent here; I ran with a static jq 1.7.1 in an isolated/tmpdir. The node-covered cases are exercised by CI, whosetestjob is green on this head — relied on CI for those.self-guards(success).has_label blockedcondition and re-ran — exactly one test goes red (a directed hold excludes needs-human, 155/1), matching the PR's claim. Restored the file afterward.Acceptance criteria, each verified in the test block at test/labels-reconcile.test.sh:556-582:
state:addressing, hold-free control →state:needs-human✓blockers()byte-identical with and withoutblocked(conflict fixture emits onlyblocker:conflict) ✓blocked→state:building(draft check precedes the exclusion at labels-reconcile.sh:365) ✓blockedrow; the diff introduces no new label, no bootstrap row ✓changelog.d/180.mdfragment present ✓Also checked
has_label(labels-reconcile.sh:535):grep -qxF— whole-line exact match, so a label likeblocker:ci-redorblocked-somethingcan never trip the exclusion.No findings, blocking or otherwise. The
state:addressingimprecision under a hold is the spec's own accepted trade-off (#180, discussion #122 default A), not a review-round question.Round summary — handing off
What shipped.
decide_state()now treats a hand-setblockedlabel as a second exclusion onstate:needs-human, exactly parallel to theneeds-rulingone: a PR whose round has passed but which carriesblockeddecidesstate:addressinginstead of handing off. This closes the ceremony#111-freeze gap where rig#126/rig#128 carriedblockedandstate:needs-humanat once and rig#126 was merged seven minutes after the reconciler wrote the green label. The exclusion is deliberately not ablockers()emission —BLOCKERSis machine-owned and the converge loop would strip a hand-set hold on the next 15-minute tick (the trap #51 documents forneeds-ruling); the rationale lives in the code comment. Ruling record: discussion #122 (armed default A fired).Rounds. One round, one commit (
1c9a82a). The full panel — codex, grok, kimi — approved the head on the first pass with no changes requested, so no revisions were made between rounds.What was verified.
LABELS="blocked"→state:addressing), control hands off (LABELS=""→state:needs-human), clearing the label re-hands-off (exclusion, not a latch),blockers()output byte-identical with and withoutblocked, a draft carryingblockedstill decidesstate:building, and mutation coverage (disabling the condition failsa directed hold excludes needs-human, 155/1).test/run.shgreen locally: 18 files, 156 labels-reconcile cases; shellcheck clean.1c9a82a: test, self-guards, action-exercise, docs-sync-exercise, release-exercise (fixture-chain, step-replay consumer + dogfood), labels/reconcile.Post-merge residue. None. No new label is introduced (the exclusion reads the existing PR-side
blockedrow, and LABELS.md carries the sentence), no migration or fixture backfill is owed, andchangelog.d/180.mdships with the PR for the release assembly. The only remaining move is the merge.