skip to content
$worker

worktree

v0.2.1

Git worktree lifecycle for parallel agents — mint, claim, and track isolated worktrees per repo, emit lifecycle trigger types, and land branches back through a per-repo FIFO queue (rebase, test gate, ff-only merge).

iiiverified
3 installs0 in 7d0 today
install
$iii worker add worktree@0.2.1
  • macOS: arm64 · x64
  • Linux: arm64 · armv7 · x64
  • Windows: arm64 · x64 · x86

exact versions are immutable; binary and bundle artifacts are digest-pinned.

skill doc

SKILL.md

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:
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.