ux(prune): dry-run lists fewer branches than --remote deletes, and a second run is needed to clear stale tracking refs (with raw git output leaking through) #63

Closed
opened 2026-07-14 20:41:58 +00:00 by hexajon · 0 comments
hexajon commented 2026-07-14 20:41:58 +00:00 (Migrated from codeberg.org)

Live transcript (2026-07-14, CLI, specifics masked), four consecutive runs in one repo:

❯ madtea prune
Found 20 merged branch(es):
  branch-01
  branch-02
  ... (20 total)
Re-run with confirm=true (CLI: --yes) to delete; set remote=true (CLI: --remote) to also delete remote branches.

❯ madtea prune --yes --remote
Deleted 20 local branch(es)
Deleted 22 remote branch(es)

❯ madtea prune
From https://<forge-host>/<org>/<repo>
 - [deleted]         (none)     -> origin/branch-01
 - [deleted]         (none)     -> origin/branch-02
 ... (22 lines)
Pruned 22 stale remote-tracking ref(s)

❯ madtea prune --yes --remote
nothing to prune

The dry run listed 20 merged branches; --yes --remote then deleted 22 remote branches. The two extras were remote-only merged branches (no local counterpart), which the preview never enumerated. For a destructive operation, the confirm step must show exactly what will be deleted: a user who reads 20 names and confirms has not consented to 22 deletions. The dry run should list the remote-only merged branches too (or at minimum, split the preview into "local" and "remote-only" sections when the remote could be affected), and the hint line should say that --remote widens the set beyond what's listed.

Problem 2 — the deleting run leaves stale remote-tracking refs behind

After --yes --remote deleted the remote branches, the local remote-tracking refs (origin/<branch>) stayed behind; it took a third invocation to prune 22 stale tracking refs. The run that deletes remote branches knows exactly which refs it just made stale — it should prune them in the same invocation and leave a clean state, not hand the user a follow-up chore they only discover by re-running.

Problem 3 — raw git output leaks through on the cleanup pass

That third run printed git's raw fetch --prune output verbatim (the From https://… header and per-ref - [deleted] (none) -> origin/… lines) followed by madtea's own summary line — duplicated information in two voices. Every other prune mode prints madtea-formatted output only. The raw passthrough should be captured and summarized (or shown only under a verbose flag), consistent with the rest of the surface. (Related but opposite polarity to the existing "not as verbose as git pull" report — the shared ask is deliberate, consistent output discipline for wrapped git operations.)

Acceptance

  • Dry-run preview enumerates every branch a subsequent confirm=true remote=true run would delete, local and remote-only, with counts that match the deletion report exactly.
  • A --remote deletion prunes the now-stale remote-tracking refs in the same run; an immediate re-run prints "nothing to prune".
  • No raw git passthrough in prune's default output; hermetic tests pin the preview/delete count parity and the single-run clean state.
  • MCP madt_prune checked for the same three behaviors (the transcript is CLI; the tool surface shares the implementation).
Live transcript (2026-07-14, CLI, specifics masked), four consecutive runs in one repo: ``` ❯ madtea prune Found 20 merged branch(es): branch-01 branch-02 ... (20 total) Re-run with confirm=true (CLI: --yes) to delete; set remote=true (CLI: --remote) to also delete remote branches. ❯ madtea prune --yes --remote Deleted 20 local branch(es) Deleted 22 remote branch(es) ❯ madtea prune From https://<forge-host>/<org>/<repo> - [deleted] (none) -> origin/branch-01 - [deleted] (none) -> origin/branch-02 ... (22 lines) Pruned 22 stale remote-tracking ref(s) ❯ madtea prune --yes --remote nothing to prune ``` ## Problem 1 — the consent set doesn't match the executed set The dry run listed **20** merged branches; `--yes --remote` then deleted **22** remote branches. The two extras were remote-only merged branches (no local counterpart), which the preview never enumerated. For a destructive operation, the confirm step must show exactly what will be deleted: a user who reads 20 names and confirms has not consented to 22 deletions. The dry run should list the remote-only merged branches too (or at minimum, split the preview into "local" and "remote-only" sections when the remote could be affected), and the hint line should say that `--remote` widens the set beyond what's listed. ## Problem 2 — the deleting run leaves stale remote-tracking refs behind After `--yes --remote` deleted the remote branches, the local remote-tracking refs (`origin/<branch>`) stayed behind; it took a **third** invocation to prune 22 stale tracking refs. The run that deletes remote branches knows exactly which refs it just made stale — it should prune them in the same invocation and leave a clean state, not hand the user a follow-up chore they only discover by re-running. ## Problem 3 — raw git output leaks through on the cleanup pass That third run printed git's raw `fetch --prune` output verbatim (the `From https://…` header and per-ref ` - [deleted] (none) -> origin/…` lines) followed by madtea's own summary line — duplicated information in two voices. Every other prune mode prints madtea-formatted output only. The raw passthrough should be captured and summarized (or shown only under a verbose flag), consistent with the rest of the surface. (Related but opposite polarity to the existing "not as verbose as git pull" report — the shared ask is deliberate, consistent output discipline for wrapped git operations.) ## Acceptance - Dry-run preview enumerates every branch a subsequent `confirm=true remote=true` run would delete, local and remote-only, with counts that match the deletion report exactly. - A `--remote` deletion prunes the now-stale remote-tracking refs in the same run; an immediate re-run prints "nothing to prune". - No raw git passthrough in prune's default output; hermetic tests pin the preview/delete count parity and the single-run clean state. - MCP `madt_prune` checked for the same three behaviors (the transcript is CLI; the tool surface shares the implementation).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea#63
No description provided.