---
title: "Three model-driven writers retired, one append-only ledger, one unregistered hook"
canonical: https://dxdev.com/blog/ledger-capture-not-from-prose/
datePublished: 2026-09-09
---
A ticket key sat inside a paragraph of prose in the ledger, and the capture hook we'd half-wired treated that mention as a reason to mint a new unit.

## Three writers out, one ledger in

Our internal tracker for finished units of work used to run through three model-driven writers. Their job was to read a session transcript, decide what "units" of work happened, and rewrite a block of markdown with the result. Every session got re-summarized by a model, and every re-summarization was a chance for drift. Numbers moved between runs on the same underlying work.

We retired all three writers this session and replaced them with a single [append-only ledger](/blog/verify-deployment-not-guess/). You write a row, you never rewrite a row, and the current state is "sum the rows." There is no model in the write path. If something is wrong, you find the bad row instead of arguing with a summarizer about what it meant last time.

That part shipped clean. The ledger is live, and we built a prototype board that reads straight off it instead of off model output.

## Capture on mention

The next step was auto-capture. The obvious trigger was "a ticket key appears." It was cheap to implement and cheap to reason about, and it already had a name in our heads before we'd tested it: capture on mention.

We built the hook against that trigger and ran it over the day's actual session logs to see what it would have captured. It fired constantly. Ticket keys show up in prose for reasons that have nothing to do with a unit of work happening. A key appears when you close it, but also when you reference it in a decision writeup, quote someone else's comment thread, list it in a "carried over to" note, or cross-link it in passing while talking about something else. A mention-triggered hook can't tell text that reports work happened from text that talks about the ticket. It would have logged the same unit two or three times over, or logged units for tickets where nothing had moved.

## The reviewer said no

We ran the plan past a [second AI reviewer](/everyday-ai/the-review-that-said-clean/) before registering the hook. The verdict came back flat: **DO NOT REGISTER YET**. Its finding was precise. A ticket key merely mentioned in prose is not a real creation trigger. Mention is not completion, and it is not a state change. Nothing about it lets you safely say a unit happened here. The code was structurally sound, and the trigger it was wired to was the wrong signal.

We had no fallback ready, so the hook stayed unregistered. A plausible-looking version would have double-counted or missed things without any noise. A wrong ledger costs more than no ledger, because nothing about an append-only log tells you a bad row got appended. There is no rewrite step to catch it.

## A day later, with receipts

The hook sat there for a day, built and unregistered, while we worked out what to do about a trigger we couldn't fully justify.

What unstuck it was accepting that mention-detection would stay imperfect and putting a different kind of check on it. Every row the hook writes now carries where it came from, when, and what asked for it. A wrong capture becomes a receipt you can trace and prune, where before it would have been a number silently folded into a sum. We turned the hook on the next day with that in place, a hard cap on how long it's allowed to run, and a rule that it never blocks the session if it fails.

## Validation lives at the write

Append-only design pushes all of your validation to the write path, because there is no cleanup step downstream. Validation doesn't only mean getting the trigger right before you flip it on. Sometimes it means admitting the trigger will never be fully right and building the receipt that lets you catch it anyway. A rewritable summary can absorb a bad row and correct it on the next pass. A ledger can't undo one, so it has to be able to point at one.

If a system records work automatically, check what event triggers each entry and whether that event proves the work happened or only that it was mentioned. Then check that every entry carries its source, time and requester.
