Skip to content

afx cleanup: add a flag (--full) to also remove worktree + delete branch #852

@amrmelsayed

Description

@amrmelsayed

Problem

afx cleanup -p <id> only kills the Tower terminal and unregisters the builder from the agent farm. The worktree and branch are deliberately preserved, with the cleanup output printing the manual follow-up commands:

[info] Worktree preserved at: /Users/.../.builders/pir-XXXX
[info] To remove: git worktree remove "/Users/.../.builders/pir-XXXX"
[info] Branch preserved: builder/pir-XXXX
[info] To delete: git branch -d "builder/pir-XXXX"

The full teardown sequence is always three steps:

afx cleanup -p <id>
git worktree remove --force .builders/<branch>
git branch -d builder/<branch>

The help text — "Clean up a builder worktree and branch" — describes the aspiration of the command, not its actual behavior. There's a small footgun here: a reader who's only seen the help text will assume the command does what it says, then discover halfway through cleanup that they need two more commands.

Why preservation is the right default

This isn't an argument to change the default. Today's behavior protects two real cases that hit us twice in one Shannon session (2026-05-25):

  • PIR #1585 / PR #1783: worktree had 1 uncommitted file — the iter-1 rebuttals .md doc the builder wrote and referenced in its consult-result notification ("Full rebuttal: codev/projects/.../1585-review-iter1-rebuttals.md"). The substantive content shipped via the review file that was on the PR, but the rebuttals file itself was untracked scratch worth inspecting before discarding.
  • PIR #1793 / PR #1794: same pattern, two iterations of consult so two untracked rebuttal files in the worktree.

Both times the architect agent paused to inspect the scratch before removing. That's exactly the discipline preservation enforces — if cleanup removed everything in one step, mid-iteration scratch would be silently lost.

Proposed change

Add an opt-in flag that does the full teardown in one command, after explicit acknowledgment:

afx cleanup -p <id> --full
# or: --remove-worktree --delete-branch (composable)

Suggested behavior:

  1. --full (or both --remove-worktree + --delete-branch): perform the existing cleanup, then run git worktree remove and git branch -d in sequence.
  2. If the worktree has uncommitted/untracked changes when --full is invoked:
    • Refuse by default with the existing "To remove" hint AND a one-line listing of which file(s) are uncommitted, so the user can decide whether to inspect.
    • --full --force (or --full -f) overrides — runs git worktree remove --force.
  3. If the branch hasn't been merged into the current HEAD when --full is invoked:
    • Refuse by default, point the user at the unmerged commit list.
    • Re-use the existing -f, --force flag for branch deletion (currently scoped to "branch not merged").
  4. Keep the existing default (no flag) preserving both, exactly as today.

The composable form (--remove-worktree / --delete-branch) is nice because some workflows want to keep the branch around for a while after the worktree is gone (e.g., GitHub PR archived for reference, local worktree clutter removed).

Out of scope

  • Changing the existing default (still preserve unless asked).
  • Auto-discarding uncommitted scratch (the value of preservation IS the human-in-the-loop decision on what's scratch vs work).
  • Touching the -f, --force semantics for the "branch not merged" case — leave that as-is to avoid regressing today's safety guard.

Help text fix (separate, can ride along)

Independently of whether --full lands, the current help text overpromises:

Clean up a builder worktree and branch

A more accurate one-liner would be:

Stop the builder; print teardown commands for worktree + branch (use --full to also run them)

…or whatever phrasing fits once the flag lands.

References

  • Today's friction: PIR #1585 / PR #1783, PIR #1793 / PR #1794 in cluesmith/shannon — both had untracked rebuttal .md scratch in their worktrees post-merge, both required separate git worktree remove --force calls after afx cleanup.

Unassigned — for the team to discuss the right flag shape and default behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions