Repository navigation
Closed
Bug
Copy link
Labels
host-runNeeds an agent run or builder session on a host; not a person or the issue lane.Needs an agent run or builder session on a host; not a person or the issue lane.priority: highSignificant impact, or blocks an imminent release; staff this cycle.Significant impact, or blocks an imminent release; staff this cycle.status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.A maintainer confirmed the item is real and in scope; ready to be worked.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.A briefed fix or small feature; blast radius bounded by the brief, tests exist.
Description
Problem
A lanes App pull request that the merge group refuses with verify:earlier-fail cannot leave that state on its own once its pr-verify fail-note threads are resolved.
- The verdict reader (
babysit_verify_evidence.py:50-71) refuses a head when a FAIL at another head of the PR shares this head's change digest. A later pass at the current head does not clear it. - The designed release is an address-feedback round whose push gives the verifier a new change to judge (
babysit_verify_evidence.py:119-123). - address-feedback works from unresolved review threads. When every fail-note thread for the FAILed head is already resolved, the round finds nothing, ends
success, and pushes nothing. The head never changes, so the refusal never clears and the dispatcher keeps re-running a no-op round.
Seen on #12783 on 2026-11-03. The merge group refused head 4a0b6a3a (FAIL on 4ec5632c). address-feedback runs 41606722334 and 41610279787 both ended success with no push.
Done when
- When a PR's reader refusal is
verify:earlier-failand every fail-note thread for the FAILed head is resolved, address-feedback reads that FAIL's notes from its verdict artifact (the source of truth), not from thread state, and makes its change against them. - If the diff already addresses every note, the round says so and escalates to
needs-sessioninstead of endingsuccesswith no push. - A test covers both paths: notes taken from the artifact produce a change, and notes already addressed produce an escalation with no silent success.
Out of scope
Loosening the reader, such as no longer counting a FAIL whose notes were resolved. That is the change-digest gate's own judgment.
Metadata
Metadata
Assignees
Labels
host-runNeeds an agent run or builder session on a host; not a person or the issue lane.Needs an agent run or builder session on a host; not a person or the issue lane.priority: highSignificant impact, or blocks an imminent release; staff this cycle.Significant impact, or blocks an imminent release; staff this cycle.status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.A maintainer confirmed the item is real and in scope; ready to be worked.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.A briefed fix or small feature; blast radius bounded by the brief, tests exist.
Type
Fields
Work status
confirmed
Activity
github-actions commentedon Nov 3, 2026
Proposed work class: work-class: scoped
Basis: automated intake triage; the lanes App applied this class.
Size: Medium
Complexity: Medium
Uncertainty: Known
Flags: none
Blocked by: none
Understood: address-feedback ends
successwith no push on a PR refused withverify:earlier-failonce its fail-note threads are resolved. The PR stays stuck and the dispatcher re-runs a no-op round.The recorded decision settles all three open questions (new
needs-sessionreason, model round judges already-addressed, notes come from the artifact through a reader-reported FAILed head). Nothing is left open.#12809 (comment)
No duplicates found among open issues.
Automated triage, applied by the lanes App.
melodic-automation-lanes commentedon Nov 3, 2026
Not admitted to the issue lane: it carries host-run. Admission takes the issue once each of those is met, when intake triages it again (
.github/actions/resolve-config/admission-rules.json).melodic-automation-lanes commentedon Nov 3, 2026
Refinement. A read-only run explored this repository, searched its issues and pull requests, and read the allowed docs to draft what follows. On an issue a listed human filed, it counts as the issue's spec once posted, while unedited; it is still model text, so check it.
Intended outcome: address-feedback, on a lanes PR refused with verify:earlier-fail whose fail-note threads are all resolved, takes the FAIL's notes from its verdict artifact and pushes a change, or escalates to needs-session when the diff already addresses them.
Related work
Open questions
RECOMMENDED: Add a new reason entry with route fail and label needs-session, so the comment states the real cause. Basis: .github/scripts/pr-unblock/escalation-routes.json:26
RECOMMENDED: Yes: the model round gets the artifact notes as work items and may report already-addressed. A no-push round with that report escalates. Basis: .github/scripts/pr-unblock/address-feedback.mjs:1066
RECOMMENDED: Reuse the pr-feedback-dispatch.yml verdict download and note-formatting path, so the artifact stays the single source. Basis: .github/workflows/pr-feedback-dispatch.yml:181
Sources read
To answer, post a comment that starts with this line:
Automated refinement by the lanes App. Answer with a comment whose first line is
**Decision recorded.**.kyle-sexton commentedon Nov 3, 2026
Decision recorded.
Agent choice, not an owner answer.
The Issue Triage session read the code on main and answered intake's three questions. None changes the intent, the primary design, or a security or cost boundary.
Builder: Automation Lanes. The select job needs a read-only
actionsscope to download the verdict artifact, and the round needs a new dispatch input; both are workflow-file edits the issue lane cannot push, so this issue ishost-run, not lane work.1. "Which escalation reason should the already-addressed case use?"
Add a new address-feedback reason with route
failand labelneeds-session, whosewhysays that every note of the earlier FAIL is already addressed by the diff and that only a session can move the head. Do not reusefeedback-untaken-threads: it describes an unresolved thread the lane does not take, and here every thread is resolved. Do not reusefeedback-verifier-openeither: it carries no label, and its cause is a note the lane left open.Basis:
.github/scripts/pr-unblock/escalation-routes.json:26(feedback-untaken-threads:needs-session, cause an unresolved thread the lane does not take),.github/scripts/pr-unblock/escalation-routes.json:22(feedback-verifier-open, no label).2. "Who decides that the diff already addresses every note?"
The model round decides. Select passes the artifact's notes to the model as work items, and the model may report
already-addressedfor each note it finds the diff already answers. When the round pushes nothing and every note is reportedalready-addressed, the job that would otherwise endsuccessescalates with the new reason from answer 1. A pushed change counts only when it answers a named artifact note. A change that only moves the change digest does not count, because that would loosen the reader by another route, which the issue rules out of scope.Basis:
.github/scripts/pr-unblock/address-feedback.mjs:1066-1087(select is deterministic and with no kept threads endsstop-resolvedorfeedback-untaken-threads),plugins/source-control/skills/babysit-prs/scripts/babysit_verify_evidence.py:57-60(any change at the head that differs from the FAILed change clearsverify:earlier-fail).3. "How does the round get the notes from the artifact under a read-only token?"
Do not reuse the download step in the dispatch workflow's post-verify-notes job. It is inline shell in another workflow's job, keyed to the pr-verify run that triggered it, so select cannot call it. Reuse instead the mechanism that carries the reader's answer into a round as an input, which the pass-only round uses for
verify-superseded-head:verify:earlier-failrefusal, the way it already reportssupersededAtHead.verify-pass-headandverify-superseded-head. It admits that round on the reader'sverify:earlier-failrefusal whether or not a review thread is unresolved: today a round other than pass-only is skipped before the reader runs when no thread is unresolved, and a pass-only round is sent for a pass only while a fail-note thread is open.readVerdictandnoteComments.successorstop-resolved.The artifact stays the single source of the notes, and the reply side posts nothing new for them. Today an earlier-fail refusal only logs a warning and the round runs anyway, which is why the no-op round repeats.
Basis:
.github/scripts/pr-unblock/feedback-dispatch.mjs:686-688,712-715(reader read before dispatch; reader-derived head inputs),.github/scripts/pr-unblock/verify-cap.mjs:237,245-249(reader answer carries onlycapandsupersededAtHead; earlier-fail warns and runs),.github/scripts/pr-unblock/verify-notes.mjs:87,223(noteComments,readVerdict),plugins/source-control/skills/babysit-prs/scripts/babysit_verify_evidence.py:3359-3370(the reader holds the FAILed set when it refusesverify:earlier-fail),.github/scripts/pr-unblock/feedback-dispatch.mjs:62-65,602-604(pass-only rounds need an open fail-note thread and run no model; other rounds skip with no unresolved thread before the reader read),.github/scripts/pr-unblock/verify-notes.mjs:39.Acceptance criteria
verify:earlier-fail, its report names the FAILed head or heads behind the refusal.verify:earlier-failand with no unresolved review thread gets an address-feedback model round; the dispatcher does not skip it as having no unresolved review thread.stop-resolvedorskip.failand labelneeds-session, and does not endsuccessorstop-resolved.successorstop-resolved.whythat names the already-addressed earlier FAIL.verify:earlier-fail.verify:earlier-fail, all fail-note threads resolved, artifact notes present, and the round receives those notes as work items.needs-sessionreason, with no silentsuccess.melodic-automation-lanes commentedon Nov 3, 2026
The latest triage verdict holds nothing: admission moved the issue to
status: confirmed; its comment says why.fix(ci): bound the dispatcher's earlier-fail reads on thread-less PRs
fix(ci): hold earlier-fail-notes.mjs on the merge floor
fix(ci): retry a failed earlier-fail read instead of taking it as no …
fix(ci): bound earlier-fail retries by a window instead of a record