Skip to content

fix(worktree): never auto-discard a named agent's unintegrated work on PTY exit - #496

Open
snehithareddy28 wants to merge 1 commit into
chaitanyagiri:mainfrom
snehithareddy28:fix/named-agent-worktree
Open

snehithareddy28 wants to merge 1 commit into
chaitanyagiri:mainfrom
snehithareddy28:fix/named-agent-worktree

Conversation

@snehithareddy28

@snehithareddy28 snehithareddy28 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What & why

Closes #297. When a named agent's terminal exits, teardownPty force-removes its isolated worktree — git worktree remove --force — with no check for work that was never integrated. The keep-if-unintegrated gate only existed for ephemeral workers, so the same teardown that preserves a worker's half-finished branch destroys a named agent's. As the issue says, this is the dangerous one of the restore family: the others break a restore, this one destroys work. The fix is the one the reporter asked for — apply the protection that already exists to the case it skipped.

One rule for every isolated agent. releaseWorktree() in git.ts removes a worktree only when it is clean and fully contained in the branch it was cut from; otherwise the worktree and its branch stay, and the caller is told why. It fails safe: whatever git cannot answer counts as work. Named agents now go through it on every teardown route — natural PTY exit, crash, kill from the UI, kill by voice. A preserved worktree is logged to log.jsonl, reported to god with the exact git worktree remove command to drop it by hand, and tracked in the same list the GC sweep already drains once the work lands in base. The agent's HIVE_ROOT/agents/<id> directory is its memory, not scratch, so the sweep never reclaims it alongside the worktree.

Two small enablers. The base branch is now recorded at spawn (worktreeBases) instead of computed and thrown away; a worktree tracked without one is compared against the parent repo's current branch, and kept when even that is unknown. And the GC sweep skips an id that is live again as a named agent (restore re-enters the worktree by cwd), not only a live worker.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Docs
  • Build / CI

Evidence

Before

Test step log on main: releaseWorktree does not exist — named agents have no gate at all; teardown goes straight to git worktree remove --force

After

Test step log with the fix: clean and integrated → removed; uncommitted changes → preserved; commits the base lacks → preserved with the branch intact; released once merged; git cannot answer → kept


Notes for review:

Discord: asr2805

…n PTY exit

The moment a named agent's terminal exited, teardownPty force-removed its
isolated worktree — `git worktree remove --force` — with no check for work
that had never been integrated. The keep-if-unintegrated protection existed
only for ephemeral workers, so the same code path that preserves a worker's
half-finished branch destroyed a named agent's.

One rule now covers every isolated agent: releaseWorktree() (git.ts)
removes a worktree only when it is clean AND fully contained in the branch
it was cut from; otherwise it and its branch stay, and the caller is told
why. It fails safe — whatever git cannot answer counts as work. Named
agents go through it on every teardown route (natural exit, crash, kill
from the UI or by voice). A preserved worktree is logged, reported to god
with the exact command to remove it by hand, and tracked in the same list
the GC sweep already drains once the work lands in base — without touching
the agent's HIVE_ROOT/agents/<id> dir, which is its memory, not scratch.

The base branch is now recorded at spawn (worktreeBases) instead of being
computed and thrown away; a worktree tracked without one is compared
against the parent repo's current branch, and kept when even that is
unknown. The GC sweep also skips an id that is live again as a named agent
(restore re-enters the worktree by cwd), not just a live worker.

Five tests in test/worktree-release.test.cjs on real repos: clean and
integrated → removed; uncommitted changes → preserved; commits the base
lacks → preserved (branch survives); released once merged; git cannot
answer → kept.

Closes chaitanyagiri#297
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PTY exit deletes a named agent's worktree unconditionally, discarding unintegrated work

1 participant