| name | guardian |
| description | ADR-set health sweep for adr-kit. Runs the due health tier(s) — cheap (drift + stale + lint) and/or LLM (suggest + audit) — applies mix-by-finding-type responses, and stamps the state file when done. Invoke after seeing an [adr-guardian] ... DUE block at session start, or on demand to run a full health sweep. Accepts optional arg: cheap | llm | all. LLM tier always asks before spending. |
| argument-hint | [cheap | llm | all] |
| allowed-tools | ["Read","Bash","Edit","Write","Task","Glob","Grep"] |
adr-kit guardian
You are running the ADR-set health sweep. Your job is to run whichever tier(s) are due (or were requested), surface findings using the mix-by-finding-type responses below, and stamp the state file when done.
Before you start
Resolve the plugin bin path (same resolver used in /adr-kit:judge and /adr-kit:init):
ADR_KIT=$(ls -d ~/.claude/plugins/cache/rvdbreemen-adr-kit/adr-kit/*/ 2>/dev/null | sort -V | tail -1)
If ADR_KIT is empty (plugin not installed), fall back to a local checkout:
ADR_KIT=$(git rev-parse --show-toplevel 2>/dev/null)/
Verify the guardian bin exists:
ls "$ADR_KIT/bin/adr-guardian" 2>/dev/null || echo "guardian not found"
Step 1 — Determine which tier(s) to run
If invoked with an explicit argument (cheap, llm, or all), honour it.
Otherwise, read the current state to determine due tiers:
"$ADR_KIT/bin/adr-guardian" state
Compute which tier is due based on cheap_tier.last_run vs guardian.drift_stale_days and llm_tier.last_run vs guardian.llm_stale_days (defaults: 1d cheap, 14d LLM). If neither is due, report "Both tiers are current — nothing to sweep" and stop.
Read docs/adr/.adr-kit.json if it exists to get any custom guardian.* config.
Step 2 — Cheap tier (drift + stale + lint)
Run only when cheap tier is due or explicitly requested.
2a. Drift check (declarative, adr-judge)
git diff HEAD~5 HEAD --unified=0 | "$ADR_KIT/bin/adr-judge" \
--diff - \
--adr-dir docs/adr/ \
--json > /tmp/guardian-drift.json 2>&1
DRIFT_EXIT=$?
Read /tmp/guardian-drift.json. Count violations and advisories.
Response (mix-by-finding-type: Drift): Surface prominently. List each violation with file:line + ADR id. Offer to (a) fix the code, (b) write a new ADR covering the new pattern, or (c) supersede the violated ADR. Highest-priority finding type.
2b. Stale ADR detection (adr-retire)
"$ADR_KIT/bin/adr-retire" \
docs/adr/ \
--format json > /tmp/guardian-retire.json 2>&1
Read /tmp/guardian-retire.json. Collect the candidate set: extract the list of ADR ids flagged for retirement (candidates).
Change-based filtering: Read retire_seen from the state file (adr-guardian state). Compare the fresh candidate set against retire_seen. Only surface candidates that are new (present in fresh set but not in retire_seen). This avoids daily nagging about the same stale ADRs. If all candidates are already in retire_seen, skip the retire response silently for this sweep.
Response (mix-by-finding-type: Stale ADR): For each new retire candidate, draft a retirement/supersession skeleton for human review — never auto-apply. Show:
- The ADR id and title.
- The retirement signal (tech removed / superseded target missing / policy drift / age threshold).
- A draft Status flip:
Deprecated, <today> or Superseded by ADR-NNN, <today> (if a superseding ADR exists).
- Ask the user to confirm or skip each.
2c. Health lint (adr-lint / adr-status)
"$ADR_KIT/bin/adr-lint" docs/adr/ 2>&1 | tail -20
"$ADR_KIT/bin/adr-status" --adr-dir docs/adr/ 2>&1 | tail -5
Response (mix-by-finding-type: Health): Emit a PASS/ADVISORY/FAIL summary. For FAILs: list the gate name and ADR, offer to fix via /adr-kit:adr (re-run the adr-generator subagent on that ADR).
2d. Stamp cheap tier
After completing 2a–2c, record the sweep. The --retire-seen argument must contain
the full fresh candidate set (all ids, not just the new ones). The detector uses
the stored set for the next session's change comparison.
"$ADR_KIT/bin/adr-guardian" stamp cheap \
--violations <N_drift_violations> \
--retire <N_retire_candidates> \
--lint "<F>F/<A>A" \
--retire-seen '<json_array_of_ALL_retire_candidate_ids>'
Step 3 — LLM tier (suggest + audit) — ALWAYS confirm cost first
MANDATORY cost confirmation gate. Before running this tier, read docs/adr/.adr-kit.json:
-
If guardian.llm_autorun is false (default), print:
[adr-guardian] LLM tier: adr-suggest + full audit will invoke claude-sonnet-4-6.
Estimated cost: ~$0.10–0.30. Run now? (y/N)
And wait for an explicit y / yes. If the user says no (or presses Enter on the default), skip the LLM tier and stop at Step 4.
-
If guardian.llm_autorun is true, proceed without asking (user opted in explicitly in config).
Run only when llm tier is due or explicitly requested (and user has confirmed cost).
3a. Missing-ADR detection (adr-suggest)
git diff HEAD~10 HEAD --unified=0 | "$ADR_KIT/bin/adr-suggest" \
--diff - \
--adr-dir docs/adr/ \
--json > /tmp/guardian-suggest.json 2>&1
Read /tmp/guardian-suggest.json.
Response (mix-by-finding-type: Missing ADR): Passive. List candidates where needs_adr=true with confidence >= medium. Offer to author selected ones via the adr-generator subagent. User picks; never auto-create.
3b. Full audit (adr-judge with LLM pass)
git diff HEAD~10 HEAD --unified=0 | "$ADR_KIT/bin/adr-judge" \
--diff - \
--adr-dir docs/adr/ \
--llm \
--json > /tmp/guardian-audit.json 2>&1
Read /tmp/guardian-audit.json. Same response as Step 2a drift, but covering semantic violations not expressible as regex.
3c. Stamp LLM tier
"$ADR_KIT/bin/adr-guardian" stamp llm \
--suggest <N_suggest_hits> \
--audit <N_audit_findings>
Step 4 — Wrap-up
Print a summary:
[adr-guardian] sweep complete
cheap tier: <X> drift violations · <R> retire candidates · <lint_summary>
llm tier: <S> missing-ADR suggestions · <A> audit findings (or: skipped)
→ use /adr-kit:adr to author new ADRs, /adr-kit:judge for detailed resolution
Constraints
- Never auto-apply ADR edits. All stale/retire changes are drafts for human review.
- Never auto-create ADRs. Missing-ADR suggestions are presented; user picks.
- Never skip the cost-confirm gate when
llm_autorun: false.
- Always stamp after each tier completes so the next session's cooldown is correct.
- Model can self-call. This skill is NOT
disable-model-invocation. When the session model sees an [adr-guardian] ... DUE block injected at SessionStart, it should proactively offer to run the due tier via /adr-kit:guardian.
SessionStart block handling (for in-session model)
When the in-session model reads an additionalContext block starting with [adr-guardian]:
- Check which tier(s) are marked
DUE.
- For the cheap tier: offer immediately: "ADR drift/health check is due — run
/adr-kit:guardian cheap to sweep (free, ~30s)?"
- For the llm tier: offer: "ADR semantic check is due (bi-weekly) — run
/adr-kit:guardian llm? This will confirm cost (~$0.10–0.30) before spending."
- If the user accepts either, invoke this skill with the appropriate argument.
- Apply the mix-by-finding-type responses as documented above.