A rehearsal that sees itself
Two more items of the PWA audit. The clean-install rehearsal now copies whatever the repository holds rather than a hand-kept list, and is a repository of its own, so the revision check sees the copy itself. The browser tests count records, indexes and sources the way the site does: only what is published. And a test that failed now and then on the "/" key has stopped failing.
What the audit found
The PWA audit report of 26 September still held two items, R4 and D1. The 6-in-1 gave both to one chat.
R4. Bootstrap is the clean-install rehearsal. The project is copied into a temporary folder, npm ci and verify:code run there, and then a third language is added by configuration alone. What was copied came from a hand-kept list in scripts/bootstrap.ts, and research/ was not on it. Since 24 September, though, verify:code reads that folder: validate:provenance and validate:incoming check the data against the research packages and the provenance index. The last green Bootstrap ran on 23 September, before those checks existed, and nobody had run it since.
D1. The browser tests counted records, people, organizations, models and sources from the canonical files in data/. The site, however, is built from the public projection, that is, from published material only. So far the numbers matched. But the rules allow a state where they don’t: a draft record, or a source that no published record cites. For the tests that is a difference of one, and they would fail on a page that is right.
Reproduced first
R4. I built a copy from the current list and ran the provenance step in it. Both checks failed with ENOENT on research/. Something worse turned up as well, and the report had good reason to ask separately how git behaves. The copy sits inside the project’s working tree, so git inside it climbs up to the parent repository. validate:revisions compared the copy’s data/events with the parent’s HEAD through a path that .gitignore hides, saw no change and passed. I then rewrote a record’s text in the copy on purpose, without raising contentVersion. The check again answered “0 changed records” and exited successfully. In the copy, it had been passing on nothing.
D1. For this one I made a separate worktree outside the working tree. I added a draft record and a source nothing cites, and named both ids in the provenance exceptions. validate:data and validate:provenance passed. atlas.spec.ts failed in three tests: 859 cards against 858, and 1,511 sources against 1,510.
What changed
Git now decides what goes into the copy. It gets everything the repository tracks, plus untracked files, minus whatever .gitignore keeps out. There is no longer a list to keep up to date. So a new check that reads something in the repository will find it in the copy, and a check that reads something ignored will fail there just as it would in CI. The copy itself is now a repository of its own: a clone without a checkout, with the working tree laid over it and its index on the same HEAD. Git in the copy sees what git sees here. So that this is more than a promise, the rehearsal runs a control after verify:code. It rewrites one record’s text without raising the version and requires validate:revisions to refuse it. Then it puts the file back byte for byte.
The third-language scenario relies on evt-0001, “Turing test” and the year 1950. All of that is still true in data/, so I left it as it was.
The counts in the browser tests now come from the same projection the site is built from. This sits in tension with a rule of that same file: a test must not agree with the island merely because both call one function. I resolved it this way. What gets published is a rule of the projection, not something the browser draws. A copy of that rule in the tests would only drift away from the original, the way a copy of the dates once did. The browser test asks a different question: does the page list everything published, once each. Whether the projection publishes the right things is now checked by a new unit test in tests/domain.test.ts. It runs on a set where the canonical files and the projection differ: a draft with its own source and its own person, a source nothing cites, and an organization reachable only through a link between entities.
The test that failed now and then
During D1, atlas.spec.ts:405, “the key list on the axis names its keys and holds the axis still”, failed again. It had failed twice before, once in CI. This time I fixed it rather than rerunning it.
First I put a key log on the whole test file: on its own, this test did not fail in eighty runs. The log showed the “/” key arriving five milliseconds after Esc and landing on the button of the key list that had just closed. The axis treats such a key as the list’s own and leaves it alone. Chrome does not take focus off a closed dialog’s button instantly, and the test only waited for the list to disappear from the screen. No person presses that fast, but the test did. Now the next key waits until focus has left the list.
Along the way a second fault in the test turned up. press('?') in Playwright sends “?” on the “/” key without Shift. Neither an English nor a Ukrainian layout does that, and the axis took it for “/” and opened the search field under the list. Now the test presses Shift and “/”, as a keyboard does, and checks that no field opened under the list. Four full runs on the altered set gave 360 of 360. In three of the four, focus was still on the closed list at the moment the test began to wait, so the wait is not an empty one.
I did not change the island itself. A window of a few milliseconds is nothing a reader will notice. Whether the island should ask if the list is open, rather than where focus is, is a separate question outside this audit.
What was not verified
Bootstrap in CI on Linux passed in full (run 36309447477): verify:code in the copy, the control refusal of validate:revisions, and the third language in Chromium. Locally on Windows it failed four times in a row on one unit test, density.test.ts, “builds the island from the live collection…”. In the first test:coverage run in a fresh copy that file took 21 seconds against a limit of 15, with 71 % of the time spent importing modules. The same copy then passes the whole of verify:code, and that test in 3 seconds. No single probe reproduced it: not an empty vitest cache, not a fresh npm ci, not freshly copied data files, not a run from a process set up like bootstrap’s. The cause was not found. I did not raise the test’s limit, because that would hide a fault nobody understands.
The rule by which the island treats a key as the list’s own is unchanged. Real Android, iOS, Safari and Firefox were not checked: the affected spec passed in Chromium only, 90 of 90 on main’s data.
Entry written September 27, 2026
Commits this entry accounts for
d30a6b1