/branch-audit
--- name: branch-audit description: Audit local git branches for safe deletion with content-level verification (PR state + git cherry), flag-don't-delete discipline, and a mandatory user approval gate. Auto-invoke when user says "audit branches", "prune branches", "clean up
$ npx -y skills add qf-studio/pilot --skill branch-audit --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/branch-audit
Context preview
The summary Claude sees to decide when to auto-load this skill.
--- name: branch-audit description: Audit local git branches for safe deletion with content-level verification (PR state + git cherry), flag-don't-delete discipline, and a mandatory user approval gate. Auto-invoke when user says "audit branches", "prune branches", "clean up
SKILL.md
branch-audit.SKILL.mdBranch Audit — safe stale-branch investigation and pruning
--- name: branch-audit description: Audit local git branches for safe deletion with content-level verification (PR state + git cherry), flag-don't-delete discipline, and a mandatory user approval gate. Auto-invoke when user says "audit branches", "prune branches", "clean up branches", "stale branches", or "which branches can I delete". ---
Why this exists
This repo accumulates local branches fast: interactive sessions, the Pilot daemon (`pilot/GH-*`), recovery work, backups. A 2026-07-05 audit found 29 local branches; 21 were safely deleted — but only after content-level verification caught, among others, **15 unpushed commits on `feat/aws-bench` that existed nowhere else**. Ancestry checks alone would have missed that.
**Prime directive: a branch is never deleted without explicit per-branch user approval, and never on ancestry evidence alone.**
Method
Step 1 — Enumerate COMPLETELY
git branch --format='%(refname:short)' # never pipe through head — a truncated
# list caused a missed-branch round
git worktree list # branches checked out in worktrees
# cannot be deleted and may be live sessionsExclude from the audit: the current `main` checkout, any `claude/*` branch attached to a worktree (possibly a live session), and `pilot-worktree-GH-*` daemon worktrees (per CLAUDE.md, leave alone).
Step 2 — Per-branch evidence (mechanical)
For each branch collect:
git log -1 --format='%h %cd %s' --date=short "$b" # tip
gh pr list --head "$b" --state all --json number,state # PR record
git cherry main "$b" | grep -c '^+' # non-equivalent commits
**`git cherry` is the ground truth, not `--merged`.** This repo squash-merges: merged branches are never ancestors of main, and `git branch --merged` is useless. `git cherry` compares patch IDs — 0 non-equivalent commits means every change is content-identical to something on main.
Step 3 — Classify
| Class | Criteria | Action | |---|---|---| | SAFE | merged PR **and** cherry = 0 | propose deletion | | SAFE (ancestor) | no PR, cherry = 0, `merge-base --is-ancestor` = yes | propose deletion | | HOLDS-WORK | cherry > 0 | deep-check (Step 4) — never propose blind | | CHECKED-OUT | in a worktree | check worktree dirty state; live sessions untouchable |
Step 4 — Deep checks for HOLDS-WORK branches
1. **Local vs remote divergence** — the highest-stakes check:
git fetch origin "$b"
git rev-list --count origin/"$b".."$b" # local-only commits = sole copy
If local is ahead: **push to origin as backup before anything else**. 2. **Closed-PR supersession** — a closed (not merged) PR may carry the answer:
gh pr view <n> --json comments --jq '.comments[-2:][].body'
Precedent: `recover/TASK-300-ghost-sha-guard` showed 2 "unique" commits, but PR #3189's closing comment named the superseding PR (#3193) and the guard was verified live in `internal/executor/git_freshness.go` — same logic, different patch IDs. Verify the supersession claim **in main's code**, not just the comment. 3. **Lost-docs salvage** — backup branches can hold `.agent/` files missing from main (graph nodes referencing nonexistent files are the tell):
git diff --name-only $(git merge-base main "$b") "$b" | while read f; do
[ -e "$f" ] || echo "MISSING: $f"
doneSalvage via a docs PR before considering deletion.
Step 5 — Approval gate (mandatory)
Present the full classification table with evidence. Deletion requires explicit user approval, per branch or per named group. Branches the user has flagged as sensitive (historically: `feat/aws-bench`, `feat/openrouter-engine`) stay untouched regardless of evidence.
Step 6 — Execute in safety order
1. Backup pushes first (`git push origin <branch>` for any sole-copy work) 2. Salvage PRs second 3. Deletions last: `git branch -D <approved...>` 4. `git remote prune origin`
Pitfalls learned in the field
- `gh pr merge --delete-branch` fails with *"'main' is already used by
worktree"* in multi-worktree layouts — merge succeeds remotely; verify with `gh pr view --json state`, clean the local branch manually.
- A branch checked out in a clean worktree at a merged tip loses nothing when
the worktree is removed — but confirm `status --porcelain` is empty first.
- Deleted branches remain reflog-recoverable for ~90 days; mention this in the
approval ask.
Read more
Branch Audit — safe stale-branch investigation and pruning
--- name: branch-audit description: Audit local git branches for safe deletion with content-level verification (PR state + git cherry), flag-don't-delete discipline, and a mandatory user approval gate. Auto-invoke when user says "audit branches", "prune branches", "clean up branches", "stale branches", or "which branches can I delete". ---
Why this exists
This repo accumulates local branches fast: interactive sessions, the Pilot daemon (`pilot/GH-*`), recovery work, backups. A 2026-07-05 audit found 29 local branches; 21 were safely deleted — but only after content-level verification caught, among others, **15 unpushed commits on `feat/aws-bench` that existed nowhere else**. Ancestry checks alone would have missed that.
**Prime directive: a branch is never deleted without explicit per-branch user approval, and never on ancestry evidence alone.**
Method
Step 1 — Enumerate COMPLETELY
git branch --format='%(refname:short)' # never pipe through head — a truncated
# list caused a missed-branch round
git worktree list # branches checked out in worktrees
# cannot be deleted and may be live sessionsExclude from the audit: the current `main` checkout, any `claude/*` branch attached to a worktree (possibly a live session), and `pilot-worktree-GH-*` daemon worktrees (per CLAUDE.md, leave alone).
Step 2 — Per-branch evidence (mechanical)
For each branch collect:
git log -1 --format='%h %cd %s' --date=short "$b" # tip gh pr list --head "$b" --state all --json number,state # PR record git cherry main "$b" | grep -c '^+' # non-equivalent commits
**`git cherry` is the ground truth, not `--merged`.** This repo squash-merges: merged branches are never ancestors of main, and `git branch --merged` is useless. `git cherry` compares patch IDs — 0 non-equivalent commits means every change is content-identical to something on main.
Step 3 — Classify
| Class | Criteria | Action | |---|---|---| | SAFE | merged PR **and** cherry = 0 | propose deletion | | SAFE (ancestor) | no PR, cherry = 0, `merge-base --is-ancestor` = yes | propose deletion | | HOLDS-WORK | cherry > 0 | deep-check (Step 4) — never propose blind | | CHECKED-OUT | in a worktree | check worktree dirty state; live sessions untouchable |
Step 4 — Deep checks for HOLDS-WORK branches
1. **Local vs remote divergence** — the highest-stakes check:
git fetch origin "$b" git rev-list --count origin/"$b".."$b" # local-only commits = sole copy
If local is ahead: **push to origin as backup before anything else**. 2. **Closed-PR supersession** — a closed (not merged) PR may carry the answer:
gh pr view <n> --json comments --jq '.comments[-2:][].body'
Precedent: `recover/TASK-300-ghost-sha-guard` showed 2 "unique" commits, but PR #3189's closing comment named the superseding PR (#3193) and the guard was verified live in `internal/executor/git_freshness.go` — same logic, different patch IDs. Verify the supersession claim **in main's code**, not just the comment. 3. **Lost-docs salvage** — backup branches can hold `.agent/` files missing from main (graph nodes referencing nonexistent files are the tell):
git diff --name-only $(git merge-base main "$b") "$b" | while read f; do
[ -e "$f" ] || echo "MISSING: $f"
doneSalvage via a docs PR before considering deletion.
Step 5 — Approval gate (mandatory)
Present the full classification table with evidence. Deletion requires explicit user approval, per branch or per named group. Branches the user has flagged as sensitive (historically: `feat/aws-bench`, `feat/openrouter-engine`) stay untouched regardless of evidence.
Step 6 — Execute in safety order
1. Backup pushes first (`git push origin <branch>` for any sole-copy work) 2. Salvage PRs second 3. Deletions last: `git branch -D <approved...>` 4. `git remote prune origin`
Pitfalls learned in the field
- `gh pr merge --delete-branch` fails with *"'main' is already used by
worktree"* in multi-worktree layouts — merge succeeds remotely; verify with `gh pr view --json state`, clean the local branch manually.
- A branch checked out in a clean worktree at a merged tip loses nothing when
the worktree is removed — but confirm `status --porcelain` is empty first.
- Deleted branches remain reflog-recoverable for ~90 days; mention this in the
approval ask.
Other skills on pilot.
- /pilot-aws
Operate the AWS-hosted Pilot daemon (founder box) — status, dashboard, logs, queue queries, start/stop/restart, rebuild/upgrade, metrics tunnel, troubleshooting. Auto-invoke when user says "pilot aws", "check the box", "aws daemon", "pilot on aws", "box status", "restart pilot
Open skill - /pilot-debug
Pin Pilot executor failures against known v2.149.x patterns before exploring adjacent systems. Invoke when user says "pilot failed", "executor stuck", "epic stuck", "autopilot didn't fire", or similar pilot-failure phrasing.
Open skill

