# 08 — Worktrees
The checkout mode, `linked-worktree` or `main-worktree`, says where a topic branch opens. It is a Git workflow parameter — `git.checkout_mode` in the target configuration, recorded in `.release-kit/manifest.json`, reported by `rk status`, and changed only through the landing verbs — so every clone and every agent sees the same mode, and changing it is a visible, reviewed re-landing. A landing does not ask for it: it defaults to `linked-worktree` and sets where the helper verbs `rk worktree add` and `rk issue start` open a topic branch. Where the target opted into the workflow guards, it also decides whether the location guard lands in the hook block, which `rk status --check` judges.
`linked-worktree` is the default and the first-class form: every code-changing branch lives in a linked worktree at `../<project>@<flattened branch>`, and the main checkout commits nothing — not on a branch, not detached — just as the forge trunk takes no direct push. `main-worktree` is the supported alternative: short-lived branches are worked in the repository's original working tree, which switches to them, worktrees remain available and fully functional beside it, and nothing refuses either form.
## Why worktrees are first-class
Parallel work forces the worktree form regardless of mode. Two writers — human or coding agent — cannot share one checkout's HEAD, index, and uncommitted files, so every concurrently-worked branch takes its own worktree: one branch, one worktree, one writer. Git's own refusal to check one branch out twice is the lock, and `rk` never overrides it. In either mode, concurrent work takes worktrees; the mode decides only whether the main checkout may also be a writing seat.
## The naming rule
A worktree's path derives from the project and the branch: the branch name flattens by replacing every `/` with `-`, and the worktree is the sibling `../<project>@<flattened branch>`. Flattening is not injective — `feat/a-b` and `feat-a/b` derive the same directory — so `rk worktree add` refuses a collision by name and never suffixes silently. A worktree made by hand at some other path works and is reported off-path by `rk worktree list`, never refused; `rk worktree add` for its branch refuses to make a second seat and names `git worktree move` to the derived path as the move. A branch started from an issue reaches its seat through this same derivation and these same refusals, so nothing in the rule changes when the forge chose the name.
## The layouts the sibling rule admits
The sibling parent is the main checkout's parent, whatever it is: `rk` derives it from the standing main worktree and stores nothing, so the layout is chosen by where the project is cloned and needs no configuration.
- Flat — the main checkout beside its peer projects, worktrees interleaving with them. The default, and what most projects want.
- Workspace — the main checkout inside a directory scoped to this project alone, holding its seats and whatever else the project keeps beside them, a plans directory for example: `release-kit.ws/release-kit` with `release-kit.ws/release-kit@feat-x` beside it. Every worktree keeps the project name in its basename, so editor, tmux, and fuzzy-finder titles stay meaningful — the failure mode of layouts that name the inner directories after the branch alone. Promotion is a move per seat, at any time; the [runbook](../runbooks/worktree.md) states the commands.
- Detached root — the main checkout untouched and the worktrees somewhere else entirely. Reported off-path by `rk worktree list`, never refused, fully functional; `rk worktree add` will not produce those paths, so the operator makes and moves them.
- Unsupported: the bare-repo workspace — a bare repository with peer checkouts under it. `rk` refuses a bare main record: the sibling derivation has no main checkout to compose with, and the convention rests on one main worktree that commits nothing.
No directory suffix is canonical: the workspace's name is the operator's, and `rk` neither reads a suffix nor refuses one. This repository's own operator writes `<project>.ws`, which is a convention and not a rule.
## The sequence
1. Create the worktree from its source — an adopted local branch, a remote tip, a base, or the trunk.
2. Prepare its environment — a worktree is a fresh checkout.
3. Integrate through the authority and the merge form the record names; [integration](./12-integration.md) owns both paths and both forms.
4. Prune after the integration, worktree before branch, on the proof that mode leaves.
5. Promote to a workspace — optional: a layout move per seat, never required.
`rk worktree add` resolves the source by precedence: an existing local branch is adopted into its worktree, a lone matching remote tip becomes a local tracking branch — which is how a forge-minted issue branch or the release bot's branch is seated from its real tip, never silently recreated from the trunk — and anything else is created from `--base` or the refreshed trunk, with a release line always taking an explicit base because a line is cut from a tag, never the tip.
Pruning rests on the same proof as branch pruning, and three proofs are admissible: a merged request whose recorded head equals the branch's tip, a locally recorded integration whose recorded branch tip equals it, or a trunk that reaches the branch's current tip after the branch moved from where it was created. The third proof needs no record and no forge answer. The branch's own reflog proves the creation: its entry with an all-zero old object ID names the commit the branch started at. A fresh branch still at that commit is not merged, only new, and a branch whose creation entry expired gives no third proof. It holds after a local rebase integration, a GitLab fast-forward merge, and a hand `git merge --ff-only`, because each one keeps the commits of the branch. A squash writes a new commit, and a GitHub rebase merge writes new commits with new SHAs, so after either one the trunk does not reach the tip, and the merged request or the ledger entry stays the proof. Every proof is re-observed at the moment of action. A tip, a lock, or dirt that moved after verification keeps the worktree; the worktree is removed before its branch, so a failed removal leaves the branch and its work untouched; and a forge that cannot answer keeps every candidate whose only proof would be its answer, while a local integration record or the reachable-tip proof still holds without it. The step-by-step form is [the worktree runbook](../runbooks/worktree.md), `rk guide worktree`.
## Changing the mode
The mode change is an upgrade with exactly one overridden parameter: `rk upgrade --checkout-mode <mode> --apply` rewrites the record, and the hook block under the workflow guards, from what the record already states, and the committed diff is the visible change, reaching every clone through the trunk like any change. A plain `rk upgrade` keeps the recorded mode across releases.
The blocks are branch-versioned files, so a bare branch opened before a change to `linked-worktree` mode does not carry the guard until it takes the trunk's tip; the change protects the future, not the past. The transition closes the gap in order: land the mode change on the trunk through its pull request; move the main checkout to the trunk and pull, so the main checkout itself is guarded from here on; adopt each open bare branch into its worktree with `rk worktree add <branch> --apply` — the main checkout is off it, so the adoption is clean. A branch that must keep committing before it merges is guarded either way, because hooks are installed per clone, not per branch; it rebases onto the trunk only where its own tree must show an agent the new blocks. Switching to `main-worktree` while worktrees exist needs no procedure: the verbs are mode-free and every worktree keeps working.
## The escape and its cost
Every desk-level mirror dies to `--no-verify`, and the forge cannot see local topology: the linked-worktree mode's guard is honest about both. Its one named escape is the sweep: a CI checkout is commonly detached on the main worktree, so a linked-worktree target's `pre-commit run` sweep sets `SKIP=no-commit-to-branch,rk-worktree-location` in its environment. The same escape serves deliberate main-checkout surgery, stated here once.
## What a worktree does not isolate
A worktree isolates the working tree — HEAD, index, uncommitted files — and nothing else on the machine: ports, services, containers, and shared build caches are still shared. Hooks are per clone: the block is committed, `pre-commit install` arms it once in the clone, and the armed hooks fire in every linked worktree through the common git dir.
## Enforcement distances
The forge protections are the enforcement, identical in both checkout modes and blind to local topology; the checkout mode picks which desk-level mirrors stand where the target opted into the workflow guards. Under them, the trunk guard refuses a trunk commit in either checkout mode and either integration mode. In `linked-worktree` mode the main checkout also refuses every other commit, by the location guard, and in `main-worktree` mode a branch commit in the main checkout stays open. The location guard is integration-blind: it exits zero on the trunk in either integration mode, and a main-checkout branch commit still refuses. A local integration passes both guards because `rk integrate` moves the trunk ref and fires no commit hook. Which guards render at all is [integration](./12-integration.md)'s answer, and the two-distances doctrine of [setup](./02-setup.md) holds unchanged.
## Harnesses
A coding-agent harness that creates worktrees of its own is pointed at `rk worktree add`, so its branches carry the grammar, its paths the naming rule, and its cleanup the forge-confirmed path.
## Where this connects
The branch forms are [the model](./00-model.md); the mode choice is made in [setup](./02-setup.md); the integration a seat feeds is [integration](./12-integration.md); the release path from there is [operate](./03-operate.md); a release line's worktree follows [branch for release](./07-branch-for-release.md).