fix(host-service): don't reuse a tombstoned workspace on same-branch create

Merged
DI
3mo ago
+62-1

What & why

Fixes #6383, refs #6380.

Since #6296 turned workspace deletes into tombstones (archivedAt + archiveReason, row kept for the board's Merged/Deleted history), the create flow's branch-idempotency lookup findExistingWorkspaceByBranch still matched on (projectId, branch) with no archived filter — the one consumer #6296's "consumers updated" pass missed. Recreating a deleted workspace's name therefore "reused" the tombstone: exit 0, alreadyExists: true, no worktree created, and the desktop redirected to the dead row's id, which workspace.list filters out — landing users on the "Workspace not found" screen. CLI/SDK/MCP callers got a success response for a workspace that doesn't exist (the automation-fleet failure described in #6383).

This PR adds the isNull(workspaces.archivedAt) guard to the lookup, matching the convention every sibling lookup already follows (adopt-existing-worktree.ts, workspace.list, session folder dedupe). #6296 explicitly planned for re-creating a workspace on a tombstoned branch ("Tombstones aren't phantoms — same-branch history must survive re-creating a workspace on that branch"); this makes that path reachable.

Note: #6384 proposes the same guard — happy to defer to it if it lands first; this PR adds an independent regression test and desktop-flow verification.

How I tested it

  • New integration test (workspace-create-delete.integration.test.ts): create → delete (assert tombstone) → create on the same branch → asserts a fresh live row with a real worktree on disk, alreadyExists: false, and the tombstone's history untouched. Watched it fail on main (alreadyExists came back true) before the fix.
  • End-to-end in the dev desktop app (before/after): reproduced the bug twice on main — each recreate left zero new rows, no worktree, no branch, and the renderer sat on the tombstone's id showing "Workspace not found" (screenshots below). With the fix, repeated create → delete → recreate cycles on the same name each produced a fresh live workspace coexisting with its tombstone, verified in host.db and on disk.
  • bun run lint (clean — no warnings), bun run typecheck, full @superset/host-service suite: 1112 pass / 0 fail.

Known limitation (deliberately out of scope): a create that lands while a destroy of the same branch is still mid-pipeline (archive-first, worktree removed last) can adopt the dying worktree; that window predates this change and is better handled by serializing create/destroy separately.

Checklist

  • PR title follows conventional commits (type(scope): subject)
  • bun run lint and bun run typecheck pass (CI fails on lint warnings too)
  • "Allow edits from maintainers" is checked on fork PRs

Summary by cubic

Prevents workspace.create from reusing a tombstoned workspace on the same branch, fixing “Workspace not found” after recreate. Previously the idempotency lookup matched archived rows, returned alreadyExists: true, and created no worktree; now it ignores tombstones and inserts a fresh live workspace with a real worktree.

  • Filters archived rows in findExistingWorkspaceByBranch by adding isNull(workspaces.archivedAt) in @superset/host-service.
  • Adds an integration test for delete → same-branch recreate asserting a new id, alreadyExists: false, live worktree, and preserved tombstone history.
  • No API or schema changes; adopt path already excludes archived rows. Known gap unchanged: a create racing a delete on the same branch can adopt the dying worktree.

Summary by CodeRabbit

  • Bug Fixes

    • Creating a workspace after deleting a previous one now creates a new active workspace instead of reusing the archived workspace record.
  • Tests

    • Added coverage confirming archived workspaces remain unchanged and newly created workspaces receive a distinct ID and worktree.