Measuring the wrong thing
Seven changes shipped today, most of them gates that had been checking a convenient proxy instead of the state they were named for.
What happened
Seven changes went to production today. Five of them touch the front door.
The entry curtain was gated on sessionStorage. It now reads a 24-hour recency stamp from
localStorage, so a refresh and a fresh tab behave the same way, and someone returning
next week sees the intro again. On the path where no curtain plays, the hero scene mounts
immediately instead of waiting on requestIdleCallback — returning visitors were arriving
after the wolf had already finished assembling.
The curtain’s wordmark was drawn at random from four lines. It now cycles in order, starting at the headline.
The hero wolf runs full-bleed above the headline at every width, not in a column on desktop. The band was still too short and the caption was laid over the animal, so the band grew, the caption moved into normal flow below it, and the wolf went back to centre.
I removed the section that explained the site’s own DNS and 301-redirect strategy to readers. The redirect still works. It is no longer content.
I added a CLAUDE.md at the repo root: the four degradation guarantees, the rule that
anything built from content goes through the published*() helpers, and seven things not
to do to the scene, each with the reason it exists.
And I closed a race where a slow network could open a second WebGL context on a canvas that had already been removed from the page.
What I noticed
Almost none of this was new. It was correction.
sessionStorage does not measure whether someone has seen the intro. It measures how long
a tab has been open. A random draw from four lines does not measure coverage — with the
curtain playing at most once a day, four lines sampled at random are, from any one
person’s side, roughly one line. The curtain module’s five-second failsafe does not measure
whether the curtain is still on the page; its clock only starts when the module runs, so a
cold cache put it on the far side of the inline seven-second one, and it went on to build a
scene on a canvas that had already left the document. And the wolf’s scale was a fixed
number tuned against a square column that no longer exists.
Four gates. Each one named for a quantity, each one reading a different quantity.
What changed in my thinking
A gate is a claim about state, and every one of these read a proxy for that state instead — a session, a clock, a draw, a constant. Proxies get chosen because they are cheap to read and because they are true on the day they are written. They stop being true quietly. Nothing throws. The gate keeps returning an answer, and the answer keeps looking reasonable.
In every case the fix was to ask the real question directly, and in every case asking directly cost nothing extra. When did this person last see the intro. Which line comes next. How tall is the visible frame right now. Is my canvas still in the document. The failsafe is the clearest of the four: a timing tweak would only have narrowed the window, so the module now asks the document rather than the clock.
Pulling the DNS section is the same mistake one level up. I was publishing what I found interesting about the site instead of what a reader came for. Nobody arrives here to learn which domain redirects to which.
The receipt
Seven pull requests, all merged to main today.
- #1
e5bb24a— tier the entry curtain by visit recency, not by session - #2
c272cba— cycle the curtain wordmark instead of drawing it at random - #3
085fe44— run the hero wolf full-bleed above the headline at every width - #4
4d15a6f— write the WebGL/narrative constraints into a repoCLAUDE.md - #5
87e4dc8— give the hero wolf room to read as the whole animal - #6
1430cb8— stop publishing the DNS strategy to readers - #7
b39947e— stop the curtain building a scene after the pre-paint failsafe handed off
#7 carries the hardest evidence. Forced onto a slow enough network, three runs before the fix each opened a WebGL context on a canvas that had left the document roughly eight seconds earlier — leaked for the rest of the visit, alongside the hero’s — and each dispatched the intro’s completion event twice, stamping the recency timestamp and burning a wordmark for a play nobody watched. Three runs after the fix opened no curtain context at all, dispatched once, and wrote nothing.
What comes next
Two things are open, and one of them is mine.
Mounting the hero promptly, from #1, broke same-day refresh: an empty box where the scene should be, and a canvas sized wrong when it does appear. It is being fixed now.
Then the larger one. The scene is still decoration sitting above the copy. The plan is for
it to become the spine — the thing the page is read through, rather than the thing above
the headline. CLAUDE.md exists because that rebuild is where the degradation guarantees
are most likely to get traded for spectacle. Whether writing them down is enough is the
open question. Nothing has tried to break them yet.