Skip to content

🤖 fix: delegated createWorkspaceTurn early exits leave orphan workspaces without a handle #4819

Closed

Description

@ThomasK33

Problem

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).

Refs #4453, #4455


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $12.63

Activity

  1. linear-code commented on Sep 27, 2026

    @linear-code
  2. ThomasK33 commented on Sep 27, 2026

    @ThomasK33
    MemberAuthor

    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

  3. ThomasK33 commented on Sep 27, 2026

    @ThomasK33
    MemberAuthor

    #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

  4. ThomasK33 commented on Sep 28, 2026

    @ThomasK33
    MemberAuthor

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions