Repository navigation
feat(sidebar): attach existing worktrees to folder workspaces from the sidebar menu - #26101
wuhaoyujerry wants to merge 5 commits into
Conversation
Add a separate "Attach to Folder Workspace…" action to the left-sidebar worktree menu. It reuses the parent picker in a folder mode, targets the exact captured checkout and execution owner, and confirms the result from authoritative workspace lineage before updating the sidebar. Repository membership and checkouts are never changed. - Folder-attached worktrees show "Remove from Folder Workspace" and "Change Parent Worktree...", and a display-only "repo/" title prefix instead of a gray repo line. - Drop "Remove from group" from the worktree menu; it ungrouped the whole repo. - Folder workspace PR list: clicking a row opens that worktree and its Checks tab; a chevron expands inline details. - Import folder attachment backend, sidebar nesting, and board folding (PR stablyai#18199) and the native workspace scope CLI guide. - Localize new copy in six locales; record OpenSpec changes and validation.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Host-collision handling, stale-lineage detection, projection failure reporting, accessibility, and unrelated scope issues remain unresolved.
Review effort: Balanced
Findings: 4
Open (7)
Avoid nested interactive controls in folder rows · New Validate worktree instance when determining folder parent · New Use host-qualified folder identities for attachment lookups · New Handle unapplied lineage projection as an acknowledged failure · New Remove generated Vitest failure reports · New Separate or document the folder import feature scope · New Assert created workspace through the E2E DOM · New
What changed in this PR
Adds sidebar attachment of existing worktrees to folder workspaces, including host-routed lineage persistence, nested sidebar/board rendering, and parent-management UI. The PR also bundles separate repository-import diagnostics and CLI guidance changes.
Changes:
- Adds folder-parent selection, mutation guards, lineage projection, nesting, and board integration.
- Updates titles, context menus, PR-check navigation, localization, and tests.
- Adds folder-group import diagnostics and workspace-scope documentation.
| File | Description |
|---|---|
tests/e2e/folder-setup.spec.ts |
Tests folder workspace creation. |
tests/e2e/folder-group-explanations.spec.ts |
Tests group-import explanations. |
src/shared/project-group-types.ts |
Adds scan diagnostic types. |
src/renderer/src/store/slices/worktrees/metadata/worktree-parent-mutation-guard.ts |
Guards concurrent parent updates. |
src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.ts |
Normalizes workspace lineage availability. |
src/renderer/src/store/slices/worktrees/metadata/worktree-lineage-actions.ts |
Applies guarded lineage mutations. |
src/renderer/src/store/slices/worktrees/metadata/worktree-folder-parent-projection.ts |
Projects confirmed folder parents. |
src/renderer/src/store/slices/worktrees.ts |
Registers folder-parent actions. |
src/renderer/src/store/slices/worktree-helpers.ts |
Extends the worktree slice API. |
src/renderer/src/i18n/locales/zh.json |
Adds Chinese translations. |
src/renderer/src/i18n/locales/ko.json |
Adds Korean translations. |
src/renderer/src/i18n/locales/ja.json |
Adds Japanese translations. |
src/renderer/src/i18n/locales/fr.json |
Adds French translations. |
src/renderer/src/i18n/locales/es.json |
Adds Spanish translations. |
src/renderer/src/i18n/locales/en.json |
Adds English UI copy. |
src/renderer/src/i18n/en-runtime-required.json |
Updates required runtime copy. |
src/renderer/src/components/sidebar/WorktreeTitleInlineRename.tsx |
Adds display-only title prefixes. |
src/renderer/src/components/sidebar/WorktreeParentPickerRow.tsx |
Renders folder candidates. |
src/renderer/src/components/sidebar/WorktreeParentMenuItems.tsx |
Adds parent menu actions. |
src/renderer/src/components/sidebar/WorktreeList.tsx |
Passes workspace lineage into rows. |
src/renderer/src/components/sidebar/WorktreeContextMenuView.tsx |
Integrates parent menu component. |
src/renderer/src/components/sidebar/WorktreeContextMenuOverlays.tsx |
Passes folder picker context. |
src/renderer/src/components/sidebar/WorktreeContextMenu.test.ts |
Tests folder-parent labels. |
src/renderer/src/components/sidebar/WorktreeCard.tsx |
Passes repository title prefix. |
src/renderer/src/components/sidebar/WorktreeCard.lineage.test.tsx |
Tests attached-child titles. |
src/renderer/src/components/sidebar/worktree-parent-picker-interaction.ts |
Extracts picker interactions. |
src/renderer/src/components/sidebar/worktree-parent-picker-filtering.ts |
Supports custom candidate search. |
src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts |
Exposes lineage toggle handling. |
src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx |
Labels folder-attached children. |
src/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsx |
Adds attached-child collapse controls. |
src/renderer/src/components/sidebar/worktree-list/listing/use-section-rows.ts |
Supplies workspace lineage to row building. |
src/renderer/src/components/sidebar/worktree-list/grouping/row-types.ts |
Extends folder row metadata. |
src/renderer/src/components/sidebar/worktree-list/grouping/row-builders.ts |
Supports nested base depth. |
src/renderer/src/components/sidebar/worktree-list/grouping/recent-project-rank-scan-budget.test.ts |
Updates grouping fixture. |
src/renderer/src/components/sidebar/worktree-list/grouping/project-group-sections.ts |
Emits attached folder children. |
src/renderer/src/components/sidebar/worktree-list/grouping/group-sections.ts |
Integrates folder nesting into lanes. |
src/renderer/src/components/sidebar/worktree-list/grouping/group-keys.ts |
Adds folder collapse keys. |
src/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-rows.ts |
Builds folder and child rows. |
src/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-attached.ts |
Resolves attached children. |
src/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-attached.test.ts |
Tests attachment resolution. |
src/renderer/src/components/sidebar/worktree-list/grouping/build-rows.ts |
Removes attached children from ordinary lanes. |
src/renderer/src/components/sidebar/worktree-context-menu-policy.ts |
Adds folder-parent menu policy. |
src/renderer/src/components/sidebar/worktree-card-model.ts |
Adds repository-origin presentation. |
src/renderer/src/components/sidebar/worktree-card-meta-row.tsx |
Adjusts attached-child metadata. |
src/renderer/src/components/sidebar/worktree-card-header.tsx |
Prefixes attached-child titles. |
src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsx |
Adds folders to the board. |
src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.search.test.tsx |
Tests board folding. |
src/renderer/src/components/sidebar/workspace-kanban-folder-workspaces.ts |
Builds folder-aware board rows. |
src/renderer/src/components/sidebar/workspace-kanban-folder-workspaces.test.ts |
Tests board folder projection. |
src/renderer/src/components/sidebar/useAddRepoServerPathFlow.ts |
Adds scan-review routing. |
src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts |
Tests paired-host review. |
src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.ts |
Completes Git-root opening. |
src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.ts |
Adds local scan-review routing. |
src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts |
Tests group-import intent. |
src/renderer/src/components/sidebar/use-worktree-parent-picker-transition.ts |
Captures folder context. |
src/renderer/src/components/sidebar/use-worktree-parent-picker-transition.test.tsx |
Tests captured picker ownership. |
src/renderer/src/components/sidebar/use-worktree-context-menu-model.tsx |
Wires attachment state and actions. |
src/renderer/src/components/sidebar/use-worktree-context-menu-commands.ts |
Removes row-level ungrouping. |
src/renderer/src/components/sidebar/use-workspace-kanban-board-projection.ts |
Handles folder visibility. |
src/renderer/src/components/sidebar/use-workspace-kanban-board-projection.test.tsx |
Tests folder host filtering. |
src/renderer/src/components/sidebar/use-folder-workspace-parent-picker.ts |
Loads and applies folder parents. |
src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts |
Includes workspace lineage in ordering. |
src/renderer/src/components/sidebar/nested-repo-scan-review.ts |
Decides when scan review is shown. |
src/renderer/src/components/sidebar/host-section-reveal.test.tsx |
Updates section fixture. |
src/renderer/src/components/sidebar/folder-workspace-attachment-menu.test.ts |
Tests attachment menu policy. |
src/renderer/src/components/sidebar/complete-nested-folder-open.ts |
Opens reviewed Git roots. |
src/renderer/src/components/sidebar/complete-nested-folder-open.test.ts |
Tests reviewed-root handoff. |
src/renderer/src/components/sidebar/agent-send-picker-collapse.test.tsx |
Updates section fixture. |
src/renderer/src/components/sidebar/AddRepoSteps.tsx |
Adds remote scan-review policy. |
src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts |
Tests SSH group-import review. |
src/renderer/src/components/sidebar/AddRepoStartSteps.tsx |
Exposes group-import action. |
src/renderer/src/components/sidebar/AddRepoNestedImportStep.tsx |
Displays scan explanations. |
src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx |
Passes group-import callback. |
src/renderer/src/components/sidebar/AddRepoDialog.tsx |
Tracks explicit group-import intent. |
src/renderer/src/components/sidebar/add-repo-local-start-actions.ts |
Adds group-import menu entry. |
src/renderer/src/components/sidebar/add-repo-dialog-types.ts |
Shares review callback types. |
src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.tsx |
Separates open and expand actions. |
src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.test.tsx |
Updates row fixture. |
src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksPanel.tsx |
Opens child Checks tabs. |
src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksPanel.test.tsx |
Tests PR-row activation. |
src/renderer/src/components/repo/NestedRepoScanExplanations.tsx |
Renders scan diagnostics. |
src/renderer/src/components/repo/NestedRepoScanExplanations.test.tsx |
Tests diagnostic rendering. |
src/main/runtime/orca-runtime-create-managed-remote-worktree.ts |
Enables folder-parent resolution. |
src/main/project-groups/nested-repo-scan-rules.ts |
Reports effective exclusion rules. |
src/main/project-groups/nested-repo-scan-rules.test.ts |
Tests exclusion provenance. |
src/main/project-groups/nested-repo-discovery.ts |
Collects bounded diagnostics. |
src/main/ipc/repos-nested-scan.test.ts |
Tests SSH diagnostic transport. |
src/cli/index.test.ts |
Updates parent selector help tests. |
src/cli/help.ts |
Documents folder selectors for set. |
skill-guides/orca-cli.md |
Adds workspace-scope guidance. |
openspec/specs/orca-cli-workspace-scope/spec.md |
Defines CLI scope requirements. |
openspec/specs/.gitkeep |
Retains specification directory. |
openspec/config.yaml |
Adds OpenSpec configuration. |
openspec/changes/archive/2026-10-06-resolve-folder-workspace-scope-before-discovery/validation.md |
Records scope validation. |
openspec/changes/archive/2026-10-06-resolve-folder-workspace-scope-before-discovery/tasks.md |
Records scope tasks. |
openspec/changes/archive/2026-10-06-resolve-folder-workspace-scope-before-discovery/specs/orca-cli-workspace-scope/spec.md |
Archives scope specification. |
openspec/changes/archive/2026-10-06-resolve-folder-workspace-scope-before-discovery/proposal.md |
Archives scope proposal. |
openspec/changes/archive/2026-10-06-resolve-folder-workspace-scope-before-discovery/.openspec.yaml |
Records scope change metadata. |
openspec/changes/archive/2026-10-06-attach-worktree-to-folder-workspace-ui/validation.md |
Records attachment validation. |
openspec/changes/archive/2026-10-06-attach-worktree-to-folder-workspace-ui/tasks.md |
Records attachment tasks. |
openspec/changes/archive/2026-10-06-attach-worktree-to-folder-workspace-ui/proposal.md |
Archives attachment proposal. |
openspec/changes/archive/2026-10-06-attach-worktree-to-folder-workspace-ui/.openspec.yaml |
Records attachment metadata. |
openspec/changes/archive/2026-10-05-explain-folder-group-import-exclusions/tasks.md |
Records import-explanation tasks. |
openspec/changes/archive/2026-10-05-explain-folder-group-import-exclusions/proposal.md |
Archives import-explanation proposal. |
openspec/changes/archive/2026-10-05-explain-folder-group-import-exclusions/.openspec.yaml |
Records import change metadata. |
openspec/changes/archive/.gitkeep |
Retains archive directory. |
config/scripts/vitest-sqlite-runtime-files.mjs |
Adds SQLite test runtime coverage. |
config/scripts/generate-bundled-skill-guides.test.mjs |
Tests bundled scope guidance. |
.vitest/json/output.json |
Adds a local failed-test artifact. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| <button | ||
| type="button" | ||
| className="mt-0.5 shrink-0 rounded text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" | ||
| aria-expanded={expanded} | ||
| aria-label={toggleDetailsLabel} |
| const edge = workspaceLineageByChildKey[worktreeWorkspaceKey(worktree.id)] | ||
| return edge ? parseWorkspaceKey(edge.parentWorkspaceKey)?.type === 'folder' : false |
| const children = attached.get(parentScope.folderWorkspaceId) ?? [] | ||
| children.push(worktree) | ||
| attached.set(parentScope.folderWorkspaceId, children) |
| if (!applied && !findCapturedFolderParentChild(get(), context)) { | ||
| throw new Error('Checkout changed before the view could be refreshed.') | ||
| } |
| "success": false, | ||
| "testResults": [ | ||
| { | ||
| "assertionResults": [], | ||
| "startTime": 1791353209974, |
| if (onImportGroup) { | ||
| secondaryActions.unshift({ | ||
| kind: 'group', | ||
| icon: FolderTree, | ||
| title: translate('repoScan.importGroup', 'Import folder as group'), |
| await expect | ||
| .poll(() => | ||
| orcaPage.evaluate(() => { | ||
| const state = window.__store!.getState() | ||
| const folder = state.folderWorkspaces.find( |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (3)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review. 📝 WalkthroughWalkthroughThe changes add bounded diagnostics to nested-repository scans and show scan explanations in repository-import flows. They add folder-workspace parent resolution, attachment, lineage persistence, sidebar nesting, and Workspace Board representation. The sidebar parent picker and context menu support folder attachment. The Orca CLI guide documents folder-wide repository selection, and CLI help shows folder selectors for both worktree creation and parent updates. Priority: ➖ Normal Merge Risk: 🔵 Low · up to A remote folder workspace can show its attached worktree as a separate Board item when its host is inherited from the project group. The documentation now covers setting an existing worktree's parent; the Board issue is limited to this host-fallback case. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Out of Scope Changes checkExplanation The PR includes a separate folder-import feature that is not required by Full details: Docstring CoverageExplanation Docstring coverage is 11.63% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 86 functions across 65 files. (3 skipped: 3 unsupported.)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟡 Minor · Document folder parents for worktree set. · orca-cli.md:123
skill-guides/orca-cli.md:123
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winDocument folder parents for
worktree set.This sentence limits folder parent selectors to
worktree create, but the PR also supportsworktree set --parent-worktree folder:<id>for an existing worktree. Update the guidance to includeworktree set; otherwise, the guide incorrectly implies that existing worktrees cannot be attached to folders. The PR objective confirms this behavior.
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
3a4a3fff-5342-4bd1-b845-7708c42a3fd5
📒 Files selected for processing (105)
config/scripts/vitest-sqlite-runtime-files.mjsskill-guides/orca-cli.mdsrc/cli/bundled-skill-guides.tssrc/cli/help.tssrc/cli/index.test.tssrc/main/ipc/repos-nested-scan.test.tssrc/main/project-groups/nested-repo-discovery.tssrc/main/project-groups/nested-repo-scan-diagnostics.test.tssrc/main/project-groups/nested-repo-scan-rules.test.tssrc/main/project-groups/nested-repo-scan-rules.tssrc/main/runtime/orca-runtime-create-managed-remote-worktree.tssrc/main/runtime/runtime-managed-worktree-metadata.folder-store.test.tssrc/main/runtime/runtime-managed-worktree-metadata.test.tssrc/main/runtime/runtime-managed-worktree-metadata.tssrc/renderer/src/components/repo/NestedRepoScanExplanations.test.tsxsrc/renderer/src/components/repo/NestedRepoScanExplanations.tsxsrc/renderer/src/components/right-sidebar/FolderWorkspacePrChecksPanel.test.tsxsrc/renderer/src/components/right-sidebar/FolderWorkspacePrChecksPanel.tsxsrc/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.test.tsxsrc/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.tsxsrc/renderer/src/components/sidebar/AddRepoDialog.tsxsrc/renderer/src/components/sidebar/AddRepoDialogStepContent.tsxsrc/renderer/src/components/sidebar/AddRepoNestedImportStep.tsxsrc/renderer/src/components/sidebar/AddRepoStartSteps.tsxsrc/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.tssrc/renderer/src/components/sidebar/AddRepoSteps.tsxsrc/renderer/src/components/sidebar/WorkspaceKanbanDrawer.search.test.tsxsrc/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsxsrc/renderer/src/components/sidebar/WorktreeCard.lineage.test.tsxsrc/renderer/src/components/sidebar/WorktreeCard.tsxsrc/renderer/src/components/sidebar/WorktreeContextMenu.test.tssrc/renderer/src/components/sidebar/WorktreeContextMenuOverlays.tsxsrc/renderer/src/components/sidebar/WorktreeContextMenuView.tsxsrc/renderer/src/components/sidebar/WorktreeList.tsxsrc/renderer/src/components/sidebar/WorktreeParentMenuItems.tsxsrc/renderer/src/components/sidebar/WorktreeParentPickerPopover.tsxsrc/renderer/src/components/sidebar/WorktreeParentPickerRow.tsxsrc/renderer/src/components/sidebar/WorktreeTitleInlineRename.tsxsrc/renderer/src/components/sidebar/add-repo-dialog-types.tssrc/renderer/src/components/sidebar/add-repo-local-start-actions.tssrc/renderer/src/components/sidebar/agent-send-picker-collapse.test.tsxsrc/renderer/src/components/sidebar/complete-nested-folder-open.test.tssrc/renderer/src/components/sidebar/complete-nested-folder-open.tssrc/renderer/src/components/sidebar/folder-workspace-attachment-menu.test.tssrc/renderer/src/components/sidebar/folder-workspace-parent-candidates.test.tssrc/renderer/src/components/sidebar/folder-workspace-parent-candidates.tssrc/renderer/src/components/sidebar/host-section-reveal.test.tsxsrc/renderer/src/components/sidebar/nested-repo-scan-review.tssrc/renderer/src/components/sidebar/rendered-sidebar-worktree-order.tssrc/renderer/src/components/sidebar/use-folder-workspace-parent-picker.test.tsxsrc/renderer/src/components/sidebar/use-folder-workspace-parent-picker.tssrc/renderer/src/components/sidebar/use-workspace-kanban-board-projection.test.tsxsrc/renderer/src/components/sidebar/use-workspace-kanban-board-projection.tssrc/renderer/src/components/sidebar/use-worktree-context-menu-commands.tssrc/renderer/src/components/sidebar/use-worktree-context-menu-model.tsxsrc/renderer/src/components/sidebar/use-worktree-parent-picker-transition.test.tsxsrc/renderer/src/components/sidebar/use-worktree-parent-picker-transition.tssrc/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.tssrc/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.tssrc/renderer/src/components/sidebar/useAddRepoNestedImportFlow.tssrc/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.tssrc/renderer/src/components/sidebar/useAddRepoServerPathFlow.tssrc/renderer/src/components/sidebar/workspace-kanban-folder-workspaces.test.tssrc/renderer/src/components/sidebar/workspace-kanban-folder-workspaces.tssrc/renderer/src/components/sidebar/worktree-card-header.tsxsrc/renderer/src/components/sidebar/worktree-card-meta-row.tsxsrc/renderer/src/components/sidebar/worktree-card-model.tssrc/renderer/src/components/sidebar/worktree-card-presentation.tsxsrc/renderer/src/components/sidebar/worktree-context-menu-policy.tssrc/renderer/src/components/sidebar/worktree-list/grouping/build-rows.folder-workspace-lanes.test.tssrc/renderer/src/components/sidebar/worktree-list/grouping/build-rows.tssrc/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-attached.test.tssrc/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-attached.tssrc/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-rows.tssrc/renderer/src/components/sidebar/worktree-list/grouping/group-keys.tssrc/renderer/src/components/sidebar/worktree-list/grouping/group-sections.tssrc/renderer/src/components/sidebar/worktree-list/grouping/project-group-sections.tssrc/renderer/src/components/sidebar/worktree-list/grouping/recent-project-rank-scan-budget.test.tssrc/renderer/src/components/sidebar/worktree-list/grouping/row-builders.tssrc/renderer/src/components/sidebar/worktree-list/grouping/row-types.tssrc/renderer/src/components/sidebar/worktree-list/listing/use-section-rows.tssrc/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsxsrc/renderer/src/components/sidebar/worktree-list/rows/item-row.tsxsrc/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.tssrc/renderer/src/components/sidebar/worktree-parent-picker-filtering.tssrc/renderer/src/components/sidebar/worktree-parent-picker-interaction.tssrc/renderer/src/i18n/en-runtime-required.jsonsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/fr.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/store/slices/worktree-helpers.tssrc/renderer/src/store/slices/worktrees-folder-parent-actions.test.tssrc/renderer/src/store/slices/worktrees.tssrc/renderer/src/store/slices/worktrees/metadata/worktree-folder-parent-actions.tssrc/renderer/src/store/slices/worktrees/metadata/worktree-folder-parent-projection.tssrc/renderer/src/store/slices/worktrees/metadata/worktree-lineage-actions.tssrc/renderer/src/store/slices/worktrees/metadata/worktree-lineage-refresh.tssrc/renderer/src/store/slices/worktrees/metadata/worktree-parent-mutation-guard.tssrc/shared/project-group-types.tstests/e2e/folder-group-explanations.spec.tstests/e2e/folder-setup.spec.tstests/e2e/folder-workspace-nesting.spec.ts
💤 Files with no reviewable changes (1)
- src/renderer/src/components/sidebar/use-worktree-context-menu-commands.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.test.tsx (1)
165-180: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePrefer role-based queries in this test.
The test uses
container.querySelectorAll('button')and a positional destructure. A role-based query is more resilient to markup changes. UsegetByRole('button', { name: ... })for the toggle, open, and external-link buttons. The checks for accessible names then become part of the query itself.Based on learnings: prefer
getByRole(role plus name) over container queries in React Testing Library tests.Source: Learnings
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
795d9dd6-9454-403d-acf0-eec7d3818b95
📒 Files selected for processing (29)
skill-guides/orca-cli.mdsrc/cli/bundled-skill-guides.tssrc/renderer/src/components/repo/NestedRepoScanExplanations.test.tsxsrc/renderer/src/components/repo/NestedRepoScanExplanations.tsxsrc/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.test.tsxsrc/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.tsxsrc/renderer/src/components/sidebar/WorktreeCard.lineage.test.tsxsrc/renderer/src/components/sidebar/folder-workspace-attachment-menu.test.tssrc/renderer/src/components/sidebar/workspace-kanban-folder-workspaces.test.tssrc/renderer/src/components/sidebar/workspace-kanban-folder-workspaces.tssrc/renderer/src/components/sidebar/worktree-card-presentation.tsxsrc/renderer/src/components/sidebar/worktree-context-menu-policy.tssrc/renderer/src/components/sidebar/worktree-list/grouping/build-rows.folder-workspace-lanes.test.tssrc/renderer/src/components/sidebar/worktree-list/grouping/build-rows.tssrc/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-attached.test.tssrc/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-attached.tssrc/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-host-isolation.test.tssrc/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-rows.tssrc/renderer/src/components/sidebar/worktree-list/grouping/group-sections.tssrc/renderer/src/components/sidebar/worktree-list/grouping/project-group-sections.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/fr.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/store/slices/worktrees-folder-parent-actions.test.tssrc/renderer/src/store/slices/worktrees/metadata/worktree-folder-parent-actions.tstests/e2e/folder-setup.spec.ts
💤 Files with no reviewable changes (6)
- src/renderer/src/i18n/locales/fr.json
- src/renderer/src/i18n/locales/zh.json
- src/renderer/src/i18n/locales/en.json
- src/renderer/src/i18n/locales/ko.json
- src/renderer/src/i18n/locales/ja.json
- src/renderer/src/i18n/locales/es.json
🚧 Files skipped from review as they are similar to previous changes (4)
- src/renderer/src/components/repo/NestedRepoScanExplanations.tsx
- src/renderer/src/components/sidebar/WorktreeCard.lineage.test.tsx
- src/renderer/src/components/sidebar/worktree-card-presentation.tsx
- src/renderer/src/components/repo/NestedRepoScanExplanations.test.tsx
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
| ( | ||
| attachedByFolderId.get( | ||
| composeWorktreeHostIdentity(folderWorkspaceToWorktree(workspace).hostId, workspace.id) | ||
| ) ?? [] | ||
| ).map(getWorktreeHostIdentity) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
rg -nP -C3 'buildWorkspaceBoardWorktrees\s*\(' src/renderer
rg -nP -C3 'connectionId' src/shared/folder-workspace-types.tsRepository: stablyai/orca
Length of output: 5654
🏁 Script executed:
set -o pipefail
printf '%s\n' '--- board implementation ---'
nl -ba src/renderer/src/components/sidebar/workspace-kanban-folder-workspaces.ts | sed -n '1,180p'
printf '%s\n' '--- drawer caller ---'
nl -ba src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsx | sed -n '55,110p'
printf '%s\n' '--- sidebar identity references ---'
rg -n -F -- 'getFolderWorkspaceAttachmentIdentity' src/renderer src/shared
printf '%s\n' '--- attachment helper references ---'
rg -n -F -- 'getAttachedWorktreesByFolderWorkspaceIdentity' src/renderer src/shared
printf '%s\n' '--- identity definitions and folder workspace types ---'
rg -n -C8 -F -- 'function getFolderWorkspaceAttachmentIdentity' src
rg -n -C8 -F -- 'function getAttachedWorktreesByFolderWorkspaceIdentity' src
nl -ba src/shared/folder-workspace-types.ts | sed -n '1,130p'
printf '%s\n' '--- folder workspace conversion and project-group types ---'
rg -n -C8 -F -- 'function folderWorkspaceToWorktree' src
rg -n -C8 -F -- 'projectGroup.connectionId' src/renderer src/sharedRepository: stablyai/orca
Length of output: 26644
🏁 Script executed:
set -o pipefail
nl -ba src/renderer/src/components/sidebar/workspace-kanban-folder-workspaces.ts | sed -n '1,180p'
nl -ba src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsx | sed -n '55,110p'
rg -n -C8 -F -- 'getFolderWorkspaceAttachmentIdentity' src/renderer src/shared
rg -n -C8 -F -- 'getAttachedWorktreesByFolderWorkspaceIdentity' src/renderer src/shared
rg -n -C8 -F -- 'function folderWorkspaceToWorktree' src
nl -ba src/shared/folder-workspace-types.ts | sed -n '1,130p'Repository: stablyai/orca
Length of output: 37993
🏁 Script executed:
set -o pipefail
printf '%s\n' '--- attachment helper ---'
nl -ba src/renderer/src/components/sidebar/worktree-list/grouping/folder-workspace-attached.ts | sed -n '1,125p'
printf '%s\n' '--- renderable folder workspace construction ---'
rg -n -C10 -F -- 'RenderableFolderWorkspace' src/renderer/src/components/sidebar/worktree-list/grouping
printf '%s\n' '--- default host build-rows signature and callers ---'
nl -ba src/renderer/src/components/sidebar/worktree-list/grouping/build-rows.ts | sed -n '45,145p'
rg -n -C8 -F -- 'buildWorktreeRows' src/renderer/src/components/sidebar
printf '%s\n' '--- project group and folder workspace loading/normalization ---'
rg -n -C8 -F -- 'folderWorkspace.projectGroupId' src/renderer src/shared
rg -n -C8 -F -- 'getRenderableFolderWorkspaces' src/renderer/src/components/sidebarRepository: stablyai/orca
Length of output: 42286
Use one shared helper for the board’s folder identity.
buildWorkspaceBoardWorktrees uses folderWorkspaceToWorktree(workspace).hostId, while the sidebar uses the folder’s executionHostId, its own or project-group connectionId, and defaultHostId. The board also omits defaultHostId when it builds the attachment map.
When a folder inherits an SSH host from its project group, the sidebar uses an SSH key but the board falls back to local. The attached worktree then remains a standalone row instead of being folded into the folder card. Pass the project-group context and defaultHostId into buildWorkspaceBoardWorktrees, and use getFolderWorkspaceAttachmentIdentity for the folder lookup.


ELI5
You can now take a worktree you already have and tuck it under a folder workspace from the left sidebar: right-click the worktree, choose Attach to Folder Workspace…, and pick the folder. Before, the only way to do that was the CLI. The worktree's files and branch never move; only where it shows up in the sidebar changes.
What Changed
Before: the worktree menu could only set a Git parent. A worktree could be put under a folder workspace only when it was created with the CLI. The menu also had a "Remove from group" item that, despite its name, ungrouped the whole repository, not just the worktree you clicked.
After:
repo/nameas the title (display only; rename still saves just the name) instead of a gray repo line. The branch stays in the hover card.Mechanism: the action captures the exact checkout identity and execution owner when the menu opens, sends one
worktree.setwith thefolder:<id>parent to that owner, then re-readsworktree.lineageListand projects only the confirmed edge. A null Git lineage is never read as "no folder parent". A write whose outcome is unknown is never resent or rolled back automatically; a per-checkout guard blocks overlapping parent changes. No new RPC, storage schema or dependency, and no worktree, folder, terminal or agent is created.This PR incorporates #18199’s backend work by @DominionZA2 and adds the sidebar UI. Since #18199 has been inactive, I’m proposing this combined PR as the path to merge the feature. If #18199 merges first, I’ll rebase to remove the overlapping changes.
Folder import improvements included: Add Project now offers Import folder as group. Repository discovery reports observed exclusions from .gitignore, built-in directory policy, hidden folders, and symbolic links, and distinguishes depth limits, unreadable paths, cancellation, and timeouts. Selecting a Git repository for group import explains why grouping is unavailable and allows opening it as a normal project. Diagnostics are bounded and come from the execution host; older hosts without diagnostics do not invent explanations. This is a separate user-facing importer change, not a prerequisite for attachment, and is included in this PR’s review scope. The CLI guide also explains how to resolve folder-workspace membership before creating children.
Why
The CLI already could attach a worktree to a folder, but users working in the app had no way to do it, and moving a worktree between folders needed a re-create. A separate action, not a combined Git/folder picker, keeps the existing Git-parent behavior and copy unchanged and avoids making folder parents look like Git ancestry. Confirming from authoritative lineage, instead of trusting the write reply, keeps old or unreachable paired hosts from showing a relationship that was not stored.
Linked Issue
Fixes #18198
Visual Proof
Captured from the hidden Electron E2E (disposable repos, light theme).
repo/nametitlesThere is no screenshot of the pre-change menu (it simply lacks the attach item); the "before" frame is the same fixture before attaching.
Testing
I manually tested these changes locally
Automated tests added/updated, or explained why not below
Hidden-Electron E2E
tests/e2e/folder-workspace-nesting.spec.ts(isolated profile,ORCA_BACKGROUND_LAUNCH=1): real right-click → search → select, Git-parent replacement, keyboard selection, Escape cancel, move between folders, detach, board folding, and full app quit/relaunch persistence with unchanged checkout identity.29 test files touched or added by this change: 279 tests pass (
pnpm test <files>), covering candidates, captured identity/owner, mutation guard, rejected/unknown/unverified writes, omitted-vs-empty lineage, menu policy, picker interaction, card title/metadata, and PR-row click.pnpm typecheck: onlysrc/main/acp/acp-structured-host-restore-failed.test.ts(227)fails, and it fails identically on currentorigin/main.pnpm buildstops at that same error.pnpm lint: onlymobile/import-cycle warnings (untouched files) fail--deny-warnings.pnpm run check:code-quality:changedandcheck:max-lines-ratchetpass.Full
pnpm teston my machine has hundreds of failing files (Bun-runner/native/ACP suites). I re-ran a sample on cleanorigin/mainand they fail the same way, so I treat them as environmental, not as caused by this change.Also moved one new SQLite test onto the Node runtime list, and fixed two upstream test fixtures that needed the new
workspaceLineageByChildKeyargument.Tested on macOS only, local execution. SSH and paired mixed-version hosts are covered by mocked transport tests only; I had no real environment.
AI Disclosure
Written and tested with an AI coding agent (Pi harness) under my direction, including the OpenSpec change, code, tests and this description. I reviewed the behavior in a running app.
Review
AI review summary:
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Remove from Parentwording is unchanged for Git parents.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred) — blocked locally by pre-existing failures noted under Testing; CI will cover