Add step 6 to issue triage: check for open PRs before opening new ones - #328
Merged
Merged
Conversation
Triage could confirm a bug and open a repro PR without ever noticing that a fix was already sitting in review. The only PR-adjacent instruction was a clause in step 3 aimed at dating a *merged* fix, and it reached for search_issues rather than search_pull_requests -- so a contributor who titled theirs "fix #285" was invisible. Adds step 6, between duplicate-hunting and classification. It searches the bare issue number and skims the open list, because several of these PRs name their issue only in the title and #286 covers two at once. It also asks for an assessment rather than a bare link: #287 addresses #285 but re-acquires the lock inside tick(), which docs/design.rst:55 says a node deliberately does not do, and "fixed by #287" would leave the reporter waiting on a merge that isn't coming. An open PR is not a new outcome -- the classification is unchanged -- so it lands as a section in decision-table.md that overrides outcome A's branch-and-PR sequence, plus a required "Open PR" line in the report block that reads "none found" rather than going absent. Reviewing or replying on someone else's PR is now out of bounds on every run, not just dry ones, and check_no_writes.py enforces that alongside the never-close rule. Also pins down the attribution footer: no session links and no Claude-Session trailer in anything public. #324 went out with one in its body, which resolves for nobody but the account that opened it. Evals: new case 7 on #285/#287, where the PR is the whole answer. Case 1 also had to change -- PR #308 is open against #307, so its expectation of "branch and open a PR" was wrong the moment this rule existed. Both read a frozen fixtures/open-prs.json holding every open PR, not just the relevant one: the search is the step under test, and a fixture with a single entry would hand over the answer. Case 6 reads it too, since a batch run turns up #324 for #74. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends the issue-triage skill to search for open pull requests that already address an issue before proposing a fix. This prevents duplicate work and surfaces stalled PRs waiting for review as the most actionable finding.
Changes
SKILL.md: Documents step 6, which searches for open PRs addressing the issue and assesses whether they fix the mechanism traced in step 2. Clarifies that this is distinct from step 3 (checking for merged fixes) and changes the action but not the classification. Adds guidance on when to open a competing PR (only if the existing one is stale/inapplicable) and what assessment to include in the issue comment.
decision-table.md: Adds "When a PR is already open" section explaining that the classification doesn't change but the action does — specifically, don't open a competing PR unless it's stale, and always include 1–2 sentences assessing whether the open PR addresses the mechanism, carries a test, or contradicts documented design.
SKILL.md and decision-table.md: Updates step numbering (6→7, 7→8, 8→9, 9→10) and clarifies that a bare link to an open PR is not enough — the assessment is the deliverable.
snapshot.py: Adds
--prsmode to freeze the open pull request list intofixtures/open-prs.json, with the same pruning logic as issues. Includesstateanddraftfields (unlike issues, where closed state is self-announcing; for PRs, "is it open and is it a draft" is the fact under test).evals/fixtures/open-prs.json: New fixture holding 9 open PRs as of 2026-07-27, including fix #285 #287 (fix Beat crash after Redis SERVER_CLOSED_CONNECTION_ERROR #285), Fix/due at calculation 307 #308 (fix Bug:
due_atproperty calculates incorrect next run time due to misuse ofremaining_estimate#307), and test: reproduce #74 — rrule skips the occurrence at dtstart #324 (regression test for rrule: first run delayed #74). Shared by all cases that could surface an open PR.evals/evals.json: Updates case 1 (
reproducible-bug-and-duplicate) to read the open-prs fixture and expect it to surface Fix/due at calculation 307 #308 without opening a competing PR. Adds case 7 (open-pr-already-proposed) testing Beat crash after Redis SERVER_CLOSED_CONNECTION_ERROR #285, which has an open PR (fix #285 #287) titled only by number — guards against missing PRs that don't name their issue in words.evals/README.md: Documents why the open-prs fixture must hold every open PR (so "search the number, then skim the list" is testable), and updates the case table to note that case 1 now holds back the branch-and-PR sequence because Fix/due at calculation 307 #308 is open, and case 7 tests finding a PR despite its minimal title.
check_no_writes.py: Adds
add_reply_to_pull_request_commentto the NEVER_ALLOWED list, preventing the skill from reviewing or replying on someone else's PR. Clarifies the distinction: closing is the maintainer's call; reviewing is why step 6 exists.Notable details
The open-prs fixture is shared across all cases, not per-case, because the step under test is "search the number, then skim the list" — a fixture holding only the PR the case is about would hand the answer over before the search ran.
Step 6 uses both
search_pull_requests(for the number) andlist_pull_requests(to skim all open ones), because several PRs in the real repo name their issue only in the title or body, not in words — fix #285 #287 isfix #285, Fix/due at calculation 307 #308 isFix/due at calculation 307.The assessment of an open PR goes in the issue comment and run report, not as a review on the PR itself — the reporter and maintainer are the audience, and a contributor whose two-year-old PR suddenly gets a critique from a triage bot is not.
Attribution footer is always the bare
https://claude.ai/code, never a session-specific link — those resolve for nobody but the person who ran it and are noise in a thread the reporter has to read.