v0.8.0 — the chain's own machinery
A review run now knows when to stop, and a check can no longer report clean while something is missing
Two failures shaped this release, and both were silent. A review run had a ceiling — three rounds, five while something blocking was open — and nothing underneath it, so the ceiling got spent like a budget: one run went seven rounds and the last three found nothing that mattered. And a ledger check narrowed to one work item's own file reported everything fine while the shared ledger rotted beneath it — three review rounds and two fix passes all said ok, and the unscoped read at the pull request found fifteen stale rows and one false claim, every one in a file that branch had touched.
A run now has a floor. Stop when a round finds nothing that leaves the root and nothing that crashes. Whatever else that round found is deferred to a named answerer or becomes an issue. The reviewer answers it in a line of its own, the round record carries a row for it, and chain_check.py reads that row at the pull request rather than leaving it to whoever is awake.
A fix pass may add a unit; that unit's fix may not. The rounds a floor removes are exactly the rounds that were reading what the previous fix pass had just created — measured across four rounds of one work item, three consecutive rounds found their finding inside the unit the previous fix added rather than in the fix itself. By construction the fix ships reviewed and the unit it added ships unreviewed, in one commit. The record now declares the depth of each new unit, and a unit added to answer a finding inside another new unit is refused, with the two places it goes instead named in the refusal.
A narrowed ledger check announces what it did not read. Guidance binds only a session that reads it, and the session this trap was sprung on had narrowed the command on its own initiative. So the tool says it now: a scoped run opens by naming every ledger it skipped, one per line, and how to read them. A run that narrowed to exactly what the defaults would have opened says nothing.
Two names for one file are one ledger, matched by inode rather than by a spelling of the path — so a case variant on a case-insensitive filesystem, a hard link and a symlink all count as read. Comparing paths had put a platform inside the answer.
A round record written after the fixes it commissioned no longer looks like one written before them. By the time a late record is committed its verdict cells read fixed at <sha>, which is exactly what a correct record looks like after its own update pass — so lateness left no trace, and the reviewer's drafted text died in a report while the next segment rebuilt it from scratch. The checker now refuses a record whose adding commit descends from a commit its own verdicts name as the fix.
What else moved
- Every segment's record now says what ran it — the agent and the model, joined by a word rather than a punctuation mark. Two work items were metered segment by segment before this, and not one of those readings can be attributed afterwards, because the model lived only in a session transcript.
- A measurement meant to span versions was being written to the issue the next release deletes. The rolling log and the durable ledger are now told apart, the durable one found by a label rather than hardcoded. And a rolling log used to be born empty; it now opens carrying what it rolls from, the version it closes on, and where the cross-version readings go.
- Older work items are not made red. Every new rule is keyed to the id of the work item that wrote it: a record from before the cutoff prints instead of failing, because a merged record has no honest repair. A row that is present and malformed is refused at any age, since formatting is always the author's.
Where the cost went, honestly
The last work item of this release took fifteen review rounds where three is the rule. Two of them reviewed the feature. The other thirteen were the tool reviewing its own fixes and its own paperwork, and half of every finding was located in a record rather than in code. That is measured in #161, which is the whole of 0.8.1 — alone in its release, so that everything after it is the first work run under a chain that stops.
Full changelog: v0.6.0...v0.8.0