feat(desktop,host-service): project-less session workspaces
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.projectIdis now nullable (+ migration 0019); workspacetypegains'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 optimisticid, 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
rmhard-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=OFFis a no-op — the table rebuild would have firedON DELETE SET NULLintoterminal_sessionson upgrade.createDbnow migrates with FKs off +foreign_key_check; regression test includes a negative arm proving the corruption - PR runtime skips null-project rows;
gitWorktreeStateTaskgainsignoreInitialCommitso 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=truecarries 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.projectIdwidened to nullable (identity field — no default, no heal synthesis)
CLI / MCP / SDK
- Omitting
projectId/--projectcreates a session; branch/PR/base-branch/task inputs without a project are rejected clearly; SDK gainsworkspaces.createSession()with types on all public surfaces
Out of scope (follow-ups)
- Nestable sidebar groups → #6278 (stacked)
- Automations targeting sessions (cloud
automations.v2ProjectIdNOT 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 refusalmigration-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.