[BUG] Parent-child task delegation across parallel tabs may lose state
Problem
When a parent task is in Tab A and a child task opens in Tab B (parallel tabs), delegateParentAndOpenChild() may produce state inconsistency due to view-local state isolation. PR #909's viewLocalState buffer gives each tab its own independent mode/apiConfig, but the delegation logic's atomicReadAndUpdate pattern only considers single-instance scenarios (#357), not cross-tab scenarios.
Evidence
Code locations:
Reproduction:
1. Open Tab A (sidebar) → start Task X (parent task)
2. In Tab A, trigger delegation: parent delegates to child task Y
3. Child task Y opens in Tab B (editor tab) — uses view-local state from Tab B
4. Parent task X continues in Tab A — uses view-local state from Tab A
5. If user switches mode/apiConfig in Tab B while delegation is active:
- Tab B's viewLocalState changes
- Tab A still sees old viewLocalState (isolated)
- Delegation state may become inconsistent (parent thinks child is in "code" mode, but it's actually "architect")
Current test coverage:
- ✅ Single instance:
ClineProvider.sticky-mode.spec.ts — concurrent mode switches on same task
- ❌ Cross-instance: No test for delegation across parallel tabs with different view-local states
Root Cause
PR #909's view-local state isolation design:
// ClineProvider.ts (simplified)
class ClineProvider {
private viewLocalState: Partial<ExtensionState> = {}
getState() {
return { ...globalState, ...this.viewLocalState } // Local overrides global
}
}
When delegation occurs:
- Tab A (parent):
delegateParentAndOpenChild() → reads parent's viewLocalState
- Creates child task with mode/apiConfig from Tab A's viewLocalState
- Child opens in Tab B → Tab B has its own viewLocalState (different mode/apiConfig)
- Parent continues in Tab A → still sees old state
- Inconsistency: parent thinks child is using "code" mode, but actually child in Tab B is using "architect"
This issue is similar to the delegateParentAndOpenChild serialization in #357 [Epic 2] Task Lifecycle Fixes, but #357 only handles single-instance concurrency.
Proposed Fix
Option A: Pass explicit state snapshot during delegation (recommended)
// In ClineProvider.ts — capture state at delegation time, not read from viewLocalState
async delegateParentAndOpenChild(parentTaskId: string): Promise<void> {
// Capture current state explicitly (don't rely on viewLocalState which may change)
const snapshot = {
mode: this.viewLocalState.mode ?? this.globalState.mode,
apiConfigName: this.viewLocalState.currentApiConfigName ?? this.globalState.currentApiConfigName,
// ... other fields needed for child task
}
await atomicReadAndUpdate(async () => {
const parent = await this.getTask(parentTaskId)
const child = await this.createChildTask(parent, snapshot)
// Child uses explicit snapshot, not viewLocalState
})
}
Option B: Cross-tab state sync on delegation
// When child opens in Tab B, sync parent's state to Tab B's viewLocalState
async delegateParentAndOpenChild(parentTaskId: string): Promise<void> {
const parentSnapshot = this.captureState()
await atomicReadAndUpdate(async () => {
// ... create child task
// Notify Tab B (via VSCode window events or postMessage) to sync state
vscode.postMessage({
type: 'syncDelegationState',
snapshot: parentSnapshot
})
})
}
// In ExtensionStateContext.tsx — handle syncDelegationState
case 'syncDelegationState': {
setViewLocalState(prev => ({ ...prev, ...payload.snapshot }))
break
}
Option C: Add "delegated" flag to viewLocalState
- When a task is delegated, mark
viewLocalState.isDelegated = true
- Delegated tasks use global state (no view-local override)
- Simple but limiting: delegated tasks cannot have their own per-view mode
Scope
- Impact: Users who delegate tasks across parallel tabs — delegation may lose context or use wrong mode/apiConfig
- Risk: Low-Medium — Option A is safest (explicit snapshot), Option B requires cross-tab communication
- Priority: Medium-High (delegation is core feature, state loss causes confusion)
Related Issues
Testing Requirements
-
Add test in ClineProvider.parallelMode.spec.ts:
it("delegates parent task from Tab A to child task in Tab B with correct state", async () => {
// Create 2 ClineProvider instances (Tab A, Tab B)
// Tab A: start parent task, set mode = "architect"
// Tab A: delegate to child task
// Child opens in Tab B (with Tab B's viewLocalState)
// Verify child uses parent's state snapshot, not Tab B's viewLocalState
})
it("handles concurrent mode switch during delegation across tabs", async () => {
// Tab A: start delegation
// Tab B: switch mode/apiConfig simultaneously
// Verify delegation completes with correct state (no corruption)
})
-
Test delegation with different viewLocalState modes between parent/child tabs
-
Test concurrent delegateParentAndOpenChild + handleModeSwitch across tabs
[BUG] Parent-child task delegation across parallel tabs may lose state
Problem
When a parent task is in Tab A and a child task opens in Tab B (parallel tabs),
delegateParentAndOpenChild()may produce state inconsistency due to view-local state isolation. PR #909'sviewLocalStatebuffer gives each tab its own independent mode/apiConfig, but the delegation logic'satomicReadAndUpdatepattern only considers single-instance scenarios (#357), not cross-tab scenarios.Evidence
Code locations:
ClineProvider.ts:delegateParentAndOpenChild()— usesatomicReadAndUpdatefor state transitionsClineProvider.sticky-mode.spec.ts:it("should handle concurrent mode switches on the same task")— only tests the same instanceTaskHistoryStore.crossInstance.spec.ts: cross-instance concurrency pattern exists, but not tested for delegationReproduction:
Current test coverage:
ClineProvider.sticky-mode.spec.ts— concurrent mode switches on same taskRoot Cause
PR #909's view-local state isolation design:
When delegation occurs:
delegateParentAndOpenChild()→ reads parent's viewLocalStateThis issue is similar to the
delegateParentAndOpenChildserialization in #357 [Epic 2] Task Lifecycle Fixes, but #357 only handles single-instance concurrency.Proposed Fix
Option A: Pass explicit state snapshot during delegation (recommended)
Option B: Cross-tab state sync on delegation
Option C: Add "delegated" flag to viewLocalState
viewLocalState.isDelegated = trueScope
Related Issues
atomicReadAndUpdate)fix(webview): isolate parallel provider view stateTesting Requirements
Add test in
ClineProvider.parallelMode.spec.ts:Test delegation with different viewLocalState modes between parent/child tabs
Test concurrent
delegateParentAndOpenChild+handleModeSwitchacross tabs