Skip to content

docs(router): document BCK.ROUTER.0012 and 0013 - #304

Merged
r-marques merged 2 commits into
mainfrom
docs/router-eip712-domain-code-0012
Aug 18, 2026
Merged

docs(router): document BCK.ROUTER.0012 and 0013#304
r-marques merged 2 commits into
mainfrom
docs/router-eip712-domain-code-0012

Conversation

@r-marques

@r-marques r-marques commented Aug 17, 2026

Copy link
Copy Markdown
Member

Documents the two EIP-712 signing-domain refusals introduced by nevermined-io/nvm-monorepo#2959 (merged as 95a006f66, closing nevermined-io/nvm-monorepo#2934).

Both blockers are gone. #2959 is merged, and #305 (this PR's old base) landed as 9f7d0e86f. This is rebased onto main and no longer stacked.

What #2959 does

The Router used to build the x402 EIP-712 signing domain's name/version from the seller's advertised accepts[].extra. It now sources them from its own canonical per-token table and holds the seller's advertised pair to it byte for byte.

Why two codes, not one

The same check fails for opposite owners, and the first draft of this PR had a single code for both:

code status owner retry
BCK.ROUTER.0012 400 validation the seller advertised a domain its own token does not sign under No — report it to them, pay elsewhere
BCK.ROUTER.0013 500 internal Nevermined's canonical token table has no domain for the selected token No — report it to Nevermined

One code meant our own table gap returned 400 validation with a hint saying "this is the seller's bug, pay a different seller" — an error message that sends an operator down the wrong path. 0013's hint says explicitly not to report it to the seller.

Neither is retryable: both fail deterministically until someone fixes the underlying table, and in both cases nothing was signed, charged or reserved.

Surfaces

All 10 surfaces that restate the codes — plus

⚠️ Correction (Kike's review of nvm#2969): an earlier revision of this line claimed the drift guard already held all of these. It did not. products/router/guardrails.mdx — a hand-maintained 13-row table on the public site — was in neither TABLE_SURFACES nor PROSE_SURFACES, and sync-errors-docs.yml only generates development-guide/api-errors/codes.mdx, so nothing covered it. nvm#2969 now adds it: 13 BCK.ROUTER codes across 3 table(s). Until that merges, this file's table is kept correct by review, not by CI.

Also updated here: the products/router/rails-x402.mdx section #305 introduces, which gains the byte-for-byte check and the 0012 reference here.

Skill bumped 0.1.2 → 0.1.3 with lastUpdated: 2026-08-17, per the convention #299 set.

⚠️ The Windsurf copy could not hold both codes

.windsurf/rules/nevermined-router.md is at Windsurf's 6,000-character cap, which truncates silently and drops the end of the file — where the guardrails live. Adding both codes took it to 6,469. Measured, not estimated:

attempt chars over
as drafted 6,469 +469
tersest two-code wording 6,366 +366
… + every pure example displaced 6,179 +179
… + rationale cut to "the seller's bug and ours respectively" 6,054 +54

Two codes do not fit at any wording. Rather than let it overflow, or cut a rule to make room — which CLAUDE.md forbids ("what it trades away is presentation, never a rule") — that one file omits 0012 by declaration.

Why 0012 and not 0013: 0013 is a 500, and a non-retryable 500 is the one shape an agent retries by default — the same reason that file already keeps 0010 while omitting 0007. 0012 is a 400 whose verdict the file still states as a rule ("only 0006 and 0007 are retryable").

Only the capped file diverges. Cursor, Cline, Amazon Q, Copilot, AGENTS.md and both table surfaces all carry both codes.

What was displaced to make room is presentation, never a rule: the category enum example, the spelled-out payment.fee column list, and the gloss on 0007 (itself a declared omission on that surface). The file is now 5,994 / 6,000 — 6 characters left, and CLAUDE.md's figure is updated to match.

The omission is recorded on development-guide/build-using-nvm-skill.mdx, which is the promise this makes to readers, and needs its matching checker change: https://github.com/nevermined-io/nvm-monorepo/pull/2969.

Merge order

Both directions now clear each other, so there is no strict order between this PR and nvm#2969 — each leaves one advisory drift until the other lands. Router codes vs published skill is not a required check.

Verification

  • Drift check against the #2959 branch: ✓ published skill matches api-errors.ts — 13 BCK.ROUTER codes across 2 table(s).
  • mintlify broken-links: no broken links found, whole site.

🤖 Generated with Claude Code

@mintlify

mintlify Bot commented Aug 17, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
Nevermined 🟢 Ready View Preview Aug 17, 2026, 9:08 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@r-marques
r-marques force-pushed the docs/router-eip712-domain-code-0012 branch from c96de0a to 916a14e Compare August 17, 2026 10:27
@r-marques
r-marques changed the base branch from main to docs/router-0006-and-symbol-source August 17, 2026 10:27
@r-marques r-marques changed the title docs(router): document BCK.ROUTER.0012, and correct how the asset symbol is resolved docs(router): document BCK.ROUTER.0012 and 0013 Aug 17, 2026
r-marques added a commit that referenced this pull request Aug 18, 2026
…ct how the asset symbol is resolved (#305)

* fix(router): qualify BCK.ROUTER.0006 in the windsurf rules, and correct how the asset symbol is resolved

Two independent corrections, both true against nvm-monorepo main today — deliberately
carrying no reference to any unreleased error code, so this can land on its own.

1. `.windsurf/rules/nevermined-router.md` wrote the retryable pair as bare `0006`/`0007`
   where its four sibling IDE rule files write `BCK.ROUTER.0006`. nvm-monorepo's
   `check-skill-error-drift` guard matches on the qualified form, so it has been reporting
   the code as undocumented — the ONLY drift between the published skill and `api-errors.ts`
   today, and it turns that check red on every nvm PR touching the error catalogue.

2. `products/router/rails-x402.mdx` said the asset symbol "is read from the entry's
   `extra.name` field". That is the pre-#2768 behaviour and has been false since
   nevermined-io/nvm-monorepo#2775: the symbol comes from the `asset` ADDRESS, matched
   against the canonical token addresses. The old text sends a reader debugging in exactly
   the wrong direction. Adds a short section on what `extra.name` actually is — the token's
   own EIP-712 domain, "USD Coin" on Base mainnet and "USDC" on Base Sepolia — since
   mistaking it for a ticker is what produced that bug.

Split out of #304, which documents two error codes that do not exist
until nvm-monorepo#2959 merges and therefore has to wait for it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: correct the windsurf headroom figure to 5,991 (review fold)

This PR's edit to `.windsurf/rules/nevermined-router.md` grew it by 11
characters, so the figure CLAUDE.md pins was wrong the moment it landed.

Re-measured with the exact command CLAUDE.md prescribes (characters, not
bytes — `wc -c` reads 6,063 here because the file is full of `—` and `⚠️`):
base 237a9b5 = 5,980, head = 5,991. The payments sibling is unchanged at
3,759, verified the same way.

The number is maintained precisely because the failure is silent: Windsurf
drops the END of the file, where the guardrails live. Nine characters of
headroom now remain, so the next edit to this file has to displace
something rather than absorb it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Base automatically changed from docs/router-0006-and-symbol-source to main August 18, 2026 11:37
r-marques and others added 2 commits August 18, 2026 13:40
The two EIP-712 signing-domain refusals introduced by nevermined-io/nvm-monorepo#2959.
Two codes rather than one because the same check fails for opposite owners: 0012 (400) is
the seller advertising a domain its own settlement token does not sign under; 0013 (500) is
a gap in Nevermined's canonical token table. Coding ours as the seller's told an agent to
report OUR defect to an innocent counterparty, and buried the only signal that the table
has drifted.

Added to all 10 surfaces the drift guard holds to api-errors.ts, plus the rails-x402 page.
Skill 0.1.2 -> 0.1.3.

⚠️ MERGE AFTER nvm-monorepo#2959. The drift guard is bidirectional — diffErrorTable
(scripts/lib/skill-error-table.ts:232) flags a code documented here that api-errors.ts does
not define. Landing this first produces 4 stale drifts on nvm main, and publishes a skill
telling autonomous buyers to branch on codes the API cannot return.

Stacked on #305, which carries the two corrections that are independent of #2959.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Documenting BCK.ROUTER.0012 and 0013 pushed
`.windsurf/rules/nevermined-router.md` to 6,469 characters — 469 over
Windsurf's 6,000 cap, which truncates silently and drops the END of the
file, exactly where the guardrails live. The overflow would have shipped
unnoticed: nothing errors, the rules just stop.

The file could not absorb both codes. Measured, not estimated: at the
tersest honest wording, and after displacing every pure example the file
still had, two codes came to 6,054 — still over. So one had to go.

`0012` is the one omitted, and only from Windsurf:

- It is a **400**, and the file already states the rule that decides it —
  "only 0006 and 0007 are retryable". Nothing is lost that the reader
  cannot act on.
- `0013` is a **500**, the one shape an agent retries by default. That is
  the same reason this copy keeps `0010` while omitting `0007`.
- Cursor, Cline, Amazon Q, Copilot, AGENTS.md and both table surfaces all
  carry BOTH codes. Only the capped file diverges.

Displaced to make room, all presentation rather than rules, per CLAUDE.md
("what it trades away is presentation, never a rule"): the `category`
enum example, the spelled-out `payment.fee` column list, and the gloss on
`0007` — itself a declared omission on this surface.

Also updates the headroom figure to 5,994 (6 characters left) and records
the omission on the install page, so the promise the page makes to
readers stays true.

Needs the matching nvm-monorepo change to PROSE_SURFACES before the drift
check accepts it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@r-marques
r-marques force-pushed the docs/router-eip712-domain-code-0012 branch from 916a14e to ee7dc7d Compare August 18, 2026 12:37
@r-marques
r-marques marked this pull request as ready for review August 18, 2026 13:05
@r-marques
r-marques requested a review from a team as a code owner August 18, 2026 13:05
@eruizgar91

Copy link
Copy Markdown
Member

pr-coord:reviewing — automated fallback review in progress (no human claim after grace window).

@eruizgar91 eruizgar91 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paired review with nevermined-io/nvm-monorepo#2969, which teaches the drift checker about the .windsurf omission this PR makes. Reviewed at ee7dc7d5a. No findings on this PR — everything below was checked and came back clean, and the four findings from the paired review all landed on #2969, where the code they concern lives.

What was verified, not read

  • The 6,000-character claim, measured. .windsurf/rules/nevermined-router.md at this head is 5,994 characters / 6,068 bytes. CLAUDE.md's updated figure is exact, and the byte/character gap (74) is why a wc -c check would have failed this file wrongly.
  • The paired change is clean end to end. check-skill-error-drift.ts from #2969, run against this branch: ✓ published skill matches api-errors.ts — 13 BCK.ROUTER codes across 2 table(s).
  • The claimed merge-order independence holds. #2969's checker against docs main reports 15 drifts; main's checker against this branch reports exactly one, [undocumented] BCK.ROUTER.0012 — .windsurf/rules/nevermined-router.md. Each PR really does clear the other, in either order.
  • 0012 really is absent and 0013 really is present in .windsurf, and both are present in .cursor (8,514 chars), .clinerules (8,439) and .amazonq (8,351) — none of which is anywhere near a cap, confirming the divergence is scoped to the one file that needed it.
  • Cross-surface consistency, machine-checked where possible. SKILL.md, references/errors.md, AGENTS.md, .github/copilot-instructions.md and products/router/guardrails.mdx all state 0012 = 400 non-retryable and 0013 = 500 non-retryable, matching api-errors.ts on status and retry verdict — verified by parsing each table with the drift checker rather than by reading. The three uncapped IDE rule files carry byte-identical prose for both codes. .windsurf's 0013 gloss is terser but states the same verdict, and the blanket "only 0006 and 0007 are retryable" rule is present and covers it.
  • No surface was missed. Every file in the repo mentioning BCK.ROUTER.0011 now carries both new codes, with the one declared exception.
  • Skill version 0.1.3 / lastUpdated 2026-08-17 is consistent in both places it appears; no stale 0.1.2 anywhere in the tree.

On the .windsurf trade-off

I argued the substance on #2969 rather than duplicating it here — short version: 0012 is the right code to give up, and for a firmer reason than the PR body gives. 0012's specific remedy is not actually lost to the agent at runtime: the catalogue hint ("…report it to them and pay a different seller meanwhile") ships in the error response body, independent of any rules file. What a rules file uniquely controls is the pre-arrival default, and for a 400 that default is already right. For 0013 it is wrong — a 500 is the one shape an agent retries before it ever reads the body, and no hint can undo a retry that already fired. Keeping 0013 is the correct half of the trade.

The one caveat, raised on #2969: that trade rests on the blanket retryable sentence staying in this file, and with six characters of headroom the next character-scavenging edit is likely to rewrite exactly that line.

Considered and deliberately not raised

  • What was displaced — the "Search & Research" enum example, the spelled-out payment.fee column list, the "too many concurrent" gloss on 0007. All three are presentation: the rule each supported survives (GET /api/v1/catalog/categories still tells you to fetch the enum rather than guess it; fee.capChargedCents is still named in the following sentence). This meets CLAUDE.md's "trades away presentation, never a rule" bar.
  • products/router/rails-x402.mdx references 0012 but not 0013. Defensible — that section addresses the seller publishing extra.name/extra.version, and 0013 is our table's gap, not theirs. guardrails.mdx carries it for buyers.
  • development-guide/api-errors/codes.mdx does not yet carry 0012/0013, so guardrails.mdx's "Every code below also appears in the canonical API error catalogue" is briefly false if this merges first. Not a defect here: codes.mdx is auto-generated and must not be hand-edited, and #308 — the sync PR for 95a006f — is open with auto-merge on. Just worth landing it alongside.
  • products/router/guardrails.mdx is not actually covered by the drift guard, contrary to this PR's description ("All 10 the drift guard holds to api-errors.ts"). TABLE_SURFACES holds two files, PROSE_SURFACES six, and neither list contains it. It is a hand-maintained public table of retry verdicts with nothing pinning it. Raised as a finding on #2969, where the surface list lives, with a verified one-line fix — flagged here only because the claim is in this PR's body.
  • Router codes vs published skill being red is expected, declared, and not a required check. Excluded.

No changes requested. The verdict is issued separately.

@eruizgar91 eruizgar91 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — no blockers, nothing to address.

Inline review: #304 (review)

@r-marques
r-marques merged commit 264a246 into main Aug 18, 2026
3 checks passed
@r-marques
r-marques deleted the docs/router-eip712-domain-code-0012 branch August 18, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants