Skip to content

convention: no rule governs report keying, retention, or overwrite under CLAUDE_PLUGIN_DATA #2278

Description

@kyle-sexton

docs/conventions/ carries eighteen conventions and none of them governs how a plugin names, keys,
retains, or overwrites what it writes under ${CLAUDE_PLUGIN_DATA}. The hazard is recognized inside
the fleet — two claude-config surfaces write the rationale out in full — and applied inconsistently
even within one plugin, which is the signature of a missing rule rather than a per-skill oversight.
Without a convention the next report-writing skill reproduces it, and the two open adoptions (filed
separately in this batch, one on claude-config:audit-instructions and one on claude-memory:audit)
have no doctrine to point at.

Rows

  • RKD-04docs/conventions/ (verified absence, 18 entries) — no convention governs report filename, keying, history retention, or overwrite under ${CLAUDE_PLUGIN_DATA}; the nearest governing text scopes what may live there, not how it is named.
  • RKD-06plugins/bug-report/skills/write/SKILL.md:97 — the "looks scoped but isn't" case: keying on the kebab-cased basename of the project root, which the fleet's own text names as insufficient. Carry it into the convention as the named example; this is not filed as a bug-report defect and must not become one.

Evidence

RKD-04 — a verified absence

ls docs/conventions/ returns eighteen entries at HEAD.
grep -rn "CLAUDE_PLUGIN_DATA\|plugin data" docs/conventions/ returns hits in three files, none
governing keying:

  • docs/conventions/hook-config-delivery/README.md — a persistence channel for hook config.
  • docs/conventions/topic-docs/README.md — a tier table ("Machine state | ${CLAUDE_PLUGIN_DATA} … Never committed"), tier placement, per-plugin exemptions, and a non-interactive fallback root ${CLAUDE_PLUGIN_DATA}/topic-docs/<slug>/ keyed by topic slug, not project — an instance of the same gap, not a rule that closes it.
  • docs/conventions/topic-docs/CHANGELOG.md — the same tier language.

The nearest governing text is docs/MIGRATION-PLAYBOOK.md:256-258, [SPEC]-marked:

"${CLAUDE_PLUGIN_DATA} for machine state only. [SPEC] … Never a channel for consumer
configuration…"

That governs what may live there. It says nothing about naming, keying, retention, or overwrite.

The hazard is already written out, twice, inside one plugin — and applied to some of its skills and
not others.
Both verified verbatim at HEAD:

  • plugins/claude-config/skills/unhobble/SKILL.md:53-62 — "${CLAUDE_PLUGIN_DATA} is machine-global, so two checkouts sharing a basename…"
  • plugins/claude-config/skills/audit-pass/reference/run-state-and-resumability.md:10-20 — "machine-global, not per-project, so state keyed by working directory would collide or fragment", with <state-key> = <repo-identity>/<worktree-discriminator> specified in full.

audit-pass keys correctly. #2250 has since moved audit-prompting-postures onto the same scheme.
audit-instructions and claude-memory:audit still write a fixed filename. Four writers, one plugin
family, two doctrines — that inconsistency is what a convention exists to remove.

Upstream is silent, not permissive. 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 -."

No project segment in the formula, and reports/history are not among the documented intended uses
("installed dependencies such as node_modules … generated code, and caches"). Nothing to wait for.

Two further docs facts the convention should encode:

"The data directory is deleted automatically when you uninstall the plugin from the last scope where
it is installed. … The CLI deletes by default; pass --keep-data to preserve it."

— uninstall-fragility argues against unbounded per-project report trees and for the machine-health
pattern: one file per run plus an appended history line, with a stable "read the last audit" path.
See plugins/machine-health/skills/audit/SKILL.md:73-75, verified verbatim ("one file per run, so a
same-day rerun does not overwrite the earlier report"; state/history.jsonl, "the trend source of
truth"). That pattern closes data loss but not a read-back path — a skill that serves its stored
report back needs project identity, not merely non-destructive history. The convention has to say both.

RKD-06 — the "looks scoped but isn't" case

plugins/bug-report/skills/write/SKILL.md:97 at HEAD:

2. Otherwise, write to `${CLAUDE_PLUGIN_DATA}/bug-reports/<project-slug>/`, where `<project-slug>` is the kebab-cased basename of the project root (`${CLAUDE_PROJECT_DIR}`, or the git toplevel when unset). The plugin data directory is per-plugin, not per-project — without the slug, Step 2's duplicate scan would match another repository's report on the same symbol.

The line already states the hazard and already names the reason — it just picks a colliding key. Two
same-named checkouts (a fork, a same-named worktree) share one slug directory and the duplicate scan
then cross-matches between them. It escapes overwrite only because its filenames are timestamped.
plugins/bug-report/skills/setup/SKILL.md:29,32 restates the same key.

The originating item filed this explicitly as "context, not a defect to fix." Honor that. It belongs
in the convention as the worked example of a key that looks scoped and is not, next to the
unhobble:55 sentence that names the same insufficiency. Filing it as a bug-report plugin defect
would misrepresent the source.

Where a new convention has to register

docs/PLUGIN-PHILOSOPHY.md:468-489 enumerates all eighteen current conventions by link and is the
registration surface — and the collision magnet for any other item adding a convention.
docs/MIGRATION-PLAYBOOK.md:227-230 names the authoring template (docs/conventions/hook-telemetry/)
and the versioned-contract requirement; scripts/check-changelog-parity.sh exists, so check whether a
convention CHANGELOG.md is gated before authoring.

Adjacent, deliberately not duplicated

Provenance

Severity: MED · Provenance: SELF_REPORTED. The convention-absence sweep came from a fresh-context discovery:explore pass, but the originating item's own instruction — run /plugin-quality:audit against the named components before filing — was never carried out. The absence, the eighteen-entry listing, the greps, and the doc quotes were independently re-verified against HEAD by the validation pass.
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-04, RKD-06
Verified against repo HEAD 9b34a82a.

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