# Worktrees and repos

> Orca-managed git worktrees, repo registration, lineage/parent links, sparse checkout, workspace statuses, and CLI/RPC create/list/remove semantics.

- Repository: stablyai/orca
- GitHub: https://github.com/stablyai/orca
- Human docs: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c
- Complete Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/llms-full.txt

## Source Files

- `src/shared/types.ts`
- `src/cli/specs/core.ts`
- `src/cli/handlers/worktree.ts`
- `src/main/runtime/orca-runtime.ts`
- `src/main/git/worktree.ts`
- `docs/worktree-delete-preflight.md`
- `orca.yaml`

---

---
title: "Worktrees and repos"
description: "Orca-managed git worktrees, repo registration, lineage/parent links, sparse checkout, workspace statuses, and CLI/RPC create/list/remove semantics."
---

Orca registers git repositories and folder projects in persistent state (`orca-data.json`), maps each linked git checkout to a stable worktree id (`<repoId>::<absolute-path>`), and drives create/list/remove through `OrcaRuntimeService` RPC methods that the `orca` CLI calls. Git operations live in `src/main/git/worktree.ts`; user-authored fields and parent/child lineage live in `worktreeMeta` and `worktreeLineageById`.

## Architecture

```mermaid
flowchart TB
  subgraph clients [Clients]
    CLI["orca CLI"]
    Desktop["Electron renderer IPC"]
  end
  subgraph runtime [Runtime]
    RPC["runtime-rpc worktree.* / repo.*"]
    ORS["OrcaRuntimeService"]
  end
  subgraph persist [Persistence]
    DATA["orca-data.json"]
    META["worktreeMeta"]
    LINE["worktreeLineageById"]
  end
  subgraph git [Git]
    WT["git worktree add/remove/list"]
    SC["git sparse-checkout"]
  end
  CLI --> RPC
  Desktop --> ORS
  RPC --> ORS
  ORS --> DATA
  ORS --> META
  ORS --> LINE
  ORS --> WT
  ORS --> SC
```

| Layer | Responsibility |
| --- | --- |
| `Repo` / `Worktree` types | Canonical shapes in `src/shared/types.ts` |
| `orca-data.json` | `repos`, `worktreeMeta`, `worktreeLineageById`, `sparsePresetsByRepo`, `settings.workspaceStatuses` |
| `OrcaRuntimeService` | Selector resolution, visibility, lineage, create/remove ordering, SSH branches |
| `src/main/git/worktree.ts` | Porcelain listing, `addWorktree` / `addSparseWorktree`, `removeWorktree` |
| CLI (`src/cli/handlers/`) | Thin RPC wrappers for scripting agents |

Desktop surfaces also use IPC handlers (`worktrees:create`, `worktrees:list`, `worktrees:remove`, …) that share the same main-process logic as the runtime.

## Repo registration

A registered **repo** is a `Repo` record: `id`, `path`, `displayName`, `badgeColor`, `addedAt`, optional `kind`, and optional `connectionId` for SSH-backed remotes.

<ParamField body="kind" type="'git' | 'folder'">
`git` (default): requires a valid git repository at `path`. `folder`: non-git project root; worktrees are synthetic workspace instances over the same directory.
</ParamField>

<ParamField body="worktreeBaseRef" type="string | undefined">
Per-repo default base ref for new worktrees when create args omit `baseBranch`. Set via `orca repo set-base-ref` or `repo.setBaseRef` RPC.
</ParamField>

<ParamField body="externalWorktreeVisibility" type="'hide' | 'show'">
Controls whether git worktrees Orca did not create appear in the sidebar. New git repos default to `hide`; legacy repos may still default to `show`.
</ParamField>

<ParamField body="connectionId" type="string | null">
When set, repo git/filesystem operations delegate to SSH providers; local preflight/PTY teardown paths are skipped.
</ParamField>

### CLI: repo commands

| Command | RPC | Notes |
| --- | --- | --- |
| `orca repo list` | `repo.list` | All registered repos |
| `orca repo add --path <path>` | `repo.add` | Path must be absolute on the runtime host; remote CLI rejects relative paths |
| `orca repo show --repo <selector>` | `repo.show` | One repo |
| `orca repo set-base-ref --repo <selector> --ref <ref>` | `repo.setBaseRef` | Persists `worktreeBaseRef` |
| `orca repo search-refs --repo <selector> --query <text>` | `repo.searchRefs` | Branch/tag search for base picker |

Additional RPC-only repo operations include `repo.create`, `repo.clone`, `repo.update`, `repo.rm`, `repo.sparsePresets`, `repo.saveSparsePreset`, and hook helpers (`repo.hooks`, `repo.hooksCheck`, …).

### Repo selectors

Runtime resolves repos with optional prefixes:

| Form | Match |
| --- | --- |
| `id:<uuid>` | `Repo.id` |
| `path:<absolute>` | `Repo.path` (normalized) |
| `name:<displayName>` | `Repo.displayName` |
| bare value | Tries id, then path, then display name |

Ambiguous matches throw `selector_ambiguous`; none throw `repo_not_found`.

## Worktree identity and git shape

### Stable id

Worktree ids use `WORKTREE_ID_SEPARATOR` (`::`):

```text
<repoId>::<absolute-worktree-path>
```

Folder projects can add a session suffix: `<repoId>::<path>::workspace:<uuid>` for multiple workspace instances on one directory. Filesystem callers strip the suffix via `splitWorktreeIdForFilesystem`.

### Git-level vs app-level

| Type | Fields | Source |
| --- | --- | --- |
| `GitWorktreeInfo` | `path`, `head`, `branch`, `isBare`, `isSparse?`, `isMainWorktree` | `git worktree list --porcelain` (+ sparse detection) |
| `Worktree` | Metadata + git fields | `mergeWorktree(repoId, gitInfo, WorktreeMeta)` |
| `RuntimeWorktreeRecord` | Above + `parentWorktreeId`, `childWorktreeIds`, `lineage`, `git` | Runtime resolution cache |

`isMainWorktree` is true for the first porcelain block (primary checkout). Linked worktrees from `git worktree add` are `false`.

### Ownership and visibility

`classifyWorktreeOwnership` labels each detected checkout:

| `WorktreeOwnership` | Meaning |
| --- | --- |
| `orca-managed` | Strong Orca metadata (`orcaCreatedAt`, …) or path under configured Orca workspace layouts |
| `external` | Git worktree outside Orca layout roots |
| `unknown-legacy` | Under an Orca root but without strong metadata |

`worktree.list` returns only **visible** managed worktrees (respects `externalWorktreeVisibility`). `worktree.detectedList` returns the full scan for one repo with `ownership`, `visible`, `authoritative`, and `source` (`git` | `metadata-fallback` | `session-fallback`).

## Lineage and parent links

Parent/child relationships are stored in `worktreeLineageById` keyed by child worktree id. Each `WorktreeLineage` record includes:

- `worktreeId`, `worktreeInstanceId`, `parentWorktreeId`, `parentWorktreeInstanceId`
- `origin`: `orchestration` | `cli` | `manual`
- `capture`: `source` + `confidence` (`explicit` | `inferred`)
- Optional orchestration fields: `orchestrationRunId`, `taskId`, `coordinatorHandle`, `createdByTerminalHandle`

**Instance ids** on `WorktreeMeta` prevent stale lineage after a path is reused for a different checkout.

### Create-time resolution

When creating via CLI/RPC, lineage input can include:

| Input | Effect |
| --- | --- |
| `--no-parent` / `noParent: true` | No lineage record |
| `--parent-worktree <selector>` | Explicit parent (`explicit-cli-flag`) |
| Default (CLI) | Infers parent from `ORCA_TERMINAL_HANDLE` terminal, else cwd enclosing worktree (`cwd-context`) |
| `orchestrationContext` (RPC) | Parent from orchestration dispatch |

Conflicting `--parent-worktree` and `--no-parent` are rejected. Multiple inferred parents with different ids yield warning `LINEAGE_PARENT_CONTEXT_CONFLICT` and no lineage. Cycle detection walks the parent chain and rejects self-parents.

Resolved worktrees attach lineage only when both child and parent instance ids match stored lineage (`attachLineageToResolvedWorktrees`). Missing git checkouts trigger `pruneLineageForMissingRepoWorktrees`.

### CLI lineage output

`orca worktree create` prints stderr summaries: `parent: <id> (explicit from …)` or `parent: none`, plus lineage warnings when not using `--json`.

`orca worktree set` can update parent via `--parent-worktree` or clear with `--no-parent`.

RPC: `worktree.lineageList` returns all lineage records.

## Sparse checkout

Sparse worktrees use cone mode:

1. `addWorktree` (or checkout existing branch)
2. `git sparse-checkout init --cone`
3. `git sparse-checkout set -- <directories>`
4. `git checkout <branch>`

On failure after partial create, `addSparseWorktree` force-removes the worktree and may surface a cleanup-failed message.

**Metadata:** `WorktreeMeta.sparseDirectories`, `sparseBaseRef`, `sparsePresetId` (cleared when the worktree is no longer sparse on refresh).

**Presets:** `SparsePreset` per repo in `sparsePresetsByRepo`. RPC: `repo.sparsePresets`, `repo.saveSparsePreset`. Create accepts `sparseCheckout: { directories, presetId? }` on `worktree.create` (desktop/mobile RPC); the bundled CLI `worktree create` spec does not expose sparse flags yet.

Directories must be repo-relative paths.

## Workspace statuses

Board-style statuses are user-defined `WorkspaceStatusDefinition` entries in global settings (`id`, `label`, optional `color`, `icon`). Defaults include `todo`, `in-progress`, `in-review`, `completed` (max 12 statuses, labels up to 32 chars).

Per-worktree assignment: `Worktree.workspaceStatus` / `WorktreeMeta.workspaceStatus`, set at create (`workspaceStatus` on `worktree.create`) or via `worktree.set`.

Statuses are orthogonal to git state; they organize the sidebar/board only.

## Create lifecycle

```mermaid
sequenceDiagram
  participant C as Client
  participant R as OrcaRuntimeService
  participant G as git/worktree.ts
  participant S as Store
  C->>R: worktree.create
  R->>R: resolveRepoSelector
  R->>R: resolveLineageForWorktreeCreate
  R->>G: addWorktree / addSparseWorktree
  R->>S: setWorktreeMeta + setWorktreeLineage
  R->>R: createTerminal (default)
  R-->>C: CreateWorktreeResult
```

<Steps>
<Step title="Resolve repo and base ref">
`baseBranch` → `repo.worktreeBaseRef` → `getDefaultBaseRef(repo.path)`. If none, error asks for explicit `--base-branch`.
</Step>
<Step title="Branch and path">
Sanitize name, resolve branch (optional `branchNameOverride`), detect conflicts with existing local/remote branches or open PRs, compute path via `computeWorktreePath` (`settings.workspaceDir`, `nestWorktrees`).
</Step>
<Step title="Git worktree add">
Local: `addWorktree` or `addSparseWorktree`. SSH: `createManagedRemoteWorktree`. Folder: metadata-only instance id.
</Step>
<Step title="Persist metadata">
Stamp `orcaCreatedAt`, `orcaCreationSource` (`cli` | `runtime` | `desktop` | `ssh`), optional links (`linkedIssue`, `linkedPR`, GitLab slots, `pushTarget`, `workspaceStatus`), lineage.
</Step>
<Step title="Terminal and activation">
Creates first terminal by default. `--activate` or `--run-hooks` reveals workspace in the app; `--run-hooks` also runs `orca.yaml` setup per repo policy.
</Step>
</Steps>

<Note>
`orca worktree create` does not switch the active desktop workspace unless `--activate` or `--run-hooks` is passed. Setup hooks follow `RepoHookSettings.setupRunPolicy`; use `--run-hooks` to force when policy would skip.
</Note>

### Create result fields

`CreateWorktreeResult` / `RuntimeWorktreeCreateResult` include:

- `worktree` (with lineage attachment on list/show)
- `lineage`, `warnings` (lineage warnings)
- `warning` (hook/terminal failures)
- `setup` (`WorktreeSetupLaunch` when setup script runs)
- `initialBaseStatus`, `localBaseRefRefresh` (desktop IPC paths)

## Remove lifecycle

`worktree.rm` / `removeManagedWorktree` share destructive semantics:

| Phase | Local git repo | SSH repo |
| --- | --- | --- |
| Registration check | `findRegisteredDeletableWorktree` | Provider `listWorktrees` |
| Archive hook | Optional with `--run-hooks`; skipped by default on RPC with warning | Provider-owned |
| Preflight | `assertWorktreeCleanForRemoval` (non-force) before PTY kill | N/A (provider delete) |
| PTY teardown | `killAllProcessesForWorktree` after clean preflight | Not local PTYs |
| Git remove | `removeWorktree` + prune; optional branch delete unless `preserveBranchOnDelete` | Provider `removeWorktree` |
| Metadata | `removeWorktreeMeta`, lineage prune, terminal history dir | Same |

<Warning>
Non-force delete runs `git status --porcelain` preflight **before** killing terminals. Dirty/untracked worktrees fail without tearing down PTYs. Force delete skips preflight and may kill PTYs first (IPC path). Orphan paths (`is not a working tree`, missing dir) skip PTY kill and follow existing prune/metadata cleanup.
</Warning>

Concurrent deletes on the same worktree id share one in-flight promise; conflicting `force`/`run-hooks` options throw `Worktree deletion already in progress`.

Unregistered paths require `--force` and pass safety checks (`canSafelyRemoveOrphanedWorktreeDirectory`).

## List, show, and orchestration summary

| Command / RPC | Behavior |
| --- | --- |
| `orca worktree list` / `worktree.list` | Visible resolved worktrees; optional `--repo`; default limit 200 with `totalCount` / `truncated` |
| `worktree.detectedList` | Full repo scan + ownership (RPC/desktop) |
| `orca worktree show` / `worktree.show` | One record by selector |
| `orca worktree current` | Resolves cwd to enclosing worktree → `id:<id>` (local only for cwd shortcuts) |
| `orca worktree ps` / `worktree.ps` | Cross-worktree terminal/agent summary (`RuntimeWorktreePsSummary`, status `active` \| `working` \| `permission` \| `done` \| `inactive`) |
| `worktree.sleep` / `worktree.activate` | RPC workspace focus helpers |

## Worktree selectors

| Form | Match |
| --- | --- |
| `id:<worktreeId>` | Exact id |
| `path:<absolute>` | Filesystem path (first match if duplicate registration) |
| `branch:<name>` | Branch selector match |
| `issue:<number>` | `linkedIssue` |
| `active` / `current` | **Local CLI only:** cwd → enclosing worktree → `id:…` |
| bare value | id, path, or branch |

Remote/paired runtimes: `active`/`current` and cwd-derived `path:` from the client machine are invalid; use server-absolute `path:` or `id:`.

`orca worktree current` lists up to 10,000 worktrees and picks the longest matching path prefix.

## RPC method inventory (worktree / repo)

**Repo:** `repo.list`, `repo.add`, `repo.show`, `repo.update`, `repo.setBaseRef`, `repo.baseRefDefault`, `repo.searchRefs`, `repo.sparsePresets`, `repo.saveSparsePreset`, `repo.create`, `repo.clone`, `repo.rm`, `repo.reorder`, hook/issue helpers.

**Worktree:** `worktree.list`, `worktree.detectedList`, `worktree.show`, `worktree.create`, `worktree.set`, `worktree.rm`, `worktree.ps`, `worktree.lineageList`, `worktree.sleep`, `worktree.activate`, `worktree.persistSortOrder`, `worktree.resolvePrBase`, `worktree.resolveMrBase`.

Method schemas live under `src/main/runtime/rpc/methods/`.

## Persistence snapshot

```text
orca-data.json
├── repos[]
├── worktreeMeta{ "<repoId>::<path>": WorktreeMeta }
├── worktreeLineageById{ "<childId>": WorktreeLineage }
├── sparsePresetsByRepo{ "<repoId>": SparsePreset[] }
└── settings.workspaceStatuses[]
```

User-facing hook scripts come from repo-root `orca.yaml` (`scripts.setup`, `scripts.archive`) merged with per-repo `RepoHookSettings`; see Repository hooks for trust and `--run-hooks` policy.

## Verification

```bash
orca status
orca repo list --json
orca worktree list --repo id:<repoId> --json
orca worktree current --json
orca worktree create --repo id:<repoId> --name feature-x --parent-worktree active --json
orca worktree rm --worktree id:<worktreeId>
```

Expect `parent:` stderr lineage on create (non-JSON), `removed: true` on successful rm, and `selector_not_found` when cwd is outside any managed worktree.

## Related pages

<CardGroup>
<Card title="Quickstart" href="/quickstart">
First repo, worktree, and `orca worktree current` verification.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
Full flag lists for `repo` and `worktree` command groups.
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Selector grammar, `--json` shapes, and remote-runtime resolution rules.
</Card>
<Card title="Repository hooks" href="/repository-hooks">
`orca.yaml` setup/archive scripts and `--run-hooks` on create/remove.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
`connectionId` repos, remote worktree CRUD, and CLI selector constraints.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Delete preflight failures, selector ambiguity, and runtime reachability.
</Card>
</CardGroup>
