Skip to content

fix(claude-config): audit-prompting-postures report path has no project dimension, so any other root overwrites it #2250

Description

@kyle-sexton

Defect

audit-prompting-postures persists its only durable deliverable to a single fixed-name file with no
project dimension
, so a run rooted anywhere else silently overwrites it. The audit artifact is
destroyed by ordinary use of the skill.

Evidence at HEAD

plugins/claude-config/skills/audit-prompting-postures/SKILL.md:78:

Persist the report to `${CLAUDE_PLUGIN_DATA}/audit-prompting-postures/last-audit.md` and summarize

${CLAUDE_PLUGIN_DATA} resolves to ~/.claude/plugins/data/{id}/, where {id} is the plugin
identifier — never the project (plugins reference).
The collision is definitional, not incidental.

The mismatch sits inside one file: Phase B at SKILL.md:51-53 inventories project surfaces ("resolve
${CLAUDE_CONFIG_DIR:-~/.claude}, project .claude/, CLAUDE.md files") while the output is
machine-scoped. And the mandated schema at SKILL.md:81-86 requires a findings table, a coverage line
and a Sources line — no header — so nothing contractual records which root produced the file. A
surviving report is not even attributable after the fact.

The filed fix sketch is wrong — do not implement it as written

The originating item proposed ${CLAUDE_PROJECT_DIR} with an explicit "when set, else" fallback token.
That would introduce a second, separately-filed defect into the same file. Per the
plugins reference, the placeholder substitutes
inline in skill content, so the model never sees the literal token and cannot evaluate "when set" —
which is exactly the defect filed against audit-pass as ledger row F9. Implementing this row as
sketched would ship that defect two rows away from where it was filed.

The derivation must therefore be expressed as commands to run, never as a conditional over a token
that has already been substituted away.

And it must reuse the existing scheme, not mint a second one

audit-pass already owns per-project identity for this exact concern.
skills/audit-pass/reference/run-state-and-resumability.md:10-20:

`${CLAUDE_PLUGIN_DATA}` is machine-global, not per-project, so state keyed by working directory would
collide or fragment depending on where the operator happened to stand.

**`<state-key>` = `<repo-identity>/<worktree-discriminator>`.**

- **`repo-identity`** — for a git repository, the first configured remote URL normalized to
  `host/owner/repo`, lowercased, `.git` suffix and credentials stripped. With no remote,
  `local/<sha256 of the canonicalized repo root>` truncated to 12.
- **`worktree-discriminator`** — `sha256` of the canonicalized worktree root, truncated to 8.

A second scheme for the same concern is the drift this batch exists to remove, so this row adopts that
one. One extension is required: that ladder has a git-with-remote rung and a git-without-remote
rung, and no non-repo runggrep confirms the only rendered form in the skill is the abstract
local/<sha256 …>. audit-pass does not need one (it refuses non-git targets), but
audit-prompting-postures is report-only and legitimately audits non-repo roots: the run that produced
this finding was rooted at C:\Users\KyleSexton, which is not a git repository. The rung is added here
while the shape is kept.

Grading

  • Severity: HIGH — the skill's only durable deliverable is a single fixed-name file that a run from
    any other root silently overwrites.
  • Provenance: AUDITOR_VERIFIED. Verdict: PRESENT_AT_HEAD (0.29.0 → HEAD, line unchanged).
  • Rationale status: rationale_falsified in effect — the symptom is real, the filed mechanism is
    not. Caught cross-ledger during batch-4 reconciliation rather than in-ledger.
  • Dedup: no tracker issue found. Adjacent, not duplicate: the audit-pass report-path findings
    (report path lands inside the scan set) and the delegated-report-keying row share one root cause — a
    report path with no target dimension — with different consequences.
  • In-tree precedent: plugins/bug-report/skills/write/SKILL.md:97 already keys by project slug and
    states the reason ("The plugin data directory is per-plugin, not per-project").
  • Originating inbox item: 20260811-021645-plugin-audit-four-components-and-guard-deadlock-ownership.md
    (batch-4 ledger I9, row CC-F1; mechanism replaced per reconciliation OR-3).

Also in scope for this row

The mandated report header at SKILL.md:81 — resolved root, scope filter, UTC timestamp — so a
surviving file is self-describing rather than merely un-overwritten.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions