docs(router): add the agent-autonomy walkthrough page - #280
Conversation
The written version of the #2596 demo: an agent given only an API key and a cap discovers services from the public catalog, buys across two payment protocols on two mainnets, reports one ledger, and is then refused by its own spend cap. Every number, hash and error on the page comes from one real run against live merchants (Base mainnet 8453 + Tempo mainnet 4217, $0.08 total), with both payments reconciled against the chains. Also documents two things that are easy to get wrong and are not stated elsewhere: - `?search=` matches titles/descriptions/categories, not slugs, and `limit` is capped server-side at 20 — so the by-slug route is the way to fetch one service. - On the MPP rail `settlement.network` is the bare name "tempo" with no chain id, so it does not distinguish mainnet 4217 from Moderato 42431. The asset address does, and both candidates start 0x20c0. Registered in the Nevermined Router nav group after "ledger". 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. |
eruizgar91
left a comment
There was a problem hiding this comment.
Reviewed the new page against the rest of the Router section and against the run it documents.
Checked and clean: both internal links resolve, the frontmatter matches the sibling pages, the docs.json entry is well-formed and correctly placed after ledger, the MDX components are properly closed with the blank lines Mintlify needs, there is no bare angle-bracket text that would trip the same parse error #281 is fixing, and the cent arithmetic is self-consistent end to end (9 → 7 → 2 → 1 → refused at 7, including the rounding-up detour that makes CoinGecko 7¢ rather than 6¢).
Three inline notes, all MEDIUM or LOW: an unstated live-vs-sandbox requirement, a merge-order dependency on nvm-monorepo#2794, and a duplication of the Tempo asset table that #279 added to rails-mpp.mdx earlier today.
Not re-raising the #2793 x402 outage — you flagged it yourself and the decision to hold the merge is yours. Nothing on the page needs to change for it.
Verdict: APPROVE.
|
|
||
| | | | | ||
| | --- | --- | | ||
| | **An API key** | Authenticates it to Nevermined. Never sent to any merchant. | |
There was a problem hiding this comment.
MEDIUM — the page never says which deployment $NVM_API_URL has to point at, and here that is load-bearing rather than incidental.
Every merchant in this walkthrough is mainnet (Base 8453, Tempo 4217), but quickstart presents https://api.sandbox.nevermined.app and https://api.live.nevermined.app side by side, and sandbox is the natural first pick for someone about to try a demo. #279 — merged into this same section today — documents why that combination cannot work:
A sandbox deployment funds testnets only and a live deployment funds mainnet only — that split is the real-money firewall and an operator cannot widen it. If you call a mainnet service from sandbox, no entry survives the filter and you get
400 BCK.ROUTER.0001.
So a reader who exported the sandbox URL fails at step 3 with a code this page never mentions — and step 6 has just taught them that the expected refusal looks like 402 BCK.ROUTER.0003, which makes the 400 read as something else entirely.
Worth one line in this grant table (or the intro) stating that the run requires a live deployment, linking the x402 rail for the firewall itself.
There was a problem hiding this comment.
Agreed, and the sequencing you point out is the real sting — a reader meets 400 BCK.ROUTER.0001 immediately after step 6 has taught them that the expected refusal is 402 BCK.ROUTER.0003, so the 400 reads as "something else broke" rather than "wrong deployment".
Added a <Warning> in the grant section that states the live requirement, names the 400 explicitly, and contrasts it with the 402 so the two are not confused. Links the x402 rail for the firewall itself, since that is where #279 documents it.
| Both reconcile exactly against the chains: Base **−0.010000 USDC**, Tempo **−0.060029 USDC.e** (the 0.060001 payment plus 28 units of gas, which Tempo bills in the stablecoin itself). | ||
|
|
||
| <Note> | ||
| On the MPP rail, `network` is a bare name — `"tempo"` — with no chain id. It does not distinguish Tempo mainnet (4217) from the Moderato testnet (42431). If you need to verify *which* chain a payment settled on, check the `asset` address: USDC.e on mainnet is `0x20c0…b9537d11c60e8b50`, while Moderato's PathUSD is `0x20c0…0000`. Both begin `0x20c0` — compare the whole string, don't eyeball it. |
There was a problem hiding this comment.
LOW — as of today this duplicates rails-mpp.mdx rather than adding to it. #279 added the same USDC.e vs PathUSD table there, carrying the same "both begin 0x20c0" warning.
Two copies of a pair of near-identical addresses is precisely the content most likely to drift apart. Suggest linking the MPP rail for the addresses and keeping only what is unique to this page — that settlement.network comes back as a bare "tempo" with no chain id, which is what makes the asset the sole discriminator in the first place.
There was a problem hiding this comment.
Confirmed — rails-mpp.mdx lines 51–56 carry the same address pair and the same "both begin 0x20c0" warning, added by #279 the same day. You are right that a near-identical address pair kept in two places is the content most likely to drift.
Rewritten to link the MPP rail for the addresses, keeping only what is unique here: that settlement.network comes back as a bare "tempo" with no chain id, which is precisely why the asset is the sole discriminator. That framing is better than what I had — the addresses were incidental, the missing chain id was the point.
(I checked the headings rather than guessing the anchor: the table sits under The asset allowlist, so #the-asset-allowlist.)
|
|
||
| ## Reproducing it | ||
|
|
||
| The full runnable version — a preflight that checks every precondition, plus the six-step run — ships in the `nvm-monorepo` repository at `.claude/skills/nvm-router-demo/`: |
There was a problem hiding this comment.
MEDIUM — this path does not exist yet, and will not be reachable for most readers once it does.
.claude/skills/nvm-router-demo/ is not on nvm-monorepo origin/main; it ships in nevermined-io/nvm-monorepo#2794, which is still open. Merging this page first leaves the entire "Reproducing it" section pointing at nothing. The script names do match (demo.sh, preflight.sh), so this is purely ordering — worth gating the merge on #2794.
Separately, and independent of ordering: nvm-monorepo is a private repository, so on a public docs site this section is unactionable for anyone outside the org. Either mark it explicitly as internal, or give external readers a path that works — the six curl calls above are already the whole run, so saying so would cost nothing.
There was a problem hiding this comment.
Both halves are right, and the second is the one I should have caught myself: nvm-monorepo is private, this site is public (verified: isPrivate=true vs isPrivate=false). So that section was unactionable for every external reader regardless of merge order.
Taking your suggestion — the six curl calls really are the whole run, so the section now leads with that. What the preflight actually buys is kept as prose everyone can act on:
- read the wallet address back from the live Delegation, never from a saved value (
402 BCK.ROUTER.0009does not name the address it checked); - fund both chains — separate balances;
- confirm the merchant still answers
402, else it is relayed withpaid: falseand a run "succeeds" having bought nothing; - check the catalog is loaded, not serving placeholder rows.
The script is now a clearly-marked internal aside. That also dissolves the ordering dependency — the page no longer references anything that must land first, so #2794 and this can merge in either order.
Three notes from @eruizgar91, all verified before acting. MEDIUM — the page never said which deployment $NVM_API_URL must point at, and sandbox is the natural first pick. Every merchant here is mainnet, so a sandbox key fails at step 3 with 400 BCK.ROUTER.0001 — a code the page never mentioned, right after step 6 taught the reader that the EXPECTED refusal is 402 BCK.ROUTER.0003. Adds a Warning stating the live requirement, naming the 400 and explicitly distinguishing it from the 402, linking the x402 rail for the firewall. LOW — the settlement Note duplicated the USDC.e vs PathUSD addresses and the "both begin 0x20c0" warning that #279 added to rails-mpp.mdx earlier the same day. Two copies of a near-identical address pair is exactly what drifts, so it now links there and keeps only what is unique to this page: that settlement.network is a bare "tempo" with no chain id, which is what makes the asset the sole discriminator. MEDIUM — "Reproducing it" pointed at .claude/skills/nvm-router-demo/, which is not on nvm-monorepo main (it ships in #2794, still open) and — the sharper half — nvm-monorepo is PRIVATE while this site is PUBLIC, so the section was unactionable for every external reader. Rewritten to lead with what is true for everyone: the six curl calls on the page ARE the whole run. The preflight's value is kept as prose (read the wallet back from the live Delegation, fund both chains, confirm the merchant still 402s, check the catalog is loaded) rather than as a script only staff can execute. The script is now a clearly-marked internal aside, so the page no longer depends on #2794 and the merge order is free. Verified: mintlify broken-links reports no broken links, including the two new internal links. The rails-mpp anchor is #the-asset-allowlist — I checked the headings rather than guessing.
Closes the
agent-autonomy.mdxdeliverable of nevermined-io/nvm-monorepo#2596 (epic #2268).Adds
products/router/agent-autonomy.mdx— the written walkthrough of the Router autonomy demo — and registers it in the Nevermined Router nav group afterledger.Every page before it describes a piece of the Router. This one runs the whole thing start to finish and shows the actual output, so a reader can see what an autonomous agent's spending really looks like before giving one a budget.
Everything on the page is real
Every number, hash and error came from one execution against live merchants on Base mainnet and Tempo mainnet. Total cost $0.08.
0xd25f25cc162f347e2e0374841a1270b61f7dff53d5c6af0fbcacdab2caaf5bb80x44136dc5348e5442770ad1ebeb9cb4e641225f8d4abdcf9e4b2f82f33a054214The structure follows the run: discover → budget → buy over MPP → buy over x402 → report → refused. The refusal is given the weight it deserves — it's the property that makes an agent something you can leave running, and it's what most "agent pays for things" demos leave out.
Two things documented here for the first time
?search=matches titles, descriptions and categories — not slugs, andlimitis capped server-side at 20. So neither searching for a slug nor paginating the whole catalog is a correct way to fetch one known service; the by-slug route is.settlement.networkis the bare name"tempo", with no chain id, so it does not distinguish Tempo mainnet (4217) from Moderato (42431). Theassetaddress does — USDC.e0x20c0…b9537d11c60e8b50vs PathUSD0x20c0…0000. Both begin0x20c0, so the page says explicitly not to eyeball them.There's also a
<Warning>about mode-B protocol selection: because the Router picks the rail, a merchant advertising both is paid over MPP — so a "green" x402 verification can be exercising the other rail entirely, with the one-liner to check.Verification
mintlify broken-links→ no broken links found, with the new page in place and both its internal links (/products/router/guardrails,/products/router/ledger) resolving.development-guide/api-errors/codes.mdx(two bare<token>tags that MDX reads as unclosed JSX). It is onmaintoday and aborts the link check for the whole repo before any page is examined. Not touched in this PR — filed separately as nevermined-io/nvm-monorepo#2795.codes.mdxis byte-identical tomainhere.docs.jsonwas edited as text, so the diff is +1 line rather than a whole-file reformat.🤖 Generated with Claude Code
Note on #2793
I briefly flagged this PR as blocked because the x402 rail looked broken. It is not — that was a ~40-minute window of merchant-side flakiness, and fresh payments settle cleanly (8 settled / 4 failed on the day, failures clustered in that window). The page's content and hashes are accurate and it is fine to merge.
#2793 remains open and worth fixing, but it is about the ledger not reconciling when a merchant misbehaves — it does not contradict anything on this page.
Review addressed (
81ed29a)Three notes from @eruizgar91, all verified before acting:
400 BCK.ROUTER.0001— right after step 6 has taught the reader that the expected refusal is402 BCK.ROUTER.0003. Added a<Warning>stating the live requirement, naming the400and explicitly contrasting it with the402, linking the x402 rail for the firewall.rails-mpp.mdx:51–56carries the same pair and the same "both begin0x20c0" warning from docs(router): document the funding path, and correct the stale x402 funded-networks default #279. Now links there and keeps only what is unique here — thatsettlement.networkis a bare"tempo"with no chain id, which is why the asset is the discriminator. (Anchor verified against the headings:#the-asset-allowlist.)nvm-monorepois private and this site is public, so that section was unactionable for external readers regardless of ordering. Rewritten to lead with the sixcurlcalls, which are the whole run, with the preflight's value kept as prose anyone can act on. The script is now a marked internal aside.The merge-order dependency is gone — the page no longer references anything that must land first, so this and nvm-monorepo#2794 can merge in either order.
mintlify broken-links: no broken links, including both new internal links.