v2.0.0 — a plan may not name a skill only its author has
An audit plan names its skills by string, and until now nothing asked whether anyone
else could load them. On the machine that wrote the plan every name resolves — so
/audit:doctor printed a green row while a teammate's clone loaded none of it, and
every task ran without the conventions it was written to follow.
Measured on a real repository: six named skills, five of them from ~/.claude/skills
(in no repository and no marketplace), and four live tasks in the phase due to run next.
What is new
Every discovered skill, subagent and MCP server now carries a verdict — would a clone
of this repository load this? — and the basis behind it:
- a capability under
.claude/travels; - one in a home directory never does;
- one from a plugin travels only when the committed
.claude/settings.jsondeclares
it in bothextraKnownMarketplacesandenabledPlugins. When exactly one is
present, the message names the one that is missing — committing onlyenabledPlugins
is a documented trap that keeps working for whoever added it.
Four surfaces read that one verdict:
| surface | what it does |
|---|---|
/audit:doctor |
a plan portability row — a warning at most, naming the skill, where the plan names it, and why |
/audit:status --gate --fail-on stranded-skills |
the CI half, deliberately out of the default gate |
| the control panel | lists what stays here, keeps it out of the pickers, predicts the refusal inline, and refuses the write |
| the report | marks a stranded name from the repository alone, so a shared artifact reads the same on every machine |
Read this before upgrading
portability ships "strict", and that changes what an untouched repository accepts.
The panel will refuse a save it took the day before, whenever a name in the patch resolves
on your machine and would not survive a clone. This is the one clause COMPATIBILITY.md
promises against — broken on purpose, recorded there with the reason, because the defect it
prevents is silent on exactly the machine that causes it.
Two ways out, each one row in the Settings tab or in .claude/audit.config.json:
{ "portability": "warn" } // diagnose everything, refuse nothing
{ "portability": "off" } // restore the previous behaviour exactlyOther things worth knowing:
- a name discovery has never seen is still accepted, at every tier — an inventory is not
a whitelist; stranded-skillsfails when the scan could not run, rather than passing quietly;_panel_discovery._mcp_namesis now_mcp_entriesand returns rows, not strings;check_plan_skillstakes the config pair, mirroringcheck_policy.
Also fixed
- Two docstrings asserted a layer their module had already left — now a lint
(layer_doc_drift), proved red in both directions. - The policy tab rendered an MCP server's name in the
sourcecolumn. - A regression case that could not fail: it compared a level to
"finding"while the
collector writes"FINDING".
Full detail in CHANGELOG.md.