Skip to content

fix(app,server): guard stop/fail writes against a concurrent force-stop - #235

Merged
abhishek-verma merged 1 commit into
mainfrom
fix/capture-force-stop-clobber-guard
Sep 4, 2026
Merged

fix(app,server): guard stop/fail writes against a concurrent force-stop#235
abhishek-verma merged 1 commit into
mainfrom
fix/capture-force-stop-clobber-guard

Conversation

@abhishek-verma

Copy link
Copy Markdown
Owner

Summary

Follow-up to #234 (already merged) — a final review pass on that PR now that the review session limit reset found two real gaps left over from it:

  • interruptMeetingCapture was hardened against racing a concurrent stop/force-stop (UPDATE ... WHERE status NOT IN ('stopped','error')), but stopMeetingCapture and failMeetingCapture — the two functions most likely to actually race force-stop, since force-stop exists specifically to be called while one of them looks stuck — never got the same guard. A stop or fail that resolves after a concurrent force-stop already finalized the session could silently overwrite its ended_at/status. Both now use the same guarded write, skipping the SSE announce too when they lose the race.
  • teardownCaptureSession's single-flight silently dropped a joining caller's flushRemainder preference in favor of whichever caller happened to start the shared task. The flag is now mutable on the shared entry, so a joiner requesting a flush can still get it as long as it arrives before that step actually runs.

Also two cleanups from the same review:

  • recorder.ts's local withDeadline duplicated with-runtime-message-timeout.ts's identical helper (just a hardcoded null fallback) — consolidated into one shared withDeadline, with withRuntimeMessageTimeout as a thin specialization.
  • CapturePage's Force stop button used the mutation's isPending across the whole component, so force-stopping session A also disabled the button while viewing session B; now scoped via the mutation's own variables.

Declined as accepted debt (non-blocking, cosmetic): recorder.ts still exceeds the 400-line pre-commit warning threshold (628 lines) — a candidate for splitting the start/stop race-tracking into its own module, but out of scope for this fix.

Test plan

  • bun run typecheck (apps/app, apps/server) passes
  • bun run lint / bun run fallow — no new warnings in changed files
  • apps/server: test:capture, test:api, test:agent, trust-invariants all pass
  • apps/app: full test suite passes

🤖 Generated with Claude Code

Final review pass on the merged capture-stop-hang PR found two real
gaps left over from that work:

- interruptMeetingCapture was hardened against racing a concurrent
  stop/force-stop (a conditional UPDATE ... WHERE status NOT IN
  ('stopped','error')), but stopMeetingCapture and failMeetingCapture —
  the two functions most likely to actually race force-stop, since
  force-stop exists specifically to be called while one of them looks
  stuck — never got the same guard. A stop or fail that resolves after
  a concurrent force-stop already finalized the session could silently
  overwrite its ended_at/status. Both now use the same guarded write,
  skipping the SSE announce too when they lose the race.
- teardownCaptureSession's single-flight silently dropped a joining
  caller's flushRemainder preference in favor of whichever caller
  happened to start the shared task. The flag is now mutable on the
  shared entry, so a joiner requesting a flush can still get it as long
  as it arrives before that step actually runs.

Also: recorder.ts's local withDeadline duplicated
with-runtime-message-timeout.ts's identical helper (just a hardcoded
null fallback) — consolidated into one shared withDeadline, with
withRuntimeMessageTimeout as a thin specialization. And CapturePage's
Force stop button used the mutation's isPending across the whole
component, so force-stopping session A also disabled the button while
viewing session B; now scoped via the mutation's own `variables`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the fix label Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

❌ Tests failed — 17/2179 failed

Suite Passed Failed Skipped
agent 592/602 10 0
build 25/25 0 0
⚠️ cli 0/0 0 0
eval 91/91 0 0
server-agent 492/492 0 0
server-api 170/171 0 1
server-browser 10/10 0 0
server-capture 36/36 0 0
server-integration 0/12 0 12
server-lib 342/350 7 1
server-memory 41/41 0 0
server-root 46/50 0 4
server-scheduler 27/27 0 0
server-tools 272/272 0 0
⚠️ trust-invariants 0/0 0 0
Failed tests
  • agenttestProvider — request body > forwards model-backed fields for non-ACP providers
  • agenttestProvider — request body > forwards ACP fields when present
  • agenttestProvider — request body > forwards acp-custom command for the probe spawn path
  • agenttestProvider — local server unreachable > wraps a fetch failure with a clear local-server-unreachable message
  • agentagentFetch > attaches the profile header
  • agentagentFetch > does not overwrite an explicit profile header
  • agentscheduled provider resolution > falls back through the configured default when an explicit scheduled provider is local runtime only
  • agentscheduled provider resolution > falls back through the configured default when an explicit refine provider is local runtime only
  • agentfetchMcpTools > lists tools without invoking Function-based schema compilation
  • agentfetchMcpTools > follows paginated tools/list responses
  • server-liblib/clients/llm/provider baseUrl forwarding (test-provider / refine-prompt path) > forwards a custom baseUrl for anthropic
  • server-liblib/clients/llm/provider baseUrl forwarding (test-provider / refine-prompt path) > forwards a custom baseUrl for openai
  • server-liblib/clients/llm/provider baseUrl forwarding (test-provider / refine-prompt path) > forwards a custom baseUrl for google
  • server-liblib/clients/llm/provider baseUrl forwarding (test-provider / refine-prompt path) > forwards a custom baseUrl for openrouter
  • server-liblib/clients/llm/provider baseUrl forwarding (test-provider / refine-prompt path) > forwards baseUrl for azure and it overrides resourceName
  • server-liblib/clients/llm/provider baseUrl forwarding (test-provider / refine-prompt path) > azure still resolves from resourceName when baseUrl is unset
  • server-liblib/clients/llm/provider baseUrl forwarding (test-provider / refine-prompt path) > azure throws when neither resourceName nor baseUrl is set

View workflow run

@abhishek-verma
abhishek-verma merged commit 8c863d7 into main Sep 4, 2026
18 of 21 checks passed
@abhishek-verma
abhishek-verma deleted the fix/capture-force-stop-clobber-guard branch September 4, 2026 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant