99d8d8f173
* 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>