ObjectStack’s branch reaper: the plan changed when the agent counted the right denominator

#topic

ObjectStack’s branch reaper: the plan changed when the agent counted the right denominator

Primary operator trace; work August 27–October 12, 2026; checked October 18. Issue #12771, report-only PR #13500, arming PR #15224, separate Copilot-branch census #13503 and subsequent Actions run list. Question: can a plan preserve uncertainty and prevent an agent from turning superficially successful cleanup into a destructive mistake? This is internal maintenance, not an independently requested product feature.

The starting observation was two agent seats' 403 errors attempting to delete a remote branch. On August 27 the fleet recorded 304 claude/ heads, but only 15 proven empty/ancestor branches and 11 unmeasurable in its clone. Its own issue explicitly warns that squash-merging breaks the converse: not being an ancestor does not mean the work was not merged. A PM put three options to the maintainer: give agent seats delete access, add a narrow workflow, or accept the clutter. The maintainer chose the workflow, constrained it to branches whose matching PR had MERGED into the correct base, and required a dry run and a separate human look before enabling deletion. The agent had no authority to broaden deletion to closed-unmerged branches or those without PRs.

On August 30 the delegated developer measured 335 claude/ branches using PR state and an exact pr.head.ref match: 111 merged, 43 closed but unmerged, 11 open, and 170 without a matching PR. That last group dominated the ongoing accumulation, not the merged-branch leak. The repository's native delete-on-merge setting had already cleared all but one of 1,386 recently merged agent PR heads in the agent's checked August 20–30 sample; an event-triggered reaper would mostly duplicate GitHub. The plan changed to a weekly sweep for legacy merged heads, leaving the no-PR problem for another policy decision. The agent rejected a tempting commits/{sha}/pulls shortcut because it returns PRs containing the commit even if the branch is not the PR head; its report admits and reruns a grep-prefix mistake and a zero-row environment argument error. These are first-party measurements, not independently reproduced cohort results. An August 30 real runner dry-run reported 111 of 335 eligible, 168 without matching PRs (a changed snapshot); it deleted nothing. The report-only PR merged through a queue with 33 passing checks, not itself evidence of safe deletion.

On September 4, after the maintainer approved arming, a separate agent PR added a scheduled weekly deletion job, with contents: write limited to the deletion job, retained a dry-run default for manual dispatch, and kept the PR-base and merged-state guards. The agent reports two runner checks that skipped the delete step. The PR merged September 4; its first scheduled job on September 7 reports classifying 432 claude/ branches, 110 reapable, 244 with no matching PR, 30 open and 48 closed unmerged, then deleting all 110 reapable heads with zero failures. This updates the earlier issue's pre-run state; the successful Actions annotation is direct evidence of execution, not just a planned release. October 5 found 0 of 483 reapable, 412 with no matching PR, and October 12 found 0 of 565 reapable and 485 with no matching PR; both run successfully but report no further deletions. The no-PR bucket is not proof all heads are abandoned; it includes any still in progress and exact head-name matching limitations. No count of mistaken deletions, branch restorations or developer time saved is reported.

A sibling September 2 census of copilot/ branches makes the denominator trap sharper. Of 678 older heads, 575 matched merged Copilot PRs, 90 closed-unmerged, zero open and 13 without PRs; the 665 bot-authored PRs were January–May and all predated this repo's current delete-on-merge setting. Thus 575/678 is a historical stranded-branch share, not evidence that GitHub's present setting is failing for Copilot. The maintainer must separately authorize adding that prefix, and the issue refuses to widen deletion just because it counted the heads. The sibling logs ~1,382 REST calls for measurement; elapsed operator effort, API/service cost, discarded candidate scripts and impact on everyday branch selection are unknown.

What this adds to the area: a shared plan gives next-agent status; this issue gives a falsifiable hypothesis, an excluded misleading instrument, an exact population, owner ruling not expandable by an agent, dry-run and post-merge scheduled action with a checkable deletion count. The decision→oracle chain is visible for repository hygiene, while the independent request→release→requester chain remains missing. Compare the September 7 deletion count with the October 12 no-PR bucket as different classifications, not a failure of a deliberately narrow policy. Do not extrapolate this unusually regimented repo to ordinary teams or take its agent-generated issue reports as neutral external observation.