Problem
docs-hygiene:audit-progressive-disclosure (0.23.1) proposes treatments without checking whether the target file is owned upstream or whether a recorded decision set its tier. A session following the skill can offer an edit that the next sync reverts.
Observed in the melodic-software/.github hygiene sweep (melodic-software/.github#153): the audit flagged .claude/rules/pr-body-contract.md as tier-mismatch (always-loaded, only relevant at PR time) and the session offered "move it out of .claude/rules/". The file is sync-managed from melodic-software/standards (distribution/sync-manifest.yml), and standards ADR-0008 made it always-loaded on purpose. The consumer repo says the file is synced in README.md:66 and in the .claude/ai-slop.json _comment, both outside the audited targets (CLAUDE.md .claude/). The synced file itself carries no marker. The correct disposition was an upstream issue (melodic-software/standards#641).
Evidence
SKILL.md:51 (tier-mismatch row) and hard rules SKILL.md:98-114: no ownership or decision check before a treatment. "Skip surfaces" (SKILL.md:110) excludes only vendor/.
context/tier-model.md:30-33: the "must apply broadly, in every session" test has no exemption for a recorded reason to stay always-loaded.
scripts/detect.sh emits no ownership fact.
- Secondary (low):
SKILL.md:101-102 says Tier 3 carries no treatment, and the tier-mismatch row allows only T1/T2, but nothing in the output schema enforces it; the session emitted a T3 with a treatment.
- No shared ownership check exists to reuse. Partial, skill-local precedents:
audit-noise/SKILL.md:141 (vendored-verbatim baseline, dismissed rather than redirected), audit-derivability/context/rubric.md:179 (check git log for a recorded decision), extract-ssot/context/lessons.md Lesson 14 (generator-owned regions). compress, audit-derivability, and extract-ssot share the exposure (inferred, not run).
claude plugin validate and skill-quality:check both pass; this is a behavioral gap, not a static one.
Proposed fix (cheapest first)
- SKILL.md hard rule next to "Skip surfaces": before any treatment, grep the repo for the target path near
synced|sync-managed|vendored|generated|upstream, and check docs/adr/ / docs/decisions/. On a hit, keep the finding and set its treatment to "file with the owner, citing the decision". Bash(grep:*) is already allowed.
context/tier-model.md "Boundaries": a recorded reason to stay always-loaded (for example, sessions without the plugin must see it) satisfies the every-session test.
- Eval case in
evals/evals.json: an always-loaded synced rule expects an "upstream" disposition and no local edit.
- Lift the rule into a plugin-root
context/upstream-ownership.md and point compress, audit-derivability, extract-ssot, and audit-noise at it, as SKILL.md:82 already does for clean-tree-fallback.md.
- (Larger) a consumer-declared ownership key that
detect.sh reports.
Add a Gotchas line: the ownership statement usually lives outside the audited targets (a README inventory, a tool config comment).
Acceptance
- Re-running the skill on
melodic-software/.github with targets CLAUDE.md .claude/ routes the pr-body-contract.md finding to the owner instead of proposing a local move.
- A Tier 3 row never carries a treatment.
Audit packet: plugin-quality evidence run 20260928T150734Z (local to the auditing machine).
Problem
docs-hygiene:audit-progressive-disclosure(0.23.1) proposes treatments without checking whether the target file is owned upstream or whether a recorded decision set its tier. A session following the skill can offer an edit that the next sync reverts.Observed in the
melodic-software/.githubhygiene sweep (melodic-software/.github#153): the audit flagged.claude/rules/pr-body-contract.mdastier-mismatch(always-loaded, only relevant at PR time) and the session offered "move it out of.claude/rules/". The file is sync-managed frommelodic-software/standards(distribution/sync-manifest.yml), and standards ADR-0008 made it always-loaded on purpose. The consumer repo says the file is synced inREADME.md:66and in the.claude/ai-slop.json_comment, both outside the audited targets (CLAUDE.md .claude/). The synced file itself carries no marker. The correct disposition was an upstream issue (melodic-software/standards#641).Evidence
SKILL.md:51(tier-mismatchrow) and hard rulesSKILL.md:98-114: no ownership or decision check before a treatment. "Skip surfaces" (SKILL.md:110) excludes onlyvendor/.context/tier-model.md:30-33: the "must apply broadly, in every session" test has no exemption for a recorded reason to stay always-loaded.scripts/detect.shemits no ownership fact.SKILL.md:101-102says Tier 3 carries no treatment, and thetier-mismatchrow allows only T1/T2, but nothing in the output schema enforces it; the session emitted a T3 with a treatment.audit-noise/SKILL.md:141(vendored-verbatim baseline, dismissed rather than redirected),audit-derivability/context/rubric.md:179(checkgit logfor a recorded decision),extract-ssot/context/lessons.mdLesson 14 (generator-owned regions).compress,audit-derivability, andextract-ssotshare the exposure (inferred, not run).claude plugin validateandskill-quality:checkboth pass; this is a behavioral gap, not a static one.Proposed fix (cheapest first)
synced|sync-managed|vendored|generated|upstream, and checkdocs/adr//docs/decisions/. On a hit, keep the finding and set its treatment to "file with the owner, citing the decision".Bash(grep:*)is already allowed.context/tier-model.md"Boundaries": a recorded reason to stay always-loaded (for example, sessions without the plugin must see it) satisfies the every-session test.evals/evals.json: an always-loaded synced rule expects an "upstream" disposition and no local edit.context/upstream-ownership.mdand pointcompress,audit-derivability,extract-ssot, andaudit-noiseat it, asSKILL.md:82already does forclean-tree-fallback.md.detect.shreports.Add a Gotchas line: the ownership statement usually lives outside the audited targets (a README inventory, a tool config comment).
Acceptance
melodic-software/.githubwith targetsCLAUDE.md .claude/routes thepr-body-contract.mdfinding to the owner instead of proposing a local move.Audit packet: plugin-quality evidence run
20260928T150734Z(local to the auditing machine).