Rationale
Several sessions work on one repository at once, and each needs its own checkout of its own branch. Git provides the linked worktree and takes its destination as an argument, so every caller decides where one goes — and fifty-three of them decided differently before this rule (ADR 0021). The cost was not the disorder; it was that no person and no agent could answer "where is branch X" or "where does a new one go" without looking or inventing, and an answer that has to be invented every time is invented differently every time.
The topology removes the decision. Given the repository's identity — the primary checkout, read from git's common directory and never from the current directory — and a branch name, there is exactly one path, and it is the same answer from the primary checkout, from a linked worktree, and from a session that does not know where it was started.
Required behaviour
- The derivation is one function of two inputs. The container is the primary
checkout's sibling, its directory name with
-wtappended; a branch's worktree is the branch name under the container, one directory per/-separated component, hierarchy kept.feature/providers/openai-streaminglives at<repo>-wt/feature/providers/openai-streaming. Nothing flattens, hashes, numbers or renames. - Git is the registry. No policy section, configuration file, table or list records a
container or a branch's path. The executable reads the common git directory,
git worktree list --porcelainandfor-each-ref, and derives everything else. Adding a branch requires editing nothing anywhere. - The primary checkout hosts the trunk. The trunk is discovered — the remote's HEAD,
init.defaultBranch, the one conventional name, the primary checkout's own branch — never hardcoded. Every non-trunk branch that is checked out has exactly one worktree, at its canonical path. - Work starts with the tool.
majordomus worktree create <branch>(orensure) derives the destination, validates the branch name by git's own rules, creates the branch from the trunk when it is new, holds the repository's worktree lock, refuses a destination that is occupied or a branch that is checked out elsewhere, and verifies what git registered. A baregit worktree add <path>is how a worktree ends up where nobody will find it. - An agent does not choose a path. Before implementing, it establishes the current
branch, the current worktree, the canonical worktree and the issue if the branch names
one (
majordomus worktree status); a mismatch is corrected with the tool, never worked around by continuing in the primary checkout or wherever the session happened to start. - A commit is guarded. The pre-commit hook asks
majordomus worktree guard, which refuses a feature branch committed from anywhere but its canonical worktree and the primary checkout committed on a branch that is not the trunk. Detached worktrees are exempt. The hook carries no logic of its own. - A worktree somewhere else is a diagnostic, and only migration repairs it. Every
condition has a stable code (
worktree.path_mismatch,worktree.container_occupied,worktree.destination_conflict,worktree.ephemeral, ...) carried by the command line, the API, MCP, the Cockpit and the tests, each with a remedy.worktree migrate --planshows the moves and changes nothing;worktree migratemakes them. - Migration is lossless or it is not a migration. A dirty worktree is moved as it is —
modified, staged, unstaged and untracked files included — never reset, stashed, cleaned
or checked out. A fingerprint is taken before and after each move and compared; a step is
reported as moved only when they are equal, and a difference is reported as a failure
with what differs. A destination that exists is never overwritten. A locked worktree
waits to be unlocked. A detached worktree is never moved; a session's scratch checkout
(under the temporary directory or
.claude/worktrees/) is reported and moved only on request. - Removal is explicit and keeps the branch.
worktree removerefuses uncommitted work without--force, never touches the primary checkout, and never deletes a branch. Cleanup eligibility — merged into the trunk, clean or not checked out — is derived state and acted on by a person. - The projections follow. The four
worktree.*capabilities are declared once and project to MCP, HTTP, OpenAPI, the Cockpit and the generated reference; the command line renders the same typed answers;-wtis written once in the crate and the CI check holds every document and template that names it to that constant.
The rule is repository-wide. A context document under .ai/ may add to it and may not
weaken it.
Failure behaviour
majordomus worktree guard exits 10 with the diagnostic and its remedy; the pre-commit
hook relays the exit code, so the commit does not happen. worktree status, list,
topology, validate and doctor exit 10 while an error-level diagnostic stands.
worktree create refuses, exit 10, naming what is in the way, and creates nothing.
worktree migrate leaves a step blocked or failed with its reason and exits 10 when any
step did not complete; nothing half-moved is reported as moved.
scripts/ci/worktree-check fails the build when the hook no longer asks the guard, when
-wt appears in the crate anywhere but its one constant, when a document or template that
names the container disagrees with the constant, or when the behavioural case fails.
Verification
cargo test --manifest-path apps/majordomus-cli/Cargo.toml --test worktree (the
derivation, the identity from every directory, the standings, the migration of dirty
worktrees with fingerprints, the container occupant, conflicts, locks, the ephemeral
standing, concurrency, parity between the command line and the registry);
test/cases/96_worktree_topology.sh (the built executable through the shell tool's own
wiring: the hook refuses, doctor sees the enforcement entry, the migration moves and
verifies); scripts/ci/worktree-check (the gate).