The Engine and the Eye
A check can drive a page's arithmetic over every input it has, read the shipped HTML and match every sentence in it, and still never once look at what the page puts on the screen. Below is a working tool with five checks attached. Break its arithmetic and they react; break only its display, leaving the arithmetic perfect, and four of the five stay green. Then the same question, asked of every page on this site at once.
This is not a thought experiment. On 6 August 2026 two tools were built here in one night, both with careful verifiers, both green at 120 of 120 and 174 of 174 checks. A reviewer multiplied every displayed count on one of them by ten and reversed the shipped ranking rule on the other. Neither verifier moved. The record of that night ends with the line "sweep the corpus for verifiers that never load their page", and this is that sweep.
1. Break it yourself
The tool counts primes. Nothing about the mathematics is the point: it is elementary, cheap, and easy to re-derive by a second method, which is exactly what a demonstration about checking needs. The interesting distance on this page is never between the question and the answer. It is between the answer and the screen.
The tool
What you should see, and it is not symmetrical. Switch on an engine defect: the check that drives the engine goes red, and so does the one holding the screen against an independent answer. The check holding the screen against the engine stays green, because the screen is faithfully showing the engine's mistake and the two agree. Now clear that, switch on a display defect, and the engine-driving check goes green again while every number in the panel above is wrong. Only the two that read the rendered page notice, and only one of the five is positioned to catch a defect wherever it is.
The five are not straw men. Four of them are shapes this site's own checks actually take, and its coupling census counts how many of each there are: an independent re-derivation that never touches the page, a check that reads the shipped bytes, a check that imports the page's module, and a check that drives the built page in a real browser. The fifth is the one worth copying.
2. The same question, asked of every page here
A demonstration proves that the gap can exist. It says nothing about how wide it is. So the same defect was injected into the pages on this site that have one, and every check attached to those pages was re-run against it.
The population, watched rather than guessed
A page can only have this defect if its own script puts a number on the screen. Rather than grep for that, every page was loaded in a real browser with a counting probe patched over the DOM's write path, and asked what it wrote. Of pages, write at least one number of their own, numbers between them in the first seconds of a load. The site's shared banner and buttons write numbers on every page too, and are attributed to their own scripts by reading the call stack at each write, then excluded: they are nobody's claim.
The mutation
Into each of those pages goes a script that runs before anything else and patches every sink through which a script can put characters where a reader will see them. It moves the last digit of every number that passes through. The page's data is untouched. Its engine is untouched. Every sentence already in the HTML is untouched. The page computes exactly what it always computed, and then the screen shows something else.
The corruption is deliberately total. Every number the page writes is wrong at once, not one carefully chosen number, so a check that stays green cannot plead that the literal it happened to assert on did not move. It was shown the easiest possible version of the defect. That makes the figure below an upper bound on how sensitive this site is to its own display layer, which is the honest direction for such a number to err in.
The result
| arm | what it means | checks | |
|---|---|---|---|
| saw it | red with the screen corrupted, green with the placebo | ||
| blind | green while every number the page rendered was wrong | ||
| artifact | red under the placebo too, so not evidence of anything |
of pages measured have at least one check that noticed. The rest passed while every number they put in front of a reader was wrong.
The prediction that makes this more than a description: a check running in Node cannot see a browser render, so every check that notices must be one that launches a browser and navigates to the page, and every check without one must stay green.
And then it happened a second time, one level further down. A later check appeared to break the prediction outright: no browser by any label, and it noticed. It launches Chromium. The census's detector wants a literal
import 'playwright' or a literal
chromium.launch(, and that file resolves its driver by looping over a list of
candidate paths and then calls (pw.chromium ?? pw).launch(), so neither half
matches. The detector was wrong, not the check. It is named rather than absorbed:
The wider test used here is written out in full in
lib/screen-browser.mjs, and deliberately does not edit the shared classifier,
whose labels are pinned against a recorded census. Widening a detector until a prediction holds
is the one move that would make this whole page worthless, so the difference between the two
detectors is published instead of removed.
The sharper number, and it is the good news
Split those rows by how the census says each check reaches its page and the table stops being a scold and starts being a map. The labels below are the census's own, one per check, chosen by the precedence described in the note above, so the browser column here is a lower bound on the browsers actually present:
| how the check reaches the page | checks | saw it |
|---|
Every check that noticed had a browser, and of the checks that actually launch one and navigate to the page, noticed ( did not). That is worth saying plainly, because it is the opposite of what the two failures of 6 August might suggest: when someone here writes a browser check, it usually is looking at the numbers, not merely at whether the page loads. The gap is not the quality of the browser checks. It is that most layers have none.
The ones that had a browser and were blind anyway are worth a look, because they say what
a browser check is actually for. The clearest is
verify-is-fire-a-plasma.mjs. It launches Chromium, loads the page, clicks through
the species chips, drags the temperature slider and reads four separate readouts back out of
the rendered document. Every assertion it then makes is against a word: does the verdict
say not a plasma, does the readout name caesium, does the spectrum line mention radical
bands, is the swatch a colour and does it change. Not one of them is against a number, so
moving every digit on the page changes nothing it looks at. That is a real check of the page's
behaviour and it is a good one. It is simply not the check its green light is read as, and the
distance between those two is this whole page.
Two layers are held out of that pair of counts and named here rather than dropped quietly: the two fixed later the same night, below. A browser family was added to each after the sweep had already recorded them blind, so counting them as browser checks that were blind anyway would be false in both directions at once. Their re-runs after the fix are a separate record.
Which makes the cure a matter of coverage rather than of craft, and puts a price on it: one family, about twenty lines, per page that shows a number. The next section is that family.
The placebo, one byte away
The treatment and the control are the same script, injected in the same place, the same length, differing in one character: the value of a flag, 1 or 0. Nothing a check can read without executing the page can tell them apart. A check red in both arms went red because a script appeared, and is reported as an artifact rather than as a catch.
Where every page in the population went
A rate with a hidden denominator is not a rate, so here is the whole population accounted for. Of the pages whose own script writes a number:
And the instrument was made to prove it was pointed at the data
A page whose script never runs would leave every check green and look exactly like a page full of blind checks. So before any green light was believed, the page was loaded here with the same injected bytes the checks were about to see, and the probe was asked how many writes it had actually changed. Pages where it changed nothing are recorded as unmeasured, never as blind. That bucket is the in the accounting above.
3. What to do about it, in about twenty lines
The cure is not a framework. It is one family in a verifier that serves the site over a local socket, loads the page in a browser, and holds what the DOM shows against what the engine returns. Both tools of 6 August have one now. Either is a copyable template, and so is this:
// serve public/ on a loopback socket, load the page, and hold the
// rendered text against the engine's own answer for the same input.
const server = http.createServer(/* … static files out of public/ … */);
await new Promise(r => server.listen(0, '127.0.0.1', r));
const { chromium } = await import('playwright');
const browser = await chromium.launch({ args: ['--no-sandbox'] });
const page = await browser.newPage();
await page.goto(`http://127.0.0.1:${server.address().port}/strata/<slug>/?n=1000`);
await page.waitForSelector('#v-count');
const shown = await page.$eval('#v-count', el => el.textContent.trim());
const want = String(ENGINE.count(1000)); // the page's own module
ok(shown === want, `the count a reader sees is the engine's: ${shown}`);
// and the stronger form: the screen against a SECOND opinion, not the engine's
ok(shown === String(independentSieve(1000)),
'the count a reader sees is right, whatever the engine thinks');
A sentence claiming a fix is cheap is worth less than the fix, so two of the blind layers got
one the same night, chosen as the two that put the most numbers on a screen with nothing
watching. The sudoku layer renders 3,360 numbers and its
check, which reads the shipped bytes and takes twenty seconds doing it, stayed green through
every one of them; it now serves the page, clicks show the completion, and holds all 81
cells a reader sees against the completion it derived for itself.
The closest-planet layer had a check that
re-derives the astronomy beautifully and never touches the page at all; it now reads the two
ranked lists, which are the page's whole argument, and holds their order and every distance in
them against its own numbers. Both were then put back under the corrupt arm on their own, and
both go red. The record of that is screen-fixcheck.json and
screen-fixcheck2.json, and it is the only reason those two sentences are here
rather than in the section above about what ought to be done.
The last two lines of the template are the ones that matter. A browser check that compares the screen to the page's own engine catches every defect in the display and none in the engine, because when the engine is wrong the screen faithfully shows the wrong thing and the two agree. Comparing the screen to an independent answer catches both. That distinction is visible in the instrument above: switch on an engine defect and watch which of the two browser checks stays green.
4. What this does not show
Dependence is not sensitivity, and neither is this. A check that notices every number on a page changing at once might still miss one wrong digit. This measures whether a check is positioned to see the display at all, which is a lower bar, and the only bar that has an answer for every page.
The mutation is display-deep, except where a page reads its own screen back. The probe changes values on the way out. A page that writes text and then reads it back would take a corrupted value into its own logic, which is more than a display defect. That is measurable, so it was measured: of the pages read back text they had written. On the rest the mutation is display-only by construction.
Numbers are not the only thing a screen can get wrong. A reversed sort order with the same digits in it, a colour rule inverted, a label attached to the wrong row: none of those move a digit, and this probe would not catch them. The reviewer who found the 6 August defects found exactly that kind by hand. What a sweep can measure and a reviewer cannot is how many pages have nothing watching at all, and a page with nothing watching is open to every one of these, not only to the one that was tested.
Attributing a write to a script by its call stack is a heuristic. Code called through a scheduler or a promise can lose its frames. The full attribution, every origin path with its write count for every page, is in the committed census, so any row here can be checked by hand rather than believed.
Four shards ran in parallel, and pages are edited in place. A check reading another shard's momentarily-injected page could go red for a reason this experiment did not cause, which would inflate the count of checks that noticed. So a sample of the reds was put back under the corrupt arm on its own, with nothing else running, and the reproduction rate is reported rather than assumed. Contamination can only manufacture a red, never a green, so the count of blind checks is unaffected either way.
The check
Every figure on this page is recomputed by
verify-the-engine-and-the-eye.mjs from the committed records, and the numbers
in the block below are written into the page by a script rather than typed. Run it:
node research/verifier-independence/screen-census.mjs # the population, in a browser node research/verifier-independence/screen.mjs --validate # the two controls must go red node research/verifier-independence/screen.mjs --shard 0 --of 4 --out screen-0.json node verify-the-engine-and-the-eye.mjs
The apparatus, its three arms, its placebo and its exclusions are documented
in the headers of research/verifier-independence/lib/screen-probe.mjs and
screen.mjs. The records are screen-census.json and
screen-0..3.json, committed.