Context
Deferred from the review thread on #668 (the fix for #654). The bot raised a P2 (paginate close-linkage before deciding no PR is open); triaged as a file-an-issue disposition rather than a fix-in-diff, because the failure is a reversible edge (see "Why not fixed in #668" below).
The filter
plugins/work-items/tools/work-item-tracker/adapters/github/README.md (§ Open linked PRs) documents the open-linked-PR signal that drops an in-flight candidate from the /work-items:work pickable frontier. It queries GitHub's computed close-linkage:
closedByPullRequestsReferences(first:20, includeClosedPrs:false) { nodes { number state } }
then reports true iff any node has state == OPEN.
The bug
first:20 bounds the connection to the first page. includeClosedPrs:false suppresses only CLOSED (unmerged) PRs, so MERGED nodes are retained (correctly, per the filter's own rationale). An issue with more than 20 linked closing PRs whose single OPEN closing PR sorts after the first page therefore returns a page of all-MERGED nodes, the filter sees no OPEN, and reports false = pickable.
Consequence — false-keep → double-dispatch: an issue whose work is already in flight is re-picked from the frontier, so two runs can be dispatched against the same item (duplicate PRs).
Remedy
Root-cause fix is cursor pagination — walk pages (pageInfo { hasNextPage endCursor }) until an OPEN node is found or the connection is exhausted. A first:100 bump is not a fix (it only moves the boundary). Note the connection exposes no server-side OPEN-state filter — its args are after/before/first/last/includeClosedPrs/orderBy/userLinkedOnly, and orderBy (CREATED_AT/UPDATED_AT) cannot guarantee OPEN-first ordering — so pagination is the only correct route. This is a documentation-driven adapter, so the fix is to the documented query snippet (and any executing wrapper), not compiled code.
Why not fixed in #668
The trigger (>20 linked closing PRs on one issue, with the OPEN one paginated past page 1) is implausible in practice, and the failure mode — double-dispatch producing a duplicate PR — is detectable and reversible (close the dup). That places it outside the fix-in-diff carve-out (data-loss / fail-open-gate / security / silent-wrong-feeding-irreversible), so it is captured here for prioritization rather than blocking #668. The #668 README already documents the first:20 bound and flags pagination as the remedy; this issue tracks doing it.
Refs #668, #654
Context
Deferred from the review thread on #668 (the fix for #654). The bot raised a P2 (paginate close-linkage before deciding no PR is open); triaged as a file-an-issue disposition rather than a fix-in-diff, because the failure is a reversible edge (see "Why not fixed in #668" below).
The filter
plugins/work-items/tools/work-item-tracker/adapters/github/README.md(§ Open linked PRs) documents the open-linked-PR signal that drops an in-flight candidate from the/work-items:workpickable frontier. It queries GitHub's computed close-linkage:then reports
trueiff any node hasstate == OPEN.The bug
first:20bounds the connection to the first page.includeClosedPrs:falsesuppresses onlyCLOSED(unmerged) PRs, soMERGEDnodes are retained (correctly, per the filter's own rationale). An issue with more than 20 linked closing PRs whose singleOPENclosing PR sorts after the first page therefore returns a page of all-MERGEDnodes, the filter sees noOPEN, and reportsfalse= pickable.Consequence — false-keep → double-dispatch: an issue whose work is already in flight is re-picked from the frontier, so two runs can be dispatched against the same item (duplicate PRs).
Remedy
Root-cause fix is cursor pagination — walk pages (
pageInfo { hasNextPage endCursor }) until anOPENnode is found or the connection is exhausted. Afirst:100bump is not a fix (it only moves the boundary). Note the connection exposes no server-side OPEN-state filter — its args areafter/before/first/last/includeClosedPrs/orderBy/userLinkedOnly, andorderBy(CREATED_AT/UPDATED_AT) cannot guarantee OPEN-first ordering — so pagination is the only correct route. This is a documentation-driven adapter, so the fix is to the documented query snippet (and any executing wrapper), not compiled code.Why not fixed in #668
The trigger (>20 linked closing PRs on one issue, with the OPEN one paginated past page 1) is implausible in practice, and the failure mode — double-dispatch producing a duplicate PR — is detectable and reversible (close the dup). That places it outside the fix-in-diff carve-out (data-loss / fail-open-gate / security / silent-wrong-feeding-irreversible), so it is captured here for prioritization rather than blocking #668. The #668 README already documents the
first:20bound and flags pagination as the remedy; this issue tracks doing it.Refs #668, #654