Skip to content

fix(claude-memory): audit reads back a machine-global last-audit.md, so report and fix modes can serve another repository's findings #2277

Description

@kyle-sexton

claude-memory:audit writes its report to a fixed
${CLAUDE_PLUGIN_DATA}/audit/last-audit.md — machine-global, with no project dimension — and then
reads it back. report mode serves whatever that file currently holds and fix mode acts on it.
So on a machine with more than one repository, a claude-memory:audit report in project B can present
project A's findings as project B's, and fix mode can propose edits derived from another
repository's memory layer. That is a wrong answer served, not merely a lost artifact — which is why an
append-only history alone does not close it: the read path needs project identity.

Four sites, not the two the originating item enumerated.

Rows

  • RKD-03plugins/claude-memory/skills/audit/SKILL.md:91-92 (read), context/audit.md:104 (write), reference/criteria.md:418 (write), context/fix.md:4 (read) — one fixed machine-global path used as both sink and source.
  • RKD-05 (this plugin's half) — plugins/claude-memory/skills/audit/evals/evals.json — no eval asserts uniqueness, non-overwrite, or per-project scoping for the report path.

Evidence

plugins/claude-memory/skills/audit/SKILL.md:91-92 at HEAD — the read path:

Read the most recent audit report from `${CLAUDE_PLUGIN_DATA}/audit/last-audit.md`. If
missing, first check the pre-rename location `${CLAUDE_PLUGIN_DATA}/health/last-audit.md`

plugins/claude-memory/skills/audit/context/audit.md:104 — the write path:

Use the output format from criteria.md. Save to `${CLAUDE_PLUGIN_DATA}/audit/last-audit.md`

plugins/claude-memory/skills/audit/reference/criteria.md:418 — the same instruction restated:

Save the report to `${CLAUDE_PLUGIN_DATA}/audit/last-audit.md` for the `report` action to

plugins/claude-memory/skills/audit/context/fix.md:4fix mode reads it back.

The harness fact, verbatim. https://code.claude.com/docs/en/plugins-reference § Persistent data
directory
, fetched 2026-08-11:

"The ${CLAUDE_PLUGIN_DATA} directory resolves to ~/.claude/plugins/data/{id}/, where {id} is
the plugin identifier with characters outside a-z, A-Z, 0-9, _, and - replaced by -."

Keyed to the plugin identifier only — no project, checkout, worktree, or session segment. The
directory is also deleted on uninstall from the last scope where the plugin is installed unless
--keep-data is passed, so it is uninstall-fragile as well as collision-prone.

Note what shape of fix this row does and does not accept. For a write-only artifact, a
non-destructive history (one file per run plus an appended line, as plugins/machine-health/skills/audit/SKILL.md:73-75
does) is sufficient. It is not sufficient here. Because SKILL.md:91 and context/fix.md:4 read
the file back, the fix must either key the path by project identity or verify project identity at read
time. Serving the newest report is not the same as serving this project's report.

The scheme to reuse is already in-fleet. plugins/claude-config/skills/audit-pass/reference/run-state-and-resumability.md:10-20
defines <state-key> = <repo-identity>/<worktree-discriminator> with the rationale written out —
"${CLAUDE_PLUGIN_DATA} is machine-global, not per-project, so state keyed by working directory would
collide or fragment". #2250 adopted exactly that scheme for claude-config:audit-prompting-postures,
including a nonrepo/<hash> rung and a mandated self-describing report header; that PR is the working
template. Do not mint a second scheme, and do not express the path as a condition over
${CLAUDE_PROJECT_DIR} "when set" — that placeholder substitutes inline in skill content
(plugins-reference, fetched 2026-08-11), so the model never sees the literal token and cannot evaluate
the condition.

RKD-05 — nothing pins the property

plugins/claude-memory/skills/audit/evals/evals.json carries no assertion on this axis at all. Across
the fleet the keying property has zero eval coverage outside the two cases #2250 added to the
postures skill; audit-pass's nearest assertions are about report containment and about
suppression-anchor collisions inside one run, both different subjects.

Cross-lane note for whoever picks this up

plugins/claude-memory/skills/audit/context/audit.md was edited at :8-40 by #2234 (widening Step 1
discovery to user-global surfaces). This row edits :104. Different hunks, same file — rebase rather
than assume.

Adjacent, deliberately not duplicated

Provenance

Severity: MED · Provenance: SELF_REPORTED. The originating item declares itself "the evidence packet, not the ticket text" and directs that /plugin-quality:audit be run against these components before filing; that pass was never run. The file:line facts and doc quotes above were independently re-verified against HEAD by the validation pass, which also found four sites where the item reported two.
Origin: handoff-inbox item 2026-08-10-claude-config-report-keying-and-dispatch
Ledger: .work/handoff-inbox-batch-4/ledgers/I6-report-keying-dispatch.md § RKD-03, RKD-05
Verified against repo HEAD 9b34a82a. Line anchors re-derived at that commit — SKILL.md and context/audit.md both shifted under #2234.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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