feat(ci): flag possible same-issue PR overlap on open - #8061
Conversation
…eeze A fork PR's Stage-2 AI reviewers start only on CI's workflow_run:completed event, which GitHub drops silently under load. When that event is dropped after CI passes, no fork-review check-run is ever posted, pr-readiness reads the lane as not-started/only-skipped and counts it pending, and no future event can recompute it -- so the fork PR is frozen pending forever and is eventually superseded. Add fork-review-heal.yml: a scheduled sweep that, for an open fork PR whose latest CI run for the head SHA concluded success, re-dispatches only the review lanes that never fired via each reviewer's new workflow_dispatch entrypoint keyed to the head SHA. Each reviewer resolves its own PR from that SHA with the existing open-PR-by-head-SHA resolver, so no new trust surface is added and fork code is never checked out or executed. A lane is missing only when it has no in-progress or completed non-skipped check-run, and reviewers open an in-progress check-run immediately, so the sweep is self-terminating and bounded by a dispatch cap. A fork PR whose CI has not passed stays pending by design, attributed to CI; the awaiting-approval boundary stays blocking-but-attributed and is never auto-cleared.
Add a shared overlap engine (scripts/detect_pr_overlap.py) and the pr-duplicate-detect.yml workflow. On open it upserts one advisory comment naming earlier open PRs that reference the same issue and touch intersecting files; on merge it points at and closes superseded open same-issue PRs and records co-authorship where a workflow can place it. Flagging requires both a shared issue and a non-empty changed-file intersection, never matches a PR against itself, and never blocks merge. Document the Co-authored-by boundary (a workflow cannot rewrite the composed squash-merge commit) in docs/ci/ci-and-reviews.md.
…overlap - Materialize the open-PR candidate set in a single gh pr list call instead of a pr view plus GraphQL call per open PR, and lower the default --limit. - Close a superseded open PR on merge only on a strong overlap (exact set match or shared files covering at least half the merged PR's files); leave weaker single-file overlaps to the on-open advisory. - Add a no-auto-close-duplicate label opt-out that points at the merge but keeps the close under human control, preserving pointer-before-close ordering.
The on-merge co-authorship note previously listed every strongly- overlapping PR under "now closed as superseded", including PRs carrying the no-auto-close-duplicate label that were deliberately left open. The note claimed a close that did not happen for opted-out PRs. Track closed and left-open PR numbers separately and word each part of the note accurately: closed PRs under "Closed as superseded" and opted-out PRs under "Left open". The overlap is still recorded for attribution either way. Pointer-before-close ordering, the strong- overlap gate, the label opt-out mechanics, and the marker-keyed idempotent upsert are unchanged. Tests assert the note reports a real close for a closed PR and never claims a close for an opted-out PR. Docs updated to match.
Scope reset — this revision is not a merge candidateI am converting this PR to draft and pausing implementation pending a maintainer decision on #8046. The current head (
I posted the narrowed design contract on #8046. The proposed replacement is on-open advisory only: same closing issue + shared files → one idempotent “possible overlap” comment asking authors to coordinate. It will never block, label, close, rank, or attribute either PR, and it will never execute fork code. Please do not review the current diff. If maintainers accept the contract, I will rebuild this same PR from current |
|
@timwukp Thanks for the unusually self-disclosing PR body. The audit agrees with your own read that this head is not a merge candidate, and it also confirms the idea itself is not superseded. What landed on main since you opened this: #7982 removed the two-publisher/skipped-twin hazard you cited when you withdrew proposal 4, and #8503 staged the Stage-2 fork reviewers behind Fast Gate, so those lanes now trigger on What main still does not have: any same-issue overlap detection. Our ask: keep this in draft until #8046 has a decided contract, then rebuild on current main rather than rebase. The branch is 663 commits behind and currently conflicting, and main rewrote these same lanes extensively, so a rebase would fight a very large residual diff. On rebuild, please narrow to the advisory-only on-open lane plus Posted from the 2026-09-08 open-PR relationship audit (read-only, one auditor per PR); reply here if any of this is wrong. |
Important
DRAFT — scope reset pending a maintainer decision on #8046.
Current head
68fc008ais not a merge candidate. It is conflicted, five commits / 304 upstream commits behind, and still contains proposal 4 even though that proposal was withdrawn after #7982 was found to have already addressed the stated mechanism. It also contains on-merge close/attribution behavior whose authority exceeds the evidence available to an overlap detector.Please do not review the current diff. The intended replacement contract is recorded below; the original scope is preserved under Superseded original scope for the public timeline.
Problem / Motivation
Contributors and agents can begin work on an issue while an earlier open PR already covers overlapping files. The issue cross-reference exists, but the later author may not see it before duplicated implementation, CI, and review effort accumulates.
Why it matters
The repository loses contributor time and reviewer capacity, and whichever PR reaches the queue first can merge while the other remains open. This is a coordination failure, not evidence of misconduct or ownership.
What changed
No final implementation is proposed at the current head. The current revision is being held only while maintainers decide this narrowed, advisory-only contract:
If maintainers accept this contract on #8046, this branch will be rebuilt from current
main; the fork-review healer and all on-merge close/attribution behavior will be removed, and the result will be squashed to one commit. If maintainers decline it, this PR will close without further implementation work.Tests
Pending the scope decision. The replacement must prove at least:
Manual verification
Pending maintainer acceptance and branch reconstruction.
Why no screenshot: this is CI/process automation with no rendered UI change.
Pattern harvest
Rule candidate: When automation can detect possible duplicate work but cannot prove intent or equivalence, prefer an early, idempotent advisory over auto-closing or auto-attributing contributions.
Related: #8046
Superseded original scope at 68fc008 (preserved for the timeline)
Implements the four process/tooling proposals from issue #8046. This is forward-looking automation, not credit adjudication. No misconduct is alleged by the reporter, and Case B (#7852/#7898) was retracted, so no action is taken on it.
Proposal 4 (the plain bug): heal never-fired fork-review lanes
GitHub does not guarantee
workflow_rundelivery, so a fork PR can have CI conclude success yet never start any Stage-2 fork reviewer. With no review check-run and no future event to create one,pr-readiness.ymlfreezes that PR atpendingforever, so it cannot merge and is eventually superseded (the #7553 case)..github/workflows/fork-review-heal.yml: a scheduled, idempotent, self-terminating sweep that finds OPEN fork PRs whose CI succeeded but whose fork-review check-runs never posted, and re-dispatches only the missing lanes.workflow_dispatch(head_sha)entrypoint to all fivefork-*-review.ymlreviewers, reusing their existing open-PR-by-head-SHA resolver. Fork code is never checked out or executed; a PR whose CI has not passed is left pending by design, never force-passed. The maintaineraction_requiredand undelivered-dispatch boundaries are documented, not worked around.Proposals 2 and 1: duplicate / overlap detection
scripts/detect_pr_overlap.py: a pure, network-free, unit-tested engine. Overlap = same referenced issue (GraphQLclosingIssuesReferencesunion aFixes/Closes/Resolves #Nbody fallback) AND non-empty changed-file intersection; never self-matches; OPEN candidates only. Candidate set fetched in a singlegh pr listcall..github/workflows/pr-duplicate-detect.yml:no-auto-close-duplicatelabel opts a PR out of auto-close (pointer only).Proposal 3: docs
CONTRIBUTING.mdbinds the “already on it” rule to automated/agent contributors and explains that an open PR referencing the same issue is the machine-readable signal the on-open lane surfaces.docs/ci/ci-and-reviews.mddocuments both new mechanisms.Testing
Full pytest is deferred to CI because the authoring sandbox is network-restricted (PyPI blocked; pytest/PyYAML/yamllint/actionlint uninstallable). In-sandbox substitute all green:
bash -non every new/edited workflow run-script; engine self-test (20 checks); two behavioural drivers executing the real extracted steps againstghstubs (fork-review-heal 11 checks, pr-duplicate-detect 17 checks) covering dispatch/comment/close side-effects, idempotency, the strong-overlap gate, and the label opt-out;py_compile; docs lint (257 files). PyYAML+pytest tests are committed for CI (test/test_fork_review_heal.py,test/test_pr_duplicate_detect.py).Needs a repo admin at merge time (not code)
no-auto-close-duplicatelabel if it should exist before the on-merge lane runs (the workflow only reads it).The revision declared #8046 as its closing target.