The 12:00 BST slot missed. It was the second miss of the morning. 09:00 had gone the same way, Fix C alarming twice in four hours. Queue id=37: schedule_sweep fired the publisher at 11:00:08Z, the publisher cancelled the row, the substitute pool recursed into future-scheduled rows. Fixing that particular failure was the day’s main work.
The substitute pool’s candidate list surfaced a different question. The pool was pulling from the same tag across multiple upgrade decisions. Some of those candidates shared the same devlog entries. Different thesis angles, same underlying source material. The pipeline could draft both. Nothing in the queue logic would stop it.
That was the sibling-rehash gap.
The rule
The citation policy is simple. A draft can cite a previous article only when it’s doing one of three things: improving on it with better results or more complete understanding, documenting a divergent path on the same problem, or showing regression where something that worked now fails. Anything else is a sibling-rehash. A sibling-rehash doesn’t get written.
The rule makes sense. The question is where it lives.
Before the 2026-06-13 implementation, the rule lived in CJ’s head and in informal editorial practice. That’s adequate for a manual drafting process where every draft is reviewed before entering the queue. In a pipeline that runs automatically (picker generates variants, CJ picks one, drafter spawns, article enters queue), “in CJ’s head” is a gap between the decision point and the enforcement point.
Layer one: the prompt instruction
The first layer is a prompt instruction to the drafter. Sonnet 4.6 receives the thesis variant and the devlog entries for the tag. The prompt now explicitly states the citation policy: if the thesis would only recap source material already covered by a published article, return the structured refusal JSON instead of a draft. The refusal carries a reason, which lands in the audit log.
This works most of the time. When the policy is stated clearly, Sonnet 4.6 recognises coverage overlap reliably. When the thesis is genuinely distinct from anything previously published, the drafter proceeds. When the thesis would produce a sibling-rehash, the structured refusal fires.
The word “most” is doing work in that sentence.
A prompt instruction is a probabilistic constraint. The model follows it reliably under well-specified conditions: clear rule, clear examples, clear output format. It follows it less reliably when the overlap is subtle. When the thesis is adjacent to but not identical to prior coverage, or when the devlog entries for the tag are thin and the model fills gaps from training data, no amount of prompt precision closes the gap entirely. Prompts are probabilistic.
Layer two: the filter
The second layer is deterministic. When a draft cites prior work, it declares a sequel_to field in the frontmatter: the published slug it claims to be improving on, diverging from, or regressing from, alongside a relation value (improvement, divergence, or regression). The substitute filter validates that contract at queue entry. A draft that cites an overlapping article without a valid sequel_to contract gets rejected before it reaches the queue, defensively, as a matter of policy, regardless of whether the LLM believed the draft was editorially distinct.
This is the layer the prompt can’t replace. The filter doesn’t reason about the content of the articles. It checks a field. The field is either there or it isn’t. The declared relation is either one of the three valid values or it isn’t. The named slug either exists in the published canon or it doesn’t. None of those checks are probabilistic.
The asymmetry matters. Layer one catches the obvious cases early, before a Sonnet API call produces a draft that’s already been written. Layer two catches the cases layer one misses, without relying on the model to recognise its own coverage overlap.
Why not just the filter
The obvious question is why the filter alone isn’t sufficient. If the filter is the reliable layer, put the rule in the filter and skip the prompt instruction.
The filter catches drafts that carry sequel_to without a valid contract. It doesn’t catch drafts that should carry sequel_to but don’t. Detecting that requires recognising the coverage overlap, reading both articles and deciding whether one recaps the other. That’s not a field check; it’s editorial judgement. The drafter prompt handles that case: by instruction, the model is expected to recognise when the thesis would only recap prior coverage and fire the structured refusal before any draft is produced.
The two layers cover different failure modes. The prompt handles “the model doesn’t recognise it’s rehashing.” The filter handles “the model recognised it was extending prior work, cited it in the prose, but didn’t declare the frontmatter contract.” Neither layer alone closes both gaps.
Out of scope: cross-variant deduplication, detecting when two variants from different upgrade decisions would produce the same article before any draft is attempted. That belongs in the picker, not the drafter or filter.
What makes it an editorial rule
An editorial rule enforced only in a prompt is an aspiration. It’s a better aspiration than nothing: the model usually follows it, the structured refusal surfaces the cases where it doesn’t, the audit log accumulates evidence of both. But it stays an aspiration until something deterministic backs it up.
The sequel_to contract is the deterministic backing. It’s a machine-checkable field on a machine-produced file. The substitute filter checks it at queue entry without human review. That’s the point at which the rule transitions from “the model is told not to do this” to “the pipeline cannot produce this output without satisfying the contract.”
The distinction matters more in auto-mode than in manual drafting. In manual mode, CJ reviews every draft before it enters the queue. The filter is a safety net. In auto-mode, the filter is the primary guard. It has to be reliable. Reliable means deterministic.
Where this lands
The citation policy combines prompt refusal with filter validation. It’s the first editorial rule that runs in both places. The pattern generalises. Any quality or editorial constraint that needs to hold in auto-mode is a candidate for the same treatment: state it clearly in the prompt so the model has the best chance of following it, then make the contract machine-checkable at the output boundary so the cases it misses don’t reach the queue.
The prompt teaches. The post-processor enforces. For anything that matters, you need both.



