Show the reviewer what is still live, and keep the settled as a lookup
Two changes that both come down to not spending a reviewer's minute on something with nothing left to decide.
The front page is now the live set
Measured across the eleven documents in .intent/: 68 assumptions, of which 30 are grounded_in_code and 10 confirmed_by_user. Those 40 are settled, and they are mostly the agent scoping the DOCUMENT rather than describing the code — "Cloudflare is a deployment detail and should not appear in this view" is not a thing anyone reviews. All 68 were rendered in one list under "review these first", with confidence demoted to an italic aside, which was not enough to tell them apart.
So only the 28 unverified ones are rendered, and the confidence label goes with them: in a list where everything is unverified there is nothing left to distinguish. What was never exercised (98 entries across those eleven documents, live by definition) moves up from being the last thing before the footer to its own section ahead of the tour.
Answered questions are kept, out of the reading path
A question that has been answered and grounded is no longer a question, and an interview can produce any number of them. But discarding them is worse than useless: a reviewer re-derives an answer somebody already has. Across 32 runs on disk there are 188 such answers, 5.9 per document, and they read exactly like a reviewer's own questions — whether a dispatch input is restricted to a known list, whether the deployed Compose version supports a flag that was used.
They reached the document as a COUNT in meta.interview and nowhere else. Now the server stamps them into evaluated, collapsed behind a disclosure, to be consulted on arrival at the same question. The server stamps it for the same reason it stamps meta.interview: a section an agent could write by hand is one it could invent.
Selection is structural, no text matching. seeded is out — the standing six ask for the plan, the verbatim asks, the trials, the assumptions, the incidental files and what was run, and their answers already ARE the document's own fields, so including them would say everything twice. Unanswered and unresolved are out: an open question belongs on the front page as a live finding.
InterviewRound gains anchors, recorded by the reviewer when it composes the question, because that is the only moment anyone knows which hunk raised it. Of 188 diff-provoked questions already on disk, 73 name a hunk in their prose and the rest name none, so this is forward-only by agreement rather than back-filled badly.
The anchors render as plain labels in the viewer, not links: evaluated anchors are hunk ids, while a stored tour stop's anchors are a path plus line ranges, so there is nothing to match them to. A chip that could not navigate would be worse than a label that does not pretend to.
The filter is repeated in three renderers rather than shared. The worker is a standalone bundle and the viewer has no module step, so neither can import the validator; the worker's renderer already carried a "mirrors renderPrDescription" comment. The predicate is one line, and stamping a flag into the schema instead was not worth changing the format's public contract for a reduction available today.
ALSO: the cross-session lookup, reworked into two explicit steps.
Step 1 narrows by file. Step 2 attributes by content, and it is the step that answers: a session that wrote a line put it in a tool INPUT, while one that read the file got it back in a tool OUTPUT. On commit 05c90a4, matching 30 added lines, the session committed the next day scores 30/30 authored and 0 observed while three others score 0 authored against 19 to 30 observed. Step 1 cannot tell them apart. On 2ceb8ca, step 2 overturned step 1's top candidate, which had authored 1 line of 15.
The parsers gained attribution() for this, kept off ParsedEntry because the spine parses every entry and would otherwise carry a copy of every tool result it exists to elide. The earlier edited/mentioned split is gone: step 2 supersedes it, and it barely existed anyway, since spine.ts has zero Edit-tool writes across every session on disk.
Also
- •Improve blog cards and article artwork.
- •Find other sessions that touched review hunks.
- •Let the author read the code before it reports the transcript silent. Many of the reviewer's questions are not about the session at all. "deleteWorkflow reads status, then calls removeExecutionTree, then deleteExecution. No transaction, no row lock" is answered by the surrounding function, which lies OUTSIDE the diff.
- •Say how each question ended, and ask the developer before giving up. The interview had a stopping rule, MAX_BATCHES, but it governs the CONVERSATION rather than the question. A halt is not a verdict: whatever was still open fell into one undifferentiated bucket, so a secret blast radius nobody had considered rendered exactly like "why did you pick this name".
- •Let what is still uncertain say which hunk it is about. A reviewer arrives at a tour stop wanting three things: what changed, what to check, and what is still uncertain HERE. The document could only answer the first two.
- •Show a reviewer what is uncertain at the stop they are looking at. A reviewer at a tour stop wants three things: what changed, what to check, and what is still uncertain HERE.
- •Divide the session into what it was about, and answer from that. The spine is complete and that is its virtue: a session is a median 4.8% conversation and 95% tool output, and serving all of the 4.8% removes the one failure retrieval can never rule out, an answer nobody found. But complete is not relevant.
- •Express the author's client grant per client, not once for all of them. Granting the author file access listed
Read,Grep,Globand laterAskUserQuestionin one table, for every client. Claude Code got a correct allowlist. The other two got an access list naming four tools they do not have. - •Write down what a hunk does while reading it, not after. The largest single cost in a distillation is the reviewer drafting between interview batches. Measured over the 15 runs on disk: 303, 330, 544, 592 and 916 seconds, in every multi-batch run, and it is the biggest in-run gap every time.
- •Ask only where the plan and the change disagree. Batch one was a union: the baseline set, the house rules, and everything the diff provoked. That union is why the count ran 18 to 34 — and question count is what drives the author's single answer call, a median 57.8 KB of answers and about 14.4K generated tokens, all of it on the critical path.
- •Stop a client that installs nothing from deleting the role definitions. A Claude Code session's intent-author and intent-reviewer subagents disappeared whenever another tool connected to this server. The only symptom was their absence: the server reported them installed, and some time later they were simply gone.
- •Typecheck the tests, which is why a duplicate import went unnoticed. roles.test.ts carried seven duplicate import bindings — mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, tmpdir and join, each imported twice — through a green
npm run typecheckAND a greenvitest run. - •Stop the test suite deleting the developer's Codex role definitions. Running
npx vitest runremoved ~/.codex/agents/intent-*.toml. Not a hypothetical: planting that file and running the suite deleted it, twice, before and after the diagnosis. - •Regenerate styles.css for the one class the walkthrough added. CI's "Generated files are current" step has been failing since 721f51b, on main as well as on this branch. The whole diff is 23 characters:
Commits for this release: v1.0.6…v1.0.7