Skip to content

fix(flow-chat): guard peer turn, cancel, queue and history state races - #3878

Merged
bobleer merged 2 commits into
openBitFun:mainfrom
bobleer:bob/fix-flow-chat-peer-races
Nov 12, 2026
Merged

bobleer merged 2 commits into
openBitFun:mainfrom
bobleer:bob/fix-flow-chat-peer-races

Conversation

@bobleer

@bobleer bobleer commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fix several state races in the flow chat that show up mostly on peer device surfaces.

  • Stale DialogTurnStarted. A delayed start event could reactivate a completed/cancelled/errored turn or supersede a newer admitted turn. It is now dropped.
  • Cancellation ownership. USER_CANCEL awaited imports before resolving the target, so it could send the cancel to a newly selected device, and a failed old cancel moved a newer turn back to processing. Cancellation now captures its surface and turn, checks a cancellation generation, and never rolls back on behalf of an older request. LocalSessionDriver.cancel ignores a late outcome for another turn.
  • Pending queue. remove/setStatus acted on the currently active surface, so a send settling after a device switch mutated the wrong queue. Peer queues were gated by local Runtime attach fences and never drained; the cancel gate was never released by an authoritative terminal record; two drains could overlap. Queue ops are now surface-scoped, peer queues drain once canonical history is ready, and an uncertain submission after a surface switch is kept as a failed draft instead of being dropped or resubmitted.
  • History catalog revisions. A catalog revision change kept old loadedRanges/activeRange, so late pages from the old revision were accepted, and a live tail turn past the persisted catalog returned not-found. Cached windows are invalidated on revision change, stale retries use the current generation, and the reader reacquires its range before extending it.
  • Lost peer acknowledgements. A peer HostInvoke mutation whose reply was lost surfaced as a generic error and a peer delete that actually succeeded was reported as failed. Such failures now carry the existing outcome_unknown: code, and an uncertain peer deletion is settled by an authoritative metadata read.

Type and Areas

Type: bug fix

Areas: web UI (flow chat, peer device adapter)

Motivation / Impact

Users switching between devices or cancelling during peer sessions could see revived turns, wrong-device cancels, stuck or duplicated queued messages, broken history scrolling after a catalog refresh, and false "delete failed" errors.

Verification

  • npx vitest run src/flow_chat/store/FlowChatStore.test.ts src/flow_chat/services/flow-chat-manager src/flow_chat/state-machine/SessionStateMachine.test.ts src/flow_chat/components/modern/ModernFlowChatContainer.history-restore.test.tsx src/infrastructure/api/adapters/peer-device-adapter.test.ts in src/web-ui: all new tests pass. 4 existing PendingQueueModule localStorage persistence tests fail identically on unmodified main in my environment, so they are unrelated to this change.
  • tsc --noEmit in src/web-ui

Remote scenarios: Peer Device Mode paths covered by unit tests with mocked transports only.

Reviewer Notes

AI-assisted change, lightly tested as listed above.

Checklist

  • This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts.
  • Relevant verification is recorded above, or skipped checks are explained.
  • User-facing strings, docs, and locales are updated where applicable.

- Drop a delayed DialogTurnStarted that would revive a settled turn or
  supersede a newer admitted turn.
- Bind cancellation to the turn and device surface it was issued for, so a
  late failure cannot roll back a newer turn or reach another device.
- Run pending queue status/remove on the surface where the send began, let
  peer queues drain once canonical history is ready, and keep an uncertain
  submission as a failed draft instead of dropping or resubmitting it.
- Invalidate cached history windows when the turn catalog revision changes
  and reacquire the reader's range before extending it.
- Report lost peer mutation acknowledgements as outcome_unknown and settle an
  uncertain peer session deletion by reading the metadata back.
@bobleer
bobleer merged commit f032e02 into openBitFun:main Nov 12, 2026
15 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant