diff --git a/plugins/claude-config/.claude-plugin/plugin.json b/plugins/claude-config/.claude-plugin/plugin.json index 9ff32eda8e..da4dff62a2 100644 --- a/plugins/claude-config/.claude-plugin/plugin.json +++ b/plugins/claude-config/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "claude-config", - "version": "0.35.3", + "version": "0.35.4", "description": "Eight configuration-health skills (plus setup) for a repo's Claude Code configuration: audit (settings.json / .mcp.json / hooks / plugins / permissions drift), audit-automation-gaps (evidence-gated verdicts on automation gaps), audit-permission-grants (allow-rule / allowed-tools grants for auto-mode durability and portability), audit-permission-state (which settings scopes exist and what rules each one holds — managed policy, user-global, project, local, and the pre-v2.1.211 start-directory copy), audit-instructions (locally-owned instruction surfaces vs current model capability — proposes removals/rewrites of instructions the model no longer needs, and detects cross-surface instruction conflicts), audit-prompting-postures (the additive lane — posture guidance the prompting guide says a component's purpose needs but the component does not carry), audit-pass (one coordinated, ordered, resumable pass over a named target — three-scope inventory, run-time-derived exclusion set, stable finding identity, suppression memory, resume, one human gate — delegating every check to the plugin that owns it), and unhobble (the empirical bare-baseline experiment: reversibly strip a repo's standing instructions, log real stumbles against the current model, re-add only what evidence earns).", "author": { "name": "Melodic Software", diff --git a/plugins/claude-config/CHANGELOG.md b/plugins/claude-config/CHANGELOG.md index 4762dd70ce..49b6b868f6 100644 --- a/plugins/claude-config/CHANGELOG.md +++ b/plugins/claude-config/CHANGELOG.md @@ -3,6 +3,35 @@ All notable changes to the `claude-config` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.35.4] + +### Fixed + +- **`audit-pass` no longer asks the model to evaluate a placeholder it never sees.** Its default + `target` read "`${CLAUDE_PROJECT_DIR}` when set, else `git rev-parse --show-toplevel`". That + placeholder is substituted inline in skill content before the file reaches the model, so the literal + token is never visible and "when set" is a test about a value that has already been resolved. The + default is now stated in prose — the project root Claude Code resolved for this session, else + `git rev-parse --show-toplevel` — with the prohibition itself written out so the shape does not come + back. **This was a contradiction inside one plugin**: #2250 landed exactly this prohibition in + `audit-prompting-postures` while `audit-pass` kept the shape, so two sibling skills disagreed about + the same placeholder. +- **Both instances, not just the filed one.** The report named `SKILL.md:42-43`; the same unevaluable + condition also sat in the non-git refusal ("with no explicit `target` and no + `${CLAUDE_PROJECT_DIR}`"), where it governs the diagnostic path that refusal exists to produce. + Fixing only the cited line would have left the contradiction half-standing while reading as closed. +- **The `{id}` derivation is stated, so a report cannot be written where the next run will not look.** + The skill said `${CLAUDE_PLUGIN_DATA}` resolves to `~/.claude/plugins/data/{id}/` and never said how + `{id}` is formed. Now quoted: the plugin identifier with characters outside `a-z`, `A-Z`, `0-9`, `_` + and `-` replaced by `-`, with the plugins reference's own worked example + (`formatter@my-marketplace` → `formatter-my-marketplace`). A wrong derivation is also how `--resume` + loses a partial. +- **`${CLAUDE_PLUGIN_DATA}` is recorded as absent from the Bash tool's environment.** The export is + documented for "hook processes and … MCP and LSP server subprocesses"; the Bash tool is none of + those, so `echo "$CLAUDE_PLUGIN_DATA"` in a Bash call returns an empty string even though the token + substitutes correctly in skill content. Nothing in the skill said so, which invites exactly that + shell expansion. + ## [0.35.3] ### Fixed diff --git a/plugins/claude-config/skills/audit-pass/SKILL.md b/plugins/claude-config/skills/audit-pass/SKILL.md index ba6a4a885d..74eec97e70 100644 --- a/plugins/claude-config/skills/audit-pass/SKILL.md +++ b/plugins/claude-config/skills/audit-pass/SKILL.md @@ -39,9 +39,17 @@ Bare invocation reads and reports. `--fix` is the only mutation path, and it is Parse `$ARGUMENTS`: -- **`target`** — the git repository to audit. Default: `${CLAUDE_PROJECT_DIR}` when set, else - `git rev-parse --show-toplevel`. Never the working directory — a run launched from a subdirectory - must key and scan identically to one launched from the root. +- **`target`** — the git repository to audit. Default: the project root Claude Code resolved for this + session; where no such root is available, `git rev-parse --show-toplevel`. Never the working + directory — a run launched from a subdirectory must key and scan identically to one launched from + the root. + + **Do not express this as a condition over `${CLAUDE_PROJECT_DIR}` "when set".** That placeholder is + substituted inline in skill content before this file reaches you, so the literal token is never + visible and the test is not yours to make — you would be deciding "is it set?" about a value that + has already been resolved. Work from what you can observe: the resolved path, or a command you run. + The sibling `audit-prompting-postures` states this same rule where it derives its report path, and + the two skills contradicted each other on it until this was fixed. **`target` must resolve to the active project root, and a path that does not is refused.** The delegated interfaces accept no target: `audit-instructions` takes a surface scope and inventories @@ -63,7 +71,7 @@ Parse `$ARGUMENTS`: before Phase 0 does any work, naming the path and the reason, writing nothing. **Name the directory, not an empty string.** In the case this refusal is *for*, the default - resolution above produces nothing: with no explicit `target` and no `${CLAUDE_PROJECT_DIR}`, + resolution above produces nothing: with no explicit `target` and no session-resolved project root, `git rev-parse --show-toplevel` fails outside a repository and there is no resolved root to report. So for the diagnostic only, fall back to the current directory and name **that** — a refusal that cannot say which path it refused is barely better than a silent one. The fallback is for the message; diff --git a/plugins/claude-config/skills/audit-pass/reference/report-location-and-schema.md b/plugins/claude-config/skills/audit-pass/reference/report-location-and-schema.md index 948a9a603e..6a4b9903f2 100644 --- a/plugins/claude-config/skills/audit-pass/reference/report-location-and-schema.md +++ b/plugins/claude-config/skills/audit-pass/reference/report-location-and-schema.md @@ -19,8 +19,24 @@ held. - The report goes under `${CLAUDE_PLUGIN_DATA}` at `runs///findings.json`, which survives plugin updates. **State its location precisely, because a whole target class turns on it:** that directory resolves to `~/.claude/plugins/data/{id}/` - ([plugins reference](https://code.claude.com/docs/en/plugins-reference), verified 2026-08-11), and no - documented setting relocates it. It is therefore **outside** a target below `~` and **inside** any + ([plugins reference](https://code.claude.com/docs/en/plugins-reference), verified 2026-08-12), and no + documented setting relocates it. + + **`{id}` is derived, and deriving it wrong loses the report.** Same page, verbatim: `{id}` is *"the + plugin identifier with characters outside `a-z`, `A-Z`, `0-9`, `_`, and `-` replaced by `-`"*, with + the worked example that a plugin installed as `formatter@my-marketplace` lands in + `~/.claude/plugins/data/formatter-my-marketplace/` — the `@` becomes `-`. A wrong derivation writes + the report where the next run will not look for it, which is also how `--resume` loses a partial. + + **`${CLAUDE_PLUGIN_DATA}` is not in the Bash tool's environment — do not try to expand it from a + shell.** The same page scopes the export precisely: *"All three are exported as environment variables + to hook processes and to MCP and LSP server subprocesses."* The Bash tool is none of those. The token + does substitute in **skill content**, which is how a resolved path reaches you in this file, but + `echo "$CLAUDE_PLUGIN_DATA"` inside a Bash call yields an empty string. Use the path already + substituted into the text you are reading, or rebuild it from `~/.claude/plugins/data/` plus the + mangled identifier above. + + It is therefore **outside** a target below `~` and **inside** any target at or above it. The default path is *usually* outside the scan set and is **not unconditionally** outside it — a dotfiles repository, or `~` itself, is a target where containment holds by construction, and the older unconditional claim was false there.