feat: workspace lineage — spawned workspaces nest under their parent in the sidebar
Links
- Research/plan:
plans/20260816-orca-nested-agents-research.md(in this PR)
Summary
- Host tracks who spawned each workspace:
parent_workspace_id+spawn_originon the hostworkspacestable, accepted byworkspaces.create/createSession, the CLI (defaulted fromSUPERSET_WORKSPACE_ID,--parent/--no-parentoverride), and MCPworkspaces_create. - The sidebar renders spawned workspaces as a file-tree: children nest under their parent with guide rails, hover-chevron collapse, persistent chevron +
+Nbadge when collapsed. Works inside groups/sections.
Why / Context
An agent running superset ws create (or MCP workspaces_create) from inside a workspace produces a flat, unattributed sibling today — nothing anywhere records "who spawned this." This is Orca's most-shared surface (nested worktrees + nested agents) and the prerequisite for orchestrator/worker fleets reading as one unit. Research and competitive analysis in the included plans doc.
How It Works
- Create-time edge:
resolveParentWorkspaceIdvalidates the requested parent (exists, live, same project); anything invalid records no lineage rather than failing the create — a wrong edge is worse than a flat row. Lineage never affects the git base branch. - Projection-time validation:
nestSidebarWorkspaceLineageregroups each sidebar container depth-first. A child whose parent isn't rendered in the same container (pinned away, archived, in a different section) re-roots at depth 0; cycle members are re-promoted to roots — malformed data can never hide a row. - Explicit re-parenting (correction tool):
workspace.updateacceptsparentWorkspaceId— a UUID re-nests, explicitnulldetaches to the top level, omitted leaves lineage untouched. Surfaced as CLIws update --parent/--no-parent, MCPworkspaces_update, and a "Move to top level" context-menu item on nested rows. Unlike create's ambient inference this errors loudly on any invalid target: unknown/archived parent, cross-project, self, main workspace, or a move under the workspace's own descendant (ancestor-chain walk with a visited set, so even a pre-existing DB cycle can't hang it). Moving a mid-chain node keeps its subtree. Still no DnD nesting — deliberate. - Collapse: persisted per workspace as
sidebarState.lineageCollapsedon the existingv2WorkspaceLocalStatecollection row (no new localStorage key; bounded boolean; cleaned up with the row). Expanded parents swap the status icon for a chevron on row hover (same affordance as the project header); collapsed parents show a persistent chevron + direct-child count. Section collapse composes with and preserves lineage collapse. - Migration 0024 is a generated plain
ADD COLUMN(no table rebuild, so no FK-trap exposure). SQLite records no ON DELETE action for ALTER-added FKs, so the hard-delete path detaches children app-side to match the schema's set-null intent.
Manual QA Checklist
All verified end-to-end over CDP against the dev desktop (screenshots in session):
- tRPC create with
parentWorkspaceIdpersists the edge +spawn_origin(host.db inspected) - Real CLI create with only
SUPERSET_WORKSPACE_IDin env inherits the parent (spawn_origin=cli) - Tree renders depth-first with rails at 3 levels; chips strip aligns with the row indent
- Nesting + rails render inside a group/section at the section's base indent
- Chevron click collapses/expands without navigating (route unchanged);
+Nbadge on collapsed parents; state survives reload - Section collapse → re-expand preserves lineage collapse inside
- Edge cases staged live: A⇄B cycle written directly to host.db renders both rows (re-promoted, no loop); pinned parent → child re-roots at depth 0; destroyed parent → child re-roots inside its section
- Auto-included local mains (no local-state row) render unchanged; pinned rows never nest
Testing
bun test workspace-reparent.test.ts— 8 pass (re-nest, explicit-null detach, omitted-field no-op, self/cycle/cross-project/archived/main rejections); filmed live E2E: UI menu detach, CLI re-nest/detach, cycle rejectionbun test buildDashboardSidebarProjects.test.ts— 17 pass (7 new lineage cases: nesting, grandchildren, re-rooting, in-section nesting, cycles, child counts, collapse)bunx turbo typecheck --filter=@superset/desktop --filter=@superset/host-service --filter=@superset/cli --filter=@superset/mcp --filter=@superset/workspace-client— cleanbunx biome checkon touched dirs — clean- Not run locally: host-service
*.node-test.ts(native ABI seesaw with the dev app; relying on CI)
Design Decisions
- Record-nothing on invalid parent instead of erroring: callers often infer the parent from ambient env; failing creates on a stale pointer would break agent workflows.
- Column on the host table, not renderer local state: CLI/automation spawns can't write renderer-local sidebar state, so a localStorage-only edge would always land flat.
- No instance-ID fencing (unlike Orca): our workspace ids are minted UUIDs that never get recycled; the table, not git, is authoritative.
- Metadata-only lineage (no git-base coupling): matches Orca; stacked child-from-parent-branch + merge-into-parent is the deliberate follow-up differentiator.
Known Limitations
- Sidebar DnD ordering interacts naively with nesting: dragging a child elsewhere in the same container snaps it back under its parent on the next build.
- Dev-only observation: the sidebar auto-placer (
usePlaceLocalWorktreesInSidebar) never fired in the dev environment because its readiness gate waits on sandbox brokering (BLAXEL_*env absent in dev). Pre-existing behavior, worth a prod sanity check that CLI-spawned children get placed.
Follow-ups
spawn_origin: "automation"wiring from the automation dispatch path- Nested agent rows under workspace rows (graduate the
workspace-agents-rowexperiment) - Stacked-branch option + "merge into parent"
Summary by cubic
Spawned workspaces now nest under their parent in the sidebar, and lineage is editable. Previously all workspaces rendered flat and lineage was write‑once; now parentWorkspaceId renders a collapsible tree, and you can re‑parent or detach without affecting git branches.
-
Host/API: adds parent_workspace_id (self‑FK, indexed) and spawn_origin. workspaces.create/createSession accept parentWorkspaceId and optional spawnOrigin; invalid parents are dropped on create. workspace.update supports re‑parenting or explicit null to detach with strict validation (exists, live, same project, not self/main/not descendant). Events include parentWorkspaceId (present null clears cached lineage; absent preserves for pre‑column hosts). Exports WORKSPACE_SPAWN_ORIGINS; telemetry records parent_present and spawn_origin.
-
Desktop: sidebar nests children depth‑first with persistable collapse and chevrons; collapsed parents show a +N badge. Descendants are not draggable; dragging a root moves the whole subtree, and section moves cascade to descendants. If a parent is not in the same container, children re‑root at depth 0; cycle members promote to roots. Context menu adds “Move to top level.” Deleting a parent detaches children. Chevron reveals on hover or keyboard focus; +N badge is aria‑hidden.
-
CLI/MCP:
@superset/clicreate supports--parent/--no-parent(defaults fromSUPERSET_WORKSPACE_ID; malformed env degrades to top level; explicit--parentstays strict).@superset/cliupdate adds--parent/--no-parent(mutually exclusive).@superset/mcpcreate/update accept lineage fields and setspawnOrigin="mcp". -
Mobile: synthesized cloud rows include parentWorkspaceId (null when the host cannot serve lineage), matching desktop.
-
Web/Admin/Marketing: force dark theme statically and remove
next-themes(set<html class="dark" style={{colorScheme:'dark'}}>and passtheme="dark"to the shared Toaster) to resolve React 19/Next 16 script warnings; no functional change. -
Rollout: run host migration 0025_workspace_lineage. No client migrations.
Summary by CodeRabbit
- New Features
- Added nested workspace hierarchies in the sidebar with indentation, child counts, and collapse/expand controls.
- Added “Move to top level” for nested workspaces.
- Workspace creation and updates now support assigning or removing a parent through the CLI and MCP tools.
- Workspace lineage is preserved across sessions and workspace events.
- Bug Fixes
- Added validation to prevent invalid, cross-project, self-referential, archived, or cyclic workspace relationships.
- Nested workspaces can no longer be dragged independently from their parent.