Skip to content

pr-unblock: address-feedback dead-ends on verify:earlier-fail once fail notes are resolved #12809

Closed

Description

@kyle-sexton

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-fail and 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-session instead of ending success with 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.

Activity

  1. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Nov 3, 2026
  2. github-actions commented on Nov 3, 2026

    @github-actions
    Contributor

    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 success with no push on a PR refused with verify:earlier-fail once 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-session reason, model round judges already-addressed, notes come from the artifact through a reader-reported FAILed head). Nothing is left open.

    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 actions scope 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 is host-run, not lane work.
    [quote truncated; the full decision is at the link]

    #12809 (comment)

    No duplicates found among open issues.

    Automated triage, applied by the lanes App.

  3. added
    priority: highSignificant impact, or blocks an imminent release; staff this cycle.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Nov 3, 2026
  4. added
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.
    on Nov 3, 2026
  5. melodic-automation-lanes commented on Nov 3, 2026

    @melodic-automation-lanes
    Contributor

    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).

  6. added
    status: needs-decisionAwaiting a human or maintainer judgment call.
    and removed
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    on Nov 3, 2026
  7. melodic-automation-lanes commented on Nov 3, 2026

    @melodic-automation-lanes
    Contributor

    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

    1. Which escalation reason should the already-addressed case use? The issue says needs-session, and only feedback-untaken-threads in escalation-routes.json carries that label.
      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
    2. Who decides that the diff already addresses every note? The select step is deterministic JS and cannot judge a diff. Should the model round judge it and report an already-addressed result that the workflow turns into an escalation?
      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
    3. How does the round get the notes from the artifact under a read-only token? Should select read the FAIL's verdict artifact through the reader's artifact listing, or reuse the download step in pr-feedback-dispatch.yml?
      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

    • plugins/source-control/skills/babysit-prs/scripts/babysit_verify_evidence.py:50-71: verify:earlier-fail refuses a head when a counted FAIL at another head shares its change digest; a later pass does not clear it
    • plugins/source-control/skills/babysit-prs/scripts/babysit_verify_evidence.py:119-123: a verify:fail or earlier-fail only waits for an address-feedback round whose push gives a new head
    • .github/scripts/pr-unblock/address-feedback.mjs:1066-1087: with no kept threads the select skips (stop-resolved) or escalates only for untaken unresolved threads, so resolved fail notes yield no work
    • .github/scripts/pr-unblock/address-feedback.mjs:208-221: verifier-note threads are chosen only when unresolved
    • .github/scripts/pr-unblock/escalation-routes.json:26: feedback-untaken-threads is the only reason here with the needs-session label
    • .github/workflows/pr-feedback-dispatch.yml:181: downloads the verdict artifact and posts its notes as review comments

    To answer, post a comment that starts with this line:

    **Decision recorded.** <answer>
    

    Automated refinement by the lanes App. Answer with a comment whose first line is **Decision recorded.**.

  8. added
    host-runNeeds an agent run or builder session on a host; not a person or the issue lane.
    on Nov 3, 2026
  9. kyle-sexton commented on Nov 3, 2026

    @kyle-sexton
    MemberAuthor

    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 actions scope 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 is host-run, not lane work.

    1. "Which escalation reason should the already-addressed case use?"
    Add a new address-feedback reason with route fail and label needs-session, whose why says that every note of the earlier FAIL is already addressed by the diff and that only a session can move the head. Do not reuse feedback-untaken-threads: it describes an unresolved thread the lane does not take, and here every thread is resolved. Do not reuse feedback-verifier-open either: 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-addressed for each note it finds the diff already answers. When the round pushes nothing and every note is reported already-addressed, the job that would otherwise end success escalates 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 ends stop-resolved or feedback-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 clears verify: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:

    • The verifier reader reports the FAILed heads behind a verify:earlier-fail refusal, the way it already reports supersededAtHead.
    • The dispatcher passes the FAILed head to a model round (not a pass-only round, which runs no model) as a new input, beside verify-pass-head and verify-superseded-head. It admits that round on the reader's verify:earlier-fail refusal 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.
    • Select downloads that head's FAIL verdict artifact and parses it with verify-notes.mjs readVerdict and noteComments.
    • When select cannot download or parse that artifact, the round fails closed: it ends in a failing status, never success or stop-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 only cap and supersededAtHead; 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 refuses verify: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

    • When the verifier reader refuses a lanes App PR's head with verify:earlier-fail, its report names the FAILed head or heads behind the refusal.
    • The dispatcher sends an address-feedback round for that refusal that names the FAILed head, and the round's select step reads that head's FAIL notes from its verdict artifact, not from review thread state.
    • A lanes App PR refused with verify:earlier-fail and with no unresolved review thread gets an address-feedback model round; the dispatcher does not skip it as having no unresolved review thread.
    • With every fail-note thread for the FAILed head resolved, select passes the artifact's notes to the model round as work items instead of ending stop-resolved or skip.
    • A round that pushes a change answering a named artifact note ends as it does today for a pushed fix.
    • A round that pushes nothing and reports every artifact note as already addressed ends in a new address-feedback escalation reason with route fail and label needs-session, and does not end success or stop-resolved.
    • When select cannot download or parse the FAILed head's verdict artifact, the round ends in a failing status, not success or stop-resolved.
    • The new reason is listed in the escalation routes file with a why that names the already-addressed earlier FAIL.
    • The reader's refusal rules are unchanged: a FAIL whose notes are resolved still counts toward verify:earlier-fail.
    • A test covers the artifact path: refusal verify:earlier-fail, all fail-note threads resolved, artifact notes present, and the round receives those notes as work items.
    • A test covers the already-addressed path: the same setup with a no-push round that reports every note already addressed ends in the new needs-session reason, with no silent success.
  10. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    and removed
    status: needs-decisionAwaiting a human or maintainer judgment call.
    on Nov 3, 2026
  11. added
    status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.
    on Nov 3, 2026
  12. removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Nov 3, 2026
  13. melodic-automation-lanes commented on Nov 3, 2026

    @melodic-automation-lanes
    Contributor

    The latest triage verdict holds nothing: admission moved the issue to status: confirmed; its comment says why.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    host-runNeeds 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.status: confirmedA 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.

    Type

    Fields

    Work status

    confirmed

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions