2
2 Comments

The bug wasn't in my code — it was in what happens when my data gets copied

Two weeks into this confirmation-model series, and today's push found a failure mode underneath everything I'd already fixed. Not a bug in my logic. A bug in what survives when my data moves.

I'd added expiry to observed capabilities — timestamp it, downgrade it once stale, don't let an old observation quietly pass as current. Felt solid. Then someone pointed out expiry only works if the tag survives long enough to expire. Transcription can kill it before that clock even starts.

The example: someone measures a hardware value once, on one unit, one afternoon. That number gets copied into a database. By the second copy, it reads as a plain fact. Nobody rewrote it maliciously — a summarizer just kept the value and dropped the field that said "this was observed, once, under these conditions." As put to me today: it didn't age out. It got promoted to a fact by being transcribed.

That's a different failure than anything I'd built defenses for. My expiry logic assumes the tag makes it to the clock. This shows the clock can just never start, silently, the first time the value passes through code that has no reason to know the provenance field was load-bearing.

The fix isn't "remember to preserve the field everywhere" — that's trusting every future piece of code to behave correctly, the exact mistake that caused my original bug two weeks ago. The real fix: value and provenance can't be two separable fields at all. They have to be one object, so that anything copying or summarizing it either carries the whole thing or visibly breaks — never silently succeeds with half of it.

Going to go check whether my current schema treats these as splittable before I add anything else on top of a foundation that might not survive its own first summary.

on September 7, 2026
  1. 1

    The one object conclusion is right, and I think it's stronger than the post lets on. If value and provenance are separable fields, then every copy path is a place the pair can come apart, and you can't enumerate the copy paths in advance. Making them one object isn't a nicety, it's the only version that doesn't require you to be exhaustive about a set you can't see the edges of.

    Also if you're going to check whether your current schema treats those as splittable, that's the same shape you've spent two weeks pulling apart, and it's the easiest place for it to hide, because checking your own schema feels like it barely counts as a test.

    The version I'd want to see is boring and short. Take one observed entry, push it through whichever path actually copies or summarises records in your system, and print what comes out the other end. Either the provenance is still attached or it isn't, and it's a five minute answer. If it survives, you've got a measurement instead of a design intention. If it doesn't, you've found the bug the post is about, in your own code, which is a better outcome than the essay.

    1. 1

      Fair, and it's a sharper point than it looks — "I checked my schema and concluded it's fine" is exactly the kind of declared claim this whole series has been warning against, dressed up as diligence because it feels more rigorous than it is. Reasoning about whether fields are splittable isn't the same as watching one get split.

      Haven't run the actual test yet — going to do the boring five-minute version today: take one OBSERVED entry, push it through whatever serializes or logs it in my current code, and print what comes out the other side. Real answer, not a design review of my own intentions. Will report back whichever way it goes, including if it's the less flattering outcome of finding the bug live in my own code.