feat(desktop,host-service): project-less session workspaces

Merged
KI
3mo ago
+9957-387

Summary

Makes the project optional on workspaces: a project-less workspace — a session — gets a managed folder at ~/.superset/sessions/<name> (its own git repo, initialized with an initial commit), so terminals, chat, agents, git status, and the Changes panel all work unchanged. Design doc: plans/20260808-optional-project-sessions.md.

Split note: the nestable sidebar grouping tree originally in this PR was extracted to the stacked #6278 for independent review/revert. This PR is sessions-only; sections remain flat exactly as on main.

Host-service

  • workspaces.projectId is now nullable (+ migration 0019); workspace type gains 'session'
  • New workspaces.createSession: friendly/deduped folder name under ~/.superset/sessions, git init + initial commit, optional agent/command launch, AI title (folder name frozen); idempotent on the optimistic id, and a failed insert cleans up its allocated folder (best-effort, never masking the error)
  • Destroy saga: sessions run the same dirty preflight, then async rm hard-guarded to paths inside the sessions root; no cloud call, no branch delete
  • Migration safety: drizzle's migrator wraps migrations in one transaction where the generated PRAGMA foreign_keys=OFF is a no-op — the table rebuild would have fired ON DELETE SET NULL into terminal_sessions on upgrade. createDb now migrates with FKs off + foreign_key_check; regression test includes a negative arm proving the corruption
  • PR runtime skips null-project rows; gitWorktreeStateTask gains ignoreInitialCommit so fresh sessions never read as "unpushed"

Desktop (v2)

  • Sessions section rendered above the Projects header, with an always-visible "+" that opens the create surface with "No project" preselected (?session=true carries the intent through the screen-mode route); rows hide with the workspaces-list collapse toggle, header stays
  • "No project · Session" option in the project picker (modal + page route); session mode clears/hides repo-scoped inputs (PR link, base branch)
  • Delete dialog: session copy, no delete-branch checkbox; ⌘1–9 and arrow nav order sessions first
  • sidebarState.projectId widened to nullable (identity field — no default, no heal synthesis)

CLI / MCP / SDK

  • Omitting projectId/--project creates a session; branch/PR/base-branch/task inputs without a project are rejected clearly; SDK gains workspaces.createSession() with types on all public surfaces

Out of scope (follow-ups)

  • Nestable sidebar groups → #6278 (stacked)
  • Automations targeting sessions (cloud automations.v2ProjectId NOT NULL); mobile Sessions filter; promote session → project

Testing

  • session-create-delete.integration.test.ts (7 tests): git-initialized folder + row shape, name dedupe/sanitization, destroy with zero cloud calls, dirty CONFLICT + force, unpushed-commit semantics, corrupt-path rm refusal
  • migration-0019-preserves-terminal-links.test.ts: real pre-0019→0019 upgrade, both arms
  • Full suites green (typecheck 37/37, Biome, desktop, host-service); CDP end-to-end journeys with screenshots (create via picker → folder/AI title/section; terminal cwd; Changes panel live updates; dirty + clean delete; "+" preselect)

Included: session automations (#6280, merged into this branch)

Automations complete the "no project = session" convention: an automation with no project and no workspace pin creates a fresh session workspace per run (dispatch relays workspaces.createSession), and existing session workspaces can be pinned. Ships automations.v2_project_id DROP NOT NULL (cloud migration 0064) — deploy cloud/API before any desktop release built from this. Desktop pickers, CLI (--project optional, --session on update), MCP, and SDK updated; cubic's review findings (retained-pin update bug, CLI validation order, picker tri-state, doc gaps) all fixed and threads resolved on #6280.