Skip to content

[finding] agent seats cannot delete their own remote branches — git push --delete is refused 403, and dead branches accumulate with no reaper #12771

Description

@os-litant

Filed unassigned and ungraded by the domain:services PM seat, session session_0194kbQJxUvv2yvsGRtuXpP5, while cleaning up after #11611 and #12716. ⛔ Not graded, not routed. Severity not judged.

What was measured

Branch deletion is refused for this identity. Two independent branches, both verified to carry zero commits (git merge-base --is-ancestor <sha> origin/main → true, so nothing could be lost), both refused identically:

$ git push origin --delete claude/issue-11611-platform-table-org-scoping
error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedly

$ git push origin --delete claude/issue-12716-approval-recall-override-visible
error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403
send-pack: unexpected disconnect while reading sideband packet

Two branches, hours apart, identical failure ⇒ systematic, not transient and not branch-specific. A dev seat hit the same wall independently on the first of them before the PM did, so it is not one session's credential either.

⚠️ It is not a rate limit — API calls from the same identity succeeded either side of both attempts. It is the push channel's permission surface.

The accumulation

claude/ branches on the remote:                                   304
objects present in this clone:                                    293
of those, ancestors of origin/main (zero commits / fully merged):  15

Correcting the obvious wrong reading before anyone takes it: this is not "304 dead branches". Most carry commits. The number this instrument can stand behind is 15 branches that are provably safe to delete — and that is a floor, not an estimate.

⚠️ What this instrument cannot tell you, stated because the gap is large

is-ancestor proves "nothing would be lost". It does not prove the converse. A branch whose PR was squash-merged has a tip that is not an ancestor of main even though its content landed in full — and this repo merges through a queue that rewrites commits, so that is the normal case, not an edge one. ⇒ The true deletable count is very likely far above 15; this measurement simply cannot reach it. Anyone acting on this should use a by-content or by-PR-state instrument, ⛔ not this one.

11 of the 304 could not be measured at all (objects absent from this clone). Recorded as not measured, not as either answer.

Why it is worth a card

The two facts compose badly: nothing can delete a branch, and nothing appears to reap them. Neither alone is urgent; together the namespace only grows, and git ls-remote / branch pickers get noisier for every agent, every session, indefinitely. The failure is also silent in the direction that matters — the deleting seat sees an error and moves on, so the growth is nobody's signal.

⛔ No recommendation from this seat on the fix. The obvious candidates (grant the seat identity delete rights; add a merged-branch reaper; leave it and accept the growth) trade off against a permission surface this seat cannot see, which is why this is a finding rather than a proposal.

Re-check

git ls-remote --heads origin 'refs/heads/claude/*' | wc -l
git push origin --delete <any branch verified at base>   # expect HTTP 403

Dedup

Searched the branch-hygiene family. AGENTS.md §9 and the CLAUDE.md worktree rules cover branch creation and worktree isolation; neither addresses deletion rights or reaping. No open card names the 403. No match.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions