HUMAN_REVIEWER has no plumbing — every consumer’s needs-human handoff 404s on danmt, and the log reports it as done #276
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#276
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
labels-reconcile.shhands a completed round to a human by requesting a review from$HUMAN, andlib/ruling.shaddresses its 7-day nudge to the same identity. Both resolve it thesame way, and neither identity can be configured by a consumer: the value is
${HUMAN_REVIEWER:-danmt}, and nothing plumbsHUMAN_REVIEWERin.Read at
main85290031b271cae9288173a2fd9c29fc55616a8f(VERSION0.6.4-dev):labels-reconcile.shL39 —HUMAN="${HUMAN_REVIEWER:-danmt}"lib/ruling.shL417 —local decider="${HUMAN_REVIEWER:-danmt}"actions/labels-reconcile/action.ymldeclaresexactly one input,
bootstrap, and itsreconcile labelsstep exports onlyBOOTSTRAPand
LABELS_CONFload_configaccepts onlypanel=,panel[<login>]=andtriage-actors=rows, so.github/labels.confcannot carry the identity eitherSo on every consumer whose human is not literally
danmt, the handoff request is made against alogin that does not exist on that forge.
Measured firing, twice, on
heavy-duty/stoke.GET /api/v1/users/danmt→ 404 on thatinstance. With !44 sitting at
state:needs-human, sweeps 679 and 680 (2026-08-31T20:12:48Z and20:12:50Z, running the pinned
0.6.3callers) each logged:and each ended
labels: reconciled.with asuccessconclusion. The same pair appeared on!41 (sweeps 605/607) and has been reproducible on that board since 2026-08-21. Its consumers work
around it with a clause in every issue they mint — "when the PR reaches
state:needs-human,request
@andresby hand — do not wait for the engine" — which is a per-issue tax paid for aone-line configuration gap.
The second half is the log line. L837-838:
The
logis unconditional. It says requested after a request that returned 404, on a line nostatus check guards, in a run that then concludes
success. That is thedegraded-write-reports-success class this repository's own comment at
L905-913 says #188 exists to eliminate — and
it is the reason the defect survived nine days on a consumer board that reads every sweep log: the
log agreed with the happy path.
Spec — decisions
.github/labels.conf, not in a workflow input. Add ahuman-reviewer=<login>row to the grammar and parse it inload_configbesidetriage-actors=. Reasons, in order: it is a per-repository roster fact and belongs besidepanel=andtriage-actors=, which are the same kind of fact; it is versioned and reviewedin the consumer's own tree; and it reaches both readers at once —
lib/ruling.shis calledfrom a different path than the reconciler action, so an
action.ymlinput would fix thehandoff and leave the ruling nudge still addressing
danmt.danmtstays as the fallback when the row is absent. No consumer breaks on upgrade, andthis repository's own board keeps its current behaviour with no conf change.
human-revieweris validated like the rest of the roster — one login, thesame character class
triage-actors=enforces, duplicate row rejected.plainly when it does not: a failed handoff request is the one failure the whole
state:needs-humanpath exists to deliver, so it must not read as done. Follow #101's rule —report what was attempted and that it did not happen; do not diagnose why.
successconclusion on an undelivered handoff is exactly #188's class — but it changes everyconsumer's run status and deserves its own decision. This issue makes the identity configurable
and the log honest; whether to red the run is left to a follow-up, and this issue does not
presume the answer.
Tasks
human-reviewer=toload_config's accepted rows, with validation and thedanmtfallbackHUMANatlabels-reconcile.shL39 and intodecideratlib/ruling.shL417, so both readers take the same value from the same placelogon the request's exit status and emit an honest line on failureCONTRIBUTING.md’s labels-conf reference and indocs/CONSUMERS.md, besidepanel=andtriage-actors=failed-request log line
changelog.d/276.md— one-bullet, at most 300 characters, ending withits citation
Closesthis issueAcceptance criteria
human-reviewer=<login>in.github/labels.confgets that login onthe
requested_reviewersPOST and in the ruling nudge's@address — both, from the one rowdanmt), proven by a fixture, not byreasoning
human-reviewer=row fails config validation with a message namingthe file, the same way a malformed
panel=row doesrequested <login> (round passed); it saysthe request was attempted and did not happen
is recorded in the PR
Test plan
Fixture-level throughout, in
test/labels-reconcile.test.shand the config tests. The fourconfig cases are table-driven off a temporary
labels.conf. The log case stubsforge_request_reviewerto a non-zero exit and asserts the emitted line — that stub is the wholepoint, because the live defect is invisible when the call succeeds. For the ruling reader, assert the
rendered nudge body addresses the configured login.
Dependencies
Blocked by #275.
A collision edge and nothing else (#288). #275 was minted in the same tick as this issue
(both
2026-08-31T20:27:37Z) and is an open carrier of two files this issue also writes: it wasreadywhen this edge was owed, and has beenclaimedbycodex-bot-andresmgslsince2026-08-31T20:58:32Z, its build announced at20:59:04Z. The overlap, read from both bodies at2026-08-31T20:59Z rather than assumed:
actions/labels-reconcile/labels-reconcile.shload_configat L128, L837-838test/labels-reconcile.test.shThe regions are disjoint and neither fix depends on the other's behaviour, and that is not an
exemption: the edge is what keeps every
readyissue concurrently claimable, and #275's closereleases this one. It was owed at mint and missed there; triage corrects it here rather than
leaving a second builder to discover it in a merge conflict. When #275's pull request opens,
re-read the overlap against
pulls/N/files— a claimed carrier's real deliverable set is itsdiff, not its body.
Nothing else stands in the way: the board carried no open pull requests and no third open
issue at 2026-08-31T20:59Z.
Reported by
heavy-duty/stoketriage, whose inventory of this defect isstoke #36 defect 1 — measured
present at that repository's pinned
0.6.1, at0.6.3, and here atmain, so no consumerre-pin can answer it. Until this lands, consumers must keep the hand-request clause in every issue.
This issue's
Blocked bydeclarations parse to: {#275}That is the exact set this sweep gates on — what the machine read, never a
judgment about whether it is what you meant. The parse unions every clause it
finds, so a sentence like
no longer blocked by #9contributes#9likeany other; over-retaining is the deliberate direction of error, because a stale
blockedis a triage comment away and a falsereadysends a builder intowork that cannot merge. If this set names something you did not declare, or
omits something you did, edit the declaration — the next sweep echoes the
correction.
Comment only: nothing on this path writes a label. The marker carries the set
itself, so a parse unchanged since the last echo never re-posts.
Corrected to
blocked: this issue owed a #288 collision edge at mint and did not carry one.#275 and this issue were minted in the same tick (
2026-08-31T20:27:37Z), and both writeactions/labels-reconcile/labels-reconcile.shandtest/labels-reconcile.test.sh. The issuecontract makes the newer issue name the newest open carrier unconditionally, and says in so many
words that there is "no alternative for disjoint regions" — the edge is what keeps every
readyissue concurrently claimable and makes each close release one successor. #275 is
claimedas of2026-08-31T20:58:32Z, so this is a live overlap, not a formality.What changed here, and how each half was verified rather than assumed:
## Dependencieswent from "No blockers." to a`Blocked by #275.`declarationplus the measured file-by-file overlap. The shipped parser —
blocked_referencesinactions/issueflow-reconcile/issueflow-reconcile.sh— was run against the exact bytes before thewrite and reads this body as
{#275}with no cross-repo references.readyremoved andblockedadded,2026-08-31T21:00:30Z–21:00:31Z, confirmed fromthis issue's label events rather than from
.labels.scope:docsadded. The Tasks requireCONTRIBUTING.mdanddocs/CONSUMERS.md, and.github/labeler.ymlmaps both toscope:docs— so the pull request would have worn a scope theissue lacked. Precedent: #247, #271, #273.
changelog.d/<this issue>.mdresolved tochangelog.d/276.md, the house spelling(
changelog.d/<issue>.md, BUILDER.md).Nothing in the Context, Spec, acceptance criteria or test plan changed, and the value of this issue
is untouched. This is not a hold and it needs no ruling: the wake condition is #275's close, which
the sweep converts to
readyon its own and which triage flips by hand if the sweep is slow. When#275's pull request opens, the overlap is worth re-reading against
pulls/N/files— a claimedcarrier's real deliverable set is its diff, not its body.
Collision-edge re-read against
pulls/277/files— the edge holds; this issue staysblocked.The Dependencies section above owed this re-read as soon as #275's pull request opened
("a claimed carrier's real deliverable set is its diff, not its body"). !277 opened
2026-08-31T21:05:44Z; read at2026-08-31T21:56Zit is open, non-draft, same-repo headbuild/275-merge-base-release-shape, and its diff is exactly three paths:actions/labels-reconcile/labels-reconcile.sh@@ -270,7 +270,8 @@,@@ -918,7 +919,8 @@,@@ -1031,6 +1033,7 @@load_configat L128, L837-838test/labels-reconcile.test.shchangelog.d/275.mdchangelog.d/276.mdThe set the body derived from #275's prose is the set its diff actually delivers — no path added,
none dropped — and the regions are still disjoint. Disjointness was never the exemption (#288):
the edge is what keeps every
readyissue concurrently claimable, and #275's close is whatreleases this one. No change is owed to this issue's contract.
One item recorded here for the flip tick, so it is not rediscovered then. Each of !277's three
script hunks adds exactly one line, so this issue's own line references move when that merge lands:
L39 and
load_configat L128 are unchanged (both precede the first hunk), and the unconditionallogat L837-838 becomes L838-839.test/labels-reconcile.test.shgains 87 lines after L175.Triage re-pins those references — and re-checks the acceptance criteria against the merged tree —
in the same tick it flips this issue to
ready.Board state at this write, read from this issue's label events rather than its prose:
blocked,bug,scope:docs,scope:labels, unassigned, no open pull request. Unchanged.Every issue named by
Blocked byis closed. The sweep is moving this issue toready.claude-bot-andresmgsl referenced this issue from heavy-duty/stoke2026-09-01 15:15:29 +00:00