Skip to content

Commit c37821d

Browse files
sweetmantechclaude
andcommitted
fix(sandbox): KISS lifecycle claim — combiner under lib/sessions, IfMatch via updateSession
Two review fixes: 1) Move `claimSessionLifecycleRunId.ts` from `lib/supabase/sessions/` to `lib/sessions/`. The combiner doesn't directly query Supabase — it composes two underlying helpers, so per api convention it belongs alongside other domain composers, not under the Supabase namespace. 2) KISS: delete `claimSessionLifecycleRunIdIfMatch.ts` and use the existing `updateSession` helper for the lease-refresh path. The refresh writes the same `lifecycle_run_id` value back, so an unconditional `updateSession({ lifecycle_run_id: runId })` does the work — accepts a small race where a concurrent stale-reclaim could be overwritten, but the kick path (which DOES use the atomic `claimSessionLifecycleRunIdIfNull`) remains the primary concurrency guard. Two callers (runKick, computeLifecycleWakeDecision) updated to import from the new path. No behavior change at the workflow level. Tests: 2579 / 2579 still pass. lint + tsc clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 22095c2 commit c37821d

5 files changed

Lines changed: 34 additions & 56 deletions

File tree

app/workflows/computeLifecycleWakeDecision.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getLifecycleDueAtMs } from "@/lib/sandbox/getLifecycleDueAtMs";
22
import { hasRuntimeSandboxState } from "@/lib/sandbox/hasRuntimeSandboxState";
3-
import { claimSessionLifecycleRunId } from "@/lib/supabase/sessions/claimSessionLifecycleRunId";
3+
import { claimSessionLifecycleRunId } from "@/lib/sessions/claimSessionLifecycleRunId";
44
import { selectSessions } from "@/lib/supabase/sessions/selectSessions";
55

66
interface LifecycleWakeDecision {

lib/sandbox/runKick.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { createLifecycleRunId } from "@/lib/sandbox/createLifecycleRunId";
44
import { isLifecycleRunStale } from "@/lib/sandbox/isLifecycleRunStale";
55
import { reclaimStaleLease } from "@/lib/sandbox/reclaimStaleLease";
66
import { shouldStartLifecycle } from "@/lib/sandbox/shouldStartLifecycle";
7-
import { claimSessionLifecycleRunId } from "@/lib/supabase/sessions/claimSessionLifecycleRunId";
7+
import { claimSessionLifecycleRunId } from "@/lib/sessions/claimSessionLifecycleRunId";
88
import { selectSessions } from "@/lib/supabase/sessions/selectSessions";
99
import { updateSession } from "@/lib/supabase/sessions/updateSession";
1010
import type { SandboxLifecycleReason } from "@/lib/sandbox/sandboxLifecycleTypes";
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { claimSessionLifecycleRunIdIfNull } from "@/lib/supabase/sessions/claimSessionLifecycleRunIdIfNull";
2+
import { updateSession } from "@/lib/supabase/sessions/updateSession";
3+
4+
/**
5+
* Combiner for the two `lifecycle_run_id` claim operations. Lives in
6+
* `lib/sessions/` (not `lib/supabase/sessions/`) because it does not
7+
* directly query Supabase — it composes two underlying helpers.
8+
*
9+
* - `expected = null` (initial claim): atomic `claimSessionLifecycleRunIdIfNull`
10+
* that fails when the row already has a lease.
11+
* - `expected = runId` (workflow refresh): plain `updateSession` write that
12+
* re-asserts the current lease without a conditional WHERE. Accepts a
13+
* small race where a stale-reclaim could be overwritten — the kick path
14+
* (which DOES use the atomic IfNull check) remains the primary
15+
* concurrency guard.
16+
*
17+
* @param sessionId - The session id to claim against.
18+
* @param runId - The new lease value to write.
19+
* @param expected - The expected current value; defaults to null.
20+
* @returns true on success, false when an initial claim was already taken.
21+
*/
22+
export async function claimSessionLifecycleRunId(
23+
sessionId: string,
24+
runId: string,
25+
expected: string | null = null,
26+
): Promise<boolean> {
27+
if (expected === null) {
28+
return claimSessionLifecycleRunIdIfNull(sessionId, runId);
29+
}
30+
const updated = await updateSession(sessionId, { lifecycle_run_id: runId });
31+
return updated !== null;
32+
}

lib/supabase/sessions/claimSessionLifecycleRunId.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

lib/supabase/sessions/claimSessionLifecycleRunIdIfMatch.ts

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)