zeph-worktree
Git worktree lifecycle management for Zeph subagents.
Overview
zeph-worktree creates, removes, lists, and reconciles per-subagent git worktrees. Each background subagent that opts into filesystem isolation gets a dedicated worktree cloned from the host repository, preventing concurrent agents from clobbering each other's working trees.
The crate is intentionally narrow in scope — it wraps git worktree subprocess calls with full path sanitization, capability probing, and a configurable timeout. It has no dependency on zeph-core, zeph-subagent, or zeph-channels.
Key types
| Type | Description |
|---|---|
DefaultWorktreeManager |
Production WorktreeManager<DefaultGitRunner> — the type stored by SubAgentManager |
WorktreeManager<R> |
Generic manager parameterised over GitRunner; injectable for testing |
WorktreeHandle |
Live record of one managed worktree (path, branch, subagent ID, creation time) |
DefaultGitRunner |
Production git invocation backend with configurable timeout |
GitRunner |
Trait for abstracting git subprocess calls |
StaleWorktree |
One entry reported by reconcile() — a registered worktree git considers prunable |
CleanOutcome |
Result of clean(); render with format_clean_summary |
WorktreeDiskUsage / QuotaStatus |
Disk accounting from disk_usage() and the sweep() quota verdict; render with format_usage_summary |
WorktreeError |
All errors this crate can produce |
Usage
[]
= { = "crates/zeph-worktree" }
use PathBuf;
use WorktreeConfig;
use ;
async
[!IMPORTANT] Call
probe_capabilitiesonce at bootstrap. It checks thatgit≥ 2.5 is inPATHand that the target path is a git repository. A missing git binary is caught here, not at first spawn.
Configuration
WorktreeManager is driven by WorktreeConfig from zeph-config:
[]
= true
= "worktree" # "none" | "worktree"
= "head" # "head" | "fresh"
= "main" # remote branch used when base_ref = "fresh"; "" auto-detects origin/HEAD
= ".claude/worktrees" # worktree root, relative to the repository root
= "agent/" # full branch name is "{branch_prefix}{subagent_id}"
= false
= 30 # clamped to max(1, value)
= true
# max_worktrees = 20 # optional admission cap; None = unlimited
# disk_quota_mb = 5000 # optional soft disk-usage threshold; None = disabled
= 0 # periodic reconcile-and-quota sweep; 0 disables it
= true
| Field | Default | Description |
|---|---|---|
enabled |
false |
Enable worktree isolation for background subagents |
bg_isolation |
"none" |
"worktree" creates a dedicated worktree; "none" only holds the CWD lock |
base_ref |
"head" |
"head" branches off current HEAD; "fresh" fetches and branches off origin/<default> |
default_branch |
"main" |
Remote branch used when base_ref = "fresh". An empty string triggers auto-detection of origin/HEAD |
root |
".claude/worktrees" |
Worktree root directory, relative to the repository root. Each worktree is a subdirectory named after the subagent ID |
branch_prefix |
"agent/" |
Branch name prefix; the full branch name is "{branch_prefix}{subagent_id}" |
prune_branch_on_remove |
false |
Delete the worktree branch after removal. When false, the branch persists so the agent's work can be reviewed, merged, or discarded manually |
git_timeout_secs |
30 |
Per-command timeout for all git subprocess calls |
cleanup_on_completion |
true |
Remove the worktree when the subagent finishes |
max_worktrees |
None (unlimited) |
Creation-time admission cap on concurrent git-registered worktrees under root. Counts worktrees from other concurrently running Zeph sessions over the same root, not just this session's own. Some(0) is rejected at config-validation time |
disk_quota_mb |
None (disabled) |
Soft total-disk-usage threshold (sum of logical file sizes) across all worktrees under root. When exceeded, the reconcile sweep auto-reclaims only git-prunable entries — an intact worktree is never force-removed to satisfy this threshold |
auto_reconcile_secs |
0 (disabled) |
Interval for the supervised background reconcile-and-quota sweep. Config::validate rejects values in 1..60 |
reconcile_on_startup |
true |
Run one reconcile-and-quota sweep at bootstrap, recovering from crash-left prunable worktrees without waiting for the first periodic tick |
Invariants
- Path sanitization rejects absolute paths,
..components, and names starting with-before any git call. base_ref = "fresh"never silently falls back to HEAD on fetch failure — it returns an error.git_timeout_secs = 0is clamped to1byDefaultGitRunner.create()admission (quota check + worktree add) closes the check-then-act race under concurrent callers within the same process.- Lowering
max_worktreesbelow the current worktree count does not evict existing worktrees; it only blocks new admissions untilzeph worktree cleanruns or the limit is raised.
License
Licensed under either of MIT or Apache License, Version 2.0 at your option.