Skip to content

fix: prevent parent task state loss during orchestrator delegation#11281

Merged
hannesrudolph merged 2 commits intomainfrom
fix/task-persistence-parent-disappears
Feb 7, 2026
Merged

fix: prevent parent task state loss during orchestrator delegation#11281
hannesrudolph merged 2 commits intomainfrom
fix/task-persistence-parent-disappears

Conversation

@hannesrudolph
Copy link
Collaborator

@hannesrudolph hannesrudolph commented Feb 7, 2026

Fixes #11172

Summary

Fixes the "parent task disappears in orchestrator mode" bug where parent tasks would rewind to a stale state after a child subtask returned, causing the parent to appear "lost" or "disappeared."

Root Causes

  1. JsonStreamStringify lazy mutation bug: saveApiConversationHistory() and saveClineMessages() passed live array references to safeWriteJson(), which uses JsonStreamStringify that lazily reads elements during streaming. Elements pushed after construction were silently dropped.

  2. Silent error swallowing: Both save functions swallowed all write errors, so flushPendingToolResultsToHistory() cleared in-memory state even when the disk write failed. The parent was then destroyed from the stack, leaving only stale disk state.

  3. Destructive getTaskWithId(): When api_conversation_history.json was missing, it permanently deleted the task's HistoryItem from globalState — turning transient filesystem errors into permanent data loss.

  4. Unprotected read paths: readTaskMessages() had no try/catch around JSON.parse; readApiMessages() caught parse errors but re-threw them.

Changes

  • src/core/task/Task.ts: Snapshot arrays with structuredClone() before save for deep isolation from JsonStreamStringify's lazy reads; return boolean from save methods; guard flushPendingToolResultsToHistory() to retain in-memory state on failure; add retrySaveApiConversationHistory() with exponential backoff (3 attempts: 100ms, 500ms, 1500ms)
  • src/core/webview/ClineProvider.ts: Make getTaskWithId() non-destructive (return empty history instead of deleting task); add try/catch around JSON.parse in getTaskWithId(); add flush retry with user-visible warning in delegateParentAndOpenChild()
  • src/core/task-persistence/taskMessages.ts: Add try/catch around JSON.parse in readTaskMessages()
  • src/core/task-persistence/apiMessages.ts: Return [] instead of re-throwing in readApiMessages() catch blocks

Tests

  • New and updated tests across 5 test files covering all fix scenarios
  • Full test suite passes (5,470 tests across 371 files, 0 regressions)

@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. bug Something isn't working labels Feb 7, 2026
@roomote
Copy link
Contributor

roomote bot commented Feb 7, 2026

Rooviewer Clock   See task

Reviewed changes in a6537e6 (rebased). No new issues found. The PR-specific fixes (structuredClone snapshots, boolean save returns, guarded flushPendingToolResultsToHistory, non-destructive getTaskWithId, delegation retry with user warning, graceful JSON.parse handling) remain correct and well-tested. Rebased changes from main (Bedrock AI SDK refactor, baseten provider, disabledTools, dependency updates) are orthogonal and introduce no conflicts.

  • Snapshot arrays before safeWriteJson to prevent JsonStreamStringify lazy-read mutation
  • Boolean return from save methods with guarded flushPendingToolResultsToHistory
  • Non-destructive getTaskWithId (no longer deletes task on missing file)
  • Graceful JSON.parse error handling in readTaskMessages / readApiMessages
  • Retry logic in delegateParentAndOpenChild with CRITICAL logging
  • 13 new tests covering all fix scenarios
Previous reviews

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

- Snapshot arrays before passing to JsonStreamStringify to prevent
  lazy-read mutation bugs in saveApiConversationHistory/saveClineMessages
- Return boolean success/failure from save methods instead of silently
  swallowing errors
- Guard flushPendingToolResultsToHistory to retain in-memory state
  when disk save fails
- Make getTaskWithId non-destructive (no longer deletes task from
  state when api_conversation_history.json is missing)
- Add try/catch to readTaskMessages and readApiMessages for corrupted
  JSON graceful degradation
- Add flush retry with CRITICAL logging in delegateParentAndOpenChild
- Snapshot arrays with structuredClone before JsonStreamStringify to prevent lazy-read race
- Return boolean from save methods; only clear userMessageContent on success
- Make getTaskWithId non-destructive (return [] instead of deleting task on missing file)
- Add try/catch around JSON.parse in read paths
- Add retrySaveApiConversationHistory with exponential backoff
- Add comprehensive test coverage (113 tests pass)

Fixes #11172
@hannesrudolph hannesrudolph force-pushed the fix/task-persistence-parent-disappears branch from d54e340 to a6537e6 Compare February 7, 2026 15:49
@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Feb 7, 2026
@hannesrudolph hannesrudolph merged commit 6826e20 into main Feb 7, 2026
15 checks passed
@hannesrudolph hannesrudolph deleted the fix/task-persistence-parent-disappears branch February 7, 2026 22:24
@github-project-automation github-project-automation bot moved this from New to Done in Roo Code Roadmap Feb 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Parent tasks disappear from history when files temporarily unavailable, orphaning child tasks

2 participants