The Surface Nobody Counted

This site keeps its own page-view counter, and it has never once been wrong about what it counts. It counts pages. On 4 September it published a tree of one thousand four hundred and eighty-seven files, all but its index page not pages at all: the program behind every claim, mirrored so that a reader could finally obtain the check a page tells them to run. Machines arrived and read them. In the seven days to 7 September, OpenAI's GPTBot fetched paths under that tree 2,098 times and the published datasets 1,129 times, which together is 59 per cent of everything it asked this site for, and the counter recorded none of it. Not approximately none. Of the 1,456 distinct paths this counter has recorded in its entire life, twenty-one lie under those trees, and all twenty-one are directories rather than files. Not one artifact, ever. The gap is not the interesting part. The interesting part is that this is the third time in three weeks, and that the instrument built to measure the gap had inherited the shape of the gap.

Type any path from this site into the box below. The classifier that answers is not a description of the rule. It is the rule: the same module the counter itself runs, served from this page's own directory, and the build fails if the two copies differ by a byte.

Which instrument sees this path

Real published paths, twelve per artifact class taken at an even stride through the census, plus a fixed handful of pages, doors and chrome.

A page view is recorded here when four things hold at once: the method is GET, the status is 200, the path is page-like, and the response carries content-type: text/html. Page-like means the last segment has no dot-extension, or the path ends in a slash. That is the whole gate, it is four lines of code, and every one of the four is right for the thing it was built to count.

What it is not is a measure of how much of this corpus gets read, and the distance between those two things is the subject here. A verifier is a .mjs file. It fails the third condition, because its last segment carries an extension, and it fails the fourth, because it is served as text/javascript. It fails twice. So does a dataset, a research README, a film. The counter is not making a mistake about them. It has no opinion about them at all.

What this site publishes

Every file under public/, classified by the module you just used. The count is a walk of the directory, so it is exact, and it is committed alongside the study so the classification can be rerun over it.

surfacefilessharerecorded by
published files
no instrument could see
of the published site

Films, verifiers, datasets and research artifacts. Roughly a third of the files this site serves, and until 7 September not one of them appeared in any first-party record of what gets read here. The remaining uncounted class is deliberate and stays that way: a webfont, a stylesheet, a social card and a page's own images are bytes a browser fetches while rendering a page that was already counted, so counting them would add volume and no information. The line between the two is whether a client came for the thing.

What the counter holds

The counter is a strongly consistent Durable Object. It is exact and unsampled, which means an absence in it is a fact about the record rather than a sampling artifact. Here is every row it has ever held under /checks/, /data/ or /research/, out of distinct paths and lifetime views:

viewspath

rows. Of those, end in a file extension.

Twenty-one directories, zero files. /checks/ itself has taken 23 page views in its life: the index page of a tree that had absorbed thousands of fetches. That single line is the whole finding, and it needed no sampling, no estimate and no crawler taxonomy to establish. It is what an exact record does not contain.

Who came anyway

For the other half of the question, how much traffic those surfaces actually took, the counter is useless by construction and Cloudflare's own request log is the only witness. That log sees every request the edge served. It is sampled, so what follows is an estimate, and the study says so wherever it uses one. The window is days to .

clientrequestsrecorded / notnot

an instrument recorded itnothing did

GPTBot requests
into /checks/
into /data/
nothing recorded
of its visit

Two crawlers found the artifact surface and the rest did not. GPTBot spent most of its visit there; Amazonbot went mainly for the datasets. ClaudeBot, PerplexityBot and Applebot fetched pages and nothing else at all. Three days is a short time to draw a conclusion from and this one is a snapshot rather than a trend, so the honest reading is narrow: publishing the checks changed what at least one crawler came for, within three days, and no first-party instrument would have shown it.

A tree that is only ever the copy

One cut worth keeping, because it fell out of an accident in how the measurement was taken. The partition was cut on overlapping substrings, so /research/ and /checks/research/ were both measured. For GPTBot the two came back identical: fetches over paths in each. Every single one of its research fetches was of a published check. The bare research tree, which this site has served for far longer, took . The copy made for the reader is the one being read.

What the instrument says once it exists

Everything above was measured before the fix, by cutting Cloudflare's log on hand-written path substrings. Once the classifier was deployed, the same log could be cut by the classifier itself, which gives two things the substring method cannot. An honest denominator: corpus requests, meaning pages, doors and artifacts, with chrome and the API left out because fetching a webfont is not reading anything. And the counterfactual the whole change is about, which is how much of that reading any instrument here would have recorded before 7 September against how much it records now.

clientrequestscorpusartifactscounted beforecounted now

Three weeks, three instances, one defect

That July table is the part worth dwelling on. It exists because somebody had already noticed that the counter cannot see what a machine reads, and its own documentation says so plainly: these surfaces "were invisible to it, and this table is the first measurement of the gap." It counts eight things. They are eight filenames, hand-listed: llms.txt, the search index, two MCP surfaces, an API catalogue, the MCP endpoint, the sitemap, robots.txt. Every one of them is a single file at a fixed path, because in July every machine-readable surface here was a single file at a fixed path.

Then the site published a tree. Nothing about the table was wrong; it simply enumerated the doors somebody had thought of, and a list of names cannot notice a thing that has no name yet. That is the actual defect, and it is not a bug in any of the three fixes. An instrument whose coverage is a list can only ever measure the blind spots that have already been found, which means every one of them has to be found by accident first. All three were.

So the deliverable is a gate, not a fix

The fix by itself would be a fourth patch by name, and this project has already learned in public what a fix without a gate is worth: a set of links into a private repository was found and deleted twice, about nineteen days apart, each time by someone who understood the problem and wrote the lesson down clearly, and it came back both times. Prose does not survive a mind with no memory. Programs do.

So the classification stopped being a list of surfaces and became a total function over paths, and a build gate now walks everything the site publishes and refuses to build if a top-level surface is not named in that function. Adding a directory to public/ now costs one line saying what counts it, on the day it is added, rather than three weeks later by accident. Answering "nothing counts this, deliberately" is fine and takes the same one line. What the gate stops is nobody being asked.

It earned itself on its first run. It reported 771 files under /strata/<layer>/data/ and /strata/<layer>/specimens/, the rows and the recordings that individual layers publish to stand behind their own claims, and it had them filed as chrome. They are datasets kept under the layer instead of under the room, and they are now the data surface, which is where they always belonged. Nobody had asked that question either.

Total, not merely correct

The property that matters here is not that the classifier gets each case right. It is that it is total: every string it is handed comes back with exactly one class, so nothing can fall between the cases the way /checks/ fell between them. deliberately awkward inputs, answered in your browser by the same module:

inputsurfacerecorded by

Two of those rows are worth reading twice. /a.b is chrome and /a.b/ is a page, which is the extension rule doing exactly what it says. And /CHECKS/X.MJS comes back as chrome rather than as a check, because the tree prefixes are matched case-sensitively while the eight door patterns are not. That asymmetry is real and it is left alone on purpose: the assets layer is case-sensitive too, so no such path is served here at all, and the honest place for a difference like that is in front of you rather than smoothed over.

What this still cannot see

Run it yourself, from outside

Nothing here asks you to trust a screenshot. The check that re-derives every figure on this page was rebuilt in an empty directory from files this site serves, run there, and compared against the run inside the repository, and it reproduced it exactly. Three of the eleven files it needs are the programs, published at their own repository paths. The other eight are this page and its four captures, which the site already serves because a check whose data a reader cannot obtain is the same fault one level down.

mkdir aw && cd aw
for f in \
  checks/lib/surfaces.mjs \
  checks/research/the-surface-nobody-counted/snapshots.mjs \
  checks/research/the-surface-nobody-counted/verify-the-surface-nobody-counted.mjs
do curl -L --create-dirs -o "${f#checks/}" "https://artwaste.land/$f"; done

B=public/strata/the-surface-nobody-counted
for f in app.js surfaces.mjs \
  data/hits-2026-09-07.json \
  data/insights-aipaths-7d-2026-09-07.json \
  data/insights-partition-7d-2026-09-07.json \
  data/page-data.json \
  data/published-surface-2026-09-07.json
do curl -L --create-dirs -o "$B/$f" "https://artwaste.land/strata/the-surface-nobody-counted/$f"; done
curl -L --create-dirs -o "$B/index.html" https://artwaste.land/strata/the-surface-nobody-counted/

node research/the-surface-nobody-counted/verify-the-surface-nobody-counted.mjs

Fifty-three assertions, no key, no account, and nothing you did not just download. Note what the last two curl lines are fetching: the census and the captures live under this page rather than in the repository, precisely so that this recipe can exist.