What is facts.ts, and why can a build fail with nobody touching a file?
src/data/facts.ts is a machine-checkable file, not a spreadsheet of good intentions: every number in it carries one of three states, and a number in the wrong state cannot render.
Approved means a human confirmed it and is accountable for it — citable. Pending means we collected it from a source but the owner hasn’t signed off — not citable. Conflict means two sources disagree — never citable as settled fact. The disagreement itself can be published, and often should be.
Be clear about what “approved” buys and what it does not. Every one of the seven approved facts is a campaign result of ours, signed off by the owner — approvedBy: 'owner'. That is self-attestation on our own numbers, not third-party audit, and the registry cannot turn it into one. What it does buy is a name against each figure and a refusal to print anything without one. The external facts — the ones a reader could independently check — are exactly the ones sitting at pending or conflict, because we hold them to the higher bar and have not cleared it yet.
The file also carries expiry. Any fact can hold a validUntil date, and past that date isCitable() returns false — the build fails. A build that passed in July can fail in October on the exact same fact, with nobody having edited a line. The source comment in the file states the reasoning directly:
“A vendor’s price is true on a date and false a quarter later, and the failure mode of a registry without expiry is not an error but an article that is quietly, confidently wrong.”
The companion file, data/facts.yml, is the human research log — messier, with working notes and half-checked leads. facts.ts is the subset that has cleared the three-state gate. The sha256 prefix of the current registry file is 31a941d99bee9689dc4c3c6f; anyone can hash the file we ship and compare.
How many facts are approved right now?
As of 2026-07-20, the registry holds ten facts total. Seven are approved and citable today. One is pending. Two are in conflict and blocked from being printed as settled numbers.
| State | Count |
|---|---|
| Total facts | 10 |
| Approved (citable) | 7 |
| Pending | 1 |
| Conflict | 2 |
| Citable today | 7 |
Both conflict entries are the fabrications the rest of this article is about.
Did our own registry copy the Instagram number we set out to debunk?
Yes. Our registry entry for Instagram’s API post limit held a value sourced to our own design spec, not to Meta — and that value does not appear on any page Meta currently publishes.
The page states two different numbers for the same limit. Under the heading “Rate Limit,” Meta’s documentation reads: “Instagram accounts are limited to 100 API-published posts within a 24-hour moving period.” Under the heading “Limitations,” further down the same page, it reads: “Accounts are limited to 50 published posts within a 24-hour period.” Both are current. Neither matches what our design spec had assumed.
Resolving which figure applies — 100, 50, or something conditional on account type — needs a call to /{ig-user-id}/content_publishing_limit against accounts we actually operate. We haven’t run it yet. Until we do, the registry entry stays status conflict, and no article of ours states a figure for this limit. The disagreement, sourced and dated, is the more useful thing to hand a reader than either unverified number would be.
Did a schema field fabricate Russian-market nuance that doesn’t exist?
Yes. A schema field called localeSpecific exists to prove a Russian article was actually written for a Russian reader rather than translated — during a migration, all three Russian articles were given confident prose claiming coverage of 152-ФЗ, VK Клипы, Telegram, and Russian ad-labelling rules.
Not one of the three articles mentions any of it. The field had been filled with what the articles ought to have differed by, not what they actually contained. That is fabrication committed inside the exact field built to catch fabrication, in the exact system built to stop a language model from inventing content.
The fix replaced prose with structure: { claim, evidence[] }, where evidence lists terms that must literally appear in the article body. A script, tests/content-gates.mjs, greps for them. The three Russian articles now carry no localeSpecific claim at all, because none of them earns one yet — the gate reports that honestly instead of accepting the old lie.
The warning this produces is deliberately not blocking. A gate that forces the field to be filled in is a gate that manufactures the same fabrication it exists to prevent — it would simply move the fabricated claim from “confident” to “mandatory.”
Why not just regex the article for numbers?
Because a digit pattern cannot tell a fabricated statistic from a version number, a timestamp, or a spec sheet, and every exclusion added to quiet the false positives is a step toward a gate nobody trusts.
The source comment in src/components/content/Fact.astro states the reasoning directly:
“A regex over digits fires on ‘Astro 7’, ‘2026’, ‘top 10’, ‘15 minutes’, ‘H.264’ and every version number in a code block. The fix is an exclusion list; the exclusion list grows; a gate that produces false failures gets switched off, and a gate that is switched off is worse than no gate because everyone believes it is running.”
Numbers in our articles are not pattern-matched from prose. They’re written as <Fact id="…" /> and resolved at build time against the registry above. If the id doesn’t exist, or exists but isn’t approved, the build fails before the number ever reaches a reader — not after an editor happens to notice.
A gate that is switched off is worse than no gate, because everyone believes it is running.
What doesn’t this registry catch?
It cannot make a claim true. It can only make an unverified claim expensive to publish — a source that is wrong at origin, a quote taken accurately from a bad study, or a number that was correct on approval and has since drifted before its validUntil date will all pass the gate unchanged.
The registry’s second conflict entry shows this limit against itself. Two named measurement firms have published figures for the share of AI Overview citations coming from the organic top 10, and those two figures differ by more than a factor of two. Neither figure carries a source URL in our own research log.
The registry caught two fabrications inside our own process in the space of a week, both logged with a date and a status, not smoothed over in a rewrite. It has not caught anything else yet, because it has only existed a week — every article we published before 2026-07-20 was written and reviewed without it, and hasn’t been re-checked against it since. That backlog is the next thing this registry works on, not a gap we’re pretending isn’t there.