Skip to content

fix(tracker-intake): keep issue claimed while a terminated worker's PR is in flight#3016

Open
illegalcall wants to merge 1 commit into
mainfrom
fix/2921-intake-pr-linkage-dedup
Open

fix(tracker-intake): keep issue claimed while a terminated worker's PR is in flight#3016
illegalcall wants to merge 1 commit into
mainfrom
fix/2921-intake-pr-linkage-dedup

Conversation

@illegalcall

Copy link
Copy Markdown
Collaborator

Problem

#2777 fixed #2746 by excluding terminated sessions from intake's seen set, so a genuinely dead worker no longer blocks re-spawn. But that opened a new window (#2921):

  • Intake dedups only on live sessions (seenIssueIDs) and has no PR awareness — its Store interface never exposed PR facts.
  • Manager.Spawn has no issue-level dedup.
  • A Fixes #N PR closes issue #N only on merge, not on open.

So when a worker opens a PR and is then killed or crashes while its issue is still open (PR unmerged), the terminated session drops out of seen, and the next 1-minute tick spawns a duplicate worker — and a second PR — for the same issue. The simplest trigger needs no crash: an operator killing a worker that already opened a PR.

Fix

Intake now also treats an issue as claimed when a terminated session for it left an open or merged PR, using the existing per-session PR store (ListPRFactsForSession). A terminated session keeps its pr rows because kill/terminate only sets is_terminated=1 — it does not delete the sessions row (that persistence is the same basis as #2746), and pr.session_id cascades only on row delete.

seen := seenIssueIDs(sessions)               // live sessions claim outright
o.claimIssuesWithInFlightPR(ctx, sessions, seen) // + terminated sessions with an open/merged PR

Behavior:

No schema/query/codegen change — reuses ListPRFactsForSession and adds one method to intake's local Store interface (satisfied by the production *sqlite.Store).

Tests

Added to the trackerintake package:

All existing tests (including #2777's) pass unchanged; full package + go build ./... + go vet clean.

Scope

Only touches trackerintake's dedup logic and its tests. Does not change the SQL layer, session lifecycle, or Spawn. Distinct from #2745 (reaper cannot terminate stuck sessions).

Fixes #2921

🤖 Generated with Claude Code

…R is in flight

#2777 excluded terminated sessions from intake's seen set so a genuinely dead
worker stops blocking re-spawn (#2746). But intake dedups only on live sessions
and has no PR awareness, and Spawn has no issue-level guard. So when a worker
opened a PR and was then killed or crashed while its issue stayed open (the PR
unmerged, so `Fixes #N` had not closed the issue), the next intake tick spawned
a duplicate worker — and a second PR — for the same issue (#2921).

Intake now also treats an issue as claimed when a terminated session for it left
an open or merged PR, via the existing per-session PR store (ListPRFactsForSession;
a terminated session keeps its pr rows because kill/terminate only sets
is_terminated, it does not delete the sessions row). A terminated session with no
PR, or only a closed unmerged PR, still frees its issue for re-spawn, preserving
#2746 and letting a rejected attempt be retried fresh. A PR-lookup error fails
safe toward "claimed" so a transient read cannot cause a duplicate PR. Only
terminated sessions whose issue is not already claimed by a live session are
queried, so a healthy project issues no PR lookups.

Tests: open/merged PR blocks re-spawn; closed-unmerged PR and lookup-failure
cases; live-session-claims-without-PR-lookup. Existing #2777 tests unchanged.

Fixes #2921

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@somewherelostt

Copy link
Copy Markdown
Collaborator

Thanks for contributing to Agent Orchestrator.

This PR is being picked up by the current external contributor on-call pair:

If someone is already working on this, please continue as usual.
The on-call pair is added for visibility, tracking, and support, not to take over the work.
If you need help with review, direction, reproduction, or next steps, please tag @illegalcall and @Pulkit7070 here.

For faster context or live questions, you can also join the AO Discord.

Join the session here:
https://discord.gg/H6ZDcUXmq

Come by if you want to see what is being built, ask questions, or just hang around with the community.

@AllBeingsFuture

Copy link
Copy Markdown

Superseded by #3026 — same intake fix for #2921, plus cherry-pick of #3014 migration renumber so CI can pass (main contamination: goose duplicate version 25). Could not push to original head branch (no write access).

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

Labels

None yet

Projects

None yet

4 participants