Skip to content

Upstream branch cleanup — delete merged polecat and convoy branches #1174

@jrf0110

Description

@jrf0110

Parent

Part of #204 (Phase 4: Hardening)

Problem

Every polecat creates a branch on the remote when it pushes work, and every convoy creates a feature branch. After the refinery merges the code (direct or PR), these branches are never cleaned up. Over time, rigs accumulate dozens to hundreds of stale branches:

  • Polecat work branches: convoy/<slug>/<id>/<agent>/<bead> or gt-polecat/<agent>/<bead>
  • Convoy feature branches: convoy/<slug>/<id>/head
  • Failed/abandoned branches from agents that crashed mid-work

There is zero branch cleanup code anywhere in the codebase — no git push --delete, no remote prune, nothing.

Solution: Refinery cleans up after itself

The refinery is the natural owner of branch cleanup — it's the last thing that touches a branch before it's no longer needed. After a successful merge (direct or PR), the refinery should delete the source branch from the remote.

When to delete

Scenario Branch to delete When
Direct merge to main Polecat's work branch After successful git push of the merge
PR created Polecat's work branch After PR is merged (detected by pollPendingPRs) — most Git platforms auto-delete on merge, but this should be explicitly handled
Convoy intermediate merge Polecat's work branch (merged into feature branch) After successful merge into the feature branch
Convoy landed Convoy feature branch (convoy/<slug>/<id>/head) After the landing merge to main succeeds

How to delete

git push origin --delete <branch-name>

This is a single git command executed by the refinery agent (or the container's git manager) after a successful merge. It needs the same credentials used for the merge push.

Implementation options

Option A — Refinery agent does it (prompt-driven): Add to the refinery system prompt: "After a successful merge, delete the source branch from the remote with git push origin --delete <branch>." The refinery already has git access and push credentials. Simplest approach but relies on LLM compliance.

Option B — Container git manager does it (deterministic): Add a deleteBranch(rigId, branchName) function to the container's git manager. The TownDO calls it after completeReviewWithResult reports a successful merge. More reliable than prompt-driven.

Option C — Alarm-based sweep (catch-all): A periodic alarm sub-task queries for closed/merged MR beads, collects their branch names from review_metadata.branch, and batch-deletes any that still exist on the remote. Handles edge cases where options A/B missed a branch (agent crash, container restart, etc.).

Recommended: Option B as primary + Option C as safety net. The refinery's merge completion path (completeReviewWithResult with status: 'merged') deterministically triggers branch deletion. The alarm sweep catches anything that slipped through.

Convoy feature branch lifecycle

Convoy feature branches need special handling:

  • Do NOT delete during intermediate merges — polecat branches are deleted, but the feature branch stays alive for subsequent beads
  • Delete after landing merge — once the feature branch is merged to main (in processConvoyLandings), delete it
  • Delete on convoy close/fail — if a convoy is force-closed or failed, clean up its feature branch

Edge cases

  • Failed merges: Don't delete the branch — it may be needed for retry or rework
  • Open PRs: Don't delete the branch until the PR is merged or closed
  • Abandoned branches: Branches from failed/crashed agents that never made it to review. The alarm sweep should catch these by checking for branches with no corresponding open/in_progress MR bead older than a configurable threshold (e.g., 7 days).
  • Branch delete fails: Non-fatal — log a warning, the alarm sweep will retry. Don't fail the merge because cleanup failed.
  • Protected branches: Never attempt to delete the default branch or any branch matching a configurable protection pattern.

Acceptance Criteria

  • Polecat work branches deleted from remote after successful merge (direct or PR)
  • Convoy feature branches deleted after landing merge or convoy close/fail
  • Branch deletion does not block or fail the merge operation (non-fatal)
  • Alarm-based sweep catches orphaned branches from crashed agents
  • Failed/in-review branches are NOT deleted
  • Default branch and protected branch patterns are never deleted
  • Branch deletions logged as bead events for auditability

Notes

  • No data migration needed
  • Branch names are already stored in review_metadata.branch and convoy feature branches in convoy_metadata.feature_branch — no new data collection needed
  • Most GitHub/GitLab platforms have a "delete branch on PR merge" option that users can enable at the repo level. The alarm sweep should account for branches that were already deleted by the platform.
  • git push origin --delete will fail silently (exit code 1) if the branch doesn't exist — handle gracefully

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Should fix before soft launchenhancementNew feature or requestgt:coreReconciler, state machine, bead lifecycle, convoy flowkilo-auto-fixAuto-generated label by Kilokilo-triagedAuto-generated label by Kilo

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions