Fail the build when your site states a number it can no longer defend.
Zero dependencies. One JSON file. Runs in any CI.
node check-site-facts.jschecked 64 files against site-facts.json (as of 2026-07-16)
❌ 1 problem:
llms.txt
states "45.3x" — superseded for portfolio-leverage (current: 42.6× / 42.6x / 42.6 倍)
→ historical use is allowed only in: fte-methodology.html, competitive-reports/
We audited our own marketing site and found the same number stated four different
ways. Commits were ~3,800 in one paragraph and ~3,960 in another — on the same
page. Our leverage figure was 46.9× in a report, 45.3× in the constitution, and
~30–56× on two other pages. A capability count said 112 on the site and 114 in the
source file.
The uncomfortable part: none of them were wrong. Each was true on the day it was written. They conflicted because every one had been hardcoded into prose with no date attached, and the site kept moving.
We wrote a rule — every number carries a date — and broke it four times within a day, on the very round that prompted it. Human discipline is not a control. So the rule became a file, and the file became CI.
grep cannot help here, because a stale number and a historical one look identical.
Only a declaration can tell them apart. That is the whole idea.
Copy two files into your repo:
tools/check-site-facts.js
tools/site-facts.json
No npm install. It is deliberately dependency-free, so an unrelated package upgrade
can never take the guard offline.
- Move the old value into
superseded - Set the new
value, bumpas_of - Run it, follow the failures
One file, then do what it tells you.
Dated artifacts — a report from July 4th, a trajectory table showing how a number moved — should contain old values. That is what they are for. Allowlist them.
Everything else is undated prose, and undated prose is the only thing that can go stale. Getting this distinction wrong in either direction makes the tool useless: too strict and you delete your own history, too loose and you ship contradictions.
| Check | Caught in the wild |
|---|---|
| Superseded values outside their allowlist | llms.txt still said 45.3x after the whole site moved to 42.6× |
| Pages quoting one headline must agree | Two pages built their tables from different measurement rounds |
| FAQPage schema matches the visible FAQ | Shipped 3× — schema had 4 questions, page showed 6 different ones |
| JSON-LD parses | One syntax error drops the entire block, silently |
ch in heading max-width |
max-width: 11ch squeezed every heading into a 270px ribbon |
Internal .html links |
21 links each costing a redirect hop |
The last two are not about numbers, but they are the same disease: a rule written for
one context, quietly wrong in another. ch is the width of the glyph 0 — it
describes Latin digits and nothing else, so the same CSS produced a different column
in Thai and Chinese.
- It checks only what you declare. A number you never wrote down is a number nobody is watching.
- It cannot read images. Our hero PNG had
40–70×baked into it, directly above text reading42.6×. The page contradicted itself on one screen and no script could ever have seen it. - It verifies consistency, not truth. It knows your site agrees with your facts file. It does not know your facts file is right. Proving a number still means counting it from source.
That third limit is the real one. This tool stops you from drifting. It cannot stop you from being wrong on purpose.
Runs on every push at dnafinity.com — 64 files, 11 facts, across Thai, English and Chinese. The methodology page it guards (why our own headline number is softer than it looks) is the reason the tool exists.
MIT
{ "as_of": "2026-07-16", "scan": { "root": "../public", // resolved relative to this file "extensions": [".html", ".txt"], "ignore": ["private-decks"], "css": ["styles.css"] }, "facts": [ { "id": "portfolio-leverage", "value": ["42.6×", "42.6x", "42.6 倍"], // every spelling you actually publish "superseded": ["45.3×", "46.9×"], // must not appear... "historical_ok": ["methodology.html"] // ...except here } ], "agreement": [ { "id": "leverage-agrees", "note": "Every page quoting the headline must quote the same one.", "pattern": "\\b4[0-9]\\.[0-9]\\s*(?:×|x|倍)", "strip": "[\\s×x倍]", // normalise units before comparing "files": ["index.html", "llms.txt"], "expect": "42.6" } ], "structure": { "faq_schema_must_match_visible": true, "jsonld_must_parse": true, "no_ch_unit_in_headings": true, "no_internal_html_links": true } }