Problem
Managed open_workspace(..., mode="worktree") sessions are persisted as status="active", but current WorkspaceStore has no release/close transition. Worktree opens also bypass the conversation checkout binding path, so persisted managed sessions can remain active indefinitely after the host has actually finished with them.
This makes it impossible for an external cleanup service to distinguish a genuinely live DevSpace lease from an abandoned/completed managed worktree without falling back to unsafe heuristics such as age or mtime.
Current main / 1.0.8 behavior I rechecked:
createSession() always writes status="active".
WorkspaceStore exposes createSession/getSession/touchSession but no terminal transition.
getWorkspace() can rehydrate a persisted session by ID and does not currently reject terminal state because no terminal state exists.
- MCP transport idle cleanup closes transports, not workspace sessions. Treating a transport timeout as workspace completion would be unsafe because a paused/reconnecting host is not proof that the coding task is terminal.
The result in long-running installations is ghost metadata and, more importantly, no lifecycle authority that a zero-loss worktree GC can consume.
Proposed narrow direction
Add a generic workspace release primitive rather than deleting worktrees inside DevSpace:
- Define explicit workspace lifecycle state, at minimum
active -> released, with a terminal timestamp/reason.
- Make the release transition atomic / idempotent and invalidate subsequent use of that workspace ID.
- Expose an explicit
close_workspace (or equivalent) operation so a host/model can release a workspace only when the task is genuinely terminal.
- Refuse release while DevSpace still owns a running process session for the workspace.
- Reconcile impossible ghost rows conservatively: an
active managed session whose recorded worktree root no longer exists can become a terminal missing/reconciled state. Do not infer release for an existing worktree from age, mtime, or MCP transport timeout.
- Keep actual worktree deletion out of this primitive. A separate GC layer can require the released state plus Git cleanliness, untracked-file checks, process/FD/lock checks, and integration/merge proof before using normal
git worktree remove.
Why release instead of automatic deletion
DevSpace cannot generically know the repository's accepted integration ref or whether a detached HEAD commit has been accepted elsewhere. Marking the lease terminal is portable; deciding that the worktree contents are disposable is repository policy and should remain a separate, fail-closed step.
Acceptance criteria
- Fresh active managed worktree remains active.
- Explicit close produces a durable terminal/released state and does not delete the worktree or branch/commit.
- Dirty/unmerged source is not deleted by close.
- Release fails closed while a DevSpace process session is still running for that workspace.
- A released workspace ID cannot be silently rehydrated as active.
- Missing physical managed worktrees are reconciled without deleting or guessing about existing worktrees.
- Restart/interruption does not automatically release an existing managed worktree merely because the server restarted.
- Tests cover explicit release, idempotency/race behavior, running-process protection, restart persistence, and missing-root reconciliation.
- Reconciliation work is bounded; no unbounded filesystem walk is required.
This is intentionally a lifecycle/reliability fix, not a request for age-based GC or automatic source deletion.
Problem
Managed
open_workspace(..., mode="worktree")sessions are persisted asstatus="active", but currentWorkspaceStorehas no release/close transition. Worktree opens also bypass the conversation checkout binding path, so persisted managed sessions can remain active indefinitely after the host has actually finished with them.This makes it impossible for an external cleanup service to distinguish a genuinely live DevSpace lease from an abandoned/completed managed worktree without falling back to unsafe heuristics such as age or mtime.
Current
main/ 1.0.8 behavior I rechecked:createSession()always writesstatus="active".WorkspaceStoreexposescreateSession/getSession/touchSessionbut no terminal transition.getWorkspace()can rehydrate a persisted session by ID and does not currently reject terminal state because no terminal state exists.The result in long-running installations is ghost metadata and, more importantly, no lifecycle authority that a zero-loss worktree GC can consume.
Proposed narrow direction
Add a generic workspace release primitive rather than deleting worktrees inside DevSpace:
active->released, with a terminal timestamp/reason.close_workspace(or equivalent) operation so a host/model can release a workspace only when the task is genuinely terminal.activemanaged session whose recorded worktree root no longer exists can become a terminalmissing/reconciled state. Do not infer release for an existing worktree from age, mtime, or MCP transport timeout.git worktree remove.Why release instead of automatic deletion
DevSpace cannot generically know the repository's accepted integration ref or whether a detached HEAD commit has been accepted elsewhere. Marking the lease terminal is portable; deciding that the worktree contents are disposable is repository policy and should remain a separate, fail-closed step.
Acceptance criteria
This is intentionally a lifecycle/reliability fix, not a request for age-based GC or automatic source deletion.