Skip to content

feat: in-repo .worktrees/ mode#388

Closed
sasha-id wants to merge 12 commits intostablyai:mainfrom
sasha-id:feat/in-repo-worktrees
Closed

feat: in-repo .worktrees/ mode#388
sasha-id wants to merge 12 commits intostablyai:mainfrom
sasha-id:feat/in-repo-worktrees

Conversation

@sasha-id
Copy link
Copy Markdown
Contributor

@sasha-id sasha-id commented Apr 8, 2026

Summary

  • Adds an opt-in worktreeLocation: 'external' | 'in-repo' setting that creates new worktrees at <repoPath>/.worktrees/<name> instead of inside the global workspaceDir
  • New segmented picker at the top of Settings → General → Workspace; existing Workspace Directory and Nest Workspaces controls render only in External mode
  • When in-repo mode is on and the repo's .gitignore doesn't already ignore .worktrees/, the create dialog shows an inline confirmation pane with [Cancel] / [Create anyway] / [Add and create]
  • Refactors computeWorktreePath to fold the three modes (external-flat, external-nested, in-repo) into one function with internal validation, eliminating duplicated WSL+validation blocks in two call sites (worktrees.ts and orca-runtime.ts)

Design + plan

  • Spec: docs/in-repo-worktrees-design.md
  • Plan: docs/in-repo-worktrees-plan.md

Both went through brainstorming → spec self-review → independent Opus review → fixes → user approval before implementation.

What's new for users

  • New setting: Settings → General → Workspace → Worktree Location: [External directory | In-repo .worktrees/]
  • In-repo mode places worktrees at <repo>/.worktrees/<name>
  • First create in a repo without the gitignore entry shows a confirmation dialog with three actions: Cancel / Create anyway / Add and create
  • Discoverable via settings search (worktree, location, in-repo, .worktrees, gitignore)

Architecture notes

  • src/main/git/gitignore.ts (new) — Pure parsing (isWorktreesDirIgnoredByGitignore, appendWorktreesEntry) plus IO wrappers (readGitignore, isWorktreesDirIgnored, addWorktreesDirToGitignore). Parser uses exact-string matching against the four canonical root patterns; intentionally does NOT trim whitespace because git treats it as significant.
  • src/main/git/repo.ts — New isBareRepo() sync helper, used by the gitignore IPC to short-circuit prompting on bare repos.
  • src/main/ipc/worktree-logic.tscomputeWorktreePath refactored with three explicit branches; new private ensureWithinRoot helper accepts explicit path operations so UNC paths validate correctly on Linux CI; in-repo branch runs first and short-circuits the WSL special case.
  • src/main/ipc/worktrees.ts + src/main/runtime/orca-runtime.ts — Both callers collapsed from ~10 lines (WSL root selection + standalone validation call) to a single computeWorktreePath() invocation. The CLI runtime caller was a discovery during implementation — the plan only listed worktrees.ts.
  • src/main/ipc/worktrees.ts — Two new IPC handlers: gitignore:checkWorktreesIgnored (fail-open on read errors) and gitignore:addWorktreesEntry (idempotent).
  • src/preload/index.ts + index.d.ts — New gitignore namespace on window.api.
  • src/renderer/src/components/sidebar/AddWorktreeDialog.tsxhandleCreate split into handleCreate (decision/gate) and performCreate (action body). New pendingGitignoreConfirm state. Inline confirmation pane is a separate if (...) return <Dialog> early return rather than a stacked modal. Suggested-name pool now considers in-repo mode as per-repo just like nested external mode (parameter renamed from nestWorkspaces to perRepoNamePool).
  • src/renderer/src/components/settings/GeneralPane.tsx — Two-tier picker; existing controls hidden (not disabled) when in-repo mode is on. Picker pattern mirrors the existing branchPrefix segmented control.

Test plan

Automated:

  • Unit tests for isBareRepo (3 tests, mocks git rev-parse)
  • Unit tests for gitignore.ts pure parsing (13 cases including 2 regression guards for whitespace handling)
  • Unit tests for gitignore.ts IO wrappers (7 cases using mkdtemp for real fs)
  • Unit tests for computeWorktreePath in-repo mode (4 cases) + WSL+in-repo regression test that asserts WSL helpers are NOT called
  • IPC handler tests for gitignore:checkWorktreesIgnored and gitignore:addWorktreesEntry (7 cases using mkdtemp)
  • All existing tests still pass: 631 baseline → 670 total (39 new)
  • pnpm run typecheck clean across all 3 tsconfig targets
  • pnpm run lint clean (0 warnings, 0 errors)

Manual (smoke test — please run before merge):

  • Toggle the new setting in Settings → General → Workspace and verify the existing Workspace Directory + Nest Workspaces controls hide/reappear
  • In a git repo whose .gitignore does NOT already contain .worktrees/, create a new worktree in in-repo mode → confirmation pane appears
  • Click "Add and create" → worktree lands at <repo>/.worktrees/<name> and .gitignore gains a single .worktrees/ line
  • Create a second worktree in the same repo → no confirmation pane (entry already present)
  • Click "Create anyway" in a repo without the entry → worktree created, .gitignore untouched (the new files will appear as untracked, as expected)
  • Click "Cancel" on the confirmation pane → form returns, no worktree created
  • Toggle back to External mode → existing in-repo worktrees still work (file explorer, terminal, git operations)
  • Settings search "worktree location" surfaces the new picker
  • (If on Windows) verify Windows paths use backslash separators in the new in-repo branch

Caveats — please read

  1. Subagent reviews skipped after Task 2. The Anthropic API was overloaded for most of the implementation run, so per-task spec/quality reviewer dispatch failed. Tasks 3-17 were executed inline with TDD, full test suite + lint + typecheck after every commit, but no second pair of eyes verified them mid-flight. A pre-merge code review on this PR is the right place to catch anything I missed.

  2. Plan deviation: orca-runtime.ts was refactored too. The plan only called for collapsing worktrees.ts:134-142. During Task 5's typecheck I discovered the CLI runtime has the same WSL+validation pattern at line 599. I refactored both and updated the runtime's narrowed RuntimeStore interface + test fixtures. Without this, the build would have been broken on the CLI surface.

sasha-id added 12 commits April 8, 2026 13:20
Adds the design spec for an opt-in worktree location mode that places
new worktrees at <repoPath>/.worktrees/<name> instead of inside the
global workspaceDir. Includes detect-and-prompt handling for the
repo's .gitignore, a small refactor of computeWorktreePath to fold in
path-traversal validation, and a two-tier segmented picker in the
Workspace settings section.
Bite-sized TDD plan with 18 tasks plus pre-flight, covering types,
gitignore module, computeWorktreePath refactor, IPC handlers, preload
bridge, AddWorktreeDialog wiring, GeneralPane picker, and a manual
smoke test pass. Reviewed against the design spec by both self-review
and an independent Opus pass.
sasha-id added a commit to sasha-id/orca that referenced this pull request Apr 10, 2026
Brings the focus-follows-mouse feature plus incidental titlebar and
terminal scrollbar fixes into main for local use while PR stablyai#389 is in
review against stablyai/orca upstream.

The titlebar cleanup and scrollbar fix commits are NOT in the upstream
PR because they depend on local structural changes (in-repo-worktrees
PR stablyai#388 and the previously-reverted tabs-system PR) that aren't yet on
upstream main. They will follow in a separate PR once upstream catches up.
@AmethystLiang
Copy link
Copy Markdown
Contributor

This is a good feature. Will try to help land it tmr

@AmethystLiang AmethystLiang requested review from nwparker and removed request for brennanb2025 April 12, 2026 21:07
@AmethystLiang
Copy link
Copy Markdown
Contributor

@nwparker can you help review this?

@AmethystLiang
Copy link
Copy Markdown
Contributor

@Jinwoo-H or @brennanb2025 would you be able to review this?
Just need to watch out for backward compatibility.

@sasha-id
Copy link
Copy Markdown
Contributor Author

could this reviewed and merged please

@nwparker nwparker requested review from brennanb2025 and removed request for nwparker April 21, 2026 22:41
@nwparker
Copy link
Copy Markdown
Contributor

@sasha-id , sorry for the delay. We'll work on it It makes sense.

@brennanb2025 if you can priortize this as next in line. You can help resolve merge conflicts here too

@brennanb2025
Copy link
Copy Markdown
Contributor

Hi @sasha-id, really appreciate the work on this. I've opened #1010 to discuss whether we want to support this in-repo worktree feature, as while it is a valid design, I noticed a few possible issues with agent confusion.

  • Silent search failure for agents at the main repo cwd. With .worktrees/ gitignored, ripgrep-based tools return zero results for strings that exist in sibling worktrees while Glob still returns the files — agents get logically impossible state and can make wrong decisions.
  • Config/tooling leakage from worktrees walking up into the main repo's .prettierrc, tsconfig.json, node_modules, etc.
  • git clean -dffx potential footgun: one extra -f at the main repo recursively deletes every worktree.
  • Orchestrator-style features (agents coordinating across worktrees or repos) are harder to build against nested placement, since their god-view cwd sits exactly where the search failure lives.
  • Also: I know Conductor shipped this approach and later moved away from it, citing the same agent-confusion and tooling-compatibility issues.

Would love to hear any suggestions or comments you have about this feature there — there may be mitigations for the failure modes I raised that I'm missing. Leaving this PR open for now.

Thanks!

@brennanb2025 brennanb2025 self-assigned this Apr 24, 2026
@nwparker nwparker added the size/xl Extra large PR (>2500 added lines or >50 files) label Apr 26, 2026
@sasha-id
Copy link
Copy Markdown
Contributor Author

Replaced by #1129 — closing this in favor of the cleaner version.

Why a new PR rather than a force-push: This branch was forked from main when upstream was at v1.1.7. Current upstream has moved ~700 commits ahead and PR #710 replaced AddWorktreeDialog.tsx (which d7ce61b8 modified) with NewWorkspaceComposerModal + useComposerState. A literal git rebase had a hard modify/delete conflict and structural conflicts that needed rework, not merge resolution. Force-pushing a heavily massaged history could break anyone who pulled this branch, so the work was rewritten cleanly on top of current main as 10 logical commits in #1129.

One deliberate behavioral simplification: the inline .gitignore confirmation dialog from d7ce61b8 was replaced with auto-add-on-first-create + a confirmation toast. Porting the prompt to the new composer flow would have been ~150 lines of new state-machine code; auto-add achieves the same end state given the user already opted into in-repo mode in Settings. The IPC handler is idempotent, and the settings copy is updated to be honest about the behavior. If reviewers want the explicit prompt back it's a follow-up PR.

Two bugs caught in self-review and fixed in #1129 (with new integration tests pinning the regression):

  1. Path-validation chain wasn't propagated — ensurePathWithinWorkspace was guarding against settings.workspaceDir instead of <repo>/.worktrees, breaking in-repo creation end-to-end.
  2. Bare repos in in-repo mode would silently land <bare-repo>/.worktrees/<name> inside the bare object directory; now reject with a clear error.

The original branch (feat/in-repo-worktrees) on this fork is left intact for anyone who already pulled it. The new branch is feat/in-repo-worktrees-v2.

@sasha-id sasha-id closed this Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl Extra large PR (>2500 added lines or >50 files)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants