feat(skills): distribute the skills as Claude Code plugins, document ClawHub, and give Copilot/Codex the Router - #289
Conversation
Adds .claude-plugin/marketplace.json so the two published skills install with '/plugin marketplace add nevermined-io/docs' plus one '/plugin install' each, and auto-update thereafter — replacing a six-line sparse-checkout incantation. The marketplace is named 'nevermined', so users see and type '@nevermined' rather than the repository name. Entries use source './skills' with an explicit per-plugin skills array (the shape Anthropic's own amd-skills entry uses): 'source: "./"' also works but copies the entire 33 MB docs repo into the plugin cache for a 64 KB skill. Verified with 'claude plugin validate', then by adding the marketplace and installing both plugins locally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…files (#2595) .github/copilot-instructions.md and AGENTS.md are single files with no per-skill segment, so until now they carried the payments half only — an assistant reading either could help you charge for an agent but not buy from one. Each now gains a Nevermined Router section: the buy loop, the two money traps (requestId is an idempotency key not a counter; targetUrl is a complete URL not a base), whole-cent rounding, and the full BCK.ROUTER.* guardrail table with 'never widen a Delegation to get past a refusal'. Condensed rather than complete — these files are always-on context, so every line competes with the payments content. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ent ClawHub (#2595) The install page told Claude Code users to sparse-checkout git, and never mentioned ClawHub at all — despite a CI job publishing both skills there on every push to skills/**. The registry we pay to publish to was invisible to readers. - Claude Code tab now leads with the plugin; the file copy stays as the pin-a-version fallback. - New ClawHub tab. Notes that --dir is a global flag (it must precede the subcommand) and that the payments skill's slug is 'nevermined', not 'nevermined-payments'. - 'Supported Tools at a Glance' no longer says Router is unavailable on Copilot and Codex, and gains a ClawHub row. - CLAUDE.md records both distribution channels and why source must stay './skills'. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
0010 landed in nvm-monorepo on 2026-08-07 (fdf057ffd, the #2191 fee accrual) and is documented nowhere — not in the skill, not on the docs site, not in any IDE rule file. Adding a code table to two more files without it would ship a new artifact already known to be incomplete. It is the most retry-dangerous code in the set and the reason is not obvious: it is a 500, so an agent applying 'retry 5xx' — or reading our own 'only 0006 (500) and 0007 (429) are retryable' too loosely — will retry it. But a payment credential was already minted before it failed, and because no payment record was written the requestId will NOT suppress the retry. So each attempt mints a fresh credential and fails identically. The pre-existing surfaces (SKILL.md, references/errors.md, guardrails.mdx and the four IDE rule files) are fixed separately, off main — they don't depend on this branch. The generated codes.mdx is already covered by #287. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… sections Carrying across a finding from the sibling PR #291's review: 'Unlike 0006' was an apples-to-oranges comparison. BCK.ROUTER.0006 has exactly one throw site (router.service.ts:928, a read-only summary method), so it is never raised by a payment — meaning on the paying path 0007 is the only retryable code at all. Fixed in #291's three surfaces; these two carried the same wording and would otherwise have disagreed with them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two panel agents reviewed this; between them one blocker, four mediums and seven lows. All verified by execution or against nvm-monorepo origin/main before folding — several of my own 'verified' claims did not survive. BLOCKER - `npx clawhub update` as documented simply fails: 'Error: Provide <slug> or --all'. I had tested `install` and assumed `update`. Now `update --all`, verified against a real install, plus a note that the lock is per working directory so a `--dir` install must be updated from that same directory. MEDIUM - The requestId bullet said a same-id retry 'returns the original payment', which reads as the retry SUCCEEDING. It returns 409 BCK.ROUTER.0002 with the original paymentId and no resource (router.service.ts:156-166, no status filter). The hazard is the recovery path: an agent hits an unexpected 409 and the obvious escape is a fresh id — the exact double-spend the bullet exists to prevent. Now says so, and says never to answer that 409 with a new id. - 'every guardrail' / 'every BCK.ROUTER.* code' were false in both directions. The condensed files omit 0004/0005, the refuses-outright set and the relay limits. Scoped honestly. - 'Use this if you'd rather pin a version' — the command below it clones --depth 1 off main and the repo has zero tags. Now 'take a snapshot'. LOW - CLAUDE.md implied `claude plugin validate` guards the source/skills invariant. Mutation-tested: it passes source './', a removed strict, AND a skills path pointing at a directory that does not exist. Now says what it actually checks and that the invariant is on the reviewer, with the round-trip test that does catch it. Also explains what strict: false is for. - $schema pointed at anthropic.com/claude-code/marketplace.schema.json, which 404s (it is what Anthropic's own marketplace uses). Switched to schemastore, which resolves and knows these fields. - Named both update commands: marketplace update refreshes the catalogue, plugin update updates the installed plugin. - allowedRecipients is optional and omitting it means ANY merchant — worth one clause in a file teaching an agent to mint its own budget. - /plugin blocks were in a bash fence; they are REPL commands, not shell. - ClawHub's registry version for the payments skill does not match its own SKILL.md header; the tab now says which to trust. - 'Every supported tool can now get either skill' overclaimed for Copilot, which gets a condensed section, not the skill. - Page frontmatter and lead-in still described payments only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carried back from #289's review, where the condensed copies inherited this wording from SKILL.md:195. 'Retrying with the same id returns the original payment' reads as the retry SUCCEEDING. It does not: router.service.ts:156-166 looks up (userId, requestId) with no status filter and throws 409 BCK.ROUTER.0002 carrying the original paymentId. The merchant's response is never re-delivered, and when a mode-B paid hop fails the record is marked Failed while the id stays consumed — so that purchase can never complete under that id. The hazard is the recovery path, not the sentence. An agent told the retry returns its payment retries, gets an unexpected 409, and the obvious escape from a 409 is a fresh id — which is precisely the double-spend the idempotency key exists to prevent. The 0002 row says 'No' to retrying but never said 'and don't mint a new id either'. Fixed at the source (SKILL.md) and in the two other surfaces that carried the same summary (references/paying.md, products/router/guardrails.mdx); the two condensed files are fixed in #289. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
r-marques
left a comment
There was a problem hiding this comment.
⚠️ The PR head moved during this review (fda4b5269e03→52fc1f6f9ae1). Findings were produced againstfda4b5269e03; inline anchors were re-validated against52fc1f6f9ae1, so double-check the placement of any comment that looks off.
🤖 Automated PR review — ✅ Ready to merge (after folding 1 blocker, 4 mediums, 7 lows)
Two-agent panel — code-reviewer and comment-analyzer — on a PR that is almost entirely prose but instructs autonomous agents to spend real money. Between them they found one blocking issue and eleven others, several of which broke claims I had made about my own verification. All are folded in as of 52fc1f6; nothing is outstanding.
What I reviewed
- Diff range
origin/main...HEAD— 5 files, +263/−29 at review time (fda4b52). - Panel:
pr-review-toolkit:code-reviewer,pr-review-toolkit:comment-analyzer. The security-sensitive classification would normally addsilent-failure-hunter/pr-test-analyzer/type-design-analyzer, but there is no executable code here beyond a JSON manifest, so those had nothing in scope. - Every Router fact re-checked against nvm-monorepo
origin/main— not a local checkout, which is 1234 commits behind and contains noBCK.ROUTER.0007–0010at all. - The two condensed sections were diffed against each other mechanically: zero divergence in endpoints, field names, error codes or retry verdicts.
🔴 Blocker — found by execution, not reading
npx clawhub update does not work as documented. Running it against a real install returns Error: Provide <slug> or --all. I had tested clawhub install and assumed update. Now npx clawhub update --all, verified end to end, plus a note that the lock file is per working directory — so a --dir ~/.claude/skills install must be updated from that same directory with the same flag.
🟡 Mediums
- The
requestIdbullet described a same-id retry as returning "the original payment" — which reads as the retry succeeding. It returns409 BCK.ROUTER.0002with the originalpaymentIdand no resource (router.service.ts:156-166, no status filter). The hazard is the recovery path: an agent hits an unexpected 409, and the obvious escape from a 409 is a fresh id — exactly the double-spend the key exists to prevent. Now stated, with "never answer that 409 with a fresh id". Fixed at its source inSKILL.mdtoo, via #291. - "every guardrail" / "every
BCK.ROUTER.*code" were false in both directions. The condensed files omit0004/0005, the entire refuses-outright set (splits, internal targets, redirects, forgedX-Router-*) and the relay limits. Scoped honestly. - "pin a version" was contradicted by its own command — a
--depth 1 --sparseclone offmain, in a repo with zero git tags. Now "take a snapshot you control". 0010appears only in this PR's two condensed files, inverting the documented "condensed ⊂ full" relationship. This is an artefact of the PR split, not a defect in either PR — #291 adds it to the skill, the four IDE rule files andguardrails.mdx. Merge #291 first, or accept a brief window where the fallback is richer than the artifact this page calls complete. Called out in the PR description.
💡 Lows
CLAUDE.mdimpliedclaude plugin validateguards thesource/skillsinvariant. It does not — mutation-tested: it passessource: "./", a removedstrict, and askillsentry pointing at a directory that doesn't exist. Now says what it actually checks, that the invariant is on the reviewer, and gives the round-trip test that does catch it. Also documents whatstrict: falseis for, since the previous text explainedsourceandskillsbut not the third field a maintainer would delete.$schemapointed at a URL that 404s (anthropic.com/claude-code/marketplace.schema.json— the one Anthropic's own marketplace uses). Switched to schemastore, which resolves and knows these fields.- Two different update commands —
marketplace updaterefreshes the catalogue,plugin updateupdates the installed plugin. Both named. allowedRecipientsis optional, and omitting it means any merchant the Router can reach. Worth one clause in a file that teaches an agent to mint its own budget./pluginblocks were in abashfence — they're Claude Code REPL commands and fail if pasted into a shell.- ClawHub's registry version for the payments skill doesn't match its own
SKILL.mdheader, and this PR is what makesinspectuser-facing. The tab now says which to trust. - "Every supported tool can now get either skill" overclaimed for Copilot, which gets a condensed section, not the skill. Plus the page frontmatter and lead-in still described payments only.
Strengths the panel called out
- The two always-on files do not diverge — the hard part of this change, and it's clean.
- Every checkable Router fact matches
origin/main, including0010's mechanism, near-verbatim from the source hint rather than paraphrased. - Guardrail framing survives intact into both surfaces: nothing anywhere suggests widening a Delegation or re-minting one as a recovery.
- Omitting
versionfrom the manifest is correct, not an oversight — version resolution falls through to the source commit SHA, which is what makes the "refreshes in the background" claim true. - The "payments only" sweep is complete; no stale assertion survives anywhere in the repo.
Verdict: ✅ Ready to merge — 0 outstanding blockers, 0 should-fix, 0 nits. Merge after #291.
… source rationale Second round of panel findings, both verified before folding. - The Guardrails section still closed with an unqualified 'Only 0006 and 0007 are retryable', sitting directly under a table of PAY-path errors. Both throw sites traced on origin/main: 0006 comes only from router.service.ts:928 (the payments-summary aggregation) and 0007 only from router-concurrency.service.ts:31. So on the paying path 0007 is the only retryable code and every other 500 is a stop — which is the opposite of what an agent writing a retry loop around /route would have taken from that sentence. The 0010 paragraph already said this; the summarising sentence undid it. - CLAUDE.md justified 'source: ./skills' by download size, and that rationale is wrong. Verified against the installed anthropics/claude-plugins-official: '/plugin marketplace add' materializes a FULL-content snapshot of the repo's default branch into ~/.claude/plugins/marketplaces/<name>/ — every top-level dir, no .git — regardless of the source field. The repo lands on disk either way. What source actually controls is what each installed PLUGIN copies and loads on top of that. Left as-is, a maintainer would add the marketplace, see ~32 MiB arrive anyway, conclude the trick does nothing, and simplify it back to './' — reaching the right conclusion from the wrong claim. Rewritten to argue from plugin content instead. Sizes corrected while there: 'du -sh' reports block usage, ~19% high on a tree of small files. skills/ is 203,675 bytes apparent (~200 KB, not 236 KB) and the repo is 33,619,640 bytes (~32 MiB), of which video/ and images/ are ~94%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Second review round — two more findings folded ( 1. The retry rule was scoped wrong for the paying path. The Guardrails section closed with an unqualified "Only 2. What Sizes corrected while there: One reported finding I checked and am not acting on: a claim that the Windsurf figures in #291 were inverted. They aren't — I published a base→head pair in each unit, and characters sit below bytes in both states ( |
Keeps these in step with #291, which documents 0011 across the canonical skill, guardrails.mdx and the four IDE rule files after aaitor caught it missing. Also carries across the reframing his review prompted: the point is not that 0010 is 'the exception to retry the 5xx' but that the HTTP status does not tell you whether to retry at all — 0010 is a 500 you must not retry, 0011 a 402 that reads like an ordinary payment error and equally must not be. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aaitor
left a comment
There was a problem hiding this comment.
APPROVE. A carefully-built, low-risk docs change — and your own two-round self-review already folded a blocker + 11 findings, so I focused on independently re-verifying the money-path facts against nvm-monorepo origin/main rather than re-listing what's fixed. Every BCK.ROUTER.* status, retry flag, and the 0010/0011 mechanisms in the two always-on files are faithful to source, and the two files don't diverge. The one structural risk you flagged — the condensed fallback carrying 0010/0011 + the corrected paying-path retry rule while the canonical errors.md didn't — is resolved: #291 is now MERGED on main with all three, and #289 ∩ #291 = ∅ files, so the merged result is consistent. No HIGH/MEDIUM. Two LOWs + one out-of-scope flag below.
Verification I ran
- Read all 5 changed files in post-PR state on
docs/skill-install-plugin-marketplace; CI green (2/2);marketplace.jsonparses;source: "./skills"+ per-pluginskills+strict: falseare as intended. - Cross-checked every Router fact against
origin/main(libs/helpers/src/lib/output/api-errors.ts+ throw sites):0001–0009statuses/retryable flags match the condensed table exactly;0010(500,retryable:false, credential minted before the failure sorequestIdwon't suppress a retry) and0011(402,retryable:false, 3-DS) match near-verbatim;0006's only throw site isrouter.service.ts:1615(summary aggregation) and0007's isrouter-concurrency.service.ts:31— so "on the paying path0007is the only retryable code" is correct. - Merge order: #291 is merged to
main(adds0010/0011toerrors.md/SKILL.md/guardrails.mdx/4 IDE rule files, and scopes the retry sentence to the paying path). The two PRs share zero files → clean, consistent merge. - The two always-on files carry identical Router facts (endpoints, field names, codes, retry verdicts); the only differences are cosmetic (bullets vs prose, one paragraph break).
- Links:
/products/router/overviewpresent indocs.json;clawhub.ai→ 200; all 10raw.githubusercontent.com/.../main/...URLs on the install page resolve 200 (the docs#267 trap). JSX balanced (Tabs 1/1, Tab 9/9, Note 3/3, Card 8/8); the/pluginblock uses atextfence. - Env naming: only
sandbox/livein the published surfaces; the lonestaging_*string is CLAUDE.md's own policy rule, not a leak. - Over-engineering pass (inline): lean.
marketplace.jsonis minimal; the CLAUDE.mdsource/strictwarnings are load-bearing (they stop a maintainer re-simplifying to"./"), not bloat. Nothing to cut.
Out of scope — for the next Router-skill PR, not this one
0009is framed purely as a crypto-rail condition ("wallet short on the target network" / stale cached address) in the condensed table and the "Fund the buyer wallet" step. On the card rail (MPP-stripe)0009is instead a card-issuer decline with no wallet to top up (see the0009hint onorigin/main). This is inherited verbatim from the canonicalerrors.md, so condensing it as-is here is correct — but the canonical reference itself under-documents the card-rail meaning of0009. Worth a follow-up onerrors.md/guardrails.mdx, next to where0011now lives.- Per your own workflow (and
CLAUDE.md's "sync withmainfirst"), sync this branch withmainbefore merge so the Mintlify preview reflects the reconciled0010/0011state — not required for correctness, since #289 and #291 don't overlap.
aaitor's LOW. The zero-divergence between .github/copilot-instructions.md and AGENTS.md is the load-bearing property of this change, and nothing in-file preserved it — the next editor could touch one and forget the other. An HTML comment atop each Router section names its twin explicitly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Picks up #291 (BCK.ROUTER.0010 + 0011 across the canonical skill, guardrails and the four IDE rule files). The two PRs share zero files, so this is a clean sync — taken so the Mintlify preview reflects the reconciled 0010/0011 state before merge, as the review asked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…k the deploy
The keep-in-sync marker from the review broke Mintlify Deployment:
Failed to parse page content at path AGENTS.md: Unexpected character `!`
(U+0021) before name ... (note: to create a comment in MDX, use {/* text */})
AGENTS.md IS in Mintlify's content set and is parsed as MDX, so an HTML comment
is a syntax error there. The review's premise that 'HTML comments are invisible
to the tools that consume these files' holds for Copilot and Codex but not for
Mintlify, which publishes this one.
.github/copilot-instructions.md is NOT in that set — it appears in no docs.json
route and there is no .mintignore — so its HTML comment is fine and stays. That
is why the two files now differ on this single line despite being kept in sync
on everything else.
Recorded in CLAUDE.md so the next person adding a marker here doesn't rediscover
it through a red deploy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes the publishing half of nvm-monorepo#2595. Part of epic #2268.
Why
The
nevermined-routerskill has been published since #267 — it's on GitHub, and on ClawHub atnevermined-io/nevermined-router(v0.1.1, 172 downloads). So #2595 was never blocked on where to publish. It was blocked on two things that made the skill hard to actually get:And one thing that made it incomplete: Copilot and Codex carried the payments half only, which the issue title (
Claude / Cursor / Copilot) explicitly promises.What changed
A Claude Code plugin marketplace (
.claude-plugin/marketplace.json):Two commands, and Claude Code keeps them current. The marketplace is named
nevermined, so that's what users see and type —nevermined-io/docsis only the source it's served from.Router guidance in the two single-file surfaces.
.github/copilot-instructions.mdandAGENTS.mdeach gain a condensed Router section: the buy loop, the two money traps, and the full guardrail table. Condensed on purpose — these files are always-on context, so every line competes with the payments content.The install page now leads with the plugin, gains a ClawHub tab, and its "Supported Tools at a Glance" table no longer says the Router is unavailable on Copilot and Codex.
Two findings worth keeping
source: "./"copies the entire repo. The documented same-repo shape installs 33 MB into the plugin cache for a 64 KB skill — and twice over if you install both. Entries therefore usesource: "./skills"with an explicit per-pluginskillsarray, matching the shape Anthropic's ownamd-skillsentry uses. 33 MB → 236 KB. Recorded inCLAUDE.mdso nobody "simplifies" it back.--diris a globalclawhubflag, so it has to precede the subcommand.clawhub install nevermined-router --dir ~/.claude/skillssilently ignores it; the docs show the working form.Verification
claude plugin validatepasses.SKILL.mdplus all fivereferences/land correctly, footprint 236 KB.raw.githubusercontent.comURL on the install page maps to a real, non-gitignored file (the docs#267 trap).skills/nevermined-router/SKILL.mdand itsreferences/— including the three I re-verified by hand: the four requireddelegation/createfields,Issuedsemantics, and the/router/proxyheaders.mintlify broken-linkscannot run on any PR right now. A bare<token>placeholder in the generateddevelopment-guide/api-errors/codes.mdxaborts the parse for the entire site before it checks a single page. It's pre-existing onmainand untouched here; the source fix is nvm-monorepo#2805, still open. I verified this PR's links directly instead: one internal link (/products/router/overview, present indocs.json) andclawhub.ai(200).Follow-ups (not in this PR)
Merge #291 first. This PR's two condensed files document
BCK.ROUTER.0010; #291 adds it to the canonical skill, the four IDE rule files andguardrails.mdx. Landing this one alone leaves a window where the condensed fallback is more complete than the artifact this page calls "the complete Router skill" — which inverts the relationshipCLAUDE.mdmandates and the contractSKILL.mdstates about itself. Both PRs branch offmainand don't conflict, so the order is the only constraint.#290 is stacked on this one and goes last.