What broke
Three parallel writers lost each other's marks, and the same mistakes were written up again
This site's engine saved its "written" marks by overwriting the whole candidate list, so writers running at the same time erased each other's marks and old stories came back as new drafts. On 3 October 2026, five of seven drafts in the queue retold posts that were already published.
The short version
What went wrongThe write step saved the whole candidate list from a stale copy, so parallel writers erased each other's written marks, and reruns gave the same mistakes new ids. Five of seven queued drafts retold published posts.
How we found itThe review queue held seven stock-app drafts that looked familiar, and the commit records showed five retold published posts. The first fix had only stopped lost marks.
The rule nowRefuse to write an already-written candidate, save marks under a lock on a freshly re-read list, and detect duplicates by shared commits rather than title, slug or id.
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. One part of it is a bug hunt. It reads a repository’s history, proposes candidate stories about mistakes Claude made, and puts each one on a list with a “write post” button. Press the button and the engine writes a draft, runs it through the gates and puts it in the review queue. It then marks the candidate as written, so nobody writes it twice.
That mark is where it went wrong. On 3 October 2026 we fixed it in two steps, once in the afternoon and once late in the evening.
What Claude got wrong
Claude wrote the code, as it writes all code in these repositories. The first version of the write step worked like this:
- Load the candidate list from disk.
- Write the draft and run the gates, including a model check. This takes a while.
- Set
writtenon the candidate in the copy loaded in step 1, then save that whole copy back to disk.
That is safe with one writer. With several it breaks. Each writer saves the list exactly as it was when that writer started, plus its own mark. The last writer to finish overwrites everyone else’s marks. The commit that fixed it, b6a26da, puts it this way:
three parallel writers overwrote each other and lost marks, which led to duplicates.
When a mark is lost, the candidate looks unwritten again. The button comes back, and the same story can be written a second time. The write step also never checked whether a candidate was already written. It only checked whether that same candidate was being written at that moment.
The first fix
Commit b6a26da changed two things in the write step:
- Writing a candidate that is already written is refused with an error that names the existing draft.
- The mark is no longer saved from the stale copy. A small save function re-reads the candidate list from disk, sets the mark on that fresh copy and saves it. Calls to it are chained one after another, so two saves never interleave. The code comment says why: “Re-read before saving: other posts may have been written in parallel since we loaded the list.”
The same commit also tidied the list. Candidates whose post is published are hidden, and only a one-line count with links is left. A written candidate now shows where its draft is: in the queue, in the repository, or rejected. Before, every written candidate linked to the queue, wherever the draft actually was.
How we found the second problem
The lock stopped marks from disappearing. It did not stop all duplicates. About seven hours later the review queue held seven drafts about the stock app, and most of them looked familiar. The commit from that evening, 102857b, records what we found:
The bug hunt proposed the same stock-app mistakes again under new ids, and five of seven queued drafts retold published posts.
The session notes from that evening say the five duplicates were rejected. The other two were rewritten and published.
This was a separate gap in the same design. The bug hunt gives candidates new ids each time it runs. A “written” mark is stored on an id, and when the hunt reran, it only carried marks over for ids it had seen before. A mistake that already had a published post came back under a new id and a new title, with no mark. As the engine docs put it, the same mistake “comes back from the bug hunt with a new id and a new title, so title and slug don’t catch it. The commits do.”
We can’t tell from the material how many of the five came from lost marks and how many came from new ids. Both paths ended in the same place: an old story offered as new.
The second fix: compare commits, not names
Commit 102857b added a duplicate check built on the commits a post cites in its sources. If more than half of a draft’s commits are already cited by one published post from the same repository, the draft counts as that post, retold.
The check now runs at four points:
- When the bug hunt runs. A candidate that matches a published post is marked as written right away, with a link to that post.
- When someone presses “write post”. The engine refuses and names the published post that uses the same commits.
- When the main engine writes. It skips the candidate and logs which published post it matches.
- At publishing. This is the hard stop. Here at least two commits must be shared. The engine docs explain why the bar is higher: some published posts rest on a single commit that a broader post also cites, and those are separate stories.
The publish error does not just say no. It tells the reviewer to reject the draft or rewrite it around what is new.
What this says about the design
Both failures came from the same assumption: one writer at a time, and ids that stay the same. Neither held. The engine could run writers in parallel, and the bug hunt gave the same mistake a new id when it ran again. The “written” mark depended on both.
The five duplicates sat in the review queue and were rejected there. The queue showed the cost: most of the drafts that evening were stories already told.
The rule that came out of it
The engine refuses to write a candidate that is already written. The “written” mark is saved under a lock, on a list re-read from disk, never on a copy loaded before the slow work started. Duplicates are found by the commits a draft cites, not by its title, slug or id: more than half shared with one published post means it is that post, and publishing stops at two shared commits.