The coverage lane flagged transcript.ts line 63 (diffContentLines' empty-text
return) uncovered: the same-file diff test only fed newline-terminated sides.
Add a third hunk removing a line with an empty added side (a full deletion), so
the empty arm runs and the footer proves the empty side draws no `+ ` row
(+2 -1 · 1 file). Raise the test's line budget so every hunk row stays visible.
boot() asserts over ctx.loader after awaiting the Loader, but the TUI renders
as soon as its own fiber starts: an /exit typed before the last entry settles
runs disposeRootAndExit, which takes the Loader service with the tree. The
assertions then read undefined and crashed the process with a TypeError over
an app that exited exactly as asked. The keyless personal-overlay PTY smoke
lost this race in roughly two of three runs.
An empty read window (byte cap below the first selected line: `lines: []`
with `totalLines > 0`) dropped `offset` from the persisted presentation
meta, so a replayed read card could not report where the window starts or
where a continuation resumes. Carry `offset` on `FsReadMeta`,
`ReadResultView`, and the `presentationMeta` projection, and validate it in
`readMetaFromMeta` (1-based integer; the first line number may not fall
below it). Re-record the ACP fixtures and the cordis api catalog.
Also correct the Note's `parallel-file-reads` golden path
(examples/tui-agent -> apps/cli) and record the pre-card replay-degradation
tradeoff in the Decision section.
The web_search execute result and searchMetaFromValue each spread the
same {url, title?, snippet?, publishedAt?} projection over a seam source,
which the duplication gate flags as a clone. Extract projectSource, typed
on the seam's WebSearchSource, so both sites carry a byte-identical shape
from one definition.
The coverage lane caught a regression in the prior commit: FileMutationRow now
passes the tool's raw path to the injected openFile (which resolves against cwd
in apply.ts), but diff-card.spec still asserted the row pre-resolved. Assert on
the raw path instead. Also fix the built-boot diff assertion to match on the
line body and footer text — the `+ ` prefix is a CSS ::before, absent from
textContent.
Bring the TUI diff footer onto the same terminator rule and distinct-path
count the Web DiffBlock uses (a trailing newline terminates its line; two
hunks in one file read as 1 file), so the two front ends' `+A -R · N file(s)`
footers agree. Reserve space in the diff path header for the floating copy
button so a long path no longer scrolls under it. Pass the tool's raw path to
the injected openFile (which already resolves against cwd) instead of resolving
twice. Rename the shared block-body CSS class to a card-neutral cardBody so a
terminal-spacing tweak cannot silently move the diff card. Add a same-file
two-hunk TUI unit test and an assembled built-boot assertion that the write
turn renders +1 -0 · 1 file end to end.
The rename to deepseek-official reached cordis.snapshot.yml but not its
persistent-tools sibling, so the replay catalog no longer owned the route the
agent asks for. The SDK server then mounted the real adapter, which failed the
turn on a missing key. Re-records the six transcripts and the two diagnostics
cards that still carried the old route name.
renderFetchOutput has no external consumer: only formatFetchOutput and
fetchMetaFromValue call it, both through the registry, which deep-freezes
the result value. Exporting it let a hypothetical caller mutate a cached
input or the returned RenderedFetch and desync the card's truncated flag
from the model text. Drop it from the barrel and document that the memo
needs no defensive copy because every caller is internal and read-only.
The base (feat/web-presenter) removed the content field from
WebSearchResultView/WebFetchResultView: the web card carries no content
copy and a capability-less UI falls back to the raw tool/result content
(web-result-card note). The fixture still constructed both web result
views with a content field, failing the e2e build with TS2353. Drop the
content spread and the stale Omit key; the fixture already emits the same
text as the tool/result content the fallback path renders.
The mutate seam's root-path ops, its non-array rejection, and the recursion
into an existing nested object were unexercised, as was the whole containment
side of the settings/document-updated fan-out and the before-snapshot repair
of a section a hand edit left non-object. The settings.mutate route had no
round trip at all: neither the client method nor the handler entry ran.
Every expandable tool row shares one interaction (whole-row toggle,
icon-to-chevron hover preview) and one expanded body: an IN/OUT
gutter-labeled card with per-section 150px scroll caps and sticky labels.
toolRowModel derives result output and the error first line, terminalFailed
surfaces a failing exit as the collapsed row's red dot, a hover Inspect
pill jumps to the call's trajectory record through a one-shot store
handoff, and the chat view keeps its scroll offset across view switches.