--- name: worktree tags: git, worktree, parallel, land, merge, rebase description: >- Mint isolated git worktrees for parallel agent work, track ownership in a cross-agent registry, and land finished branches back onto a target with a queued rebase, test gate, and atomic fast-forward merge. --- # worktree The worktree worker gives every agent its own isolated checkout of a shared repository. Instead of two sessions fighting over one working tree, `worktree::create` mints a locked `git worktree` on a fresh branch, records who owns it, and returns a path to use as the agent's working directory. When the work is done, `worktree::land` automates the merge-back: rebase onto the target branch, run an optional test command, fast-forward the target atomically, and clean the worktree up. Lands are serialized per repository through an engine FIFO queue, so parallel agents never race a merge. The land test gate delegates to `shell::exec`, so the shell worker must be installed and `worktree_root` must sit inside its `fs.host_roots` jail. Landing never pushes to remotes; it moves local branches only. Destructive surfaces are gated by configuration: every mutating function checks its gate first, the force paths (claim takeover, forced removal, land `force_restart`) ship closed, and a denial names the exact config key to flip. The path `worktree::create` returns doubles as the turn's filesystem scope root (`metadata.fs_scope.root`), so file access inside the worktree is fenced by the shell worker while the gates cover the lifecycle door the scope cannot see (remove, prune, branch deletion, landing). ## When to Use - A task should run in isolation from the primary checkout or from other agents working on the same repo (`worktree::create`, then use the returned `path` as the agent's working directory). - You need to see which worktrees exist, who owns them, and whether they carry uncommitted or unlanded work (`worktree::list`, `worktree::status`). - A GitHub pull request should be reviewed or exercised in isolation (`worktree::create` with `pr: ` fetches `refs/pull//head` from `origin` and branches at it). - A finished branch should merge back into `main` (or any target) with tests enforced first (`worktree::land` with `test_cmd`). - A land was blocked on conflicts and the agent resolved them in place: finish the rebase, then rerun `worktree::land`; pass `force_restart` to abort and start over instead. - Hand a worktree between sessions (`worktree::claim`, `worktree::release`) or clean up abandoned ones (`worktree::remove`, `worktree::prune`). ## Boundaries - Not a PR or code-review tool: landing fast-forwards a local branch; it never pushes, opens pull requests, or talks to a forge. - Worktrees created out of band are reported as unmanaged and never adopted; only worktrees minted by `worktree::create` are managed. - One worker instance per engine: the registry has no compare-and-set, so multi-instance deployments must shard by repository. - Merges are fast-forward only (after the rebase); there is no merge-commit or squash strategy. - For running commands or editing files inside a worktree, use the `shell` worker with the worktree path as `cwd`; this worker only manages the worktrees themselves. ## Functions - `worktree::create` — mint a locked, isolated worktree off a base ref or a pull request head (`pr`); auto-claims for `session_id` when given, names the branch by id or deterministic codename per config, and returns an advisory `dev_port` derived from the id (never reserved anywhere). - `worktree::list` — registry view, filterable by repo or session, with optional git status per worktree. - `worktree::get` — one worktree with status. - `worktree::validate` — check a path is a live managed worktree; reconciles records whose directories were removed by hand. - `worktree::claim` — take session ownership (`force` to take over). - `worktree::release` — release ownership (`force` to override). - `worktree::status` — clean flag, ahead/behind, staged/unstaged/untracked counts, diffstat, rebase-in-progress, and `integrated` with an `integration_reason`, so squash- or rebase-landed branches read as merged even while ahead of their base. - `worktree::remove` — remove a worktree; refuses dirty or unlanded work unless forced, and refuses while running processes hold files open under it (`W222`); the directory leaves its path instantly (staged into a trash area, deleted in the background); can delete the branch. - `worktree::prune` — sweep: drop records whose directories are gone and remove clean, unclaimed, expired worktrees, including integrated ones whose work already landed (cron-bound, `{}` payload). - `worktree::land` — queue the rebase / test / fast-forward / cleanup pipeline; returns a `job_id` immediately. - `worktree::land-step` — internal queue consumer that executes land phases; never call it directly (denied to agents). With `provision.copy_ignored` enabled in config, every create also replicates the source repo's gitignored files (.env files, caches) into the new worktree in the background; the create response never waits on it. Errors carry stable `W###` codes; the ones worth branching on are `W210` (already claimed), `W220` (dirty), `W221` (unmerged work), `W222` (files held open by running processes), `W401` (land already queued), `W402` (unresolved rebase from a previous land), and the land-block reasons carried on events (`W410` conflict, `W411` tests, `W412` target kept moving, `W413` target checked out dirty). `W5xx` means configuration denied the operation, not that it failed: `W500` gate off, `W501` force disabled, `W502` land target not in `gates.land_targets`, `W503` repository not in `gates.repos`, `W504` per-repo worktree budget hit. The message names the exact key; do not retry, surface that key to the operator (or drop the force flag) instead. ## Reactive triggers Register a `worktree::*` trigger when a different worker should react to lifecycle changes without polling `worktree::list` — announce lands in chat, start a follow-up agent when a sibling's branch merges, or alert on blocked lands. Reach for it when: - A land's outcome should drive the next step (`worktree::landed`, `worktree::land-blocked` with `reason` and `conflict_files`). - Ownership changes matter to an orchestrator (`worktree::claimed`, `worktree::released`). - Cleanup should cascade (`worktree::removed`). The caller of `worktree::land` gets only `{ job_id, queued }` back; the outcome arrives on these triggers, so a landing workflow should always bind one instead of polling. ### How to bind 1. Register a handler: `registerFunction('notify::on-land', handler)`. 2. Register the trigger: ```typescript iii.registerTrigger({ type: 'worktree::landed', function_id: 'notify::on-land', config: { // optional equality filters: // repo_path, worktree_id, session_id }, }) ``` All six types accept the same three optional filters; unknown config keys are rejected at registration. For event payload shapes, call `get function info` on the trigger type.