Commit f62ed0d
fix(concurrency): audit-driven fixes for parallel-conversation invariants
22 confirmed findings from a 33-agent adversarially-verified audit of the
v3.66 merge against the fork's multi-conversation contracts. Highlights:
TaskHistoryStore
- Process-wide shared instance per storage path (getOrCreate/release with
refcounting and multi-subscriber onWrite). Separate per-provider stores
each ran startup delegation reconciliation over the same files, so
'Open in New Tab' could sever a delegation that was live in the sidebar.
- reconcileDelegationState re-reads parent+child fresh from disk before
repairing and skips parents written within a 60s grace window (a fresh
delegation whose child hasn't saved yet is indistinguishable from a
crash orphan). initialize() is now idempotent.
- upsert gains preserveExistingStatus: the status decision happens inside
the write lock, closing the TOCTOU where a straggler saveClineMessages
silently reverted 'delegated' back to 'active'.
- interrupted → delegated is now a valid transition (a cancelled-then-
resumed subtask could never spawn subtasks again).
Delegation
- delegateParentAndOpenChild is fully task-scoped: no more global
handleModeSwitch (which raced concurrent delegations and polluted the
visible conversation's mode/profile); the child receives taskMode and a
task-scoped apiConfiguration resolved from the mode-bound profile, with
the parent's config as fallback. Focus follows the parent's visibility,
so a background delegation no longer steals the webview; rollback pops
the child by taskId and restores the parent without grabbing focus.
- activateProviderProfile only emits ProviderProfileChanged when
updateCurrentTask !== false — a background restore no longer swapped
the visible task's API handler and sticky profile mid-stream.
- showTaskWithId redirects 'delegated' parents to their live (or
resumable) child instead of live-resuming a lineage the child owns;
reopenParentFromDelegation replaces a live parent instance instead of
overwriting its in-memory history mid-stream.
- cancelTask: rehydrate guard resolves by taskId (protects background
conversations); a cancelled child that is itself delegated keeps its
status; the fail-closed sever write preserves status and only poisons
the reopen guard after it lands.
Queued/steered messages
- Queue snapshot in cancel is re-captured just before abort and restored
as a 3-source merge (pre-abort snapshot, old instance's late arrivals,
replacement's own queue) deduped by id — closing two loss windows.
- Steer messages consumed into a turn's volatile content are tracked
until persisted; a mid-stream failure retry re-enqueues them and a
cancel includes them in the snapshot (previously silently dropped while
showing as delivered in the transcript).
- api_req_failed asks treat an auto-dispatched queued message as feedback
+ retry instead of misreading it as decline-to-retry and dropping it;
manual Retry now sets userMessageWasRemoved so the retried request
actually carries the user message.
- editQueuedMessage re-enqueues the edit when the original was dequeued
mid-edit; image mentions resolve against the target task's cwd and
ignore rules, not the visible task's.
Webview
- Completion checkpoint diff/restore route by taskId (SeeNewChangesButtons
now sends it) — restore no longer cancels and rewinds whichever task
happens to be visible.
- Answered asks clear the stale clineAsk state, so Enter during tool
execution queues instead of answering an already-resolved ask; draft
conversations queue by their draft id (adopted by the created task).
Misc
- semble downloader dedups concurrent downloads per storage dir.
Tests updated to the new contracts and extended: store sharing/refcount,
preserveExistingStatus TOCTOU, repair grace window, task-scoped delegation
profile resolution, focus isolation, merged queue restore, orphaned-
response routing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>1 parent 494e8eb commit f62ed0d
18 files changed
Lines changed: 1446 additions & 340 deletions
File tree
- packages/types/src
- src
- __tests__
- core
- task-persistence
- __tests__
- task
- __tests__
- webview
- __tests__
- services/code-index/semble
- webview-ui/src/components/chat
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
85 | 86 | | |
86 | 87 | | |
87 | 88 | | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
88 | 101 | | |
89 | 102 | | |
90 | 103 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
120 | 120 | | |
121 | 121 | | |
122 | 122 | | |
123 | | - | |
124 | | - | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
125 | 127 | | |
126 | 128 | | |
127 | 129 | | |
| |||
410 | 412 | | |
411 | 413 | | |
412 | 414 | | |
413 | | - | |
| 415 | + | |
414 | 416 | | |
415 | 417 | | |
416 | 418 | | |
0 commit comments