Skip to content

fix(pr-pipeline): resolve pr-verify's earlier fail-note threads when a later head passes #13038

Closed

Description

@kyle-sexton

No related issue: found while driving PRs; repros #12962 and #12974

Problem

When pr-verify FAILs a head, verify-notes.mjs opens one review thread per note (first line <!-- pr-verify:fail-note -->). When a later head passes, those threads stay unresolved, and an unresolved thread blocks the merge.

The clearing step exists but only runs inside an address-feedback round: staleVerifyNotes (.github/scripts/pr-unblock/address-feedback.mjs:208-224) finds the notes a pass superseded, and publish-feedback.mjs resolves them as the lanes App. A pass on its own never triggers that round, so the notes clear only if a fix round happens to run after the pass and completes.

Repros:

Each time, the PR waits on the owner for threads already settled. Agent sessions cannot resolve them either: the auto-mode classifier refuses a session's resolve of a verifier's thread.

Done when

  1. When pr-verify passes a head, it resolves that PR's earlier fail-note threads itself (the staleVerifyNotes set for that head), through the lanes App token. They are its own threads, so no person is involved. A test shows a pass at a later head resolves the earlier notes and leaves notes on the passing head itself alone.
  2. A one-time count of open PRs that carry settled fail-note threads today (unresolved notes on a head older than a passing head), reported on this issue, so they can be cleared once the fix lands.

Notes

Activity

  1. added
    host-runNeeds an agent run or builder session on a host; not a person or the issue lane.
    on Nov 4, 2026
  2. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Nov 4, 2026
  3. kyle-sexton commented on Nov 4, 2026

    @kyle-sexton
    MemberAuthor

    Claimed by lane 3 (hub assignment H15). Plan: on a pr-verify pass, resolve that PR's superseded fail-note threads (the staleVerifyNotes set) with the lanes App token. Add a test showing a pass at a later head resolves the earlier notes and leaves the passing head's own notes alone. Then post the one-time count of open PRs that carry settled fail-note threads.

  4. github-actions commented on Nov 4, 2026

    @github-actions
    Contributor

    Proposed work class: work-class: scoped
    Basis: automated intake triage; the lanes App applied this class.

    Size: Small
    Complexity: Medium
    Uncertainty: Knowable
    Flags: none
    Blocked by: none

    Decision recorded. Fix the gap in the existing pass-only pr-unblock round rather than move thread resolution into the pr-verify job; done-when 2 is already met (0 of 21).
    Agent choice, not an owner answer.
    [quote truncated; the full decision is at the link]

    #13038 (comment)

    This decision settles the open questions. The fix goes in the existing pass-only round, and done-when 2 is already met. What remains is to check whether #12974 hit the same publish failure that #13016 fixed, or a different skip condition. Then add a test that a pass at a later head resolves earlier fail-notes and leaves notes on the passing head alone. The work is a host-run step.

    Automated triage, applied by the lanes App.

  5. added
    priority: mediumReal value, no hard deadline; normal backlog flow.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Nov 4, 2026
  6. kyle-sexton commented on Nov 4, 2026

    @kyle-sexton
    MemberAuthor

    Done-when 2, one-time count at 2026-11-04T02:0xZ: 0 of 21 open PRs carry an unresolved pr-verify fail-note thread, so there are no settled notes to clear once the fix lands. Method: one GraphQL read of every open PR's review threads, counting unresolved threads whose first comment opens with <!-- pr-verify:fail-note -->. Marker check: the repro #12974's four fail-note threads match it, and all four are now resolved.

  7. 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 4, 2026
  8. added
    status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.
    and removed
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    on Nov 4, 2026
  9. melodic-automation-lanes commented on Nov 4, 2026

    @melodic-automation-lanes
    Contributor

    Not admitted to the issue lane: no trusted-human acceptance criteria: a listed human must put the confirmed - [ ] criteria in the body or post them as their own comment, or, on an issue a listed human filed, intake's newest refinement comment must hold them unedited; 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).

  10. melodic-automation-lanes commented on Nov 4, 2026

    @melodic-automation-lanes
    Contributor

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

  11. 3 remaining items

  12. melodic-automation-lanes commented on Nov 4, 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: Find why the existing pass-only resolve round did not clear the #​12962 and #​12974 notes, fix that gap, and test that a later-head pass resolves earlier notes and leaves the passing head's own notes alone.

    Related work

    Open questions

    1. The issue says a pass never triggers a round, but the code already has a pass-only path. verify-notes.mjs:37-44 outputs pass-head-sha, pr-feedback-dispatch sends pr-unblock with verify-pass-head, and publish-feedback resolves the notes as the lanes App (pr-unblock.yml:19-23). Which condition made that path skip #​12962 and #​12974?
      RECOMMENDED: Treat the work as finding and fixing that skipped condition, not as adding a new resolver. Candidates are a PR that is not on a lanes App pull request, a head that moved before the pass was read, or a dispatch gate such as the open-at-head check at verify-notes.mjs:251-276. The next step is to read the logs of those two pass runs. Basis: .github/scripts/pr-unblock/verify-notes.mjs:37
    2. Should the pass resolve the threads inside the pr-verify job itself, as done-when 1 literally says, or should it keep the dispatch to pr-unblock and fix why the dispatch did not fire? The verify-notes.mjs header says the job's own token cannot resolve threads.
      RECOMMENDED: Keep the dispatch to pr-unblock, where the lanes App token is available, and fix the gap there, unless the owner wants the resolving moved into the pr-verify job. Basis: .github/scripts/pr-unblock/verify-notes.mjs:44
    3. Is done-when 2 already complete? The 2026-11-04 comment reports 0 of 21 open PRs with unresolved fail-note threads.
      RECOMMENDED: Yes, drop it from the criteria. The comment gives the count and the method. Basis: judgment
    4. Do PRs 10474 and 11889 (closed, merge state not shown) already deliver this? If so, this issue may be fixed-by one of them, or it may be a regression.
      RECOMMENDED: Check whether each was merged. If 10474 merged and the repro still failed, scope this issue as a regression fix for the failing condition. Basis: 10474

    Sources read

    • /home/runner/work/claude-code-plugins/claude-code-plugins/refine-data/context.json: the issue, two lane-3 comments (a claim and a count of 0 of 21 open PRs), and candidates 10474 and 11889 with similar titles
    • /home/runner/work/claude-code-plugins/claude-code-plugins/.base/.github/scripts/pr-unblock/verify-notes.mjs:37-44: a pass on a lanes App PR names pass-head-sha when a fail-note thread is open, the dispatch sends pr-unblock, and publish-feedback resolves the threads as the lanes App
    • /home/runner/work/claude-code-plugins/claude-code-plugins/.base/.github/scripts/pr-unblock/verify-notes.mjs:251-276: the pass path reads the PR's threads through staleVerifyNotes and gates on the PR being open at the head
    • /home/runner/work/claude-code-plugins/claude-code-plugins/.base/.github/workflows/pr-unblock.yml:19-23: a pass-only round with verify-pass-head resolves stale fail-note threads as the lanes App
    • /home/runner/work/claude-code-plugins/claude-code-plugins/.base/.github/scripts/pr-unblock/verify-notes.test.mjs:488-515: existing tests cover staleVerifyNotes and the pass naming its head

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

  13. kyle-sexton commented on Nov 4, 2026

    @kyle-sexton
    MemberAuthor

    Cause check (hub H16): the #12974 notes stayed open because the dispatcher skipped the pass-only round. It was not the publish failure #13016 fixed.

    • Dispatch run 41739714903, 2026-11-03T23:30:40Z: post-verify-notes found #12974: 4 open fail-note thread(s) the pass-notes at b5a5e45c… may supersede and set the pass head. dispatch then skipped: pr-unblock.yml run 41738844371 for address-feedback is not done yet. That round ran 23:24:06Z to 23:34:36Z.
    • Round 41748064570, 00:36Z, on b5a5e45: a normal round with VERIFY_PASS_HEAD empty. address-feedback-select logged skip: 0 kept thread(s), 0 earlier round(s), and publish-feedback was skipped. It never reached publish, and fix(pr-pipeline): accept feedback-already-addressed from publish-feedback #13016 (01:17Z) came later.

    The fix (#13046) is in the dispatcher: a pass-only dispatch is no longer dropped while a round is in progress, and pr-unblock's per-PR concurrency group queues it. Resolving stays in the pr-unblock round as the lanes App, and the pr-verify job gets no new token.

  14. kyle-sexton commented on Nov 4, 2026

    @kyle-sexton
    MemberAuthor

    Answers to the refinement's open questions. Each is settled by evidence, not an owner decision:

    1. The skipped condition was the dispatcher's in-progress hold. The pass-only dispatch for b5a5e45 (run 41739714903, 23:30:40Z) skipped because address-feedback round 41738844371 was still running (cause check). Fixed in fix(pr-pipeline): resolve superseded pr-verify fail-notes when a round is in progress #13046.
    2. Keep the dispatch to pr-unblock; resolving stays there as the lanes App. Basis: verify-notes.mjs:44, the job's own token cannot resolve threads. fix(pr-pipeline): resolve superseded pr-verify fail-notes when a round is in progress #13046 gives pr-verify no new token.
    3. Done-when 2 is done: 0 of 21 open PRs.
    4. fix(pr-pipeline): resolve stale pr-verify fail-note threads when a later head passes #10474 merged as f073b3e and added the pass-only round. Its body listed this drop as a known limit, so this is a gap in that path, not a regression.
  15. kyle-sexton commented on Nov 4, 2026

    @kyle-sexton
    MemberAuthor

    Decision recorded. Fix the gap in the existing pass-only pr-unblock round rather than move thread resolution into the pr-verify job; done-when 2 is already met (0 of 21).
    Agent choice, not an owner answer.

    1. Which condition made the pass-only path skip refactor(ai-briefing): resolve the view medium through the shared resolver #12962 and fix(work-items): skip a create-item that duplicates a sibling under the same parent #12974? Accept the RECOMMENDED answer: find and fix the condition that skipped, and add no new resolver. One correction: the Problem section says a pass never triggers a round, but the code has a pass-only round since fix(pr-pipeline): resolve stale pr-verify fail-note threads when a later head passes #10474 (verify-notes.mjs:37-44). The body's refactor(ai-briefing): resolve the view medium through the shared resolver #12962 repro also says a round ran after the pass and failed at publish, which fix(pr-pipeline): accept feedback-already-addressed from publish-feedback #13016 fixed.
      fix(work-items): skip a create-item that duplicates a sibling under the same parent #12974's last round, at 00:37Z, also ran before fix(pr-pipeline): accept feedback-already-addressed from publish-feedback #13016 merged. So the builder first checks whether fix(work-items): skip a create-item that duplicates a sibling under the same parent #12974 hit the same publish failure before fix(pr-pipeline): accept feedback-already-addressed from publish-feedback #13016:

      • Same failure: the remaining work is a regression test that a pass-only round at a later head goes through publish and resolves the earlier notes.
      • Different cause: the post-verify-notes log of the fix(work-items): skip a create-item that duplicates a sibling under the same parent #12974 pass run names it, through the line findStaleNotes printed (is not open at head, no review comment, or a count of open fail-note threads), or through no line when the PR is not a lanes App PR. If it wrote pass-head-sha, the dispatch job's log shows whether the round was skipped for an active round, a do-not-merge hold, or a replaced dispatch. Fix that condition and test it.

      This record does not claim that fix(pr-pipeline): accept feedback-already-addressed from publish-feedback #13016 fixed the bug; the run logs decide.
      Basis:

    2. Resolve inside the pr-verify job, or keep the dispatch to pr-unblock? Accept the RECOMMENDED answer: keep the dispatch to pr-unblock, where the lanes App resolves the threads. Done-when 1 still sets the outcome: a pass at a later head resolves the earlier notes as the lanes App, and a test shows that the notes on the passing head stay open. The existing round is how that outcome is met. Done-when 1 read literally would hand the lanes App token to the pr-verify job, which this record avoids.
      Basis:

      • verify-notes.mjs:44 ("Resolving a thread takes more than this job's token holds.").
      • .github/workflows/pr-unblock.yml:19-23 ("After a pr-verify pass at a PR's head that left fail-note threads open, it sends one with verify-pass-head: a pass-only round, ... and the reply job resolves them as the lanes App").
    3. Is done-when 2 already complete? Accept the RECOMMENDED answer: yes, so drop it from the remaining work.
      Basis: kyle-sexton's lane-3 comment of 2026-11-04T02:03:41Z on fix(pr-pipeline): resolve pr-verify's earlier fail-note threads when a later head passes #13038 ("0 of 21 open PRs carry an unresolved pr-verify fail-note thread"), which states its method.

    4. Did fix(pr-pipeline): resolve stale pr-verify fail-note threads when a later head passes #10474 or fix(pr-unblock): resolve a fail-note thread a same-head pass superseded #11889 already deliver this? Accept the RECOMMENDED answer: fix(pr-pipeline): resolve stale pr-verify fail-note threads when a later head passes #10474 merged, so this is a failure in a path that exists, not a missing feature, and neither PR closes this issue.

      So work proceeds as decided in 1.
      Basis: git log origin/main --grep for those numbers, run this session.

    Route: host-run, status: confirmed, builder laptop Lane 3 (pr-verify)

  16. 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.
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Nov 4, 2026
  17. added
    status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.
    and removed
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    on Nov 4, 2026
  18. kyle-sexton commented on Nov 4, 2026

    @kyle-sexton
    MemberAuthor

    Closed by lane 3 with this record.

    • Done-when 1: fix(pr-pipeline): resolve superseded pr-verify fail-notes when a round is in progress #13046 merged at 2026-11-04T02:38:02Z as 1acf639. A pass-only dispatch is no longer dropped while an address-feedback round is in progress. pr-unblock's per-PR concurrency group queues it, and it still resolves superseded fail-notes through staleVerifyNotes as the lanes App. Tests: feedback-dispatch and publish-feedback suites 361 pass. Negative controls fail without the fix, and when notes on the passing head would be resolved. CI: both Claude review lanes 0 findings, ci-status green.
    • Done-when 2: 0 of 21 open PRs carried settled fail-note threads (count).
    • Cause: the cause check.
    • Known limits, accepted in the PR: a pass that arrives while a round is waiting to start is still skipped. A queued pass-only round can be replaced by a newer dispatch for the same PR.
    • Times: claimed 02:02Z, merged 02:38Z.
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: mediumReal value, no hard deadline; normal backlog flow.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