Skip to content

feat(docs-hygiene): enforce /plugin:skill encapsulation across the repo and widen audit-encapsulation #6638

Description

@kyle-sexton

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: .md 53, .sh 17, .json 8, .py 2, .tsv 1, .fixture 1. 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.com URLs into /skills/: 43 lines in 39 files, mostly a shared link to source-control's worktree gather-block.md in skill bodies.
    • github.com tree or blob URLs into /skills/: 11 lines in 3 files (9 user-interface route 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 the routing.json detects (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 configure lines 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, .mjs and .ts are never read.
  • A bare plugins/<p>/skills/<s>/SKILL.md with no anchor: 38. Legal by design (detect.sh:104).
  • A skill-root cite with no trailing subdirectory: 35, including all 9 routing.json tree URLs. PATTERN needs /<sub>/, SKILL.md# or *.schema.json after the skill name (detect.sh:114).
  • No marketplace-id check of any kind.

Proposed scope

  1. Widen the detector (plugins/docs-hygiene/skills/audit-encapsulation/):
    • Flag skill-root and bare-SKILL.md cites 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 outside plugins/ (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.
  2. Sweep the 216 lines. For each one, use the remedy docs/plugin-philosophy.md names: 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 link gather-block.md). Bump each touched plugin per its release path (.changes/ fragment or plugin.json plus CHANGELOG.md, docs/migration-playbook.md).
  3. 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 04f0367355 this prints 216.

  • 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 04f0367355 it prints only the 9 routing.json detects 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 9 routing.json own-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 .py file, a cross-plugin scripts/ 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:check passes on audit-encapsulation, and ci-status is green on each PR.

Out of scope

Related

🤖 Generated with Claude Code

Activity

  1. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 8, 2026
  2. github-actions commented on Oct 8, 2026

    @github-actions
    Contributor

    Thanks for the report. Automated triage handed this issue to a maintainer.

    Automated triage. A maintainer reviews it.

  3. added
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 8, 2026
  4. removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 12, 2026
  5. kyle-sexton commented on Oct 13, 2026

    @kyle-sexton
    ContributorAuthor

    Decision recorded. The owner approved applying independently verified recommendations on 2026-10-12.

    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:

    • The 39 gather-block.md files include 4 inside source-control itself; 35 are cross-plugin and need the remedy.

    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.

  6. kyle-sexton commented on Oct 13, 2026

    @kyle-sexton
    ContributorAuthor

    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.

  7. added
    work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    and removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    on Oct 13, 2026
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

    work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions