Yesterday I published a post about my flight recorder crashing four times on its first real flight. I thought the lessons were banked.

Today I asked my agent to confirm the recorder was integrated correctly. It came back holding two bugs I shipped — and one paradox I still can’t stop thinking about: the recorder invalidated its own tamper-seal by recording the act of sealing.

Two Bugs The Author Shipped

The agent wrote the test suite in one session: 17 tests, 41 assertions. The suite paid for itself before lunch.

Bug What it meant Root cause
verify! ignored output blobs Corrupted payloads passed as “verified” — the recorder’s core promise, broken The check loop only walked :in-hash, never :out-hash
Topological sort never resolved dependencies Any dependency chain degraded to UNRESOLVABLE-DEPS One-liner compared dep IDs against node maps instead of node IDs

Neither bug crashed anything. Both failed silently — which is the worst kind of bug in a tool whose entire job is producing evidence.

The Paradox: A Seal That Breaks Itself

Then I wired auto-sealing: every run gets an Ed25519 signature over its chain head when the command finishes. The first live run failed verification.

signed-head: 46e7d0024a5d15be...
log-head:    86011ead1654c429...

The seal was valid — for a log that no longer existed. The culprit was my own success message. After signing, the code logged run-sealed to the flight recorder. That log line appended a node, which moved the chain head, which left the signature pointing at a past that was no longer the present.

Diagram

The recorder recorded the act of sealing — and the recording invalidated the seal.

The fix is a rule I now enforce everywhere: nothing appends after the seal. The notice goes in before the signature. The signature is the last write to the log, always.

Why The Bugs Were Legible

People assume Clojure’s magic is homoiconicity. It wasn’t the hero today.

Mechanism What it did
EDN logs The evidence was a readable file — stored head vs signed head, mismatch self-evident
Immutable append-only design The paradox was provable in seconds; nothing overwrote anything, so both heads existed to compare
Pure functions + tests The topo-sort bug confessed on sight: ["first" "UNRESOLVABLE-DEPS"] — deterministic output, no excavation

Data-orientation made the bugs legible. Purity made them reproducible. Writing tests made them findable. Any language can eventually find these bugs — few architectures hand you the evidence this cleanly.

Three Rules I’m Keeping

  1. Verify checks every hash it names — in-blobs and out-blobs. A verifier that samples is a liar with extra steps.
  2. A recorder’s own annotations are mutations — the seal is the last write, and the code that guards the evidence must treat itself as a suspect.
  3. Tests on day one — this suite caught two shipped bugs and one design paradox in its first hour. The second hour paid for the year.

The recorder works because it cannot lie. Now it cannot even lie about telling the truth.