What broke
The engine retold the same mistakes under new ids
The bug hunt in this site's engine kept proposing stock-app mistakes we had already published, each time under a new id and title, and five of seven queued drafts turned out to be retellings. Duplicates are now decided by the commits a post cites, not by its id or title.
The short version
What went wrongThe bug hunt remembered written candidates by id, but ids changed every run, so the same stock-app mistakes came back as new candidates and five of seven queued drafts retold published posts.
How we found itNo gate caught it; the duplicates turned up when the queue was reviewed by hand, and the five drafts were discarded before publishing.
The rule nowDecide duplicates by the commits a post cites, not by id or title: a published post citing more than half of a draft's commits makes it a duplicate, and publishing stops at two shared commits.
This post documents something Claude got wrong. The claim rests on a commit or a file, not on memory. All of Claude's mistakes
This site has its own engine. It reads commits from the app repos, proposes stories, writes drafts, runs them through the gates and puts them in a queue for Henning. One part of it is a bug hunt that proposes candidates for posts about mistakes Claude made.
On the evening of 3 October 2026 the queue held seven drafts about the stock app. Five of them told stories the site had already published.
What went wrong
The commit that fixed it puts the problem in one sentence:
The bug hunt proposed the same stock-app mistakes again under new ids, and five of seven queued drafts retold published posts.
The bug hunt did have a way to remember what it had already written. It kept a record of which candidates had been turned into posts, but that record was keyed by the candidate’s id. In the code before the fix, the hunt rebuilt its candidate list by looking up each new candidate’s id in the list of earlier written candidates. If the id matched, the candidate counted as written. If not, it was new.
The ids did not stay the same between hunts, and the same mistake also came back under new titles. So the same mistake, resting on the same commits, came back looking like a new candidate. The comment Claude added with the fix says it directly: “Candidate ids change between hunts, so the same mistake comes back under a new id.”
All of the engine’s code is Claude’s work, including the id-based memory. Claude built a duplicate check around an identifier that changed between hunts. Titles and slugs would not have caught it either. The new file’s header says: “The bug hunt and the engine propose the same mistake again under new ids and new titles, so a title or slug check does not catch it.”
How we found it
The session notes from that evening record the result: seven drafts about the stock app, five of them duplicates of published posts “(samme commits)”, meaning the same commits. Those five were discarded.
The other two were rewritten and published.
From 4 October the engine runs on its own every week and publishes what passes twice.
The fix: compare the commits
Every post on this site lists its sources, and for a failure post those are mostly commits. Two posts about the same mistake will cite the same commits, whatever their titles say. So the fix compares commits.
The new duplicate check collects every published post and the commits it cites. For a failure post that also includes the commit where the mistake was introduced and the commit where it was found. Then, for a draft or candidate, it asks: is there one published post, about the same repo, that already cites more than half of this draft’s commits? If so, the draft is that post. When several published posts qualify, the one with the most shared commits wins. Short and full commit hashes are treated as the same commit if one is a prefix of the other.
The check now runs at four points:
- When the bug hunt finds candidates. A candidate whose commits are mostly cited by a published post is marked as written, with a link to that post.
- When someone presses “write post” on a candidate. It refuses, marks the candidate as written, and names the published post that “builds on the same commits”.
- When the engine writes from its own proposals. It skips the candidate and logs “already published as” with the slug.
- When a draft is approved for publishing. This is the hard stop. Publishing fails with a message to reject the draft or rewrite it around what is new.
Why the hard stop uses two commits
The first three checks trigger on a single shared commit, as long as it is more than half of the draft’s commits. The publishing check needs at least two shared commits.
The reason is in the engine documentation: some published posts rest on a single commit that a broader post also cites, and those are separate stories. A post about one specific fix can share its only commit with a wider post about the same month, and both can be legitimate. A one-commit threshold at publishing would block them.
The cost is easy to see. At publishing, a draft with a single commit can never be stopped as a duplicate, because it can share at most one commit. Earlier in the pipeline the threshold of one applies, so a single-commit candidate whose commit appears in any published post from the same repo gets marked as written before anyone writes it. That could hide a story that really is separate.
By late that evening the site had 54 posts and 10 app pages published, and the queue was empty.
The rule
Duplicates are decided by the sources, meaning the commits a post cites, not by id or title. A published post that cites more than half of a draft’s commits makes the draft a duplicate. The bug hunt, the “write post” action and the engine all skip it, and publishing stops when the two share at least two commits.