355 chat sessions turned into 1,748 work units in one backfill, and the first thing the verifier caught was a git attribution label that was wrong.

That result changed the way I think about a chat session. It is not a blob of context that evaporates when the tab closes. It is a piece of work with inputs, a sequence, a result, and a claim that can be checked later.

I built this because I wanted the blog to come from work we had actually done, not from a weekly attempt to remember what sounded publishable. That is a different problem from summarizing chats. A summary can tell a plausible story after the fact. It cannot answer which part of a session shipped, which conclusion was verified, or where a lesson came from.

The unit became the work currency. Everything else in the pipeline is a rollup or a query over that record.

The record lives with the session

Each substantive Claude session now gets decomposed into a schema-v0.2 units block on its session manifest. Every unit carries four fields: status, verified, disposition, and lesson. The block is grounded in the transcript, not inferred from the last assistant message.

That distinction matters. A session can contain exploration, a rejected implementation, a fix, a verification step, and a follow-up that belongs in another ticket. Flattening it into one sentence erases the useful boundaries. The units block preserves them without pretending the whole chat was one successful task.

The public command shape is small enough to run from a terminal:

worklog units gen <session>
worklog units show <session>
worklog units render <session>

gen writes the units block. show prints it. render gives me an HTML review view when I need to inspect the record as a person rather than as data. The important design choice was to put the result back on the manifest. A separate analytics database would have made querying easier, but it would have split the explanation of a unit from the transcript that supports it. A daily narrative alone would have been simpler, but it would not carry per-unit verification or disposition. I needed the evidence and the index to stay adjacent.

Backfill was the first real test

The system only became useful when it met history. I did not want a clean pipeline that covered Tuesday forward and left months of real work outside the model.

The batch entry point is deliberately explicit:

worklog units backfill [--since/--until/--limit]

It decomposes substantive past sessions in shards. The bounds are not decoration. An early --all path could run away into 2014, which was a useful reminder that archival automation needs a fence before it needs a clever prompt. The corrective design was bounded batches, then a daily catchup job for recent undone sessions:

worklog units catchup

Two daily jobs keep the record moving. The unit catchup runs at 06:20 and uses prepaid capacity. A work-log sync runs at 05:50 without an LLM call. The first deconstructs recent conversations. The second merges chat history and git commits into a unified daily work log. Both were designed to avoid metered spend, which matters because a pipeline that costs money every time it notices an unfinished record eventually stops noticing.

The initial run processed 355 sessions into 1,748 units and 582 lessons. It also synced 80 daily files beginning on 2026-03-28, produced weekly commit rollups for W13 through W25, and generated narrative retros for W20 through W25. Those are not dashboard counters. The numbers were independently verified.

That verification found the mislabeled git attribution. It also caught the unrestricted backfill path and a footer leak in the weekly retro. I fixed each one before treating the backfill as a source of truth. The diagnostic path was straightforward: inspect the units and rollups, compare them against the underlying session manifests and commit history, then fix the mismatch at the generator rather than editing the report. A report that is manually patched after every anomaly is not a record. It is a document with better typography.

Rollups are views, not the source

Once individual sessions have units, the rest of the pipeline is ordinary composition:

worklog sync-daily [date/--all]
worklog weekly [--narrative]

The daily command joins git commits and session history into one work log. The weekly command creates the same kind of rollup across the week. --narrative adds prose for a retrospective, but it writes to a separate -retro.md file so the recurring regeneration cannot clobber the human-readable version.

I considered making the weekly narrative the canonical artifact, because it is the easiest thing to read. It lost because it is a view. It cannot safely explain a surprising line without walking back through the units, manifests, and commits underneath it. The same applies to a raw git log. Commits show code movement, but they miss research, verification, customer-facing diagnosis, and decisions that produced no code. The useful record has to contain both, then let the rollup summarize them.

Mining is where the blog enters the system

The blog does not get a special intake ritual. It reads the same work currency:

worklog units mine [--html/--json]
worklog units seed

mine harvests lessons and units with enough substance to spin into a post. seed clusters those lessons and appends candidate topics to a backlog. In the backfill, 582 lessons became 10 blog-post seeds.

A seed is not a post, and I do not want it to be. The source record has to survive the clustering step so I can inspect the technical sequence, the failures, and the supporting work before drafting. That is how I avoid turning an internal lesson into a generic claim about agents or automation. If the record only supports three sharp paragraphs, the post should be three sharp paragraphs.

This is what made the blog sustainable for me. I am no longer asking a model to manufacture a publishing cadence from vague memory. The system tracks work at the point where it happens, preserves the verdict and the evidence, rolls it up with the commits, then exposes the material for editorial judgment. The post begins as a verifiable unit of work, not as an empty text box.