skip to content
$worker

worktree

v0.2.2

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.2
  • macOS: arm64 · x64
  • Linux: arm64 · armv7 · x64
  • Windows: arm64 · x64 · x86

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

agent-ready brief for v0.2.2
install + config + dependencies + readme + api reference, all in one place. fetch as agent-context.md for an llm to consume.
the same content rendered as discrete blocks below is exposed as a single markdown document at /workers/worktree.md?version=0.2.2. paste it into an llm prompt or pipe it through curl from a worker.

install

install
$iii worker add worktree@0.2.2

dependencies

readme

README.md

worktree

Git worktree lifecycle for parallel agents. worktree::create mints an isolated, locked worktree off any ref and registers it in a cross-agent registry; agents work inside it through their turn's filesystem scope (metadata.fs_scope.root); worktree::land rebases the branch onto a target, runs an optional test gate through the shell worker, fast-forwards the target with an atomic compare-and-swap, and cleans up. Every lifecycle change emits a trigger type sibling workers can subscribe to, so a Slack bot can announce lands or a harness can react when a sibling's branch merges.

Install

iii worker add worktree

iii worker add fetches the binary, writes a config block into ~/.iii/config.yaml, and the engine starts the worker on the next iii boot. The land test gate delegates to shell::exec, so install the shell worker alongside it:

iii worker add shell

Quickstart

use iii_sdk::{register_worker, InitOptions};
use iii_sdk::protocol::TriggerRequest;
use serde_json::json;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let iii = register_worker("ws://localhost:49134", InitOptions::default());

    // 1. Mint an isolated worktree and hand its path to an agent.
    let wt = iii.trigger(TriggerRequest {
        function_id: "worktree::create".into(),
        payload: json!({ "repo_path": "/home/me/project", "session_id": "sess_1" }),
        action: None,
        timeout_ms: Some(30_000),
    }).await?;
    println!("agent fs_scope root: {}", wt["path"]);

    // 2. ... the agent commits work inside the worktree ...

    // 3. Land it: rebase onto main, gate on tests, ff-merge, clean up.
    let job = iii.trigger(TriggerRequest {
        function_id: "worktree::land".into(),
        payload: json!({
            "worktree_id": wt["worktree_id"],
            "target_branch": "main",
            "test_cmd": "cargo test",
        }),
        action: None,
        timeout_ms: Some(30_000),
    }).await?;
    println!("queued land job: {}", job["job_id"]);
    Ok(())
}

The land completes asynchronously; subscribe to worktree::landed / worktree::land-blocked (below) or poll worktree::get for the outcome.

Configuration

Runtime settings live in the configuration worker under id worktree and hot-reload on change (a prune_schedule change re-binds the cron live).

worktree_root: "~/.iii/worktrees"   # where managed worktrees are created
branch_prefix: "iii/"               # auto-minted branch names
branch_naming: "id"                 # or "codename" for <adjective>-<noun>-<4hex> names
prune_schedule: "0 0 * * * *"       # six-field cron for the reconcile sweep
prune_expire_hours: 72              # idle hours before a clean, unclaimed worktree is prunable
land_queue: "worktree-land"         # engine queue name for land jobs
max_land_retries: 3                 # rebase retries when the target branch moves mid-land
git_timeout_ms: 60000               # per-git-subprocess bound
test_timeout_ms: 600000             # land test gate bound
provision:
  copy_ignored: false               # replicate gitignored files into new worktrees
  include: []                       # globs; empty = every ignored path
  exclude: []                       # globs subtracted from the copy
  max_copy_bytes: 2147483648        # total budget; entries beyond it are skipped
gates:
  allow_remove: true                # worktree::remove
  allow_force: false                # remove force, claim/release force, land force_restart
  allow_branch_delete: true         # remove delete_branch and the land finalize cleanup
  allow_land: true                  # worktree::land
  allow_prune: true                 # worktree::prune (including the cron sweep)
  land_targets: ["*"]               # globs; pin to ["main"] to allow only mainline lands
  repos: ["*"]                      # globs over canonical repo paths worktrees may branch from
  max_worktrees_per_repo: 0         # live-worktree budget per repo at create time; 0 = unlimited

Copy-ignored provisioning

With provision.copy_ignored: true, every create spawns a background task that replicates the source repo's gitignored files (.env files, caches, local settings) into the new worktree: cheapest copy per platform (APFS clones via cp -c with a plain fallback on macOS, --reflink=auto on Linux), VCS metadata directories and the managed worktree root always excluded, bounded by max_copy_bytes. Provisioning is best-effort by design: the create response never waits on it, failures only log, and a retried copy skips files that already landed.

Pull request checkouts, dev ports, integration

worktree::create also takes pr: : it fetches refs/pull//head from origin (GitHub layout) and branches pr- at it, so reviewing a PR gets its own isolated checkout. Every worktree carries an advisory dev_port (10000 + hash of the id mod 10000): deterministic, collision-improbable across parallel worktrees, and never reserved anywhere; point dev servers at it to avoid port fights. worktree::status reports integrated with an integration_reason (same_commit, ancestor, no_added_changes, trees_match, merge_adds_nothing, patch_id_match) so squash- or rebase-landed branches read as merged, and worktree::prune removes integrated-but-ahead worktrees instead of holding them forever.

Removal and trash

worktree::remove never blocks on a large directory delete: the worktree is renamed into a .trash staging area under worktree_root (instant), the git admin entry is pruned, and a detached task deletes the staged directory in the background; if the rename fails the removal falls back to a synchronous git worktree remove. Entries stranded by a crash are cleared by the next prune's trash sweep, which runs even when gates.allow_prune is off (it is remove's crash recovery, not part of prune's destructive surface). Unforced removals also probe for processes holding files open under the worktree and refuse with W222 rather than deleting a directory out from under a running dev server.

Gates

Every mutating handler checks its gate before anything else, so an operator can turn off whole classes of destruction with one config flip and the change hot-reloads. Denials are structured: W500 (operation disabled), W501 (force paths disabled), W502 (land target not allowed), and every message names the exact key to flip, e.g. worktree::remove is disabled by configuration; set gates.allow_remove: true to enable. Force is the one gate that ships closed: takeovers, forced removals, and land restarts are opt-in.

Creation is gated too: gates.repos pins which repositories worktrees may branch from (globs over the canonicalized path, W503 otherwise), and gates.max_worktrees_per_repo caps live worktrees per repository at create time (W504 names the current count; orphaned records do not count, and narrowing either gate later never breaks existing worktrees).

Gates constrain this worker's own surface only. Restricting what an agent can run in a shell (rm, git branch -D, ...) is the shell worker's allowlist/denylist job; pair both for defense in depth.

Filesystem scope interplay

Agents are scoped through metadata.fs_scope.root on the turn: the console picker (or harness::send options) sets it to the worktree path, and the harness stamps fs_scope: { root, grants } onto every scoped shell::* / coder::* call the agent makes. The land test gate does the same directly: it runs shell::exec with the worktree as cwd and as the fs_scope root, so the shell worker enforces the scope for gated tests exactly as it does for agent calls.

worktree_root must still resolve inside the shell worker's fs.host_roots: a root outside the jail fails with S215 (a path inside the jail but outside the scope root fails with S220), and the test gate surfaces the S215 misconfiguration as a W411 land block naming it.

Engine queue config

Land jobs serialize per repository through an engine FIFO queue. Add this block to the engine config (the repo_key group field is carried on every job payload):

queue_configs:
  worktree-land:
    type: fifo
    message_group_field: repo_key
    concurrency: 1
    max_retries: 2

Without it the queue still delivers, but ordering falls back to this worker's in-process per-repo lock, which only holds within a single instance.

Functions

  • worktree::create — mint a locked worktree off a ref; auto-claims when session_id is given.
  • worktree::list — registry view with filters and optional git status; reports unmanaged worktrees of a repo without adopting them.
  • worktree::get — one worktree with status.
  • worktree::validate — picker-style path validation; reconciles records whose directories are gone.
  • worktree::claim / worktree::release — session ownership with force takeover and W210/W211 mismatch errors.
  • worktree::status — clean, ahead/behind, staged/unstaged/untracked, diffstat, rebase-in-progress.
  • worktree::remove — guarded removal (W220 dirty, W221 unmerged, W222 busy), instant trash staging with background delete, optional branch deletion.
  • worktree::prune — reconcile sweep, cron-bound; {} payload works.
  • worktree::land — queue a land job; returns { job_id, queued }.
  • worktree::land-step — internal queue consumer; not agent-callable.

Errors carry stable W### codes (src/error.rs). One instance per engine: iii-state has no compare-and-set, so run a single worktree worker (shard by repo if you need more).

Custom trigger types

Trigger type Fires when Payload highlights
worktree::created A worktree was minted worktree_id, path, branch, base_sha
worktree::claimed A session took ownership session_id, previous_session_id
worktree::released A claim was released session_id
worktree::removed A worktree was removed or pruned branch_deleted
worktree::landed A land fast-forwarded the target target_branch, merged_sha
worktree::land-blocked A land ended blocked reason, code, conflict_files, exit_code

Bindings accept optional equality filters: repo_path, worktree_id, session_id.

use iii_sdk::protocol::RegisterTriggerInput;

iii.register_trigger(RegisterTriggerInput {
    trigger_type: "worktree::landed".to_string(),
    function_id: "notify::on-land".to_string(),
    config: serde_json::json!({ "repo_path": "/home/me/project" }),
    metadata: None,
})?;

Conflict resolution flow

When a land's rebase hits conflicts, the rebase is left IN PROGRESS inside the worktree and a worktree::land-blocked event fires with reason: "rebase_conflict" and the conflicted paths. The owning agent resolves in place (its filesystem scope already roots at the worktree), finishes with git rebase --continue, and reruns worktree::land. A rerun while the rebase is still unresolved returns W402; pass force_restart: true to abort the rebase, reset, and start the land over.

HTTP endpoints

With the http worker (or any provider of the http trigger type) installed, the read surface is also served over HTTP: worktree/list, worktree/get, worktree/status, and worktree/validate (POST, JSON bodies matching the function schemas). Registration is best-effort; without an http provider the worker logs a warning and keeps serving the bus.

Local development and testing

cargo run --release -- --url ws://127.0.0.1:49134
cargo test

The end-to-end test boots a real engine and self-skips when iii is not on PATH. Regenerate wire-schema goldens with UPDATE_GOLDENS=1 cargo test.

api reference (json)

agent-api-reference.json
{
  "functions": [
    {
      "description": "Claim a worktree for a session. Fails with W210 when another session holds it, unless force is set.",
      "metadata": {},
      "name": "worktree::claim",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "force": {
            "default": false,
            "description": "Take the claim even when another session holds it.",
            "type": "boolean"
          },
          "session_id": {
            "description": "The claiming session.",
            "type": "string"
          },
          "worktree_id": {
            "description": "The worktree to claim.",
            "type": "string"
          }
        },
        "required": [
          "session_id",
          "worktree_id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "claimed": {
            "description": "True when the claim is now held by `session_id`.",
            "type": "boolean"
          },
          "previous_session_id": {
            "description": "The session that held the claim before, when taken over with force.",
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "description": "The claiming session.",
            "type": "string"
          },
          "worktree_id": {
            "description": "The claimed worktree.",
            "type": "string"
          }
        },
        "required": [
          "claimed",
          "session_id",
          "worktree_id"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Create an isolated, locked git worktree off a base ref and register it in the cross-agent registry. Returns the path to hand to an agent as its working directory.",
      "metadata": {},
      "name": "worktree::create",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "base_ref": {
            "default": null,
            "description": "Ref to base the worktree on. Defaults to `HEAD`.",
            "type": [
              "string",
              "null"
            ]
          },
          "branch": {
            "default": null,
            "description": "Branch to create. Defaults to `<branch_prefix><worktree_id>` (or a codename when `branch_naming: codename`).",
            "type": [
              "string",
              "null"
            ]
          },
          "pr": {
            "default": null,
            "description": "Create the worktree at a GitHub pull request head: fetches `refs/pull/<n>/head` from `origin` and branches `<prefix>pr-<n>` at it. Mutually exclusive with `base_ref` and `branch`.",
            "format": "uint64",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "repo_path": {
            "description": "Absolute path of the repository to branch from.",
            "type": "string"
          },
          "session_id": {
            "default": null,
            "description": "Session to auto-claim the worktree for.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "repo_path"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "base_ref": {
            "description": "The ref the worktree was based on.",
            "type": "string"
          },
          "base_sha": {
            "description": "The commit `base_ref` resolved to.",
            "type": "string"
          },
          "branch": {
            "description": "The branch checked out in the worktree.",
            "type": "string"
          },
          "created_at": {
            "description": "Creation time, ms since epoch.",
            "format": "int64",
            "type": "integer"
          },
          "dev_port": {
            "description": "Advisory dev-server port derived from the worktree id.",
            "format": "uint16",
            "minimum": 0,
            "type": "integer"
          },
          "path": {
            "description": "Absolute path of the new worktree; hand this to an agent as its working directory.",
            "type": "string"
          },
          "repo_key": {
            "description": "Canonical per-repo key (FIFO group for lands).",
            "type": "string"
          },
          "worktree_id": {
            "description": "Stable id, `wt_<12hex>`.",
            "type": "string"
          }
        },
        "required": [
          "base_ref",
          "base_sha",
          "branch",
          "created_at",
          "dev_port",
          "path",
          "repo_key",
          "worktree_id"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Fetch one managed worktree with its current git status.",
      "metadata": {},
      "name": "worktree::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "worktree_id": {
            "description": "The worktree to fetch.",
            "type": "string"
          }
        },
        "required": [
          "worktree_id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Lifecycle": {
            "description": "Lifecycle of a managed worktree.",
            "oneOf": [
              {
                "description": "Exists and is unclaimed.",
                "enum": [
                  "active"
                ],
                "type": "string"
              },
              {
                "description": "Claimed by a session.",
                "enum": [
                  "claimed"
                ],
                "type": "string"
              },
              {
                "description": "A land job is queued or running.",
                "enum": [
                  "landing"
                ],
                "type": "string"
              },
              {
                "description": "The last land attempt was blocked (conflict, failed tests, ...).",
                "enum": [
                  "land-blocked"
                ],
                "type": "string"
              },
              {
                "description": "The record exists but the directory is gone.",
                "enum": [
                  "orphaned"
                ],
                "type": "string"
              }
            ]
          },
          "WorktreeStatus": {
            "description": "Git status summary for one worktree.",
            "properties": {
              "ahead": {
                "description": "Commits ahead of the comparison base (upstream when set, else `base_sha`).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "behind": {
                "description": "Commits behind the comparison base.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "clean": {
                "description": "True when there are no staged, unstaged, untracked, or conflicted entries.",
                "type": "boolean"
              },
              "conflicted": {
                "description": "Unmerged (conflicted) entry count.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "diffstat": {
                "description": "`git diff --shortstat` of committed work since `base_sha`.",
                "type": "string"
              },
              "head_sha": {
                "description": "Current HEAD commit.",
                "type": "string"
              },
              "in_rebase": {
                "description": "True while a rebase is in progress in this worktree.",
                "type": "boolean"
              },
              "integrated": {
                "default": false,
                "description": "True when the branch's work is already contained in its integration target (fast-forward, merge, rebase, or squash merge). Integrated worktrees are prunable even when ahead of their base.",
                "type": "boolean"
              },
              "integration_reason": {
                "description": "Which check detected the integration: `same_commit`, `ancestor`, `no_added_changes`, `trees_match`, `merge_adds_nothing`, or `patch_id_match`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "staged": {
                "description": "Staged entry count.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "unpushed": {
                "description": "Commits not merged upstream (upstream ahead count, else commits past `base_sha`).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "unstaged": {
                "description": "Unstaged entry count.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "untracked": {
                "description": "Untracked entry count.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "ahead",
              "behind",
              "clean",
              "conflicted",
              "diffstat",
              "head_sha",
              "in_rebase",
              "staged",
              "unpushed",
              "unstaged",
              "untracked"
            ],
            "type": "object"
          }
        },
        "description": "One worktree as reported by `worktree::list` / `worktree::get`.",
        "properties": {
          "base_ref": {
            "description": "The ref the worktree was created from.",
            "type": "string"
          },
          "base_sha": {
            "description": "The commit `base_ref` resolved to at creation time.",
            "type": "string"
          },
          "branch": {
            "description": "The branch checked out in the worktree.",
            "type": "string"
          },
          "created_at": {
            "description": "Creation time, ms since epoch.",
            "format": "int64",
            "type": "integer"
          },
          "dev_port": {
            "description": "Advisory dev-server port derived from the worktree id: deterministic, collision-improbable, never reserved anywhere.",
            "format": "uint16",
            "minimum": 0,
            "type": "integer"
          },
          "lifecycle": {
            "allOf": [
              {
                "$ref": "#/definitions/Lifecycle"
              }
            ],
            "description": "Current lifecycle (reported `orphaned` when the directory is gone)."
          },
          "path": {
            "description": "Absolute path of the worktree directory.",
            "type": "string"
          },
          "repo_key": {
            "description": "Canonicalized per-repo key.",
            "type": "string"
          },
          "repo_path": {
            "description": "The repository path the worktree was created from.",
            "type": "string"
          },
          "session_id": {
            "description": "Owning session, when claimed.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "anyOf": [
              {
                "$ref": "#/definitions/WorktreeStatus"
              },
              {
                "type": "null"
              }
            ],
            "description": "Git status, when requested and computable."
          },
          "updated_at": {
            "description": "Last mutation time, ms since epoch.",
            "format": "int64",
            "type": "integer"
          },
          "worktree_id": {
            "description": "Stable id, `wt_<12hex>`.",
            "type": "string"
          }
        },
        "required": [
          "base_ref",
          "base_sha",
          "branch",
          "created_at",
          "dev_port",
          "lifecycle",
          "path",
          "repo_key",
          "repo_path",
          "updated_at",
          "worktree_id"
        ],
        "title": "WorktreeInfo",
        "type": "object"
      }
    },
    {
      "description": "Queue a land: rebase onto the target, run the optional test gate via shell::exec, fast-forward the target with an atomic CAS, then clean up. Serialized per repo via the land queue.",
      "metadata": {},
      "name": "worktree::land",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "force_restart": {
            "default": false,
            "description": "Abort a conflicted rebase left by a previous land and start over.",
            "type": "boolean"
          },
          "keep": {
            "default": false,
            "description": "Keep the worktree and branch after a successful land.",
            "type": "boolean"
          },
          "target_branch": {
            "description": "The branch to fast-forward with the worktree's commits.",
            "type": "string"
          },
          "test_cmd": {
            "default": null,
            "description": "Optional test command run in the worktree via `shell::exec` before merging (the test gate).",
            "type": [
              "string",
              "null"
            ]
          },
          "worktree_id": {
            "description": "The worktree to land.",
            "type": "string"
          }
        },
        "required": [
          "target_branch",
          "worktree_id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "job_id": {
            "description": "The queued land job.",
            "type": "string"
          },
          "queued": {
            "description": "True when the job was enqueued.",
            "type": "boolean"
          }
        },
        "required": [
          "job_id",
          "queued"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Internal: execute one queued land job. Bound to the land queue; not agent-callable.",
      "metadata": {},
      "name": "worktree::land-step",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "job_id": {
            "description": "The job to advance.",
            "type": "string"
          },
          "repo_key": {
            "default": "",
            "description": "FIFO group key; consumed by the engine's queue config, echoed here.",
            "type": "string"
          }
        },
        "required": [
          "job_id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "done": {
            "description": "True when the job reached a terminal state (landed or blocked).",
            "type": "boolean"
          },
          "job_id": {
            "description": "The advanced job.",
            "type": "string"
          },
          "phase_completed": {
            "description": "The last phase this invocation completed.",
            "type": "string"
          }
        },
        "required": [
          "done",
          "job_id",
          "phase_completed"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "List managed worktrees, optionally filtered by repo or session and enriched with git status. Also reports unmanaged worktrees of the repo when repo_path is given.",
      "metadata": {},
      "name": "worktree::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "include_status": {
            "default": false,
            "description": "Compute git status for each worktree (slower).",
            "type": "boolean"
          },
          "repo_path": {
            "default": null,
            "description": "Only worktrees of this repository.",
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "default": null,
            "description": "Only worktrees claimed by this session.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Lifecycle": {
            "description": "Lifecycle of a managed worktree.",
            "oneOf": [
              {
                "description": "Exists and is unclaimed.",
                "enum": [
                  "active"
                ],
                "type": "string"
              },
              {
                "description": "Claimed by a session.",
                "enum": [
                  "claimed"
                ],
                "type": "string"
              },
              {
                "description": "A land job is queued or running.",
                "enum": [
                  "landing"
                ],
                "type": "string"
              },
              {
                "description": "The last land attempt was blocked (conflict, failed tests, ...).",
                "enum": [
                  "land-blocked"
                ],
                "type": "string"
              },
              {
                "description": "The record exists but the directory is gone.",
                "enum": [
                  "orphaned"
                ],
                "type": "string"
              }
            ]
          },
          "UnmanagedWorktree": {
            "description": "A worktree of the repo that this worker does not manage.",
            "properties": {
              "branch": {
                "description": "Checked-out branch ref, when not detached.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "path": {
                "description": "Absolute path of the worktree.",
                "type": "string"
              }
            },
            "required": [
              "path"
            ],
            "type": "object"
          },
          "WorktreeInfo": {
            "description": "One worktree as reported by `worktree::list` / `worktree::get`.",
            "properties": {
              "base_ref": {
                "description": "The ref the worktree was created from.",
                "type": "string"
              },
              "base_sha": {
                "description": "The commit `base_ref` resolved to at creation time.",
                "type": "string"
              },
              "branch": {
                "description": "The branch checked out in the worktree.",
                "type": "string"
              },
              "created_at": {
                "description": "Creation time, ms since epoch.",
                "format": "int64",
                "type": "integer"
              },
              "dev_port": {
                "description": "Advisory dev-server port derived from the worktree id: deterministic, collision-improbable, never reserved anywhere.",
                "format": "uint16",
                "minimum": 0,
                "type": "integer"
              },
              "lifecycle": {
                "allOf": [
                  {
                    "$ref": "#/definitions/Lifecycle"
                  }
                ],
                "description": "Current lifecycle (reported `orphaned` when the directory is gone)."
              },
              "path": {
                "description": "Absolute path of the worktree directory.",
                "type": "string"
              },
              "repo_key": {
                "description": "Canonicalized per-repo key.",
                "type": "string"
              },
              "repo_path": {
                "description": "The repository path the worktree was created from.",
                "type": "string"
              },
              "session_id": {
                "description": "Owning session, when claimed.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/WorktreeStatus"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Git status, when requested and computable."
              },
              "updated_at": {
                "description": "Last mutation time, ms since epoch.",
                "format": "int64",
                "type": "integer"
              },
              "worktree_id": {
                "description": "Stable id, `wt_<12hex>`.",
                "type": "string"
              }
            },
            "required": [
              "base_ref",
              "base_sha",
              "branch",
              "created_at",
              "dev_port",
              "lifecycle",
              "path",
              "repo_key",
              "repo_path",
              "updated_at",
              "worktree_id"
            ],
            "type": "object"
          },
          "WorktreeStatus": {
            "description": "Git status summary for one worktree.",
            "properties": {
              "ahead": {
                "description": "Commits ahead of the comparison base (upstream when set, else `base_sha`).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "behind": {
                "description": "Commits behind the comparison base.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "clean": {
                "description": "True when there are no staged, unstaged, untracked, or conflicted entries.",
                "type": "boolean"
              },
              "conflicted": {
                "description": "Unmerged (conflicted) entry count.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "diffstat": {
                "description": "`git diff --shortstat` of committed work since `base_sha`.",
                "type": "string"
              },
              "head_sha": {
                "description": "Current HEAD commit.",
                "type": "string"
              },
              "in_rebase": {
                "description": "True while a rebase is in progress in this worktree.",
                "type": "boolean"
              },
              "integrated": {
                "default": false,
                "description": "True when the branch's work is already contained in its integration target (fast-forward, merge, rebase, or squash merge). Integrated worktrees are prunable even when ahead of their base.",
                "type": "boolean"
              },
              "integration_reason": {
                "description": "Which check detected the integration: `same_commit`, `ancestor`, `no_added_changes`, `trees_match`, `merge_adds_nothing`, or `patch_id_match`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "staged": {
                "description": "Staged entry count.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "unpushed": {
                "description": "Commits not merged upstream (upstream ahead count, else commits past `base_sha`).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "unstaged": {
                "description": "Unstaged entry count.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "untracked": {
                "description": "Untracked entry count.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "ahead",
              "behind",
              "clean",
              "conflicted",
              "diffstat",
              "head_sha",
              "in_rebase",
              "staged",
              "unpushed",
              "unstaged",
              "untracked"
            ],
            "type": "object"
          }
        },
        "properties": {
          "unmanaged": {
            "description": "Non-primary worktrees of the repo without a record (only populated when repo_path is given). Never auto-adopted.",
            "items": {
              "$ref": "#/definitions/UnmanagedWorktree"
            },
            "type": "array"
          },
          "worktrees": {
            "description": "Managed worktrees, oldest first.",
            "items": {
              "$ref": "#/definitions/WorktreeInfo"
            },
            "type": "array"
          }
        },
        "required": [
          "unmanaged",
          "worktrees"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Internal: hot-reload the worktree worker from the authoritative configuration when it changes — swaps the per-call snapshot and re-binds the prune cron on a schedule change.",
      "metadata": {},
      "name": "worktree::on-config-change",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Internal `worktree::on-config-change` trigger payload. The handler re-fetches the authoritative configuration and ignores this payload, so a direct call can never inject config.",
        "properties": {
          "id": {
            "default": null,
            "description": "Configuration id that changed (advisory; the handler re-fetches).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "OnConfigChangeEvent",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Ack returned by the internal `worktree::on-config-change` handler.",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "OnConfigChangeResponse",
        "type": "object"
      }
    },
    {
      "description": "Sweep managed worktrees: reconcile records whose directories are gone and remove clean, unclaimed, expired worktrees. Cron-safe: an empty payload uses defaults; dry_run reports without acting.",
      "metadata": {},
      "name": "worktree::prune",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "dry_run": {
            "default": false,
            "description": "Report what would be pruned without acting.",
            "type": "boolean"
          },
          "repo_path": {
            "default": null,
            "description": "Only sweep worktrees of this repository.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "PruneSkip": {
            "description": "One record the sweep left alone, and why.",
            "properties": {
              "id": {
                "description": "The skipped worktree.",
                "type": "string"
              },
              "reason": {
                "description": "Why it was skipped.",
                "type": "string"
              }
            },
            "required": [
              "id",
              "reason"
            ],
            "type": "object"
          }
        },
        "properties": {
          "pruned": {
            "description": "Worktree ids pruned (or prunable, with dry_run).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "skipped": {
            "description": "Records left alone, with reasons.",
            "items": {
              "$ref": "#/definitions/PruneSkip"
            },
            "type": "array"
          }
        },
        "required": [
          "pruned",
          "skipped"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Release a session's claim. Fails with W211 when the caller does not hold the claim, unless force is set.",
      "metadata": {},
      "name": "worktree::release",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "force": {
            "default": false,
            "description": "Release even when another session holds the claim.",
            "type": "boolean"
          },
          "session_id": {
            "description": "The releasing session (must hold the claim unless force is set).",
            "type": "string"
          },
          "worktree_id": {
            "description": "The worktree to release.",
            "type": "string"
          }
        },
        "required": [
          "session_id",
          "worktree_id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "released": {
            "description": "True when the worktree is now unclaimed.",
            "type": "boolean"
          },
          "worktree_id": {
            "description": "The released worktree.",
            "type": "string"
          }
        },
        "required": [
          "released",
          "worktree_id"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Remove a managed worktree. Refuses dirty (W220) or unmerged (W221) work unless force is set; optionally deletes the branch.",
      "metadata": {},
      "name": "worktree::remove",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "delete_branch": {
            "default": false,
            "description": "Also delete the worktree's branch.",
            "type": "boolean"
          },
          "force": {
            "default": false,
            "description": "Remove even when dirty or carrying unmerged commits.",
            "type": "boolean"
          },
          "worktree_id": {
            "description": "The worktree to remove.",
            "type": "string"
          }
        },
        "required": [
          "worktree_id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "branch_deleted": {
            "description": "True when the branch was deleted too.",
            "type": "boolean"
          },
          "removed": {
            "description": "True when the worktree (and its record) are gone.",
            "type": "boolean"
          }
        },
        "required": [
          "branch_deleted",
          "removed"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Git status for one worktree: cleanliness, ahead/behind, diffstat, rebase-in-progress, lifecycle.",
      "metadata": {},
      "name": "worktree::status",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "worktree_id": {
            "description": "The worktree to inspect.",
            "type": "string"
          }
        },
        "required": [
          "worktree_id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Lifecycle": {
            "description": "Lifecycle of a managed worktree.",
            "oneOf": [
              {
                "description": "Exists and is unclaimed.",
                "enum": [
                  "active"
                ],
                "type": "string"
              },
              {
                "description": "Claimed by a session.",
                "enum": [
                  "claimed"
                ],
                "type": "string"
              },
              {
                "description": "A land job is queued or running.",
                "enum": [
                  "landing"
                ],
                "type": "string"
              },
              {
                "description": "The last land attempt was blocked (conflict, failed tests, ...).",
                "enum": [
                  "land-blocked"
                ],
                "type": "string"
              },
              {
                "description": "The record exists but the directory is gone.",
                "enum": [
                  "orphaned"
                ],
                "type": "string"
              }
            ]
          }
        },
        "description": "Git status summary for one worktree.",
        "properties": {
          "ahead": {
            "description": "Commits ahead of the comparison base (upstream when set, else `base_sha`).",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "behind": {
            "description": "Commits behind the comparison base.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "branch": {
            "description": "The branch checked out in the worktree.",
            "type": "string"
          },
          "clean": {
            "description": "True when there are no staged, unstaged, untracked, or conflicted entries.",
            "type": "boolean"
          },
          "conflicted": {
            "description": "Unmerged (conflicted) entry count.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "dev_port": {
            "description": "Advisory dev-server port derived from the worktree id.",
            "format": "uint16",
            "minimum": 0,
            "type": "integer"
          },
          "diffstat": {
            "description": "`git diff --shortstat` of committed work since `base_sha`.",
            "type": "string"
          },
          "head_sha": {
            "description": "Current HEAD commit.",
            "type": "string"
          },
          "in_rebase": {
            "description": "True while a rebase is in progress in this worktree.",
            "type": "boolean"
          },
          "integrated": {
            "default": false,
            "description": "True when the branch's work is already contained in its integration target (fast-forward, merge, rebase, or squash merge). Integrated worktrees are prunable even when ahead of their base.",
            "type": "boolean"
          },
          "integration_reason": {
            "description": "Which check detected the integration: `same_commit`, `ancestor`, `no_added_changes`, `trees_match`, `merge_adds_nothing`, or `patch_id_match`.",
            "type": [
              "string",
              "null"
            ]
          },
          "lifecycle": {
            "allOf": [
              {
                "$ref": "#/definitions/Lifecycle"
              }
            ],
            "description": "Current lifecycle."
          },
          "staged": {
            "description": "Staged entry count.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "unpushed": {
            "description": "Commits not merged upstream (upstream ahead count, else commits past `base_sha`).",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "unstaged": {
            "description": "Unstaged entry count.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "untracked": {
            "description": "Untracked entry count.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "worktree_id": {
            "description": "The inspected worktree.",
            "type": "string"
          }
        },
        "required": [
          "ahead",
          "behind",
          "branch",
          "clean",
          "conflicted",
          "dev_port",
          "diffstat",
          "head_sha",
          "in_rebase",
          "lifecycle",
          "staged",
          "unpushed",
          "unstaged",
          "untracked",
          "worktree_id"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Validate a path as a managed worktree (console picker surface). Unmanaged worktrees are reported but never adopted.",
      "metadata": {},
      "name": "worktree::validate",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "path": {
            "description": "Absolute path to validate.",
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "managed": {
            "description": "True when a record manages this path.",
            "type": "boolean"
          },
          "reason": {
            "description": "Why the path is not valid.",
            "type": [
              "string",
              "null"
            ]
          },
          "valid": {
            "description": "True when the path is a live, managed worktree.",
            "type": "boolean"
          },
          "worktree_id": {
            "description": "The managing record's id, when one exists.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "managed",
          "valid"
        ],
        "title": "Response",
        "type": "object"
      }
    }
  ],
  "triggers": [
    {
      "description": "A session took ownership of a worktree.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "description": "Config accepted by every `worktree::*` trigger binding. All filters are optional equality matches; unknown fields fail at registration so a misspelled filter key fails loudly instead of silently receiving nothing.",
        "properties": {
          "repo_path": {
            "description": "Only deliver events for this repository path.",
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "description": "Only deliver events for worktrees claimed by this session.",
            "type": [
              "string",
              "null"
            ]
          },
          "worktree_id": {
            "description": "Only deliver events for this worktree.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BindingConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "worktree::claimed",
      "return_schema": {}
    },
    {
      "description": "A new managed worktree exists.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "description": "Config accepted by every `worktree::*` trigger binding. All filters are optional equality matches; unknown fields fail at registration so a misspelled filter key fails loudly instead of silently receiving nothing.",
        "properties": {
          "repo_path": {
            "description": "Only deliver events for this repository path.",
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "description": "Only deliver events for worktrees claimed by this session.",
            "type": [
              "string",
              "null"
            ]
          },
          "worktree_id": {
            "description": "Only deliver events for this worktree.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BindingConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "worktree::created",
      "return_schema": {}
    },
    {
      "description": "A land ended blocked (conflict, failed tests, dirty tree, moved target).",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "description": "Config accepted by every `worktree::*` trigger binding. All filters are optional equality matches; unknown fields fail at registration so a misspelled filter key fails loudly instead of silently receiving nothing.",
        "properties": {
          "repo_path": {
            "description": "Only deliver events for this repository path.",
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "description": "Only deliver events for worktrees claimed by this session.",
            "type": [
              "string",
              "null"
            ]
          },
          "worktree_id": {
            "description": "Only deliver events for this worktree.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BindingConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "worktree::land-blocked",
      "return_schema": {}
    },
    {
      "description": "A land completed; the target branch was fast-forwarded.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "description": "Config accepted by every `worktree::*` trigger binding. All filters are optional equality matches; unknown fields fail at registration so a misspelled filter key fails loudly instead of silently receiving nothing.",
        "properties": {
          "repo_path": {
            "description": "Only deliver events for this repository path.",
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "description": "Only deliver events for worktrees claimed by this session.",
            "type": [
              "string",
              "null"
            ]
          },
          "worktree_id": {
            "description": "Only deliver events for this worktree.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BindingConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "worktree::landed",
      "return_schema": {}
    },
    {
      "description": "A worktree claim was released.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "description": "Config accepted by every `worktree::*` trigger binding. All filters are optional equality matches; unknown fields fail at registration so a misspelled filter key fails loudly instead of silently receiving nothing.",
        "properties": {
          "repo_path": {
            "description": "Only deliver events for this repository path.",
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "description": "Only deliver events for worktrees claimed by this session.",
            "type": [
              "string",
              "null"
            ]
          },
          "worktree_id": {
            "description": "Only deliver events for this worktree.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BindingConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "worktree::released",
      "return_schema": {}
    },
    {
      "description": "A managed worktree was removed.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "description": "Config accepted by every `worktree::*` trigger binding. All filters are optional equality matches; unknown fields fail at registration so a misspelled filter key fails loudly instead of silently receiving nothing.",
        "properties": {
          "repo_path": {
            "description": "Only deliver events for this repository path.",
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "description": "Only deliver events for worktrees claimed by this session.",
            "type": [
              "string",
              "null"
            ]
          },
          "worktree_id": {
            "description": "Only deliver events for this worktree.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BindingConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "worktree::removed",
      "return_schema": {}
    }
  ]
}