Repository navigation
feat(docs-hygiene): enforce /plugin:skill encapsulation across the repo and widen audit-encapsulation #6638
Description
Problem
A plugin's content should name a skill in another plugin only by its /plugin:skill invocation, never by a path or URL into plugins/<p>/skills/<s>/. docs/plugin-philosophy.md ("Configuration ownership and scope") already bans the path form. #6483 extends the rule: it amends the philosophy so a URL into another plugin's skill folder is no longer a sanctioned citation (published docs outside skills/ stay linkable), and it sets the route-data form (id = /plugin:skill, our own marketplace id only in install recipes). #6483 is the convention source for this issue and fixes only user-interface's routing.json.
The rest of the repo does not follow the rule yet, and /docs-hygiene:audit-encapsulation cannot see most of the gap.
Counts on main at 04f0367355 (excluding CHANGELOG.md, marketplace.json and .changes/):
- Path or URL into another plugin's skills: 216 lines (346 occurrences) in 82 files. By type:
.md53,.sh17,.json8,.py2,.tsv1,.fixture1. 20 of the files are tests, evals or fixtures. The plugins with the most files are docs-hygiene 10, planning 7, harness-ops 7, harness-config 7, testing 6 and discovery 6.raw.githubusercontent.comURLs into/skills/: 43 lines in 39 files, mostly a shared link tosource-control's worktreegather-block.mdin skill bodies.github.comtree or blob URLs into/skills/: 11 lines in 3 files (9user-interfaceroute pointers, fixed by docs: add a routing-as-data convention for plugins that route to installed tools #6483;plugins/discovery/reference/parent-contract.md:318;plugins/playbooks/reference/model-adaptation/sonnet-5-5.md:81).
- Our own marketplace id (
@melodic-software): 84 lines in plugin content. 39 are install recipes (sanctioned). Of the rest, 9 are therouting.jsondetects (docs: add a routing-as-data convention for plugins that route to installed tools #6483); the others are a plugin's own id in its tests, config keys, README/plugin configurelines or npm package scope. None of them is a cross-plugin skill reference.
/docs-hygiene:audit-encapsulation coverage. Its detector reports 79 of the 216 lines and misses 137. Missed shapes, counted by occurrence on missed lines:
scripts/cites: 114. They are carved out on purpose as the "entry surface" (detect.sh:116-131). The convention has no such carve-out for a caller in another plugin.- Cites in file types it never scans: 80. The scan covers only
*.md *.sh *.json *.yml *.yaml(detect.sh:221-224), so.py,.tsv,.fixture,.mjsand.tsare never read. - A bare
plugins/<p>/skills/<s>/SKILL.mdwith no anchor: 38. Legal by design (detect.sh:104). - A skill-root cite with no trailing subdirectory: 35, including all 9
routing.jsontree URLs. PATTERN needs/<sub>/,SKILL.md#or*.schema.jsonafter the skill name (detect.sh:114). - No marketplace-id check of any kind.
Proposed scope
- Widen the detector (
plugins/docs-hygiene/skills/audit-encapsulation/):- Flag skill-root and bare-
SKILL.mdcites when the citing file belongs to a different plugin. A cite inside the same plugin stays legal (${CLAUDE_PLUGIN_ROOT}/skills/<other-skill>/...,docs/plugin-philosophy.md). - Scan every tracked text file type, not only
md sh json yml yaml. - Keep the
scripts/carve-out only for callers outsideplugins/(CI workflows, repo hooks), not for another plugin. - Add a check for our own marketplace id outside install-recipe lines.
- Update the skill's public-surface contract and its tests to match.
- Flag skill-root and bare-
- Sweep the 216 lines. For each one, use the remedy
docs/plugin-philosophy.mdnames: cite the skill by/plugin:skill, or promote the shared content to a convention doc both plugins can cite (the likely fix for the 39 files that linkgather-block.md). Bump each touched plugin per its release path (.changes/fragment orplugin.jsonplusCHANGELOG.md,docs/migration-playbook.md). - Fixtures. A test, eval or fixture that exists to exercise a detector may keep a cross-plugin path. List each one in the detector's allowlist with its reason, not as a silent exception.
Split into several PRs by plugin group if the diff is too large for one review (docs/conventions/pr-scope-budget/).
Acceptance criteria
Run from the repository root.
-
The cross-plugin path and URL scan prints
0, or only lines in files on the detector's documented allowlist:git grep -nE 'plugins/[A-Za-z0-9_.-]+/skills/' -- plugins/ ':!**/CHANGELOG.md' ':!**/.changes/**' ':!**/marketplace.json' \ | awk -F: '{split($1,a,"/"); own=a[2]; line=$0; sub(/^[^:]*:[^:]*:/,"",line); while (match(line, /plugins\/[A-Za-z0-9_.-]+\/skills\//)) { p=substr(line,RSTART+8,RLENGTH-16); if (p!=own) {print; break}; line=substr(line,RSTART+RLENGTH) } }' \ | wc -l
On
04f0367355this prints216. -
No URL into a skill folder remains in plugin content:
git grep -nE '(github\.com/melodic-software/claude-code-plugins/(tree|blob)|raw\.githubusercontent\.com/melodic-software/claude-code-plugins)/[^ )"]*/skills/' -- plugins/ ':!**/CHANGELOG.md' ':!**/.changes/**'
prints nothing (54 lines on
04f0367355). -
Our own marketplace id appears in skill, agent and reference content only in install recipes:
git grep -nE '@melodic-software\b' -- 'plugins/*/skills/**' 'plugins/*/agents/**' 'plugins/*/reference/**' ':!**/CHANGELOG.md' \ | grep -vE '(/plugin|claude plugin) install|marketplace add' | grep -vE '/(tests?|evals|fixtures)/|\.test\.|test_'
prints nothing (on
04f0367355it prints only the 9routing.jsondetects that docs: add a routing-as-data convention for plugins that route to installed tools #6483 fixes). -
Detector coverage: checked out at
04f0367355,bash plugins/docs-hygiene/skills/audit-encapsulation/scripts/detect.sh --apply-filters(with the widened detector copied in) reports all 216 lines the first command lists, plus the 9routing.jsonown-marketplace detects. After the sweep it reports none outside the allowlist. -
The detector's tests cover each newly caught shape (skill-root cite, bare
SKILL.md, a.pyfile, a cross-pluginscripts/cite, an own-marketplace id) with a case that fails before the change, and one stay-quiet case each for a same-plugin cite and an install recipe (docs/conventions/hook-precision/README.md). -
/skill-quality:checkpasses onaudit-encapsulation, andci-statusis green on each PR.
Out of scope
user-interface'srouting.jsonand the convention amendments themselves (docs: add a routing-as-data convention for plugins that route to installed tools #6483).- Arguments in prose invocations. They stay allowed: the philosophy lists an explicit invocation argument as a public interface, and docs: add a routing-as-data convention for plugins that route to installed tools #6483's "no args" rule covers route-row ids only.
- Third-party
name@marketplaceids, which stay qualified. - A plugin's own marketplace id in its own tests, config keys or package scope; these are not cross-plugin references.
Related
- docs: add a routing-as-data convention for plugins that route to installed tools #6483 (convention source: routing-as-data, plugin-philosophy and seam-phrasing amendments)
- feat: add the user-experience plugin for user research and strategy (/user-experience:shape) #6306 (
user-experience)
🤖 Generated with Claude Code
Activity
github-actions commentedon Oct 8, 2026
Thanks for the report. Automated triage handed this issue to a maintainer.
Automated triage. A maintainer reviews it.
kyle-sexton commentedon Oct 13, 2026
Decision recorded. The owner approved applying independently verified recommendations on 2026-10-12.
Basis: docs/plugin-philosophy.md:470-474; feat(docs-hygiene): enforce /plugin:skill encapsulation across the repo and widen audit-encapsulation #6638 Proposed scope item 2
Wants audit-encapsulation widened to catch every cross-plugin path or URL into skills/ and a sweep of the offending lines. Nothing has shipped; the count is now 219 lines (216 when filed).
Verification notes:
Verified by an independent fresh-context check (confirmed, risk low): gh issue view 6638 (acceptance commands); acceptance command 1 run against origin/main: 219; git grep gather-block.md origin/main: 39 files, 35 outside source-control; docs/plugin-philosophy.md:468-478; plugins/docs-hygiene/skills/audit-encapsulation/scripts/detect.sh:100-132; gh pr list search 6638: only #6640 (merged, routing-as-data)
Admission to an agent lane stays with the work-class rules and owner ratification.
kyle-sexton commentedon Oct 13, 2026
Owner batch-ratified this admission in an attended session on 2026-10-12: work-class: scoped. Rule: class C1-C3, no trust surface, the independent check confirmed the recorded decision, and no owner step remains.