Repository navigation
fix(governance): harden automated branch pruning against deletion race (#10035, #9917) - #10112
Merged
Merged
Conversation
#10035, #9917) Centralize safe-pruning guards in scripts/lib/branch-guards.sh and wire both branch-cleanup.yml and cleanup-worktrees.sh to them, closing the #10035 race where a freshly-pushed unmerged branch could be deleted. - extract_issue_number: only issue-/hotfix- branches map to a GitHub issue; date tokens (...-2026-06-12 -> 2026) and MVA work-items (MVA-3847 -> 3847) are no longer mis-read as issue numbers (root cause of the deletion). - branch_recently_pushed: skip branches with a tip commit younger than BRANCH_MIN_AGE_HOURS (default 24h) -- closes the push->PR-creation gap. - branch_has_open_pr: skip any branch that still has an open PR. - Merged-detection stays ancestor-based, never patch-equivalence (documented). - Unit tests (branch-guards_test.sh): 14/14 pass. - Document the pruning policy + guards in docs/developer/CLAUDE_GIT.md (#9917). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
…ecency check (#10035) - branch_recently_pushed: treat empty/non-numeric git-log output as not-recent (HIGH: '[ "" -eq 0 ]' exits rc=2, silently bypassing the guard). - branch-cleanup.yml merged step: add branch_recently_pushed guard for symmetry with the closed-issue step (closes the push->PR gap on the merged path too). - Test the exit-0-but-empty-stdout path (15/15). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Owner
Author
|
Addressed code review (thanks):
Pushed as |
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thinking Path
Umbrella #9927 (governance) member #10035 reported a freshly-pushed, unmerged branch being deleted within ~2 min of push. Auditing the two branch-pruning code paths (
.github/workflows/branch-cleanup.ymlandscripts/cleanup-worktrees.sh) surfaced the root cause: the closed-issue cleanup extracted the issue number withgrep -oP '\d{4,}' | head -1, which reads the year out of a dated branch name (chore/triage-delta-2026-06-12→2026) and the work-item id out of a Paperclip branch (MVA-3847-fix→3847). A wrong match against a closed GitHub issue with a merged PR deletes the unrelated branch. Neither path had the <24h / open-PR guards #10035 asks for.What Changed
scripts/lib/branch-guards.sh— single source of truth for safe pruning:extract_issue_number— onlyissue-NNNN/hotfix-NNNNbranches map to a GitHub issue; date tokens andMVA-NNNNwork-items never match.branch_recently_pushed— skip branches whose tip commit is younger thanBRANCH_MIN_AGE_HOURS(default 24h), closing the push→PR-creation gap.branch_has_open_pr— skip any branch with an open PR.branch-cleanup.yml— both deletion steps source the guards; merged step now skips open-PR branches; closed-issue step uses the safe extractor + recency/open-PR guards. Merged-detection stays ancestor-based (never patch-equivalence), documented inline.scripts/cleanup-worktrees.sh— Phase 1 remote delete + Phase 3 local/remote cleanup all routed through the guards.docs/developer/CLAUDE_GIT.md— new "Automated Branch Pruning" section documenting the policy + guards (governance(repo): enable auto-delete-head-branches + automate merged-branch pruning #9917 doc task).scripts/lib/branch-guards_test.sh— hermetic unit tests (git/gh stubbed), 14 cases.Implements #10035 and the code half of #9917. The "Automatically delete head branches" repo toggle (#9917) and the #9911 one-time backfill remain owner-driven (tracked on #9927).
Verification
bash scripts/lib/branch-guards_test.sh→ Passed: 14 Failed: 0.chore/triage-delta-2026-06-12→(empty),MVA-3847-fix→(empty),issue-9943→9943,hotfix-1234→1234.bash scripts/cleanup-worktrees.sh --dry-run --branches-only→ fresh in-flight branches (issue-10035,issue-10012, …) correctly SKIPped (<24h); genuinely-merged old branches still flagged; no date/MVA branch mis-matched.bash -nclean on all shell files; workflow YAML parses.Model Used
claude-opus-4-8