config-audit/commands/discover.md
Kjell Tore Guttormsen 09f817977c fix(commands): stop assuming shell state survives between blocks
Dogfooding `plan` + `implement` against a throwaway config surfaced one root
defect with many arms: the command templates treat consecutive fenced blocks as
one shell. They are not. Every ```bash fence runs as its own Bash call in its own
process, so a variable set in one block is empty in the next, and `$$` is a
different PID (measured: 21710 vs 22109).

The planner agent confirmed the sharpest arm at runtime, reporting that
`Mode: $RAW_FLAG` "arrived literally unsubstituted" — `--raw` was documented in
three command files while being functionally dead. A machine sweep found the same
root in 20 places across 9 files, well past the two the written fasit predicted:

  - `$RAW_FLAG` read from non-shell agent prompts (analyze, plan, implement)
  - `$TMPFILE` read across blocks (tokens, manifest, whats-active,
    plugin-health) — each command could not read the file it had just written
  - `$GLOBAL_FLAG` across blocks (fix)
  - `$TODAY` never assigned in any block (campaign), passing
    `--reference-date ""` to a write CLI in six places
  - three `$$` temp paths handed to the Read tool (fix), which expands neither

All now follow the hardened drift.md pattern: a fixed literal path, or a
re-derivation inside each block that needs it.

Also fixed, all confirmed against ground truth rather than inferred:

  - `implement` printed a rollback ID it never captured (the timestamp lived only
    inside a command substitution) — the one message a user reads after a bad run
  - `plan` reported "No analysis results found" for valid sessions, because Read
    was pointed at a glob it cannot expand; now uses Glob and verifies the
    analysis report exists before spawning the agent
  - five phase commands wrote state.yaml with two of four required fields; since
    the agent writes all four, a follow-up write silently deleted the rest
  - `implement` promised rollback deletes created files; rollback deliberately
    leaves them (M-BUG-26 still open) — the doc, not the engine, was wrong
  - `implement` claimed a score delta with no pre-change measurement
  - `verifier-agent` was told to write a report it has no tool to write
  - dead `Task` tool name in always-loaded rule context; planner-agent template
    demonstrated the inline file content its own line 110 forbids

The sweeps land as tests/commands/command-shell-state-shape.test.mjs, verified
red before the fix and proven able to fail by reintroducing the defect. Two
existing tests asserted the old bash-block mechanism rather than the intent and
were updated. Suite 1449/0; frozen v5.0.0 snapshots and all scanner code
untouched.

Not fixed, deliberately: neither command scope-gates its actions to the audit
target. The generated plan included an edit to a real file under ~/.claude,
outside the throwaway target, because the skill/agent scanners are machine-wide.
That is a design change, not a side fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0195udHgCcFegzm7ecKku2Yc
2026-08-01 20:12:17 +02:00

5.5 KiB

name description argument-hint allowed-tools model
config-audit:discover Phase 1 - Initialize session, auto-detect scope, and discover config files [current|repo|home|full] [--delta] Read, Write, Edit, Glob, Grep, Agent, AskUserQuestion, Bash opus

Config-Audit: Discover (Phase 1)

Initialize a new audit session and discover all Claude Code configuration files.

Usage

/config-audit discover              # Auto-detect scope
/config-audit discover current      # Force current directory scope
/config-audit discover repo         # Force git repository scope
/config-audit discover home         # Force home/global scope
/config-audit discover full         # Force full machine scope
/config-audit discover --delta      # Incremental re-scan (changed files only)

Implementation

Step 1: Initialize session and greet

Generate session ID (YYYYMMDD_HHmmss), create directories:

mkdir -p ~/.claude/config-audit/sessions/{session-id}/findings 2>/dev/null

Step 2: Determine scope

If the user provided a scope argument, use it. Otherwise, auto-detect:

  1. Run git rev-parse --show-toplevel 2>/dev/null
  2. If inside a git repo → repo scope
  3. If pwd is $HOMEhome scope
  4. Otherwise → current directory scope

Tell the user:

## Configuration Discovery

**Scope:** {Repository|Home|Current directory|Full machine} — `{path}`
Finding all Claude Code configuration files (CLAUDE.md, settings, hooks, rules, MCP servers)...

Step 3: Resolve paths

Scope What gets scanned
current Current directory + parent CLAUDE.md files up to root + ~/.claude/
repo Git repo root + ~/.claude/
home ~/.claude/ only
full ~/.claude/ (depth 10), managed paths, all dev dirs under $HOME

Step 4: Delta mode (if --delta)

If --delta flag:

  1. Find previous baseline from ~/.claude/config-audit/sessions/*/discovery.json
  2. If no previous: "No previous scan found. Running full discovery instead."
  3. Compare file mtimes/sizes to classify as changed/new/deleted/unchanged
  4. Only scan changed + new files

Step 5: Run discovery

Run the scan orchestrator silently to discover and scan files. Default mode emits humanized JSON — each finding in scan-results.json carries userImpactCategory, userActionLanguage, and relevanceContext alongside the v5.0.0 fields. Pass --raw through if the user requested it (produces v5.0.0 verbatim envelope; humanizer fields absent).

RAW_FLAG=""
if echo "$ARGUMENTS" | grep -q -- "--raw"; then RAW_FLAG="--raw"; fi
# Set to the flag itself for full/home scope, otherwise leave empty. Never pass
# a placeholder wrapped in square brackets: it does not start with a dash, so
# the orchestrator's arg loop takes it as the SCAN TARGET and silently scans a
# path that does not exist.
SCOPE_FLAGS=""   # e.g. SCOPE_FLAGS="--full-machine" or SCOPE_FLAGS="--global"
node ${CLAUDE_PLUGIN_ROOT}/scanners/scan-orchestrator.mjs <target-path> --output-file ~/.claude/config-audit/sessions/{session-id}/findings/scan-results.json $SCOPE_FLAGS $RAW_FLAG 2>/dev/null; echo $?

Check exit code: 0/1/2 → normal. 3 → "Discovery encountered an error. Try a narrower scope."

Step 6: Save scope and state

Write scope.yaml and state.yaml to session directory. Update state with all four fields .claude/rules/state-management.md requires: current_phase: "discover", completed_phases: [discover], next_phase: "analyze", and updated_at. The last two are what make an interrupted run resumable.

Step 7: Present summary

Read the scan results file using the Read tool. When you surface initial findings, group them by userImpactCategory and lead each line with userActionLanguage rather than raw severity prefiks — the humanizer already mapped severity to plain-language phrasing ("Fix this now", "Fix soon", "Fix when convenient", "Optional cleanup", "FYI") so the rest of the toolchain sees consistent wording.

Full scan:

### Discovery Complete

**{scope_type}** scope — found {total_files} configuration files:

| Type | Count |
|------|-------|
| CLAUDE.md | {n} |
| Settings | {n} |
| MCP configs | {n} |
| Rules | {n} |
| Hooks | {n} |
| Other | {n} |

Initial scan found {finding_count} items to review (grouped by impact: {comma-separated counts per userImpactCategory}).

**Next:** Run `/config-audit analyze` to generate your analysis report.

Delta scan:

### Delta Discovery Complete

Compared against baseline from {previous-session-id}:

| Status | Files |
|--------|-------|
| Changed | {n} |
| New | {n} |
| Deleted | {n} |
| Unchanged | {n} |

Only {changed+new} file(s) scanned (vs {total} full scan).

**Next:** Run `/config-audit analyze` to generate your analysis report.

Config File Patterns

Pattern Description
**/CLAUDE.md Project instructions
**/CLAUDE.local.md Local overrides
**/.claude/settings.json Project settings
**/.mcp.json MCP servers
**/.claude/rules/*.md Modular rules

For global: ~/.claude/CLAUDE.md, ~/.claude/settings.json, ~/.claude.json, ~/.claude/agents/*.md

Error Handling

  • If scanner fails, report to user in plain language and suggest narrower scope
  • If path doesn't exist, tell user and suggest alternatives
  • If git command fails for repo scope, silently fall back to current
  • If no config files found, explain: "No Claude Code configuration files found. Start with /config-audit feature-gap to see what's recommended."