branches-remote delete drops an unmerged remote ref with no containment proof #568

Open
opened 2026-09-21 21:09:18 +00:00 by hexajon · 0 comments
Owner

Part of #563.

The defect

madt_branches_remote action=delete deletes one or more branches on the forge with no containment check. Its own summary says so: "Delete branch(es) on the REMOTE (local branches untouched; no merged check)" (internal/mcp/tools_branches.go:68-70). The implementation is branchesDelete (internal/mcp/tools_branches_crud.go:40-49). The CLI twin is internal/cmd/branchapi/branchapi.go:90.

It is ungated, it accepts a batch, and it computes no proof of any kind. A remote branch holding commits that exist in no other ref is deleted on request.

ADR 0029's per-operation table carries one branch-delete row, which reads:

| branch delete | CLI branch -d + MCP madt_branch_delete | machine-proof | git branch -d only, git refuses an unmerged branch |

A reader takes that as a statement about branch deletion in madtea. It is a statement about one of the two branch deletes madtea ships. The table never names the other one, so the record reads as covering a surface it does not cover.

madt_prune's help routes callers here for branches prune will not take (internal/mcp/help_generated.go:3340), which means the unproven path is the documented next step after the proven one refuses.

What needs deciding

The classification is genuinely open, so this should not be closed by picking one silently.

  1. A forge branch delete is not always unrecoverable. Some forges keep the ref recoverable for a window, and the commits may survive in an open pull request. Whether that counts as recoverable under clause 2 is a product call, and madtea computes no proof of it today.
  2. If it is unrecoverable, clause 2 allows a containment check to make it machine-proof, the same shape findRemoteOrphans already uses in prune (internal/service/prune/run.go:407-413). That keeps the operation and earns the carve-out.
  3. If neither, it is omitted and the refusal names the forge web UI, matching the other forge-data deletes.

My lean is option 2, because the check already exists in the codebase and the operation is genuinely useful.

Acceptance criteria

  • The operation carries a recorded classification with its reasoning.
  • ADR 0029's table names both branch deletes, or names this one as out of scope and says why.
  • madt_prune's help no longer routes a refused branch to an unproven delete, unless that delete has earned the carve-out by then.
  • Whatever lands is covered by a test.
Part of #563. ## The defect `madt_branches_remote action=delete` deletes one or more branches on the forge with no containment check. Its own summary says so: "Delete branch(es) on the REMOTE (local branches untouched; no merged check)" (`internal/mcp/tools_branches.go:68-70`). The implementation is `branchesDelete` (`internal/mcp/tools_branches_crud.go:40-49`). The CLI twin is `internal/cmd/branchapi/branchapi.go:90`. It is ungated, it accepts a batch, and it computes no proof of any kind. A remote branch holding commits that exist in no other ref is deleted on request. ADR 0029's per-operation table carries one branch-delete row, which reads: | branch delete | CLI `branch -d` + MCP `madt_branch_delete` | machine-proof | `git branch -d` only, git refuses an unmerged branch | A reader takes that as a statement about branch deletion in madtea. It is a statement about one of the two branch deletes madtea ships. The table never names the other one, so the record reads as covering a surface it does not cover. `madt_prune`'s help routes callers here for branches prune will not take (`internal/mcp/help_generated.go:3340`), which means the unproven path is the documented next step after the proven one refuses. ## What needs deciding The classification is genuinely open, so this should not be closed by picking one silently. 1. A forge branch delete is not always unrecoverable. Some forges keep the ref recoverable for a window, and the commits may survive in an open pull request. Whether that counts as recoverable under clause 2 is a product call, and madtea computes no proof of it today. 2. If it is unrecoverable, clause 2 allows a containment check to make it machine-proof, the same shape `findRemoteOrphans` already uses in prune (`internal/service/prune/run.go:407-413`). That keeps the operation and earns the carve-out. 3. If neither, it is omitted and the refusal names the forge web UI, matching the other forge-data deletes. My lean is option 2, because the check already exists in the codebase and the operation is genuinely useful. ## Acceptance criteria - The operation carries a recorded classification with its reasoning. - ADR 0029's table names both branch deletes, or names this one as out of scope and says why. - `madt_prune`'s help no longer routes a refused branch to an unproven delete, unless that delete has earned the carve-out by then. - Whatever lands is covered by a test.
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.

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