What broke
The fix that was never built
A correct bug fix looked like it didn't work, because the build that should have shipped it failed silently. Two more checks on the same project turned out to be skippable or only advisory. All three now fail loudly.
The short version
What went wrongA correct parser fix looked like it failed because the bundle build, with output sent to /dev/null, had silently failed after a misplaced edit to the seed file.
How we found itClaude grepped the bundle for the fix, found nothing, and rebuilt with errors visible, which showed the syntax error.
The rule nowBuild with visible errors, and before deciding a fix doesn't work, check that it is actually in the output.
This post documents something Claude got wrong. The claim rests on a commit or a file, not on memory. All of Claude's mistakes
On the evening of 31 August 2026, Claude fixed a real bug in the newsroom engine, the engine that writes the news for kokkekunst.no. Then it spent the next few minutes looking at evidence that the fix didn’t work. The fix was fine. It had never been built.
This post is about that, and about two other checks on the same project that turned out to be quieter than they looked.
A source that always had nothing new
One of the news sources, a national skills-competition organisation, came back with “0 new of 15” on every run. Its feed had fresh items with valid links. The source still reported “ok”.
The cause was in the feed parser. It reads RSS with an HTML parser on purpose,
because feeds in the wild are rarely well-formed XML. But in HTML, <link> is
a void element: it can’t contain anything. So <link>https://…</link> became
an empty element with the URL lying next to it as loose text. The parser read
an empty string and fell back to <guid> every time.
Most feeds put the article URL in <guid> too, so nobody noticed. This feed
used a UUID as its guid. Every item became https://host/<uuid>, which is a
404. The source looked quiet. In fact it was broken.
Claude wrote a fix that reads the text node after <link>, added regression
tests, and ran the test suite: 53 passed, 2 skipped.
Green tests, stale build
Then it rebuilt the command-line bundle and ran the real collection against
the feed. The build command ended in >/dev/null 2>&1, which throws away both
normal output and errors. The result was still “0 new of 15”.
The next step in the transcript is the right one. Before concluding that the
fix was wrong, Claude checked whether it was in the bundle at all: a grep
for a function name from the fix found it 0 times. A rebuild with errors
visible showed why:
X [ERROR] Expected "]" but found "name"
(the seed file, line 354)
About ten minutes earlier, in the same session, Claude had added two new
sources to the seed file with a small Python script. The script inserted the
new entries just before the last ] in the file. The file has more than one
array, and the last ] belonged to a different one, so the new entries landed
in the middle of a regular expression in another function. Every build after
that failed. With errors hidden, the failure left no trace except an old
bundle that kept running.
The tests had passed because the test runner reads the TypeScript source directly, not the built bundle. Green tests said the fix was correct. They said nothing about whether it had shipped.
The repair took about a minute: remove 1,049 characters of misplaced text, insert the sources where the array actually ends, and rebuild with errors visible. This time the fix was in the bundle, and the same source returned “12 new of 15”. Twelve is the per-source cap. The containers were rebuilt too, and checked for the fix, since they would otherwise have kept running the old code. The parser fix and the two sources were committed the next morning.
What the notes got wrong
Right after this, Claude wrote a memory note for future sessions. It says the hidden errors caused “an hour” of stale builds and that a fix was “tested three times”. The session transcript doesn’t support either claim. The broken edit came about ten minutes before the discovery, and the fix went through the stale bundle once before the check. The lesson in the note is right, but the numbers in it are not. That is why this site does not use memory notes as a source on their own.
A check that could be skipped
The website itself, the static kokkekunst.no site, has build-time checks that fail the build instead of warning. One of them makes sure every URL in an article body is also listed in that article’s sources.
The first version lived only in a remark plugin, which runs while Astro parses
the markdown. With a warm .astro cache, Astro didn’t parse the file again,
the plugin didn’t run, and a build passed with a foreign link in the text.
Cloudflare Pages always builds from a cold cache, so production would have
caught it. But the comment in the code says it plainly: a check that can be
skipped is a reminder, not a safeguard.
The fix was a second check, a build guard that reads the article files raw from disk on every build and has the final say. The plugin stayed, because it fails faster and points to a file and line. Both were already in the repository’s first commit on 30 August, so git has no record of the failing build itself, only the explanation written next to the fix.
A hint that was ignored
The third case is about search, not builds. Cloudflare Pages serves the site
on its own pages.dev address as well as on www.kokkekunst.no. Every page
had a canonical tag pointing to www. On 18 September, Google ranked the
pages.dev copy above the www page for at least one search.
A canonical tag is a hint, and Google didn’t follow it. The fix, committed
the same day, is a small function that answers with a permanent redirect for
the exact production pages.dev hostname and leaves preview deployments
alone. One loose end: the repository’s README still opens by saying the site
has no Pages Functions. That sentence is now wrong.
The rule
Out of these three came one rule:
Build with visible errors. A step that can fail without saying so, or be skipped, or be politely ignored, isn’t protecting anything. Before deciding that a fix doesn’t work, check that it is actually in the output.