Four values complete the vocabulary, so the opaque body is reached only by
producers that genuinely promise no shape.
`snapshot` — current state a later snapshot supersedes. system-prompt now
exposes `renderContextSections()`, the named contributions
`renderContextSnapshot()` already joins for the model, so the body attributes
each part to the subsystem that produced it instead of re-splitting joined
prose. The runtime snapshot, time-context, and tmux-context declare it.
`notice` — a one-off account of what just happened, declared by tool-tasks,
goal state changes, tool-goal wrap-up, plan-mode switches, and
repeat-tool-guard. Its `summary` rides the COLLAPSED row: these five are the
majority of shipped producers and none of them needs expanding to be read.
The task summary bounds itself because its inputs are unbounded caller text.
`relay` — a message another agent addressed to this one; both subagent
sources declare it and the body names the sender above what it said.
`recall` — material lifted from another session's log. session-reference
needed no new field: its references already record retained and omitted
counts and the truncation flag, which the body shows first, because recalled
context is bounded on the way in.
`ContextFormed` is now discriminated by `form`, so a producer cannot declare
a shape without the facts that shape is presented from — a notice without its
summary, or a snapshot without its sections, fails to compile.
Only the two hook bridges stay opaque, by design: their content is whatever
an external program printed, so no shape can be promised for it. Unknown
kinds and unreadable records land there too.
Resolve the agent-loop import conflict by retaining both durable request context and runtime policy context. Refresh the combined session fixtures and regenerate documentation catalogs. Mark PDF artifacts as binary so staged whitespace checks do not parse PDF bytes as text.
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 read tool now projects presentationMeta ({path, lines, totalLines}) onto
its tool/result, so every scenario with a read call carries that meta; the
cordis-inspect snapshot's embedded type surface gains ReadResultView /
ReadFileLine / the widened ToolResultView. Model-facing text is unchanged.
Refreshed keyless via test:snapshot:refresh. The unrelated goal.snapshot
SQLite ExperimentalWarning failure is pre-existing on clean master.
The native adapter's route was named deepseek, colliding with pi-ai's
catalog provider of the same name, so the two DeepSeek paths could never
be mounted side by side. The web settings page needs both configurable at
once. Compositions, fixtures, goldens, scaffolding defaults, and docs all
move together (pre-release, no shim); TUI/session-query-spill/
missing-credential goldens re-recorded through their keyless refresh
modes because provider-name length shifts box padding and spill
truncation points.
Restack on the carved-out foundation (#192), per review feedback on #170.
The seam files resolve to the carve-out's revision — its prompt-order
neutrality fix (backends no longer inject 'tools'; the structured runtime
gates its own capture-tool registration) restores the subagent tools to
master's front position, so every recorded fixture is re-recorded on the
stacked tree and the authored error-finish/cancel headers re-patched to the
stacked tool list ([subagent, subagent_fork, workflow, todo_write, ...]).
Every session.jsonl fixture embedded the full composed system prompt and
complete tool-schema list in its request/header event (~8 KB on one line,
identical across the suite), so any prompt or tool-schema edit forced a
re-record or hand-edit of every fixture — see the dynamic-workflows PR for
the churn pattern this removes.
Now exactly one scenario (text-turn, flagged pinsHeader) commits and
compares that content verbatim; every other fixture stores and compares it
as {{system}}/{{tools}} tokens via the new pure scrubRequestHeaders
normalizer (applied to both compare sides and to record-mode writes, so a
re-record cannot reintroduce the content). request/header-delta payloads
are scrubbed the same way; config/reason stay verbatim — a model swap
SHOULD churn every fixture, a prompt edit should not. Replay is unaffected:
script derivation reads only assistant/chunk events.
Fixture meta-guards enforce the split: non-pinning fixtures must be fixed
points of the scrub, the pinning fixture must not be, and exactly one
scenario pins. Committed fixtures migrated through the same function.
Docs: pinned-header RFC (implemented/testing), base snapshot RFC + testing
policy + llm-replay module doc/README updated.
Master's reconstructable-requests overhaul (#179) meets the workflow tool:
- subagent-inprocess structured-output nudge becomes a system-prompt section
plus logged context (the injected-request waterfall shape is gone upstream)
- snapshot fixtures re-recorded on the merged tree so every request/header
carries the workflow tool; authored error-finish/cancel headers patched to
the merged tool list and system text
- architecture.md condensed back under its word ceiling; module graph regenerated
One re-record after the header events landed: recorded scenarios
re-harvested against the live API; the three fs-writing fixtures are
relativized (this recording's model happened to emit absolute
file_path arguments, which only round-trip through replay when the
path is cwd-independent) and, with the two never-re-recorded authored
scenarios (error-finish, cancel), rewritten in the normalized
authored-fixture form the harness documents — each now carrying the
request/header snapshot the loop logs before its first dispatch.
Keyless replay verified green across all 35 scenarios.
fs write/edit now emit a result-time contextual-diff tool_call_update
(the applied hunk with ±3 context lines, one hunk per replace_all site),
matching what claude-agent-acp sends and what makes an editor render the
change in place. The call-time snippet diff stays; the result hunk
supersedes it (ACP content-replace).
Mechanism:
- A persisted tool-private `meta` channel: execute may return
`{ content, meta }`; `meta` (JsonValue) rides on the tool/result event
and is handed back to presentResult, so the diff reproduces on replay
(event-sourced). JsonValue is now exported from dsh-session.
- The backend returns raw before/after text (storage facts) on
FsWriteOutcome/FsEditOutcome; the tool computes the hunk via the npm
`diff` package's structuredPatch. A create has no before → no result
diff; a failed/aborted mutation carries no meta.
- ToolResultView gains a DiffResultView; the bridge's result-side switch
renders it as {type:'diff'} content blocks.
RFC: docs/rfc/implemented/architecture/2026-07-02-result-time-applied-hunk-diffs.md
(justifies the npm `diff` runtime dep over vendoring and the meta channel);
the render-intent-union RFC's Non-goal is updated to record this shipped.
All fs snapshot goldens re-recorded; edit/overwrite gain the contextual
result diff, create/read/policy-reject unchanged in structure.
Five recorded ACP snapshot scenarios exercising read/write/edit end-to-end
through the real acp-agent subprocess, replayed keyless in CI:
- fs-read — read a seeded file (read tool + presentation + observed-state)
- fs-write — create a file (write, no prior version guard)
- fs-edit — read then literal-replace (read-before-edit authorization)
- fs-write-overwrite — read then rewrite (replaceIfVersion after a read)
- fs-read-window — read lines 5-8 with offset/limit (windowing + the offset
surfaced as the tool_call location line)
The goldens confirm the tools render with their new presentation — Read/Write/
Edit <path> titles, read/edit kinds, and `locations` (fs-read-window carries
`{path, line:5}`) — and that the prompts steered the model to the fs tools, not
bash (zero bash calls in any golden). Recorded against the real API, filtered to
the new scenarios so no existing fixture churned.