labels: sweep on labeled so the handoff is immediate, and let the author set state:needs-human
#96
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#96
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-humancan lag hours behind the round it describes. The fix is one line inlabels.ymlplus a change to who writes that one label.What happens today
The labels workflow triggers on
schedule(*/15),workflow_dispatch, andpull_request_target(opened, reopened, ready_for_review, converted_to_draft, synchronize).A review landing is not in that list. So the exact moment
state:needs-humanbecomes true — the third bot approving — fires nothing. The label waits for the cron, or for someone to touch an unrelated PR.And the cron does not run at the declared rate. Measured across all three repos over a two-hour window on 2026-07-20: one scheduled run each, against the eight
*/15implies. GitHub's Actions scheduler heavily deprioritises short intervals.Observed live: heavy-duty/rig#94 took its third approval at
18:55:46and sat atstate:bots-reviewingfor hours. box and cast happened to catch a scheduled run at19:03and flipped correctly. Same code, same workflow file (labels.ymlis byte-identical across the three) — the only difference was which repo got a cron tick.This is worst on the quietest repo. Every sweep reconciles the whole board, so an active repo stays fresh by piggybacking on unrelated PR events. rig has the fewest open PRs, so it depends on the cron most and receives it least — and it is the repo where a wrong label is least likely to be noticed.
Why the obvious fix does not work
Adding
pull_request_reviewto the triggers looks right and is not. On PRs from forks — which is every PR here — that event givesGITHUB_TOKENread-only permissions, and it ignores thepermissions:block if you try to raise them. There is nopull_request_review_target. The workflow's own header comment already says this; it is correct.Refs: events that trigger workflows, community #26651, community #55940.
Proposed fix
1. Trigger on label changes.
pull_request_targetsupportslabeledandunlabeled:2. The PR author sets
state:needs-humanat handoff. The handoff becomes three acts in order: post the tagged summary → request the human's review → setstate:needs-human, removing the previous state label.The two combine into something better than either alone: the agent's own label write fires the sweep that validates it. Seconds later the reconciler either confirms the label or corrects it. It is an optimistic write, not a transfer of ownership — the machine stays the authority, and the cron becomes a genuine last resort for when an agent forgets, rather than the primary path.
dan-claude-bottriage can apply an existing label (verified); only label creation 403s, so this needs no permission change.This cannot loop. The reconciler's own label writes use
GITHUB_TOKEN, and GitHub does not create workflow runs fromGITHUB_TOKEN-triggered events. Agent writes use a PAT and therefore do trigger — exactly the asymmetry wanted.Considered and rejected: a new marker label
The alternative was a
work-done/handofflabel that the agent sets and the workflow converts intostate:needs-human.It does not earn its place. The reconciler already derives
needs-humanfrom three head-current approvals, no blockers, and the human being requested — a marker label tells it nothing it does not compute. Its only real function would be as a trigger, whichlabeledalready provides for any label. That makes it a fourth label family to document, bootstrap and maintain, in exchange for nothing.Scope of the change
.github/workflows/labels.yml— the trigger lineCONTRIBUTING.md— the three-act handoff in step 6, and thestate:*row qualified: machine-owned, except the author setsstate:needs-humanat handoff and the workflow reconcilesLABELS.md— same qualificationNo change to
labels-reconcile.sh. The reconciler already recomputes every open PR from scratch on every run, which is exactly what makes the optimistic write safe.Should land in all three repos together —
labels.ymland the label taxonomy are shared.Aside, noticed while filing this: none of the three repos has a
scope:label covering the repo's own automation —.github/,labels.yml,LABELS.md, CI. This issue has no honest scope to carry. Possibly worth ascope:ciorscope:meta; filing unlabelled for scope rather than forcing a wrong one.