melodic-software

Candidates: merge the extraction, the repo tree, and the seeded pairs.

"Find where this repo's skills and agents duplicate a native Claude Code surface. Read-only unless `apply` bakes an approved reference. Enumerating what is invocable: /claude-ops:inventory. Use when: 'does this skill duplicate a built-in', 'what does Claude Code already ship for this', 'audit native overlap', 'is our install-state audit the same as /doctor', 'refresh the native-surfaces registry', 'bake the native reference into this skill', 'which of our skills overlap bundled skills'."

melodic-software 28 5 Updated 3w ago

Resources

3
GitHub

Install

npx skillscat add melodic-software/claude-code-plugins/plugins-claude-ops-skills-audit-native-overlap

Install via the SkillsCat registry.

SKILL.md

Purpose

Answers one question: which of this repository's components now overlap something Claude Code
ships itself, and what should each one do about it?

Claude Code's own surface moves every week. A skill written when no bundled equivalent existed can
wake up duplicating one, and nothing in the product says so. Plugin skills are namespaced, so a
native surface never shadows ours and the collision is silent. The model then picks between two
overlapping capabilities from descriptions alone.

This skill makes the overlap visible, records a human verdict per pair in a committed store, and, only when asked, bakes the resulting routing guidance into the components themselves.

Three things it is not: it is not an availability oracle (nothing here asserts a native surface is
present in anyone's session), it is not a verdict engine (every verdict is a human's), and it is
not a fleet editor (bare invocation mutates nothing at all).

Scope boundary

Question Owner
Which of our components overlap a native surface, and what should they say about it? this skill
What can this machine actually invoke, and where did each thing come from? /claude-ops:inventory
Is this machine's install directory healthy? /claude-ops:audit-install-state
Is the plugin fleet current, and at what scope? /claude-ops:plugins audit
What changed in the last CLI release? /claude-ops:changelog
Do our MCP tools duplicate each other or the built-ins? /mcp-tools:audit
Is this SKILL.md structurally sound, and is the listing over budget? /skill-quality:check
Is each installed skill actually VISIBLE to the model right now, and why not? /claude-ops:audit-skill-visibility

The line that matters most: inventory enumerates what resolves; this skill compares that
surface against what we ship and produces a routing verdict. Inventory never judges; this skill
never enumerates for its own sake.

The two substrates, named

Detection has exactly two inputs, and they are not interchangeable.

Native side, an inventory JSON. Produced by the sibling extractor in this same plugin:

python3 "${CLAUDE_PLUGIN_ROOT}/skills/inventory/scripts/inventory.py" \
  --binary-only --out ./claude-inventory.json

The consumer asserts schema == 1 and presence-checks every key it reads. builtin_commands,
bundled_skills, plugin_backed, integrity. A missing key is reported as broken
consumer-side, not worked around: the extractor's integrity block guards extraction-level drift,
not the emitter's own top-level key names, so a renamed key would otherwise read as an empty
surface.

Target side. This skill's own repo-tree scan. plugins/*/skills/*/SKILL.md and
plugins/*/agents/*.md frontmatter, in the repository being audited. The extractor scans
installed trees, which are not necessarily the audited repo's. Using it for the target side
would silently audit the wrong fleet.

Seeded candidate pairs live beside this skill in reference/canonical-pairs.json. They are
candidates, never verdicts: a pair there proposes a row for a human to rule on.

Run it

The engine is scripts/overlap.py. Python 3.11+, standard library only, three subcommands:

# Candidates: merge the extraction, the repo tree, and the seeded pairs.
python3 "${CLAUDE_PLUGIN_ROOT}/skills/audit-native-overlap/scripts/overlap.py" detect \
  --inventory ./claude-inventory.json --out ./overlap-candidates.json

# Registry: render the store into the generated view (--check diffs instead).
python3 "${CLAUDE_PLUGIN_ROOT}/skills/audit-native-overlap/scripts/overlap.py" generate

# Freshness: the deterministic gate.
python3 "${CLAUDE_PLUGIN_ROOT}/skills/audit-native-overlap/scripts/overlap.py" self-check

--repo, --store, --view, and --pairs are flags with repo-relative defaults, so a consumer
repository with a different layout points them wherever its files live. detect also takes
--threshold (lowest discovery score kept, default 0.30) and --top-k (most components kept per
native surface, default 3; 0 turns discovery off). Lower the threshold to 0.25 for a
recall-first sweep; below that, most added pairs share one incidental word.

Every subcommand exits 0 ok, 1 broken, 3 degraded, the sibling extractor's contract, not the
shell gates' 0/1/2, so one lane can carry both; 2 stays argparse's usage error. A degraded exit
is a passing run: it means the data is stale-but-honest or something was not locally decidable, and
the run says which. The suite is scripts/test_overlap.py, wrapped by scripts/overlap.test.sh
for the repo's test discovery.

Detection posture. Floor-honest

Under-recall stated honestly beats confident completeness. Candidates come from two origins:

  • Seeded: every pair in reference/canonical-pairs.json, emitted whether or not the extraction
    shows the native side.
  • Discovered: every native surface in the extraction, internal ones excepted, scored against
    every skill and agent in the repo from name, alias, and description tokens
    (scripts/discover.py states the formula). A pair at or over --threshold, among the --top-k
    best for its surface, is emitted with its score and the shared tokens as evidence. A pair the
    store already records is listed under discovery.existing with its verdict instead; a pair that
    is also seeded stays one seeded candidate carrying the score.

Discovery is lexical. A one-line native description rarely shares words with the component it
duplicates in concept (recap against session-flow:orient), so such a pair belongs in the seeds
once a human confirms it; a pair discovery already finds needs no seed.

Every candidate carries native.invocable_by (model+user, user-only, model-only, or
unknown), read from the registration's model_invocable and user_invocable fields, or from
disable_model_invocation in an older extraction; a field the extraction lacks makes it unknown.
model_invocable: false also sets the model-invocation-disabled marker the store's suggest-only
rule reads. From it comes recommended_integration: suggest for a user-only surface, route or
route-or-wrap for a model-invocable one (route for a built-in command or a bundled workflow,
which never take wrap), and nothing when unknown. It is a label for the human writing the row, never a store value.

Three rules:

  • Carry the integrity floor through, per lane. The inventory reports integrity per lane
    (builtin_commands, bundled_skills, plugin_backed, and bundled_workflows when the
    extraction has that lane; an extraction without it is not an error, the lane is simply not
    reported). A degraded lane makes every count from
    that lane a floor, and the report says so in the same sentence as the number. A broken lane's
    counts are omitted, the report names the lane and its cause, and every candidate whose lane is
    broken is marked re_derivable: false (its presence or absence in that lane proves nothing
    this run); the other lanes' counts stand. Only when every lane is broken does the report omit
    every native-side count.
  • Never auto-verdict. Detection emits candidates with evidence. The verdict column is empty
    until a human fills it.
  • Accept human-added candidates. A pair nobody's heuristic found is a first-class row; add it
    to the store directly, or to the seeded pairs file when it generalizes.

Report structure (bare invocation)

# Native overlap — <repo>, <date>

## Detection integrity
Inventory status per lane (ok | degraded | broken), cli_version vs validated_against, and what
that means for every count below; a broken lane is named with its cause.

## Overlap candidates
One row per (native surface, our component): origin (seeded | discovered), native name +
provenance class + hidden/gated markers, invocable_by, our component, score and shared tokens,
recommended integration (a label, not a verdict), the evidence, and the store's current verdict,
or NEW where the store has no row yet. Discovered pairs the store already records follow as one
line each with their verdict.

## Registry state
Rows whose recheck trigger has fired, rows missing a baked line, rows baked but unverified.

## Budget exposure
What baking would cost the shared skill-listing budget, and which rows are already exposed
to name-only degradation.

## Provenance
Which substrate produced which section, and anything the run could not resolve.

Provenance classes are never merged into one list. A bundled skill, a bundled workflow, a built-in
command, a plugin-backed built-in, and a session-provided skill have different disable switches and different
rosters per host; a merged list cannot be acted on.

Budget exposure, a presence-gated seam

Baked phrases live in frontmatter descriptions, which are the routing-effective surface:
descriptions load into model context by default, while bodies load only on invocation. That surface
is budgeted twice over, the combined description + when_to_use text truncates at 1,536
characters per entry by default, and the listing as a whole is capped at a share of the context
window (1% by default), on overflow keeping every skill name and dropping whole descriptions
lowest-score-first. The score is decay-weighted and the walk is first-fit, so raw invocation count
is NOT the exposure ranking and description length matters too; see
`audit-skill-visibility/reference/listing-scorer.md`.

So the report says what the baking would cost. When the skill-quality plugin is installed, invoke
/skill-quality:check listing-budget over the repo's plugin skill roots and fold its aggregate
estimate into the Budget exposure section. When that plugin is not installed, state "budget
exposure unavailable. skill-quality not installed" and continue; never reach into another
plugin's files by path to fake the number.

A fleet already over budget does not make baking pointless. It makes a baked phrase the best
available surface rather than a guaranteed one. Rows in that state carry the store's
budget_caveat flag so no later reader mistakes a baked phrase for a guarantee the model saw it.

Verdicts and the human gate

Five values, no blanket preference rule:

Verdict Means
prefer-native The native surface does this job at least as well; ours should route to it
prefer-ours Ours is materially better for this job. A reason is required, not optional
complementary Different jobs that look alike; both keep their lane and each names the other
superseded The native surface fully absorbed ours; ours is a retirement candidate
defer Undetermined. Gated, experimental, or unverifiable from this session's evidence

A run may recommend a verdict with its reasoning; it never records one. The recommendation goes
into the report labeled as a recommendation, and a human writes the store row. No component file
is touched before a verdict exists in the store.

Session-provided (cloud) surfaces are observation-only: they are absent from any binary extraction,
so their rows carry a live-roster observation, a defer verdict, and no baked line, until an
in-session capture protocol exists, a cloud row's evidence is one environment's roster on one day.

The store, the view, and the self-check

Three artifacts, one direction of flow:

  1. The store, a committed, hand-editable JSON file (default docs/native-surfaces/records.json
    in this repository; configurable). It is the SSOT. Every row carries: the native surface with its
    provenance class and markers (hidden, gated, model-invocation-disabled), our component, the
    verdict and its reason, integration (route, wrap, or suggest), evidence, a class-tagged
    observation record, a recheck trigger with its verified date, baked flags (description_phrase,
    boundary_section, native_step, suggest_sentence), and the budget caveat.
  2. The generated view. docs/native-surfaces.md, rendered from the store between HTML
    markers, per provenance lane. Never hand-edited; a --check mode regenerates and diffs.
  3. The self-check, a deterministic script over what is locally decidable: store parses and
    declares its schema, every row carries a trigger, records are well-formed including their
    observation class tags, the view matches the store, every baked line traces back to a store row,
    every non-defer extraction row has a Boundary section naming its surface, and the store's
    recorded CLI version still matches what the environment reports.

Observation records name their evidence class. Extraction-evidence reads "extracted from
binary v<X> on <date>"; live-roster observation reads "observed in <env> session
on <date>". Neither is ever flattened into "this surface is available". See
docs/conventions/native-references/README.md for why any static availability claim is wrong
somewhere by construction.

A recheck trigger names an observable event. "A Claude Code release adds, removes, or renames a
bundled skill in this row's lane" qualifies; a bare date does not. That bar is the upstream-drift
convention's, and this skill's self-check enforces trigger presence only. Deciding whether an
event actually fired is a session act performed by the report, because an offline gate cannot
re-fetch an upstream basis.

The apply step

Two baked surfaces exist, and they are gated differently because they cost differently.

The Boundary section lands with the row. A store row whose verdict is not defer and whose
observation is extraction-evidence is written together with a ## Boundary section in the
component's body, in the same change: the surfaces by provenance class, the routing split, and
the mutation gate, with the four-part detail (basis, as-of, recheck trigger, evidence) in a
reference file inside the same skill that the section links. A body loads only on invocation, so
the section spends no listing budget and moves no routing; it is what makes the verdict real for
the model, and a row without it fails the self-check. Boundary-only baking may cover several
plugins in one change.

The section names its surface. The preferred shape puts the name in the heading, as in
## Boundary, the bundled ... skill with the surface name in backticks. Where one section covers
a component that overlaps several surfaces, a generic ## Boundary heading passes as long as the
section text names each of them. The name is a code span either way, so a surface whose name is
also an ordinary English word is never satisfied by prose that happens to use the word, and a
section written for some other surface never passes for this row.

The description phrase is the gated apply. apply edits one plugin at a time. Preconditions,
all required: the store has a row for the pair; the row's verdict is not defer; the row's
observation class is extraction-evidence (session-provided rows are never baked); the row's
integration is not route when a Native step or suggest sentence is to be written; the row
does not carry model-invocation-disabled when a Native step is to be written; and the user
asked for this plugin by name. It emits one clause, front-loaded, carrying the presence gate
("when the bundled <name> skill resolves in this session, prefer it for …; this skill for
…"), the provenance class, and the routing split, and, when integration is wrap or
suggest, the Native step section or the suggest sentence. Before writing, description plus
when_to_use after baking must fit the per-entry cap the native-references convention records,
measured by /skill-quality:check <skill> (when skill-quality is not installed, state "description
length unmeasured. skill-quality not installed" and do not bake); a row that would exceed
the cap is not baked. Every wrapped or suggesting skill declares unattended in its
argument-hint. The
emitted text cites nothing outside its own plugin, a shipped plugin has no copy of this
repository's registry, so a citation would be a broken reference at install time.

Then set the row's baked flags and re-run the self-check. Parity is direction-sensitive:
every baked line must trace to a store row, and a claimed Boundary section must name that row's
surface; a row without its Boundary section breaks the self-check; a row without a description
phrase is legal pending-sweep state.

Agents are registry-rows-only. An agent's role prompt loads after dispatch, so a routing line there
reaches the model too late to change the routing; the actionable line belongs at the dispatching
skill's surface.

The sweep execution contract

Applying across the fleet is a sweep, and a sweep is executed as a sequence of closed units. Never
as one fleet-wide edit.

One plugin is one unit. For each unit, in order:

  1. Apply. Bake the description phrases and Boundary sections for that plugin's rows only.
  2. Verify, the overlap self-check passes; /skill-quality:check passes for every touched
    skill; the plugin version takes its bump; the plugin's CHANGELOG carries the entry.
  3. PR. Open one PR for that unit, with the affected store rows quoted in the body so a
    reviewer gates the routing change on the same evidence the verdict rested on.
  4. Close, the unit is closed only when its PR merges green. A merged-but-red or an open PR
    leaves the unit open, and the next unit does not start.

Two units are never in flight at once: description edits are routing-affecting, and a half-applied
fleet is a fleet whose routing nobody can reason about. Whether to run the sweep at all is a
separate human go/no-go, not something a run decides for itself.

Running in a foreign repository

This skill ships to consumers, so it never assumes this marketplace's layout. Store and view paths
are arguments with repo-relative defaults; the target-side scan walks whatever plugin tree it finds.

Where the conventions tree, the store, or the CI gates are absent, the skill degrades to
report-only
: it detects and reports overlaps, states that no store was found at the resolved
path, and makes the apply machinery unavailable rather than erroring or writing a store into a
repository that never asked for one. Report-only is a working outcome, not a failure.

CI wiring is a property of a repository, not of this skill: a consumer wires the self-check into
whatever gate they run, or runs it by hand.

Verifying an upstream claim

Any claim about what Claude Code itself ships must come from the raw markdown endpoint. curl -sSL
https://code.claude.com/docs/en/skills.md to a file, then read the file. A summarizing fetch
returns a small model's answer about the page, so absence from that answer is not evidence of
absence. A 200 is also not proof you got the page you asked for: retired slugs are silently
aliased, so confirm the slug against https://code.claude.com/docs/llms.txt and read the body's
own first heading before quoting it.

Two upstream facts this skill depends on, each with the trigger that obliges re-deriving it:

Claim Basis Recheck trigger Verified
Descriptions load into context by default, truncated at 1,536 chars per entry, listing capped at 1% of the context window, with name-only degradation on overflow. The docs state that degradation goes least-invoked-first; the shipped binary instead ranks by a decay-weighted score and grants first-fit, so use the mechanism recorded in `audit-skill-visibility/reference/listing-scorer.md`, not the documented order docs/en/skills.md (Frontmatter reference; Troubleshooting), docs/en/settings-reference.md, plus the binary for the order Either default moves, or the binary's scorer or grant loop diverges from that reference 2026-09-01
Native availability varies on settings/env, plan, platform/provider, and host surface, so no static availability claim holds docs/en/settings-reference.md, docs/en/env-vars.md, docs/en/commands.md, docs/en/cloud-environments.md A release or docs change adds, removes, or renames a gating axis 2026-08-23

Gotchas

  • A plugin skill never shadows a native one. Ours are namespaced, so both resolve and the model
    chooses. That is why the routing lives in descriptions rather than in a name.
  • plugin_backed is its own lane. security-review is reported there, not under
    builtin_commands. Read the wrong key and the row looks absent. Verified 2026-09-29 against
    Claude Code 2.1.284, by reading the plugin_backed key of an inventory.py --binary-only
    extraction on this machine, which holds security-review and nothing else. Recheck when the
    extractor's provenance lanes change or a release note moves a bundled surface between them.
  • A bundled skill can carry aliases. code-review answers to review; treating an alias as a
    separate surface produces a duplicate row for one capability. Basis:
    https://code.claude.com/docs/en/commands gives /code-review the line "Alias: /review".
    Verified 2026-09-29 against Claude Code 2.1.284 (the extraction lists review as the alias) and
    that page as fetched that day. Recheck when
    the commands page drops the alias line or a release note renames a bundled skill.
  • Absent from the binary is not absent from the product. Session-provided skills exist only in
    a live roster. "Not in the extraction" is a statement about the extraction.
  • A verdict is not permanent. The trigger is the load-bearing part of the row; a date alone
    tells a later reader nothing about whether the verdict still holds.
  • Baked text is self-contained by contract. A phrase that says "see the native-surfaces
    registry" ships a broken reference to every consumer who installed the plugin.