agent-orchestrator/backend/internal
Khushi Diwan 96d1649e46
fix(review): serialize concurrent triggers per worker to stop reviewer double-spawn (#246)
* fix(review): serialize concurrent triggers per worker to stop double-spawn

Engine.Trigger was a read-then-write (idempotency check -> reviewer spawn ->
InsertReviewRun) with no serialization and no backing constraint. Two near-
simultaneous triggers for the same worker at the same head SHA both passed the
GetReviewRunBySessionAndSHA check, both spawned a reviewer against the same
deterministic review-<id> handle, and both inserted a running run for one commit.

Add a per-worker keyed mutex (lockWorker) held across the whole Trigger body, so
the loser re-reads the freshly-recorded run and short-circuits to Created:false
instead of spawning. Back it with a partial unique index on
review_run(session_id, target_sha) (migration 0013) as a cross-restart safety
net; rows with an empty target_sha (head not yet observed) are excluded so they
are not blocked.

Adds a concurrency test asserting N simultaneous triggers spawn once and record
one run.

Closes #242

* fix(review): make migration 0013 dedup-safe and handle the unique conflict in Trigger

Pre-#242 daemons can already hold duplicate (session_id, target_sha)
review_run rows, on which CREATE UNIQUE INDEX fails and wedges startup.
Migration 0013 now collapses each duplicate group to a single survivor
(a completed pass over a still-running one, then newest by created_at)
before building the index.

Trigger now treats a unique-constraint hit as a fallback rather than an
error: InsertReviewRun maps it to the new domain.ErrDuplicateReviewRun
sentinel, and Trigger re-reads GetReviewRunBySessionAndSHA and returns
that run with Created:false instead of surfacing a raw error after the
reviewer may already have launched.
2026-06-17 00:43:10 +05:30
..
adapters fix(spawn): map invalid branch name to 400 instead of opaque 500 (#213) 2026-06-15 14:50:58 +05:30
cdc feat: ao session claim-pr + spawn --claim-pr wiring (#101) 2026-06-06 00:01:03 +05:30
cli feat(review): configurable AO code review backend (V1) (#192) (#197) 2026-06-15 01:17:17 +05:30
config Use ~/.ao as canonical state home (#233) 2026-06-14 22:52:25 +05:30
daemon feat(review): configurable AO code review backend (V1) (#192) (#197) 2026-06-15 01:17:17 +05:30
daemonmeta fix(cli): verify daemon ownership before stop signal 2026-06-01 01:45:04 +05:30
domain fix(review): serialize concurrent triggers per worker to stop reviewer double-spawn (#246) 2026-06-17 00:43:10 +05:30
httpd fix(project): detect the repo's default branch on add (#209) 2026-06-15 14:51:07 +05:30
integration fix: 7 bugs from discussion #149 smoke walk (envelope, spawn, CDC, observer) (#153) 2026-06-07 07:35:46 +05:30
lifecycle fix(session): stamp session timestamps in UTC (#215) 2026-06-15 14:51:12 +05:30
notify feat: add notifications v1 (#181) 2026-06-14 20:02:32 +05:30
observe feat(tracker): ApplyTrackerFacts reducer + shared observer skeleton (#112) (#116) 2026-06-07 21:29:00 +05:30
ports fix(spawn): map invalid branch name to 400 instead of opaque 500 (#213) 2026-06-15 14:50:58 +05:30
processalive refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
review fix(review): serialize concurrent triggers per worker to stop reviewer double-spawn (#246) 2026-06-17 00:43:10 +05:30
runfile refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
service fix(project): detect the repo's default branch on add (#209) 2026-06-15 14:51:07 +05:30
session_manager fix(session): stamp session timestamps in UTC (#215) 2026-06-15 14:51:12 +05:30
storage/sqlite fix(review): serialize concurrent triggers per worker to stop reviewer double-spawn (#246) 2026-06-17 00:43:10 +05:30
terminal refactor(terminal): per-client zellij attach replaces shared PTY + replay ring (#194) 2026-06-12 15:19:38 +05:30