Three duplicate posts, three different causes
An Instagram account that posts without a human posted the same thing twice, three separate times. Each incident had a completely different root cause, and each obvious fix would have been useless against the other two.
- Whole caption — real repeat vs unrelated
- 44% vs 36%
- Hook line — real repeat vs unrelated
- 100% vs 17%
- Independent guards
- 4
- Duplicates since
- 0
What happened
The account publishes three times a day on a schedule, with nobody watching. Over a fortnight it published a duplicate three times.
The first was a mislabelled slot. The job worked out which of the three daily posts it was by looking at the wall clock, so a run that fired hours late — which happens constantly, see below — silently believed it was the next slot and published that slot's content on top of its own.
The second was a deleted post re-arming its own topic. The ledger of what had been published was reconciled against the live feed, and deleting a post removed it from the feed, which made its topic eligible again.
The third was a lost ledger write. The publish succeeded, the row recording it did not, and the topic came back around days later.
Why one fix was not enough
Each of these has an obvious individual fix, and each of those fixes is completely useless against the other two. Identifying the slot correctly does nothing about a lost ledger write. A more reliable ledger write does nothing about a deleted post.
The thing they have in common is not a cause, it is a shape: every one of them was a single point of trust. So the answer was not a better guard, it was four guards that fail independently.
The four layers
A durable per-slot lock, where the atomic operation is a git push to a separate repository. This stops the same slot publishing twice.
Exclusion of any content whose source ID already appears in the ledger — exact match first, then similarity. This stops the same content being picked again on a later day.
A reconciliation pass before every generation that adds any live post missing from the ledger. Add-only by design: deleting a post must never make its topic eligible again, which was the second incident. This is also what makes layer two survive a failed ledger write, which was the third.
A final check against the real feed immediately before publishing. If the content is already live it aborts the slot and raises rather than posting.
The threshold was measured, not guessed
Layer two needs to decide whether two posts are the same. The obvious approach — compare the whole caption — looked reasonable and did not work.
Against the real feed, a genuine repeat scored 44% overlap and an unrelated pair scored 36%. Eight points is not a threshold, it is noise. Comparing only the hook line separated cleanly: 100% against 17%.
The check was rebuilt on hook lines, and the code carries a comment telling the next person not to 'improve' it back into a whole-caption comparison, because that improvement is the bug.