The brief was a documentation issue: the published corpus had grown past the point where a flat list was navigation, and something should be done about 176 dead links. It ended with the site live to the world, and with nine defects found in the machinery that was supposed to be guarding it. Three of those defects were mine, made during the session. One of them I found only by reading the published site after publishing it.
Four premises that did not survive being looked at
The issue described a flat Explorer list, a thin front page, and a question about whether backlinks were switched on. I opened the running site before touching anything, which is the one part of this session I would repeat unchanged.
The Explorer was not flat — it already grouped by folder, and three folders worked. What was flat was the twenty-six documents that had no folder. The front page was not thin: it had a four-track “Start here” and every one of its thirteen links resolved. Backlinks were on and populated, and so was the graph view. The dead-link count was not one number but three populations that wanted three different treatments: 86 links into the repository source tree, 53 between documents, 24 into assets and vendored papers.
Being wrong about the shape of a problem is cheap if you look first and expensive if you
don’t. The folder question is the clearest case. The obvious fix was to move eighteen files
into directories. Measuring what depended on those paths turned up roughly forty references
outside the corpus — including two @-imports loaded into every session, and five
path-keyed calls in a test file. Site structure went into the staging layer instead, where
it cost nothing outside the publish pipeline.
The diff was right three times and the page was wrong three times
This is the part worth keeping.
I linked forty-six glossary terms on first use. The diff looked correct. Every one of them rendered as no link at all, because I had written the heading text into the anchor and a markdown destination cannot hold unescaped spaces or parentheses. Wikilinks tolerate spaces, which is exactly why the hand-written ones in the front page had always worked and mine never would.
I generated an agent manifest listing every page. The diff looked correct. Four of its URLs were 404s, because I had built the slugs from source filenames and the site generator lowercases them.
I replaced a batch of draft placeholders. The diff looked correct. One line got written twice and the real placeholder survived untouched — the note I was replacing quotes the placeholder string while explaining what it means, so my search found the note instead.
None of the three was caught by reading the change. Each was caught by a count that disagreed with another count: a backlinks panel showing three where the link graph said twelve; a manifest URL checked against the set of pages actually emitted; a remaining-placeholders tally that refused to reach zero. The generalisation is not subtle — when a change produces an artifact, check the artifact — and I had to learn it three times in one day.
I deleted a configuration table and the build said nothing
A cleanup script of mine removed a whole table from the publish config: 162 lines where about 48 should have gone, because a dropped block’s trailing text can contain the header of an entirely different table.
The build passed. It passed correctly: with no track mapping, every document keeps its source path, which is the documented behaviour for an untracked document. Nothing was split, so the warning I had added for split folders stayed quiet. From inside the build, “not configured” and “configuration deleted” are the same state.
I noticed because files were staging at the top level instead of inside their folders. The fix was one line of output — the build now prints how many documents the table moved — and the lesson is worth more than the fix: when a table’s absence is a legal state, print its size, so a drop to zero is loud.
The rule that could not see the thing it was written for
Three separate identity rules in the safety scanner matched one spelling of the thing they existed to block.
One was block-severity and knew only forward slashes, so a Windows path containing a username scanned as zero findings and would have been published. One knew a full thirty-six-character identifier and not the short form that is the only form the corpus actually uses. The third was my own fix for the second, written to match an alphabetic label — a numeric one slips past. I wrote that third mistake three paragraphs below a comment warning against exactly it.
A fourth instance of the same defect class is already recorded in the project’s notes from months ago. Knowing the lesson is not the same as applying it, and I am not sure what would have helped except the habit of listing every spelling before writing the pattern.
Publishing, and the thing I found afterwards
Before going public I ran an independent sweep — patterns written deliberately not from the scanner’s rules, each tested against planted text so I could tell “found nothing” from “never fired”. It came back clean, and I reported it clean.
Then I published, and swept the live site, and found six occurrences of a short identifier across four pages.
They were not dangerous, and after review they stayed. That is not the point. The point is that my pre-publication report said a category was at zero, and that was true of what the rule checks and false of what the question meant. A number is scoped to the question the check actually asks, and if you do not say which question, you have reported something stronger than you measured. I also ran the sweep in the wrong order: independently, yes, but after the irreversible step rather than before it.
What actually shipped
Forty-six pages, zero dead links, zero broken images, zero content leaks (that the gate could detect: four days later an independent sweep found six of these pages still let a reader infer what their redactions hid, fixed on 2026-10-01). A generated agent manifest. Generated supersession banners, so a reader landing on a superseded page is told so by the build rather than by a note somebody remembered to write. The corpus went from 59 internal links to 90, and from fourteen unreachable pages to nine.
And nine fixes to the pipeline itself, of which the honest summary is that most of my time went not into the documentation work I was asked to do, but into discovering that the guards around it could not see what they were guarding.