config-audit/commands/plan.md
Kjell Tore Guttormsen 1543830c52 feat(commands): a write that leaves the repo says so before you approve it (M-BUG-41)
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
2026-08-10 05:40:12 +02:00

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

  • $ARGUMENTS may contain --raw to forward to the planner-agent's instructions; in --raw mode the agent renders v5.0.0 verbatim severity prefiks instead of humanized userActionLanguage urgency 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: append plan to 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.