Repository navigation
feat(pr-pipeline): delete stale branches daily by the branch-cleanup rule - #8959
kyle-sexton wants to merge 2 commits into
Conversation
…rule Owner decision W-7: no non-default branch lives forever. A daily maintenance-delete-stale-branches.yml sorts every branch: closed or merged PR and closed linked issue at least min-age-days (14) ago deletes, at most max-deletes-per-run (25) a run; an open issue, an open PR, a protected or ruleset branch, and a stopped lane run's branch are never deleted; anything unreadable or without a PR is listed for a person. Each delete re-reads the tip and open PRs and writes a ledger line first. Manual runs default to dry-run, which refuses every non-GET request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p suite Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
claude-security-review reviewed Security review: PR #8959 @ e8cc272Result: no security findings. Nothing was posted inline. Scope read: Checked, and why each held:
Manifest items: none. The PR touches no Actions hardening: the pinned actions, token permissions and triggers are zizmor's lane. I found nothing that needs trust-boundary reasoning beyond what is listed above. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8cc272448
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| try { | ||
| results.push({ name, sha, ...(await evaluate(call, context, { name, sha, protected: branch.protected })) }); | ||
| } catch (error) { | ||
| results.push({ name, sha, verdict: "report", reason: `a read failed: ${error.message}` }); | ||
| } |
There was a problem hiding this comment.
Reserve API calls for the deletion phase
When branch evaluation consumes the shared 600-call budget, every planned deletion fails its first re-read because deletion starts only after all branches have been evaluated. I reproduced this with 300 old merged branches: 297 were classified as deletable, 25 were planned, and zero were deleted because every re-read reported call budget of 600 spent. Subsequent scheduled runs repeat the same scan, so cleanup cannot reduce the backlog without manual intervention. Reserve calls for the revalidation and deletion phase, or process a bounded batch that leaves enough budget to delete its candidates.
Useful? React with 👍 / 👎.
|
claude-review reviewed Code review: PR #8959 (branch cleanup)I found nothing to block. I read the whole diff and checked the Closing issue: none. The PR states Manifests: the diff changes no Path-scoped rules: none of the repo's What I checked:
Notes, not findings:
|
Evaluation spent the shared 600-call budget, so with about 300 deletable branches every planned delete failed its re-read and nothing was ever deleted (Codex review on #8959). cleanup.mjs now reserves 4 calls per planned delete from the client's budget, evaluates with the rest, and stops evaluating once it has found max-deletes-per-run candidates or spent its share; the remaining branches wait for the next run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No related issue: owner decision W-7 (2026-10-21)
Summary
No non-default branch lives forever. A daily
maintenance-delete-stale-branches.ymlsorts every branch of this repository by a new branch-cleanup rule and deletes the finished ones, at most 25 a run, writing a ledger line before each delete. A lane run that stopped (needs a human, blocked, limited, funding, turn-cap) never has its branch deleted.Fix
.github/scripts/branch-cleanup/rules.mjsandcleanup.mjs: per branch, cheapest read first.release/*,dependabot/*,gh-readonly-queue/*.work-resolve-issue/issue-<n>-...) while issue n is open; other branch while an issue its newest PR body links (Closes/Fixes/Resolves/Refs) is open; anything closed undermin-age-days.parseOutcomeMarkerandDIMENSION_STEPSfromreport-outcome.mjs; a branch that never had a PR; merged then commits ahead after the tip moved; closed unmerged then tip moved; any failed read.min-age-daysago.will delete N of M deletable branch(es) (cap C)first, deletes oldest-closed first up to the cap, and right before each delete re-reads the tip (skips when it moved) and open PRs from and into the branch. The ledger{repo, branch, sha, rule, issue, pr, deletedAt}goes to the step summary and thebranch-cleanup-ledger-<run>-<attempt>artifact (uploaded underalways()). Delete isDELETE git/refs/heads/<branch>with the job'sGITHUB_TOKEN(contents: write,issues/pull-requestsread).workflow_dispatchinputdry-rundefaults to true. In dry-run the HTTP client throws on any non-GET. The lanes kill switch stops the job before it reads.branch-cleanup.min-age-days(14) andmax-deletes-per-run(25) indocs/conventions/pr-pipeline.yaml, the schema,branchCleanupFromBasein resolve-config, and a "Branch cleanup" section in the pr-pipeline README. Changelog fragment under.changes/pr-pipeline/.repo-fleet-hygiene'saudit-fleet.shis unchanged; it stays the per-branch prompt path for the reported branches.Verification
node --testin.github/scripts/branch-cleanup/: 20 pass, 0 fail. The cases: a lane branch with an open issue is kept however old; a stopped-run branch is kept after its issue closed; a closed issue at 6 d is kept, at exactly 14 d and at 50 d deleted; the later of PR and issue close counts; an open PR is kept; a merged PR with commits ahead is kept; a tip move or new open PR between list and delete is skipped; the cap is honored oldest first; dry-run writes nothing. Six single-guard mutations ofcleanup.mjs, each run alone (stop marker, tip re-read, age check removed,<turned<=, open-PR check, commits-ahead check), each fail at least one test.node --testin.github/actions/resolve-config/: 551 pass, 0 fail (the live config validates against the new schema).scripts/check-affected.sh --uncommitted: 9 passed, 4 skipped.scripts/check-changelog-fragments.sh --check,actionlintandzizmor --offlineon the new workflow: clean.fix/3856-belt-deletion-scope(fix(disk-hygiene): scope the session belt to deletion shapes and add a logged release lever #5698),fix/4240-declare-jq-prerequisites(fix(plugins): declare jq in every plugin whose hooks run it #5702, merged),feat/4347-sonnet-5-5-adaptation-chapter(feat(playbooks): add the Sonnet 5.5 model-adaptation chapter #5743),perf/affected-tests-comment-edges(perf(ci): stop comment-only mentions from making affected-test dependents #5889),feat/interview-quality-bar(feat(planning): interview asks the quality bar in round 1 #6325),feat/discovery-first-party-single-source(feat(discovery): accept any publisher's own artifact as a single-source content claim #6352),ci/rename-release-plugins(ci: rename release-plugins to release-publish-packages-plugins #6453). For a person: 26 never had a PR, 13 merged then ahead of main, 2 closed then the tip moved, 14 stopped lane runs (feat(pr-pipeline): harden the branch-cleanup job (atomic delete, plan/delete split, stopped-run branches) #8958).Related
🤖 Generated with Claude Code