DEV Community

minia2a
minia2a

Posted on Originally published at minia2a.uk

Every Number On Our Site Was Typed Once

Our homepage said this:

Your agents discover, pay, and consume APIs autonomously in USDC on Base.
300+ services across crypto, web, AI, and data.
Enter fullscreen mode Exit fullscreen mode

The catalog held 1,645. That sentence is the one line on the page whose entire job is to convey scale, and it was understating by more than five times.

The same bug ran the other way too. An internal dashboard page advertised 1,700 services — a number the catalog has never actually reached. And the worst instance was in a file no human reads: the .well-known record that LLM crawlers ingest described us as "272 agent-callable APIs", with a metadata block frozen three weeks earlier. When someone asks a model how big that directory is, 272 is the number it has to work with.

Three directions, one cause

Under-claiming, over-claiming, and telling a crawler something six times too small look like three different mistakes. They are one: a number was typed into a file, the thing it described kept moving, and nothing ever reconnected them.

That's worth naming precisely, because it isn't a content problem:

A count in your copy is a cached API response with no expiry and no owner.

Every marketing page, every .well-known record, every llms.txt is a client of your own stats endpoint that somebody hand-resolved once, months ago.

The test that finds these: grep your site for digits followed by the words you use for your own inventory — services, endpoints, APIs, users — and diff every hit against the live value. We found 17 files. One was current.

Fixing the numbers is the wrong fix

We'd already fixed the numbers once. A hand-patching pass the day before had walked straight past two 272 strings in the very file that feeds LLM catalogs — because hand-patching a list of files is exactly the kind of job that silently drops an item.

So we replaced it in two shapes.

Pages resolve at render time. A component already existed that rewrites any <span data-stat="services"> from /api/stats on load. Eleven pages used it; the pages with wrong numbers didn't. The counts became spans, the missing pages got the script tag, and the value written into the HTML is the current live one — so a crawler or a JS-less reader still sees today's figure.

Static records get rewritten daily, floored downward. JSON records can't run JavaScript, so a cron rewrites them from live stats. One rule matters more than the rest:

Prose counts floor, never round up. 1,645 publishes as "1,600+", never "1,700".

A floored claim is one you can always defend. A rounded-up one is a small lie that grows every time your catalog dips.

Test the alarm, not just the fix

Self-correcting scripts have a specific failure mode: they die quietly and everything looks fine. So the verification suite got a check comparing published claims against live — a prose count must land in (live − 100, live]. Below that, the record stopped tracking. Above it, we're over-claiming. Both fail.

Then the part that's easy to skip. We put the old number back and watched it fail:

FAIL  published counts: /.well-known/ai-catalog.json: claims 272, live 1645
Enter fullscreen mode Exit fullscreen mode

…ran the refresher, watched it heal, re-ran the suite green. A guard you've only ever seen pass is a guard you haven't tested.

The near-miss worth writing down

One page compares marketplaces. A single table row holds our count, one competitor's 5,000 registered agents, and another's 845 paid APIs.

The general rule — "a number followed by registered agents is our wallet count" — is correct on nine pages and catastrophically wrong on the tenth. The dry run said exactly what it planned to do:

L124: 5,000 -> <span data-stat="wallets">775</span>
Enter fullscreen mode Exit fullscreen mode

That edit would have republished a competitor's figure as ours. Not a stale number — a fabricated one, about somebody else's business. It never shipped, because the run was a dry run and the context got read before the write.

The fix: comparison pages get a literal-scoped rule that touches only our own value and leaves every other number alone. The lesson is narrower than "be careful":

An automation that rewrites claims has to know which claims are yours to rewrite. On a page that describes other people, pattern-matching prose is not a safe way to decide.

If you run an agent-facing service

Agents don't sanity-check your numbers the way a human skimming a landing page does. They read llms.txt, .well-known records, and OpenAPI specs literally and act on what they find.

  • One source of truth. Every published count resolves from the same endpoint your dashboard reads. If a number in your copy can't name its source, it doesn't have one.
  • Floor, don't round. Publishing less than you have costs nothing you can measure. Publishing more costs the thing that's hard to rebuild.
  • Plant the old wrong value and watch the check fail. Otherwise you have a dashboard, not a guard.

Full write-up with the exact regex traps and guardrails: Every Number On Our Site Was Typed Once

Top comments (0)