Files
PaperClipAI/server
Nicolás RodriguesandPaperclip 0b86889b6f Clear stale checkoutRunId on run finalization and add backstop sweeper
Fixes issues becoming permanently locked to a dead checkoutRunId after their
owning heartbeat run terminates: activeRun appears null but every checkout
returns 409. Root cause is that releaseIssueExecutionAndPromote clears the
execution lock columns but never clears checkoutRunId, and the existing
adoptStaleCheckoutRun helper's WHERE clause is too narrow (requires same
agent + status='in_progress') to cover reassignment, monitor-driven status
changes, or pickup by a different agent.

- heartbeat.releaseIssueExecutionAndPromote: also clears checkoutRunId when
  it points at the terminating run, scoped by an idempotent WHERE on the
  matching run id.
- issues service: new clearCheckoutRunIfTerminal helper symmetric to
  clearExecutionRunIfTerminal, with no assignee/status precondition since a
  terminal run holds no real claim. Wired into the same call sites as the
  execution-lock helper (checkout, assertCheckoutOwner, release).
- recovery service: new sweepStaleIssueLocks pass that clears lock columns
  on any issue whose checkoutRunId or executionRunId references a terminal
  heartbeat run, with one activity_log row per cleared issue. Hooked into
  the startup recovery sequence and the periodic heartbeat timer alongside
  reconcileStrandedAssignedIssues.
- Tests: route-level test for the stale-checkout self-heal on the next
  checkout attempt, plus service-level coverage for the sweeper across
  terminal-only, running-only, mixed, and idempotency cases.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-14 17:36:38 -03:00
..
2026-03-12 13:09:22 -05:00