name: validate-branch-refs
description: 'Use when checking a branch for drift before merge — references or claims a diff left stale: renamed/moved paths, removed exports, changed signatures, dropped env vars, dead doc links, outdated commands or version/port numbers. Triggers: "validate references", "check stale refs", "validate branch", "fix outdated docs", "/validate-branch-refs". Compares current branch vs base (default dev) and writes fixes.'
argument-hint: "[base-branch] [--dry-run] [--scope=docs|code|all] [--no-questions]"
allowed-tools:
- Read
- Edit
- Write
- Bash
- Glob
- Grep
- AskUserQuestion
- Agent
This skill writes edits — it is not read-only. Use --dry-run to preview.
- First non-flag token → base branch (default
dev). --dry-run→ report only; no writes, no commits.--scope=docs|code|all→ limit the reference scan.docs=*.md/*.mdx/README*/CHANGELOG*;code= everything else;all= default.--no-questions→ never prompt; log ambiguous findings as UNRESOLVED and continue.
git rev-parse --is-inside-work-treeistrue, else abort.git rev-parse --verify <base>succeeds (tryorigin/<base>; if still missing, ask for the base).- If
git status --porcelainis non-empty and not--dry-run, warn that uncommitted edits will mix with skill edits and ask whether to continue. MERGE_BASE=$(git merge-base HEAD <base>)— all diffs use${MERGE_BASE}..HEAD.
Phase 1 — Inventory the diff
In parallel:
git diff --name-status ${MERGE_BASE}..HEAD→ classify each path (A / D / M / R→rename / C).git diff ${MERGE_BASE}..HEAD -- '*.json' '*.yaml' '*.yml' '*.toml' '*.env*'→ config drift.git log ${MERGE_BASE}..HEAD --format='%H %s'→ commit context.
Build a table: change_type | old_path | new_path | symbols_removed | symbols_renamed | values_changed. For modified source files, pull removed/renamed top-level symbols from git diff -U0 (lines matching ^-(export|function|class|const|type|interface), language-aware: ts/tsx/js/jsx/py/go/rs/rb). For config files, diff changed keys (jq for JSON, plain diff otherwise).
Phase 2 — Reference scan
For each inventory row, git grep -nI the current tree (skips binaries, respects .gitignore) for:
- Paths — old path, basename, and extensionless form.
- Symbols — removed/renamed identifiers, word-boundary matched.
- Config values — old values of changed keys (env names, ports, URLs, versions).
- Commands — if
package.jsonscripts /Makefiletargets changed, their old names in docs. - Links — for renamed docs, old relative links and anchor slugs.
Exclude the changed files themselves and node_modules/, dist/, build/, .next/, coverage/, .git/. Honor --scope.
Phase 3 — Classify each hit
Produce findings: id | file:line | reference | inventory_row | proposed_fix | confidence.
- high — unambiguous rename/move, or a symbol renamed with a single definition.
- medium — a similarly-named symbol still exists, or multiple move candidates match.
- low — a Phase 4 claim that maps to no diff row.
Phase 4 — Claim validation (independent of the diff)
Scan docs (README*, docs/**, *.md, *.mdx) for claims regardless of the diff:
- backticked file paths → exist on disk?
- repo-internal anchors → anchor exists?
- env vars (
process.env.X,${X}, bash fences) → defined somewhere (.env.example, config, code)? pnpm/npm run/yarnscripts → exist in the nearestpackage.json?- ports, version pins → match config/package files?
- quoted signatures or types → symbol still has that shape?
Each failure is a low-confidence finding.
Phase 5 — Resolution
In confidence order (high → medium → low):
- high, not
--dry-run→ applyEditand log it. - medium / low →
AskUserQuestionwith file:line, the reference, candidate fixes, and Skip / "mark UNRESOLVED" / "intentional" options. Under--no-questions, mark UNRESOLVED.
Batch related questions (same symbol or renamed path) into one multiSelect call. Cap at 8 AskUserQuestion calls per run; mark the remainder UNRESOLVED.
Phase 6 — Report
Write .claude/skills/validate-branch-refs/last-run.md (only add it to .gitignore on explicit user opt-in) with the inventory table, per-finding outcome (APPLIED / SKIPPED / UNRESOLVED + reason), and counts. Then print a compact chat summary:
Branch: <head> vs <base> (merge-base <short-sha>)
Inventory: <n> files (<A>A <D>D <M>M <R>R)
Findings: <total> (high <h>, medium <m>, low <l>)
Outcome: applied <a>, skipped <s>, unresolved <u>
Report: .claude/skills/validate-branch-refs/last-run.md
Never auto-commit — the user reviews and commits.