The chain observed configuration across repos but presented every write it then proposed as though it landed where the session stands. STATE named two arms; measuring found five, and two of them are worse than the two already known: - implement — the approval prompt named NO path at all, only a count, so a plan editing ~/.claude/CLAUDE.md and one editing ./CLAUDE.md produced byte-identical prompts. - rollback — the file list rendered `.claude/settings.json`, a repo-relative FORM, while the restore writes to the absolute original. The other arms were silent; this one pointed the wrong way. - fix — paths were visible but unclassified, and --global mixed machine-wide and project rows into one unmarked table. The gate's strength comes from the target's scope class, never from the command asking: five command-owned policies would drift apart the way five copies of the lever table did. SCOPE_CLASSES is one source for class, gate, wording and predicate; templates render `disclosures[]` from the CLI instead of restating what a class means. Two orderings in that table are load-bearing, and both were measured: - plugin-managed before user-scope. Both ~/.claude/config-audit/ and the legacy ~/.config-audit/ are live, and every command writes session state there. The other order fires the gate on every write ever made and gets it switched off, which is worse than no gate. - user-scope before cross-repo. ~/.claude/.git EXISTS, so a plain .git-upward walk answers "another repo" for ~/.claude/CLAUDE.md and silently downgrades the strongest gate on the subtraction axis's primary target to disclosure. disclose is not require-ok: campaign export is cross-repo by design, so the gate there says so rather than refusing. Distinct from require-target-dir.mjs, which asks whether a scan ROOT is readable (exit 3) — a different invariant, left unmerged along with its four inline copies. Also structural, both found while building this: the hand-maintained GUARDED list in the unknown-flag sweep now derives its completeness from the directory (measured complete at 14 of 14 first, so nothing was hiding — but the 15th CLI would have been swept by nothing); and prose shape-guards use whitespace- tolerant patterns, after one went red against a command file that did say the right thing, line-wrapped. Gated: implement, fix, rollback, plan, campaign export. Suite 1596 -> 1625/0, frozen v5.0.0 and default-output baselines 0 changed files. No new GAP dimension, no lever, no finding code — utilization denominators untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013941cEohSD5Aw56FVAtBgZ
5.5 KiB
| name | description | allowed-tools | model |
|---|---|---|---|
| config-audit:plan | Phase 4 - Generate prioritized action plan with risk assessment | Read, Write, Glob, Grep, Agent, Bash | opus |
Config-Audit: Plan Generation (Phase 4)
Generate a prioritized action plan based on analysis results.
Prerequisites
- Must have completed Phase 2 (analysis)
- Phase 3 (interview) is optional — plan works with or without it
Arguments
$ARGUMENTSmay contain--rawto forward to the planner-agent's instructions; in--rawmode the agent renders v5.0.0 verbatim severity prefiks instead of humanizeduserActionLanguageurgency phrasing.
Implementation
Step 1: Verify session state
Find the most recent session with analysis completed using the Glob tool on ~/.claude/config-audit/sessions/*/state.yaml, then Read the newest match. The Read tool takes one literal path and does not expand * — pointing it at the glob makes this step report "no analysis results" even when a valid session exists.
If no session is found: "No analysis results found. Run /config-audit first to scan your configuration."
Then confirm the report itself exists — a session can carry a valid state.yaml and still be missing its report. Read ~/.claude/config-audit/sessions/{session-id}/analysis-report.md. If it is absent: "Session {session-id} has no analysis report. Run /config-audit analyze to generate it." Stop — the planner agent has nothing to read.
Step 2: Tell the user what's happening
## Creating Action Plan
Building a prioritized plan based on your analysis results...
Actions are ordered by impact, with risk assessment and dependency tracking.
Step 3: Parse flags and spawn planner agent
Check whether $ARGUMENTS contains --raw. Carry the answer yourself: the agent
prompt below is not a shell, so a variable assigned in a bash block cannot be
referenced from it. Substitute {mode} literally with --raw or with humanized
when writing the prompt.
Tell the user: "Generating your action plan (this takes about 30 seconds)..."
Agent(subagent_type: "config-audit:planner-agent")
model: opus
prompt: |
Generate action plan based on:
- Analysis: ~/.claude/config-audit/sessions/{session-id}/analysis-report.md
- Interview: ~/.claude/config-audit/sessions/{session-id}/interview.md (if exists)
Mode: {mode} ("humanized" = humanized; "--raw" = v5.0.0 verbatim severity prefiks)
Create a prioritized plan that consumes the humanized finding fields:
- Group actions by userImpactCategory (e.g., "Configuration mistake",
"Conflict", "Wasted tokens", "Missed opportunity", "Dead config")
- Lead each action with userActionLanguage ("Fix this now," "Fix soon,"
"Fix when convenient," "Optional cleanup," "FYI") rather than raw
severity. The humanizer already replaced jargon-heavy
title/description/recommendation strings with plain-language
equivalents — render them verbatim, do not paraphrase.
- Surface relevanceContext when it isn't "affects-everyone" so the
user knows whether a fix touches shared config or just their machine
- Include risk assessment per action (low/medium/high)
- Rollback strategy
- Dependency ordering
- Effort estimates
Output to: ~/.claude/config-audit/sessions/{session-id}/action-plan.md
Step 4: Present the plan summary
Read the generated plan, then classify the files its actions target. This summary
IS the approval surface — there is no separate confirmation step here, so a plan
that proposes writing to machine-wide configuration has to say so where the user
reads it. Pass one --target per distinct file the plan touches:
node ${CLAUDE_PLUGIN_ROOT}/scanners/write-scope-cli.mjs --target "<file-1>" --target "<file-2>" --repo "$PWD" --output-file /tmp/config-audit-plan-scope.json 2>/dev/null; echo $?
Exit 0 = classified; 3 = argument error (show the stderr message). Read
/tmp/config-audit-plan-scope.json. If gate is not "silent", render each
distinct string in disclosures[] verbatim directly under the action table, and
mark the affected rows — not in a footnote further down, where a user scanning the
table would miss it.
Show a concise overview:
### Action Plan Ready
**{N} actions** organized by priority:
| # | Action | Risk | Effort |
|---|--------|------|--------|
| 1 | {title} | {low/med/high} | {quick/moderate/involved} |
| 2 | ... | ... | ... |
| ... | ... | ... | ... |
Full plan: `~/.claude/config-audit/sessions/{session-id}/action-plan.md`
You can edit the plan file to remove, reorder, or modify actions before implementing.
### What's next
- **`/config-audit implement`** — Execute the plan with automatic backup and verification
- **`/config-audit interview`** — Set preferences first to customize the plan (optional)
Step 5: Update state
Update state.yaml with all four fields .claude/rules/state-management.md requires — a partial write drops the fields that make an interrupted run resumable:
current_phase: "plan"completed_phases: appendplanto the existing array (read it first; never overwrite it with a fresh list)next_phase: "implement"updated_at: current timestamp
The planner agent may already have written these. Read the file before writing and preserve whichever fields it set — a full-file Write that names only two fields silently deletes the other two.
Plan Modification
Users can edit action-plan.md before implementation — remove unwanted actions, adjust priority, or add custom actions. The implementer parses the modified plan.