Files
kandev/apps/backend/internal/github
João Salavisa 99d8d8f173 fix(github): handle UNIQUE collision in UpdatePRWatchBranchIfSearching (#1285)
* fix(github): handle UNIQUE collision in UpdatePRWatchBranchIfSearching

When a sibling PRWatch already owns the destination
(session_id, repository_id, branch) triple, the raw UPDATE tripped
the UNIQUE constraint and the change was logged as an error
("failed to update PR watch branch"). Wrap the update in a tx that
first checks for a sibling; if one exists, delete the source watch
(pr_number=0, no PR state to preserve) and cede branch ownership.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(github): close TOCTOU and cover sibling-has-PR collision case

Address PR review feedback on #1285:

- Catch SQLite "UNIQUE constraint failed" on the no-sibling UPDATE path
  and treat it as the same collision case (Greptile P2 inline). A
  concurrent CreatePRWatch that commits between our existence probe
  and the UPDATE would otherwise re-trigger the bug this PR fixes.
- Switch the existence probe from `SELECT id` + `siblingID` dummy to
  `SELECT 1` scanned into a typed `probe int` with an inline comment
  (Claude inline readability nit).
- Extract `dropSourceAndCommit` helper so the probe-hit branch and the
  TOCTOU fallback share one code path.
- Add `TestUpdatePRWatchBranchIfSearching_CollidesWithSiblingHasPR_DropsSource`
  exercising the realistic race: sibling has already found its PR
  (pr_number != 0) when source tries to migrate (Claude / Greptile P2 /
  cubic P3 all flagged the gap). Asserts sibling row + PR number
  survive untouched after source is dropped.

* test(github): cover missing-row no-op and reframe TOCTOU comment

Address Claude bot's follow-up suggestions on #1285:

- store.go: rewrite the UNIQUE-catch comment. The original wording
  implied an in-process race, but SQLite's writer pool is
  SetMaxOpenConns(1) so a concurrent in-process CreatePRWatch cannot
  commit between probe and UPDATE. Reframe as defensive belt-and-
  suspenders for an external SQLite writer / future pool changes.
- service_pr_watch_multi_branch_test.go: add
  TestUpdatePRWatchBranchIfSearching_MissingRow_NoOp covering the
  ErrNoRows -> tx.Commit() idempotency contract that the new
  transaction code introduces. Locks the prior UPDATE-by-id semantics
  of "0 rows affected, no error" for a nonexistent id.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 11:50:13 +01:00
..
2026-03-02 08:54:58 +00:00
2026-03-02 08:54:58 +00:00