Ship the dark-only theme, the seller payee fix, and close the tools/list class - #706
Merged
Conversation
CSS and markup only, no new variable, so the upsert is a no-op and cannot race the pinned deploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous deploy run sat queued for hours through a GitHub Actions major outage and never got a runner, so it does not self-start now that capacity is back. Same change, fresh trigger. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Shipped broken. The regex that removed the theme IIFE ate its `<script>`
OPENING tag, leaving the next function as bare text in <head> followed by an
orphaned `</script>`:
<head>
<meta charset="utf-8">
function a402ToggleMenu(){...}</script>
Browsers hoist stray text out of <head> and into the body, so a wall of
JavaScript rendered at the top of every page on the site. Worse and quieter:
a402ToggleMenu was never DEFINED, so the mobile burger menu was dead on every
route - and the burger is the only navigation below 880px, where the nav links
are display:none.
Nothing caught it. Div balance was fine, the strict HTML parser was fine, all
three SEO strings were present, every route returned 200, and the theme
assertions all passed. The page was well-formed; it just had a script's worth
of source code printed on it.
Reported by Mike from looking at the live site, which is the part worth
remembering: a check that reads structure will not notice text that is
structurally valid and semantically absurd.
Guarded now, mutation-tested (removing the tag again fails 3):
- <script> open/close tags must balance, since an orphaned closing tag is the
signature of a stripped opening one
- no bare `function` declaration may sit outside a script in <head>
- a402ToggleMenu must be defined INSIDE a script element
- the burger button must still be wired to it
Verified across ten routes: scripts balanced on every one, no leaked source.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The index kept ONE payTo per network via a first-wins reduce, so every payee after the first was discarded. Fine for a seller whose tools all pay the same address; wrong for a marketplace that gives each author their own revenue split. Measured on a live seller 2026-08-06: 236 paid routes across 22 authors with 22 distinct payTo addresses, of which the index retained exactly one. Twenty one payees were invisible, and any ranking that aggregates by payTo - ours included - attributed that whole origin's on-chain volume to whichever address happened to be crawled first. This is also how I got the architecture wrong in conversation. I read one 402, saw a single payTo, and concluded the origin shared one split across all its authors. The seller had to correct me; their design was right and OUR INDEX was the part flattening it. `payToByNetwork` deliberately stays a single string per network. The router's chain-derived proven-ness join, advertisedPayToEvidence, and the market pages all index it directly, and changing its type would break them quietly. `payTosByNetwork` carries the full set alongside, from one shared allPayTosByNetwork() used by all three builders plus the http/https collapse - which unions the arrays rather than spreading one object over the other, since the two schemes of a host can advertise different payees. Case-exact, because folding merges DISTINCT payees: base58/base32 addresses are case-sensitive and EVM addresses are checksummed (same rule as src/payer.js). Capped at 200 per network so a pathological origin cannot balloon a cached index entry. The test IMPORTS the reducer rather than mirroring it. The first draft reimplemented it locally and would have stayed green with payTosByNetwork deleted from the module outright - proving its own copy works, not the shipped code. Mutation-tested: reverting to first-wins fails 5 assertions, case-folding fails 1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
about_agent402's missingATool field says "Call request_tool". tools/list did
not contain request_tool. The handler existed and worked, so anyone who
already knew the name could call it, and every agent who learned about it the
way we documented could not find it. The wish board is how we decide what to
build next, and the one tool whose job is to catch an agent that just failed
to find something was itself unfindable.
This is the THIRD instance of one defect. about_agent402 and top_x402_sellers
had the same shape - working handler, absent from the only surface an MCP
client can discover from - and both were fixed by hand, with a comment in
mcp-http.js explaining the class and no test pinning it. Fixing instances
three times without pinning the class is why this one was reported from
outside instead of found here.
So the fix is mostly the guard. test-mcp-self-consistency.js reads five
agent-facing surfaces (tools/list text, about_agent402, get_payment_info,
/llms.txt, /.well-known/x402) and asserts that every tool name in a
call-this position is advertised, every named catalog slug exists, and every
referenced route is registered - plus both parity directions, a handler branch
no advertised name can reach and a listed tool with no implementation. Nothing
functional could have caught this: every existing test calls the tools whose
names it already knows.
Route existence needs TWO oracles and reports missing only when both say no.
A source scan of app.<verb>("...") is the only one that can see a POST-only
route - a live GET 404 cannot tell "no such route" from "wrong method", and
that ambiguity is precisely why the reporter would not conclude /api/wish was
broken. A live GET is the only one that can see the chain pages, registered
through a template literal the scan cannot read. The live probe never touches
/api or /v1: in FREE_MODE those handlers execute, and a consistency check has
no business calling a tool that spends money. Matching is segment-aware, so
/api/wish is never satisfied by /api/wishes.
The first draft skipped any snake_case name that was neither listed nor
handled, on the theory that unknown words are probably not tool references.
That filter skipped exactly the defect being hunted: a planted "Call
submit_wish" passed a green run. It is gone. The only escape hatch is an
explicit NOT_A_TOOL set, currently one entry, for a tool that is real but
lives on the stdio package.
Mutation-tested: removing request_tool from the listing fails 2 assertions
(structural and prose, independently), a fake tool name fails 1, a fake route
fails 1. Extractors are proven against a planted control before any clean run
is believed, the same doctrine as the free-tier egress probe.
request_tool is annotated readOnlyHint:false, alone among the 18. It records a
wish row, and a client that trusts that hint to decide what it may call
unattended would be misled by a blanket true.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A published path like /demo.js was being truncated to /demo before the lookup, which would report our own live zero-dependency demo as a route that does not exist. Found by running the same check over README and the wiki, where /demo.js is how we tell a new agent to start. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MikeyPetrillo
marked this pull request as ready for review
August 7, 2026 11:07
The balance assertion that catches a stripped <script> opening tag only matched lowercase, so an uppercase <SCRIPT> would have gone uncounted and reported a balance that was not there - a blind spot in the one check standing between us and shipping a wall of raw JavaScript at the top of every page again. Flagged by CodeQL (js/bad-tag-filter) on this branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five commits, already CI-green on the branch and (for the deploy-marked ones) live on prod.
about_agent402instructs agents to "Call request_tool" andrequest_toolwas never listed, so only callers who already knew the name could reach it. Third instance of that one defect, and the first two were fixed by hand without a test, which is why this one was reported from outside.The last commit is mostly the guard rather than the one-line fix.
scripts/test-mcp-self-consistency.jsreads five agent-facing surfaces and asserts that every tool name we tell an agent to call is advertised, every named slug exists, and every referenced route is registered - the class no functional test can see, because every functional test calls the tools whose names it already knows.Route existence uses two independent oracles and reports missing only when both say no: a source scan (the only one that can see a POST-only route) and a live GET (the only one that can see template-literal routes). The live probe never touches
/apior/v1, since those handlers execute under FREE_MODE.Mutation-tested: removing
request_toolfrom the listing fails 2 assertions independently, a planted fake tool name fails 1, a planted fake route fails 1. An earlier draft of the checker had a plausible-looking filter that skipped unknown names - i.e. exactly the defect being hunted - and a plantedCall submit_wishpassed a green run. That filter is gone.