From vp-git
Triage and prune accumulated git safety tags using sort-by-SHA grouping and a per-tag decision tree (duplicate / branch-ref / intermediate-waypoint). Read-only by default; deletes only after explicit user confirmation. Use when stacked-rebase or surgical-fix sessions have accumulated multiple safety tags across namespaces (safety/*, pre-*, post-*, backup-*, stack-*), when /stack-cascade's Step 2 collision check blocks reuse of an OP_ID, or when auditing inherited tag soup. Pairs with /stack-cascade as post-success aftercare. Covers scenarios like: 'clean up safety tags', 'too many backup tags', 'prune accumulated tags', 'tag audit', 'tag-audit', 'safety tag cleanup', 'rollback anchor cleanup', 'which tags can I delete', 'tag namespace collision', 'OP_ID already has tags'.
How this skill is triggered — by the user, by Claude, or both
Slash command
/vp-git:tag-auditThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Stacked-rebase sessions accumulate safety tags fast — each surgical operation
Stacked-rebase sessions accumulate safety tags fast — each surgical operation
deserves its own rollback anchor, but the cost of regret is high. Once an
operation is validated and pushed, those tags decay into redundancy: they
either duplicate another tag, point at the same SHA as a current branch ref,
or mark an intermediate waypoint no one will ever git reset back to.
This skill audits the safety-tag namespace, surfaces duplicates by sorting on SHA (not name), proposes deletions with reasons, and deletes only after explicit confirmation.
/stack-cascade Step 2 refuses to proceed because the OP_ID
namespace is non-empty.git tag -l 'safety/*' 'pre-*' 'post-*' returns more entries than
feels healthy and you want to know which ones still earn their keep.A safety tag earns its keep only when it points at a unique,
semantically-meaningful SHA AND that SHA represents a state you might
genuinely need to git reset back to. Anything else is rollback theater
— the deletion candidate. Step 3 implements this as a three-question
decision tree.
Sort by SHA (not name). Name-grouped output hides duplicates; SHA-grouped output exposes them — tags pointing at the same commit cluster together, making redundancy visible at a glance.
# Default is conservative: only the canonical `safety/*` namespace.
# Keep in sync with Step 2 below.
PATTERNS=('safety/*')
(
set -euo pipefail
count=$(git tag -l "${PATTERNS[@]}" | wc -l | tr -d ' ')
echo "# Matched $count tag(s) across patterns: ${PATTERNS[*]}" >&2
[[ "$count" -gt 0 ]] || { echo "# (nothing to audit — check pattern typos or working directory)" >&2; exit 0; }
git tag -l "${PATTERNS[@]}" | while IFS= read -r t; do
# Capture-then-print: bare $(...) inside printf's arg list does NOT trigger
# set -e on substitution failure (the outer printf wins with exit 0).
# Capturing to a variable lets set -e fire if rev-parse fails.
# The ^{commit} peel returns the commit SHA for both lightweight AND
# annotated tags (bare rev-parse on an annotated tag returns the tag-object
# SHA, which won't cluster with branch SHAs in Step 2).
sha=$(git rev-parse --short "$t^{commit}") \
|| { echo "rev-parse failed for tag '$t' — aborting audit" >&2; exit 1; }
printf '%s TAG %s\n' "$sha" "$t"
done | sort
)
The default ('safety/*') is intentionally conservative — it matches only
the canonical namespace stack-cascade writes to under the current
convention. Widen it explicitly for cleanup that covers other namespaces:
stack-*-start/* from cascades before the convention change.pre-mr/*,
pre-port/*, pre-fix-*, post-fix-*, pre-split/*.backup-*.PATTERNS=('safety/*' 'pre-*' 'post-*' 'backup-*' 'stack-*') # broad audit
The audit is read-only; widening the pattern is cheap. But leave the
widened pattern in place for Step 5's deletion only after the KEEP/DROP
review confirms no release tags or ad-hoc semantic tags slipped in
(pre-1.0, pre-release-*, backup-2024-05, safety-audit-2026-q1).
See "When NOT to Use" above.
Enumerate current branch tips by SHA, then interleave them with Step 1's output so SHA collisions between tags and branch refs are visible adjacently:
# Use the same PATTERNS as Step 1 (default safety/* OR your widened set).
PATTERNS=('safety/*')
(
set -euo pipefail
{
git tag -l "${PATTERNS[@]}" | while IFS= read -r t; do
# Same capture-then-print + ^{commit} peel as Step 1 — see comments there.
sha=$(git rev-parse --short "$t^{commit}") \
|| { echo "rev-parse failed for tag '$t' — aborting audit" >&2; exit 1; }
printf '%s TAG %s\n' "$sha" "$t"
done
git for-each-ref refs/heads --format='%(objectname:short) BRANCH %(refname:short)'
} | sort
)
Any TAG line whose SHA matches an adjacent BRANCH line is a deletion
candidate — the branch ref already serves as the anchor.
For each tag in the audit output, apply these three questions in order:
A tag survives only when it has a unique SHA AND no equivalent branch
ref AND marks a state genuinely worth git reset-ing back to.
Always show the proposal before any deletion. Example output (with
widened patterns covering an ad-hoc pre-* / post-* session):
Found 21 safety tags. After audit:
KEEP (5 — unique semantically-meaningful SHAs):
3be7b11a pre-split/improved-disabled [root anchor]
4a9fac77 pre-split/pr10 [pre-cascade origin]
e5f1dd7d pre-split/pr11 [pre-cascade origin]
aa71726b pre-split/pr12 [pre-cascade origin]
f675fe31 pre-split/pr13 [pre-cascade origin]
DROP (16):
pre-mr/pr10-cascade [duplicate: same SHA as pre-fix-pr11ci/pr10]
pre-mr/pr11-cascade [duplicate: same SHA as pre-fix-pr11ci/pr11]
pre-fix-pr11ci/pr10 [branch-ref: SHA matches branch pr10]
post-fix-pr11ci/pr11 [branch-ref: SHA matches branch pr11]
post-fix-pr11ci/pr12 [branch-ref: SHA matches branch pr12]
post-fix-pr11ci/pr13 [branch-ref: SHA matches branch pr13]
pre-mr/pr9.6-post-triage [intermediate-waypoint]
pre-mr/pr9.6-post-c5 [intermediate-waypoint]
... (etc)
Proceed with deletion? [y/N]
Reason codes: [duplicate], [branch-ref], [intermediate-waypoint],
or a one-line free-text reason for KEEP entries ([root anchor],
[pre-cascade origin], [narrative anchor]).
Two-pass verify-then-mutate under set -euo pipefail. Use quoted array
iteration — bash unquoted-var word-splitting fails silently under zsh
(see references/shell-portability.md).
TAGS_TO_DELETE=(
"pre-mr/pr10-cascade"
"pre-mr/pr11-cascade"
"pre-fix-pr11ci/pr10"
# ... full list from Step 4 DROP
)
(
set -euo pipefail
# Verify pass: confirm every tag exists before deleting any
for t in "${TAGS_TO_DELETE[@]}"; do
git rev-parse --verify "refs/tags/$t" >/dev/null 2>&1 \
|| { echo "Tag '$t' not found — aborting (no tags deleted)" >&2; exit 1; }
done
# Mutation pass: delete each
for t in "${TAGS_TO_DELETE[@]}"; do
git tag -d "$t"
done
)
The verify pass is a pre-flight check: a missing tag name aborts before any deletion, so the namespace can't be left half-cleaned by a typo or a stale paste from Step 4. (Concurrent tag mutation by another process between the verify and mutate passes is still possible — this is shell, not a transaction. The window is narrow if you're the only writer.)
Most safety tags are local-only: git push does not push tags by
default. Skip this step unless the tags were explicitly pushed (e.g.,
via git push --tags or a CI workflow that forwards tags).
When remote cleanup is needed, push deletion refspecs in a single atomic operation so a partial failure doesn't leave the remote half-cleaned:
TAGS_TO_DELETE=(
"pre-mr/pr10-cascade"
"pre-mr/pr11-cascade"
# ... same list as Step 5
)
(
set -euo pipefail
# Guard against empty array: `git push --atomic origin` with NO refspecs
# falls back to push.default and pushes the current branch — exactly the
# opposite of "delete zero remote tags".
[[ ${#TAGS_TO_DELETE[@]} -gt 0 ]] \
|| { echo "TAGS_TO_DELETE is empty — refusing to push" >&2; exit 1; }
refspecs=()
for t in "${TAGS_TO_DELETE[@]}"; do
refspecs+=(":refs/tags/$t")
done
git push --atomic origin "${refspecs[@]}"
)
:refs/tags/<name> is the deletion refspec; --atomic makes the
multi-ref push all-or-nothing.
Bash idiom failing silently under zsh — for t in $unquoted_var; do git tag -d $t; done passes the whole string as one arg to git tag -d, which complains about a single-revision-not-found error. The
loop "completes" without deleting anything. Always use quoted array
iteration (for t in "${arr[@]}") or xargs. See
references/shell-portability.md for the full mitigation set.
Too-broad pattern matches a release tag — git tag -l '*' is
obviously dangerous, but safety-* accidentally matches a hand-named
safety-audit-2026-q1 tag, or backup-* matches a release naming
scheme. The proposal-before-delete gate at Step 4 is the safety net —
read the KEEP/DROP list before confirming.
Deleting a tag whose SHA equals a branch tip — but the branch itself is the one you wanted to restore — if the cascade's pushed state turns out to be wrong and you want to roll back, the safety tag was the rollback anchor. Branch tips can move; safety tags do not. This is why "rollback window closed" is a precondition: don't audit until you're committed to the current state.
Audit run mid-cascade — running this skill while another git
operation is in progress in the same repo (a rebase paused on
conflict, a cherry-pick mid-application) can delete tags the paused
operation needs for --abort. Check git status first.
/stack-cascade/stack-cascade creates safety tags under
safety/${OP_ID}/before/<branch> (and historically under
stack-${PURPOSE}-start/<branch> before the convention change). Step 2
of /stack-cascade blocks if its OP_ID namespace is non-empty — that
collision-check error names this skill as the remediation. The error
shows a literal git tag -l 'safety/${OP_ID}/*' preview command;
running this skill against that pattern set produces the targeted
DROP list. For legacy stack-*-start/* tags, widen PATTERNS to
include 'stack-*' in Step 1.
/stack-cascade and /rebase-validate compose tightly along the cascade
flow: cascade executes, validate verifies meaning preservation.
/tag-audit is a separate utility that pairs with cascade's tag
namespace as post-success aftercare, but operates independently — it's
useful for any safety-tag accumulation, not just cascades, and its
preconditions (rollback window closed) decouple it from the cascade's
session.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub voxpelli/vp-claude --plugin vp-git