fix(mothership): never sweep a Chat run while one of its Sim tools is executing - #8482
Conversation
… executing The orphaned-run sweep settled a leased run once it had gone an hour without a status write and no controller held its chat lock. A Sim tool call writes nothing to its run while it executes; only its execution lease heartbeat shows it is alive. So a long tool call (a workflow run can take well over an hour) could have its run settled as interrupted while the worker still held the run, closing tool admission under it. The default one-hour run deadline masked this. The sweep now skips a leased run with an unsettled, unrevoked tool execution whose lease has not expired, both when it selects candidates and in the guarded update. It also locks the run rows before that update, so a tool admission (which locks its run row) either commits a lease the update then sees, or finds the run settled and is refused.
|
@cubic-dev-ai review this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 2 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
|
…eep settles it A lease heartbeat writes only the tool row, so one that passed its expiry check before the lease ran out could commit after the sweep read the old lease and settled the run. The sweep now locks those tool rows after the run rows, so the guarded update sees a committed renewal and a later heartbeat finds the lease expired. Lock-holder tests release on failure.
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 2 files
Confidence score: 4/5
- In
apps/sim/lib/mothership/async-runs/orphaned-runs.integration.ts, a timed-out lock barrier can leave a claim or sweep running after the test exits, mutating shared Redis and database state while the next test starts; stop or await the background promise on timeout.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/sim/lib/mothership/async-runs/orphaned-runs.integration.ts">
<violation number="1" location="apps/sim/lib/mothership/async-runs/orphaned-runs.integration.ts:245">
P2: When a lock barrier times out, `run` throws but its claim or sweep promise keeps running after the test exits. That background sweep can update the shared Redis cursor and database while a following test is starting, contaminating later tests; track and await every queued operation in the cleanup path.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
Summary
Problem. The orphaned-run sweep could settle a Chat run as interrupted while one of its Sim tools was still executing, which closes tool admission under a worker that still holds the run.
Root cause. The sweep treats a leased run as orphaned once it has gone past the recovery window (
ORPHANED_RUN_GRACE_MS, one hour) without a status write and no controller holds its chat lock. A Sim tool call writes nothing to its run while it executes. The only sign that it is alive is its execution lease heartbeat, and the sweep never checked it. A tool call that runs longer than the window, such as a long workflow run, therefore looked idle. The default one-hour run deadline hid this.Fix.
execution_lease_expires_at > clock_timestamp()). The same predicate guards both candidate selection and the guarded settle update.settleRunsnow locks the candidate run rows (in id order, after the chat rows, matching the controller claim's lock order) before the guarded update. Tool admission locks its run row, so an admission racing the sweep either commits a lease that the update then sees, or finds the run already settled and is refused.settleRunsthen locks the candidate runs' unsettled tool execution rows, after the run rows. A lease heartbeat writes only the tool row, so a renewal that commits first is seen by the guarded update, and a later heartbeat finds the lease expired.Behaviour changes
Test plan
orphaned-runs.integration.ts)orphaned-runs.tsand pass with the fix (confirmed red, then green)orphaned-runsintegration suite passes (20/20) against disposable Postgres and Redis (bun run test:integration lib/mothership/async-runs/orphaned-runs)bun run type-check(apps/sim), biome,check:test-patterns