You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
🤖 fix: delegated createWorkspaceTurn early exits leave orphan workspaces without a handle #4819
WorkspaceTurnManager.createWorkspaceTurn (mode "new") creates the target workspace first and writes the workspace-turn handle record later. Several exits in between return Err and keep the created workspace without any handle record (verified at main 9b1f7f9, src/node/services/workspaceTurnManager.ts):
agentTaskAi reawaken mismatch (~:1433);
resolveNodeAgentAiSettings throwing InvalidExplicitAiSettingError (~:1500), or any other error, which is rethrown;
the target archived during creation (persisted === "target_archived", ~:1597);
the live-owner lock or desktop admission failing before the record is persisted (~:1583-1594).
Only the owner-archived exit removes the workspace (removeWhileTaskTreeLocked). The parent's task(kind:"workspace") call fails and never receives the workspace ID. With no record carrying createdWorkspace, a mode "existing" retry cannot reach the workspace (invalid_scope), so the checkout and config entry are orphaned until the user removes them by hand. A crash in the same window has the same result.
#4453's PR only finalizes these orphans' default consent (their pending mark is cleared, so they stay off). It deliberately does not change their retention.
Proposed direction
Remove the unsuccessful workspace on these exits (it was created moments ago and its turn never started, so a forced removal is lossless, as the owner-archived exit already argues). Or persist the handle record before the first exit that can fail, so the workspace stays owner-owned and retryable. Cover the crash window as well (for example, a startup sweep keyed on the mux.taskHandleId tag of rows without a handle record whose live-owner lock is dead).
Picking this up (workspace-28). Plan: in createWorkspaceTurn, remove a workspace created by this call on every exit before its handle record persists, via the forced removeWhileTaskTreeLocked path the owner-archived exit already uses, folded into the existing pre-record finalizer so #4838's consent finalization and lock release stay intact. The crash window (startup sweep) is out of scope for this PR and stays tracked here.
Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high
#4841 merged as 5af34ae. Delegated creations that fail before their handle record persists now remove the workspace they created: invalid model, AI-settings throw, reawaken mismatch, target or owner archived, and lock or admission failure. The removal keeps a branch the creation only reused and waits for the aborted init. Still open here: the crash window in the same span, which needs a startup sweep of rows tagged mux.taskHandleId that have no handle record and a dead lock holder. SSH mirror branch refs are tracked on #4842.
Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $7.68
Verified on current main (7b6cd39): every early exit is covered, and #4841 (5af34ae) already fixed them, so this needs no new PR.
Between workspaceService.create and the handle record write, each exit runs through the function-scoped await using _creationFinalizer. While no record has persisted, the finalizer removes the created workspace with removeWhileTaskTreeLocked(..., { keepBranch: !createdTargetBranch }), clears its pending consent and releases the owner lock. The exits it covers:
a reawaken mismatch
InvalidExplicitAiSettingError, a runtime transport error, or any other throw from resolveNodeAgentAiSettings
the target or owner archived during creation
a failure of the live-owner lock or of desktop admission
an unexpected throw from target agent validation
An agent-validation error is deferred until after the record persists. After the record persists, every exit (admission failure, validation failure, send failure, pre-stream cancellation) settles the record with createdWorkspace: true, so a mode "existing" retry can still reach the workspace.
The only remaining gap is the crash window, which the coordinator accepted as a documented residual. It is tracked in #4983 (a startup sweep of rows tagged mux.taskHandleId that have no handle record and a dead lock holder). Closing this issue as fixed by #4841.
Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $25.30
Activity
linear-code commentedon Sep 27, 2026
MUX-309
ThomasK33 commentedon Sep 27, 2026
Picking this up (workspace-28). Plan: in
createWorkspaceTurn, remove a workspace created by this call on every exit before its handle record persists, via the forcedremoveWhileTaskTreeLockedpath the owner-archived exit already uses, folded into the existing pre-record finalizer so #4838's consent finalization and lock release stay intact. The crash window (startup sweep) is out of scope for this PR and stays tracked here.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:highThomasK33 commentedon Sep 27, 2026
#4841 merged as 5af34ae. Delegated creations that fail before their handle record persists now remove the workspace they created: invalid model, AI-settings throw, reawaken mismatch, target or owner archived, and lock or admission failure. The removal keeps a branch the creation only reused and waits for the aborted init. Still open here: the crash window in the same span, which needs a startup sweep of rows tagged mux.taskHandleId that have no handle record and a dead lock holder. SSH mirror branch refs are tracked on #4842.
Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high• Cost:$7.68🤖 fix: remove the workspace of a delegated creation that fails before…
ThomasK33 commentedon Sep 28, 2026
Verified on current main (7b6cd39): every early exit is covered, and #4841 (5af34ae) already fixed them, so this needs no new PR.
Between
workspaceService.createand the handle record write, each exit runs through the function-scopedawait using _creationFinalizer. While no record has persisted, the finalizer removes the created workspace withremoveWhileTaskTreeLocked(..., { keepBranch: !createdTargetBranch }), clears its pending consent and releases the owner lock. The exits it covers:InvalidExplicitAiSettingError, a runtime transport error, or any other throw fromresolveNodeAgentAiSettingsAn agent-validation error is deferred until after the record persists. After the record persists, every exit (admission failure, validation failure, send failure, pre-stream cancellation) settles the record with
createdWorkspace: true, so a mode"existing"retry can still reach the workspace.The only remaining gap is the crash window, which the coordinator accepted as a documented residual. It is tracked in #4983 (a startup sweep of rows tagged
mux.taskHandleIdthat have no handle record and a dead lock holder). Closing this issue as fixed by #4841.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high• Cost:$25.30