vibeapp.win

What brokeUpdated 3 October 2026

Everything written from memory turned out wrong

In the editorial engine behind kokkekunst.no, publisher names, feed URLs, a CLI flag, a DNS plan and a code comment were all wrong for the same reason: nobody had looked them up. Here is what broke and the rule that came out of it.

The short version

What went wrongPublisher names, feed URLs, a CLI flag, a DNS plan and a code comment were written from belief rather than checked, and several were wrong.

How we found itChecking names against publishers' websites, flags against claude --help and the gate against stored drafts revealed the errors.

The rule nowTake names from the source's own page, find feed URLs with a tool, and take version-specific facts from the installed version.

This post documents something Claude got wrong. The claim rests on a commit or a file, not on memory. All of Claude's mistakes

Kokkekunst.no is a Norwegian site about professional cooking. Its stories are drafted by what we call the newsroom engine, which reads public sources and writes short news items from them. The engine was built with Claude Code. Counted from git log on 3 October 2026, the repository has 39 commits between 31 August and 24 September 2026, and every one of them carries a Claude co-author line.

The site promises its readers that everything it says can be found in a source. In its first days of operation, the project kept breaking that promise in its own files: the source list, the docs, the spec, a code comment. Most of the errors had one cause. Claude wrote down what it believed and nobody checked it. Henning writes no code himself, so these files were written by Claude.

The source list

Every source in the engine has a publisher name. That name is printed as “by X” under each story the source contributes to, so it ends up on public pages.

On 30 August the task log records that all fourteen publisher names were checked against each publisher’s own website. Three were wrong, and both the task log and the changelog say the register had been seeded from memory. The three cases:

  • The Norwegian chefs’ association was listed under the name it used before it was renamed in 2018. That was eight years out of date.
  • The Bocuse d’Or Norway foundation was listed under a name that doesn’t exist. Its old domain answers with a 307 redirect to the foundation’s real name and site.
  • A third publisher had a foundation name that appears in no source at all. The working theory in the task log is that it got mixed up with a different organisation that has a similar name.

The feed URLs were worse. The source document now opens by saying that half of the original feed URLs in it did not exist. One line from that file explains why this mattered: a made-up feed URL that quietly returns 404 is worse than no source, because its card in the admin screen looks normal.

The docs and the spec

The same pattern showed up beyond the data.

DNS. The site needed to answer on the bare domain as well as on www. The first version of the DNS document told the DNS provider to set up an ALIAS record as the first choice. The rewritten document calls that “a wrong premise”: Cloudflare Pages only attaches an apex domain that is a zone on the Cloudflare account, ALIAS or not. The provider didn’t offer ALIAS anyway.

The CLI flag. The engine runs Claude as a text transformer with no tools. The spec assumed this would use --bare. When the flag was checked against claude --help in Claude Code 2.1.251, the help text said --bare reads only an API key and never the OAuth login. The project depends on the subscription login and is never supposed to set an API key, so the flag would have forced exactly the billing setup the project rules forbid. The fix was --safe-mode. The agent notes describe the finding as “the opposite of what the spec assumed”.

Next.js. The project runs Next.js 16. The spec described access control as “middleware”, and Next 16 has renamed middleware.ts to proxy.ts. Next.js itself anticipates this: its dev server writes a block into CLAUDE.md headed “This is NOT the Next.js you know”, telling the agent to read the bundled docs before writing code because the APIs may differ from its training data. That block was committed to the engine on 1 September.

The comment that lied

The worst case was in code. The fact gate compares proper names and numbers in a draft against the source and sends anything unconfirmed to a human queue. A comment in the gate said extraction was symmetric: the same function runs on the draft and on the source, so the noise from the heuristic cancels out.

That wasn’t true. The extractor only picked up capitalised words. A common word at the start of a sentence in the draft was treated as a name, while the source used the same word in mid-sentence, in lowercase, so it was never extracted from the source. The gate then flagged a “name” that didn’t exist.

On 30 August the gate was measured against the drafts already stored in the database, without any new model calls. It sent 3 of 3 real drafts to the queue. After the fix: 4 of 10 (40 %), under the project’s limit of 50 %. A wrong comment is worse than none, because it stops the next reader from looking.

Where the rule held

On 20 September the social-publishing module was built, and each of its three channel adapters was checked against the platform’s own documentation, as the commit messages state. Bluesky: a 300-grapheme limit and link positions counted in UTF-8 bytes. Discord: wait=true is what returns a message id, and a webhook cannot list its channel. LinkedIn: the self-serve tier has no refresh tokens, so the adapter doesn’t try. We have no record of these limits being written wrong first. What the record shows is that this time the checking came before the code.

One more detail fits the theme. Until 31 August the project had no git history. The first commit’s message says why it was made: without history, a changelog can only be written from memory. (The 30 August findings above were logged that day and committed the next.)

The rule

The project’s files now say it in more than one place: do not write a publisher name from memory, and do not guess a feed URL.

In practice it looks like this:

  • Names come from the source’s own page. The skill for adding a source starts with a step 0: fetch the publisher’s front page and read the name from the footer, the “about” page or the copyright line. A redirect to another domain usually means the name has changed.
  • URLs come from a tool, not a guess. A feed is found with a command that checks the page’s own <link rel="alternate"> and common paths, and seeding never sets a feed URL by itself.
  • Version-specific facts come from the installed version. CLI flags come from --help on the version that actually runs. Framework APIs come from the docs bundled with the installed package.

This post follows the same rule: every claim has a commit or file reference in its source notes, checked against the repository before writing.

Corrections

  1. Attribution corrected: an earlier version said "someone" wrote down what they believed and did not put the mistake on Claude. Henning writes no code himself; the files were written by Claude.