Repository navigation
feat: branch-cleanup - Add report-only branch audit CLI #3358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
64 commits
Select commit
Hold shift + click to select a range
88ce0dc
docs(specs/009): record branch naming clarification and align working…
claude fef167e
feat: add library modules for branch categorization and npm scripts
claude 0ef0385
fix: correct indentation in cleanup-branches.js main function
claude 3550f59
docs: add SpecKit planning phase artifacts for branch cleanup infrast…
claude d76a8d9
docs: add SpecKit task breakdown for branch cleanup implementation
claude 4c89a99
refactor: consolidate implementation specs to .github/specs/009-audit…
claude abe022d
fix: remove unused import in exclusion-patterns.js
claude 78ce7a5
Fix CodeRabbit issues in PR #3358
coderabbitai[bot] 1fea2b1
fix: address review docs findings (changelog date, US6 wording, task …
eleshar 60fb337
feat(branch-cleanup): auto-approve empty claude/* branches; share bra…
claude 8a91747
fix: resolve remaining CodeRabbit issues for branch cleanup PR
claude 5c6d6b5
fix(tests): resolve validate-branch-name CLI path from the test direc…
claude ab84fbe
docs(branch-cleanup): add JSDoc for cleanup script and helper functions
coderabbitai[bot] fbbf4f2
fix(cleanup): isolate the live-deletion test and add the changelog entry
claude ae6fab1
docs(specs/009): give US6 report tasks unique IDs and fix task totals
claude 852229d
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] fcba427
Distinguish failed merge queries from unmerged branches
f3c2ea7
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] f01701a
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] f4fcec7
fix(branch-cleanup): disable claude/* deletion auto-approval and reco…
coderabbitai[bot] eea79db
test(branch-cleanup): add coverage for audit helpers, reports, PR ver…
coderabbitai[bot] c69a3c5
test(branch-cleanup): align CLI tests with the claude/* auto-approval…
claude 5231213
test(branch-cleanup): cover CLI failure paths, preservation rules, da…
coderabbitai[bot] 8d92548
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] bf4d3f4
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 69e5061
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 381e590
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 0284a9d
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 11eb777
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 7ec34fe
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] be387c8
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] abef887
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] eee5ab6
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 917386f
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] c4d5fc4
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 31d412c
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] b80d5fe
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 05ee664
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 6c05fcf
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 3d99c67
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] d40395e
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 0592f34
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] b385bd2
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 64efdeb
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] a4fa584
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 8c5dd1a
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 8d38654
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 9a20c37
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] e40506f
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] b2effed
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 180688a
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 12e27e8
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] d0648a7
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] ff119a3
docs: align spec 009 task ticks and paths with delivered tests
eleshar 0246b5e
docs: wrap test paths in backticks to satisfy markdownlint MD050
eleshar 0548d57
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] af3ce43
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 105638b
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 2e798d0
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] 884f9c8
Merge branch 'develop' into task/branch-cleanup-refactor
lightspeed-docs-bot[bot] c96dc2d
Report unknown merge status as DISCUSS; align the report contract wit…
eleshar 3efd024
Align the CleanupReport entity and quickstart reason checks with the …
eleshar 82557de
Fail the audit when the remote branch listing fails; correct the inva…
eleshar 84c9f6e
Align the library API contract with the exported merge, PR and format…
eleshar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
217 changes: 217 additions & 0 deletions
217
.github/specs/009-audit-branch-cleanup/contracts/cli-interface.md
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| # Contract: CLI Interface | ||
|
|
||
| **Interface**: Command-line entry point for branch cleanup operations | ||
| **Module**: `scripts/cleanup-branches.js` | ||
| **Type**: CLI Tool | ||
| **Status**: Specification | ||
|
|
||
| ## Invocation | ||
|
|
||
| ```bash | ||
| node scripts/cleanup-branches.js [options] | ||
| ``` | ||
|
|
||
| ## Command-Line Options | ||
|
|
||
| | Option | Type | Default | Description | Example | | ||
| | ------------------- | ------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | | ||
| | `--dryRun` | boolean | true | Preview deletion candidates without executing. Direct `--dryRun=false` runs are rejected; execution belongs to the draft-PR workflow after human approval and merge. | `--dryRun=true` | | ||
| | `--deleteLocal` | boolean | false | Include local branch inventory in the preview | `--deleteLocal` | | ||
| | `--verbose` | boolean | false | Enable debug output | `--verbose` | | ||
| | `--inactiveDays` | number | 30 | Inactivity threshold in days | `--inactiveDays=60` | | ||
| | `--excludePatterns` | string | "release/\|hotfix/" | Pipe-separated regex patterns to preserve | `--excludePatterns="release/.*\|hotfix/.*"` | | ||
| | `--preserveAuthors` | string | "" | Pipe-separated author patterns to preserve | `--preserveAuthors="dependabot\|renovate"` | | ||
| | `--reportFormat` | enum | "markdown" | Output format: `markdown` or `json` | `--reportFormat=json` | | ||
| | `--reportDir` | string | ".github/reports" | Directory to write report | `--reportDir=.github/reports` | | ||
|
|
||
| ## Exit Codes | ||
|
|
||
| | Code | Meaning | Conditions | | ||
| | ---- | --------------- | ----------------------------------------------------------------------------------------- | | ||
| | 0 | Success | Categorisation complete and report generated | | ||
| | 1 | Fatal error | Direct live mode, or repository access failure (including a failed remote branch listing) | | ||
| | 2 | Partial failure | Some branches processed, some errors (see report for details) | | ||
|
|
||
| ## Standard Output | ||
|
|
||
| ### Dry-Run Mode (default) | ||
|
|
||
| ``` | ||
| [HH:MM:SS] ℹ️ Starting branch cleanup audit... | ||
| [HH:MM:SS] ℹ️ Found 127 branches to evaluate | ||
| [HH:MM:SS] ℹ️ Categorising branches... | ||
| [HH:MM:SS] ✅ Dry-run complete: 85 KEEP, 32 DELETE, 10 DISCUSS | ||
| [HH:MM:SS] ℹ️ Report written to: .github/reports/branch-cleanup-2026-09-16T14-30-45.md | ||
| [HH:MM:SS] ℹ️ Submit deletion candidates through the draft-PR approval workflow | ||
| ``` | ||
|
|
||
| ### Approval-Gated Execution | ||
|
|
||
| The CLI does not execute remote or local deletion directly. Passing | ||
| `--dryRun=false` is a fatal error (exit code 1). The scheduled/manual workflow | ||
| creates a draft PR containing the reviewed candidates; only human approval and | ||
| merge may trigger the separate deletion step. | ||
|
|
||
| ### Verbose Mode | ||
|
|
||
| Additional debug output: | ||
|
|
||
| - Branch processing details | ||
| - Git command outputs | ||
| - GitHub API queries | ||
| - Categorisation gate evaluation | ||
|
|
||
| ## Error Handling | ||
|
|
||
| ### Git CLI Errors | ||
|
|
||
| ``` | ||
| ❌ Error: 'git' command not found | ||
| Install git: https://git-scm.com/ | ||
| Exit code: 1 | ||
| ``` | ||
|
|
||
| ### GitHub API Errors | ||
|
|
||
| ``` | ||
| ⚠️ GitHub API unavailable; open-PR verification could not complete | ||
| Deletion candidates are downgraded to DISCUSS; no deletion can proceed | ||
| ``` | ||
|
|
||
| ### Invalid Arguments | ||
|
|
||
| ``` | ||
| ❌ Error: Invalid --inactiveDays value; defaulting to 30. | ||
| Provided: "invalid" | ||
| The run continues with the default value. | ||
| ``` | ||
|
|
||
| ## Report Generation | ||
|
|
||
| Reports are written to `{reportDir}/branch-cleanup-{timestamp}.{format}` where: | ||
|
|
||
| - `timestamp` format: ISO8601 (e.g., `2026-09-16T14-30-45`) | ||
| - `format` extension: `.md` (Markdown) or `.json` (JSON) | ||
|
|
||
| ### Markdown Report Structure | ||
|
|
||
| The writer is `writeMarkdownReport()` in `scripts/cleanup-branches.js`. Sections for deleted, KEEP, DISCUSS and error branches appear only when they have entries. | ||
|
|
||
| ```markdown | ||
| # Branch Cleanup Report | ||
|
|
||
| **Date:** 2026-09-16T14:30:45.000Z | ||
| **Mode:** Dry run (no deletions) | ||
| **Threshold:** 30 days inactive | ||
|
|
||
| ## Summary | ||
|
|
||
| | Metric | Value | | ||
| | ------------------------------------------------------ | ------ | | ||
| | Branches considered for deletion | 32 | | ||
| | Auto-approved deletions (empty agent-session branches) | 0 | | ||
| | Branches deleted | 32 | | ||
| | Branches preserved | 95 | | ||
| | Errors | 0 | | ||
| | Deletion success rate | 100.0% | | ||
| | Preserved:Deleted ratio | 2.97:1 | | ||
| | Total commits removed (estimate) | 118 | | ||
| | Estimated storage freed | 4.1 MB | | ||
|
|
||
| ## Metrics | ||
|
|
||
| ### Branches deleted by type | ||
|
|
||
| - **bugfix**: 12 | ||
| - **feat**: 20 | ||
|
|
||
| ### Authors affected (notification list) | ||
|
|
||
| - author@example.com | ||
|
|
||
| ## Deleted Branches | ||
|
|
||
| ### `feat/old-experiment` | ||
|
|
||
| - **Author:** author@example.com | ||
| - **Last commit:** 2026-07-30T09:12:00+00:00 | ||
| - **Age:** 45 days | ||
| - **Hash:** `abc1234` | ||
| - **Type:** feat | ||
| - **Commits removed (estimate):** 3 | ||
| - **Storage freed (estimate):** 120 KB | ||
| - **Reason:** Merged and inactive | ||
| - **Local deleted:** No | ||
|
|
||
| ## KEEP Branches | ||
|
|
||
| - `feat/user-auth` — Open PR exists | ||
|
|
||
| ## DISCUSS Branches | ||
|
|
||
| - `claude/experiment` — Invalid branch name: forbidden prefix | ||
| - `ref-incomplete-work` — Unmerged and stale | ||
| ``` | ||
|
|
||
| In a dry run, "Branches deleted" counts the branches the report lists as deletion candidates; nothing is deleted. The CLI writes no `| KEEP |` row: KEEP and DISCUSS branches are listed under their own headings, not in the summary table. | ||
|
|
||
| ### JSON Report Structure | ||
|
|
||
| The writer is `writeJsonReport()` in `scripts/cleanup-branches.js`. | ||
|
|
||
| ```json | ||
| { | ||
| "timestamp": "2026-09-16T14:30:45.000Z", | ||
| "dryRun": true, | ||
| "inactiveDays": 30, | ||
| "summary": { | ||
| "candidates": 32, | ||
| "autoApprovedDelete": 0, | ||
| "deleted": 32, | ||
| "preserved": 95, | ||
| "errors": 0, | ||
| "deletionSuccessRate": "100.0%", | ||
| "preservedDeletedRatio": "2.97:1", | ||
| "totalCommitsRemoved": 118, | ||
| "estimatedStorageFreedBytes": 4300000, | ||
| "estimatedStorageFreedHuman": "4.1 MB" | ||
| }, | ||
| "metrics": { | ||
| "deletedByType": { "bugfix": 12, "feat": 20 }, | ||
| "authorsAffected": ["author@example.com"] | ||
| }, | ||
| "deleted": [ | ||
| { | ||
| "branch": "feat/old-experiment", | ||
| "author": "author@example.com", | ||
| "lastCommitDate": "2026-07-30T09:12:00+00:00", | ||
| "age": 45, | ||
| "hash": "abc1234", | ||
| "type": "feat", | ||
| "commitCount": 3, | ||
| "estimatedStorageBytes": 122880, | ||
| "reason": "Merged and inactive", | ||
| "autoApproved": false | ||
| } | ||
| ], | ||
| "preserved": [ | ||
| { "branch": "feat/user-auth", "category": "KEEP", "reason": "Open PR exists" }, | ||
| { | ||
| "branch": "claude/experiment", | ||
| "category": "DISCUSS", | ||
| "reason": "Invalid branch name: forbidden prefix" | ||
| } | ||
| ], | ||
| "errors": [] | ||
| } | ||
| ``` | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
|
|
||
| Consumers read `.summary` for totals, `.deleted` for deletion candidates and `.preserved` for KEEP and DISCUSS branches (each carries its `category`). There are no `stats`, `branches` or `generator` fields. | ||
|
|
||
| ## Validation Test Cases | ||
|
|
||
| See `quickstart.md` for runnable test scenarios. | ||
|
|
||
| --- | ||
|
|
||
| **Contract Status**: ✅ Complete | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.