labels: state:needs-human is sticky and blind to mergeability, and nothing says which PR to merge next
#87
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:bootstrap
scope:coolify
scope:db
scope:docs
scope:drill
scope:installer
scope:labels
scope:platform
scope:runner
scope:users
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/rig#87
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?
state:needs-humanis sticky and blind to mergeability, so the board says "your turn" on PRs that cannot be merged — and there is no label that answers "which one do I merge next?"The bug
decide_state()in.github/scripts/labels-reconcile.shcomputes state from exactly three inputs: the draft flag, requested reviewers, and submitted review verdicts. It reads nothing aboutmergeable,mergeStateStatus, orstatusCheckRollup.And this line short-circuits everything:
Once the maintainer is requested, the PR reads
state:needs-humanpermanently. It stays there after the branch develops a conflict, after CI goes red, after a force-push staled every approval. Nothing demotes it.Live right now in this repo: no open PRs — the batch just drained. So the evidence here is the code plus what happened while it was draining.
grep -c 'mergeable\|mergeStateStatus\|statusCheckRollup' .github/scripts/labels-reconcile.shreturns 0, and the short-circuit above sits atdecide_state()line 90 — byte-identical to box's.It bit during the 10-PR batch merged earlier today. Every time one PR merged, the rest went
CONFLICTINGthroughCHANGELOG.md— and every one of them kept itsstate:needs-humanlabel the whole time. The board invited the maintainer to merge PRs that could not be merged, repeatedly, for hours. It was diagnosed only because someone opened them one by one.The second half bit too: with six PRs simultaneously reading
state:needs-human, nothing on the board said which to merge first — and order mattered, because merging out of sequence cost a rebase of everything below it.Why this matters more than a wrong colour: the label is the only thing that makes the board scannable. A maintainer working from GitHub mobile — the realistic case, and the whole reason the label exists — has to open every PR to discover which are ready. The label does the opposite of its job.
It is the same failure shape as a stale approval: the state looks settled because nothing contradicted it.
Fix 1 — mergeability outranks the human request
A review request is an opinion about who should look next.
mergeableis a fact about the tree. Facts should win. Proposed precedence:The human short-circuit stays — it is right that an explicit request outranks the bot rounds — it just must not outrank "this branch does not merge."
Fix 2 — a state for "the agent owes a rebase"
state:addressingcurrently means the coding agent owes a reply and fixes to a review round. A conflicted branch also needs agent work, but it is a different job, triggered by a different event, and needs a different action. Folding them together loses that.Proposal:
state:needs-rebase— the branch does not merge (conflict, or a failing required check) and the agent owes a rebase or a fix. Distinct fromaddressing, which stays "owes a reply to reviewers".If you would rather not add a state, reusing
state:addressingis still strictly better than the status quo, because it at least stops claiming the maintainer is the blocker.Fix 3 —
merge-next, so the board answers the actual questionEven with the above correct,
state:needs-humanon three PRs does not say which one to merge first. Ordering matters here: these PRs conflict with each other throughCHANGELOG.mdandtest/cli.sh, so merging out of order costs a rebase every time.Proposal: a
merge-nextlabel on exactly one PR — the head of the merge queue.Unlike
state:*, this cannot be derived from GitHub's facts: queue order is intent. So it follows theblocked/releaseconvention in LABELS.md — set by a human or by the agent maintaining the queue, and never guessed by the reconciler. The reconciler's only job would be to clear it when the PR merges or stops being mergeable, so it cannot go stale the wayneeds-humandid.With all three, the mobile view becomes answerable at a glance:
merge-next+state:needs-human-> merge this onestate:needs-rebase-> agent is on it, ignorestate:bots-reviewing/state:addressing-> not your turnAcceptance
decide_state()readsmergeable/mergeStateStatusand the check rollupstate:needs-human, even with the human requestedstate:needs-rebase(or a documented decision to reusestate:addressing)merge-nextexists, is agent/human-set, and is cleared automatically when the PR merges or stops being mergeableCONFLICTINGmust NOT resolve tostate:needs-humanNote
box, rig and cast share this reconciler and this failure, byte for byte. Full write-ups filed in all three (heavy-duty/box#136) so none is a stub; whichever lands first is the reference implementation and the other two should follow it rather than diverge.
Siblings, same defect byte for byte: heavy-duty/box#136, heavy-duty/cast#127.