Codex review of the vocabulary relocation found two doc-accuracy issues:
- presentation.ts's JSDoc used {@link ToolDefinition...}, which the
TypeScript language service cannot resolve because presentation.ts
deliberately does not import index.ts (that would create the cycle the
split avoids). Demote those three to plain `ToolDefinition` code text;
same-file and imported @links (TerminalResultView, ContentBlock) stay.
- docs/core-data-structures/tools.md's source header listed only index.ts
and schema.ts; add presentation.ts, which now owns the presentation
vocabulary the page documents.
The tool render-intent vocabulary (ToolCallView/ToolResultView + members,
FileLocation, FileDiff, ToolCallKind) is the UI-facing surface of
dsh-tools; it lived inline in index.ts alongside the registry and
execution core. Move it to its own presentation.ts module so index.ts is
the registry + execute waterfall and the presentation vocabulary is a
separate, one-directional dependency.
presentation.ts owns ONLY render-intent types and references none of the
execution types; index.ts imports the view types for ToolDefinition's
presentCall/presentResult signatures (clean acyclic index -> presentation).
The opaque `meta` presentation channel (ToolExecuteReturn, ToolResult,
ToolExecutionResult) is execution plumbing and stays in index.ts.
Public surface unchanged: index.ts re-exports the vocabulary, so consumers
(tool-fs/tool-bash/tool-web/tool-todo, the ACP bridge) keep importing from
@deepseek-ai/dsh-tools with zero churn. No producer/bridge/test edits; a
pure internal relocation with no observable-output change (snapshot goldens
untouched).
The write-diff-card fix made write's presentResult return an args-derived
whole-file diff (oldText:null) for a create / unchanged overwrite, but the
DiffResultView contract and its mirrored docs still said `diffs` is ALWAYS
the applied contextual hunks computed from before/after. Correct the type
JSDoc, the write execute-side comment, and the four mirrored surfaces
(tools.md, tools README, acp-feature-support, adding-a-tool cookbook) to
say: typically the applied hunks, or a whole-file diff when there is no
before-image (a create) — and that a mutation returns the diff result even
when it duplicates the call-time card, since a tool_call_update.content
replace would otherwise clobber the diff with the model-facing text.
Regenerate the cordis catalog (source line shift).
The pasted type-equiv block's JSDoc still said the meta payload is
`{@link JsonValue}`; the source comment is `unknown` (the meta channel is
opaque at the seam). verify-type-equiv compares type structure, not the
comment, so the drift slipped through — align the doc comment.
Address the applied-hunk-diffs review:
- CRLF write overwrite emitted bogus every-line-changed hunks: write's
`before` was LF-normalized but `after` kept the raw model content, so a
CRLF rewrite of an LF file diffed every line. Normalize write's `after`
to LF so both sides share the diff basis (edit already did). Regression
test proves it fails on the raw-after path.
- The tool-private `meta` payload is now typed `unknown` (opaque) at every
seam instead of `JsonValue`. This drops the `dsh-tools -> dsh-session`
package edge that existed only to name the type, and removes the
`FileDiff` index signature that had been widening the type solely for
JsonValue-assignability. Serializability is still enforced at runtime by
`Session.append`'s isJsonValue check, which was always the real guard.
- Sync the docs the new result/meta surface left stale: ToolResultView's
diff card + ToolExecutionResult.meta in tools.md/session.md type-equiv
blocks, the acp/tools READMEs, and the adding-a-tool cookbook; regenerate
the cordis catalog and module graph.
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.
The closed WebErrorCode union leaked fetch-transport details (redirect,
too-large, content-type) into the seam's shared vocabulary and made web
the only seam with a closed error-code union. Drop it and let WebError
carry an open code: string like LlmError/SubagentError; document the
codes grouped by owner (seam-neutral vs dsh-web-fetch-local transport).
Addresses tianyicui's leaky-abstraction review comment on WebErrorCode.
Replace the "bag of optional fields" tool-presentation types
(ToolCallPresentation / ToolResultPresentation / ToolTerminal) with a
card-tagged discriminated union — the standing FIXME(tool-presentation).
A tool declares one render intent per call/result and the ACP bridge
switches on `card`:
ToolCallView = generic | terminal | diff
ToolResultView = generic | terminal
The `diff` card is new: fs write/edit now emit an ACP {type:'diff'}
content block (an editor's inline diff), which the old shapes could not
express. The bridge also relativizes a file card's title against the
session cwd (mirroring claude-agent-acp's toDisplayPath) while keeping
locations/diff paths raw, and derives the no-capability fenced console
fallback from a terminal result's output. read gains the window-in-title
(`Read foo.txt (5 - 8)`) and an always-set location line, matching the
reference adapter field-for-field.
Migrates all three producer families (tool-fs, tool-bash, tool-todo) and
the sole consumer (the ACP bridge) together — the source does not compile
piecewise. Adds snapshot coverage for the terminal _meta path (a new
capability-advertising scenario) and re-records the fs goldens to show the
diff cards. Applied-hunk (result-time, context-line) diffs need a new
result/event shape and are a follow-up.
RFC: docs/rfc/implemented/architecture/2026-07-02-tool-render-intent-union.md
The fs tools rendered as generic cards (title = tool name, raw file content) in
an ACP editor. Give them tool-owned presentation like bash/subagent have:
- read → title "Read <path>", kind read, offset/limit as rawInput
- write → title "Write <path>", kind edit
- edit → title "Edit <path>", kind edit, a clipped old→new rawInput summary
Add a provider-neutral `locations: { path, line? }[]` to ToolCallPresentation —
the files a call reads/modifies — so a capable editor can follow along / jump to
the file (read carries its offset as the line). The ACP bridge forwards it onto
the wire `tool_call` (ResolvedCallPresentation + call() + the tool_call build in
streamSessionEventUpdate). This flips the `locations` cell in the ACP feature
matrix to supported. The SDK already carries `tool_call.locations`
(ToolCallLocation `{ path, line? }`), so no ACP types leak into dsh-tools.
presentResult is intentionally omitted: it only receives `{ content, isError }`,
not the write/edit outcome, so titling by create-vs-overwrite or replacement
count would mean parsing the model-facing text — the static title stays.
Tests: pure presentCall assertions for all three tools incl. locations and the
edit rawInput clip; a bridge test drives the REAL fs tools through ToolPresenter
and asserts locations reaches the wire tool_call (proven to fail without the
forwarding line). New withFs harness option + dsh-fs devDeps on dsh-acp.
Codex review of the turn-mirror removal found current-state docs/comments that
still claimed the removed `agent/turn-start`/`agent/turn-end` events exist:
- docs/architecture.md: the loop diagram's turn-start line still said "emit
agent/turn-start" (the turn-end line was already fixed).
- event-domain-semantics RFC: the `agent/*` domain description listed "the turn
boundaries" among the transient emits.
- docs/core-data-structures/core.md: the agent/* taxonomy blurb listed
"turn/step boundaries" as agent events.
- the proposed ACP RFC: the settle-signal rows named agent/turn-start /
agent/turn-end; retargeted to the durable `turn/end` session event + the
session/event owning-turn correlation.
- loop.ts outer-catch comment: said "closeTurn/failTurn are idempotent" — after
the emit-param removal closeTurn is called exactly once (mutually exclusive
normal/catch paths), so corrected to state that and to scope idempotency to
closeStep (which is still guarded by stepOpen).
Regenerated the cordis catalog. No behavior change.
Rename per review naming decisions:
- package dsh-file-context → dsh-fs-policy (dir, package name, plugin name,
tsconfig refs, importers, type-equiv manifest, generated catalog + module-graph)
- events fs/write-expectation → fs/write-intent, fs/edit-expectation → fs/edit-intent
(fs/observed unchanged); type FsWriteExpectation → FsWriteIntent, "expectation"
wording → "intent" throughout
- exported FileContextExec → FsPolicyExec
Make the implemented RFCs describe what shipped, not the superseded designs:
the 2026-06-17 capability-seam + tool-schemas RFCs no longer place policy on
ctx.fs or use full/partial-view authorization, and the fsspec RFC's ctx.fileContext
service prose is rewritten to the fs/* event-gate reality (freshness-based auth).
Sharpen docs/rfc/implemented/AGENTS.md: a rename is a fact to fix IN PLACE — the
"new RFC" escape hatch is for macro decision reversals only, not renames.
Code fixes from review:
- fsio.ts resolveLocalTarget/probe translate ENOTDIR (a parent path segment is a
file) into the structured FsError taxonomy instead of leaking a raw Node error;
resolve reports FS_NOT_FOUND, probe reports absent. Regression tests proven to
fail on the unfixed code.
- tool-fs HMR test now asserts prompt sections (not just tool schemas) are
withdrawn on disposal.
- fs/observed is a plain (unguarded) ctx.emit: correct the fs-policy comment,
filesystem.md, and tool-fs module doc that wrongly claimed the tool "contains"
a throwing listener; a throw surfaces as the tool's isError result.
- drop the false "loaded by the default product config" claim (no config wires
the fs tools yet), the duplicate ctx.bash service-map row, the stale
FileReadRequest catalog link-map entry, and the fs/fs README EOF blank line;
correct the dsh-fs package.json description.
Use maxTokens as the provider generation cap and remove the confusing stored-summary max config.
Strip reasoning blocks before storing compaction summaries, reject non-shrinking summaries, and retry bounded re-compaction when the surface remains over threshold.
Add config validation for numeric and type-shaped knobs plus unit and real-API e2e coverage for reasoning-capable summarization.
Honor cancellation and disposal around async pre-step setup before the loop can open a step or call the model.
Route compaction summarization through agent/request so router agents can select the model, and remove the stale model argument from agent/pre-step.
Document serial events and the approximate convergence bound, regenerate the Cordis catalog, and add regression coverage for router compaction, HMR cleanup, and assembly/pre-step interruption.
Two cohesion cleanups on the filesystem tool package:
- Fold window.ts + types.ts + formatReadOutput into one cordis-free
read-render.ts. Line windowing, the FileReadOutcome shape, and output
formatting are one concern (the read tool's rendering); splitting them across
three files added no value. read.ts is now just the tool (schema + I/O).
- Drop observe.ts and emit fs/observed with a plain ctx.emit in read/write/edit.
The event is contractually a synchronous, side-effect-only recorder
(file-context's listener is a WeakMap.set), so the per-call try/catch guarded
against a contract violation that cannot happen under the shipped listener —
defensive code for an impossible case. The event contract (dsh-fs JSDoc,
README, RFC) is updated to state the fire-and-forget semantics plainly.
Add @deepseek-ai/dsh-tool-todo (a new packages/todo/ group): a model-facing
todo_write(todos: [{content, status}]) tool with whole-list-replace semantics.
Each call appends the full list as a todo/write event to the calling agent's
session log; the current list is the most recent such event (last-write-wins).
Single-owner — a non-agent caller is rejected. Beyond the schema's
type/required/enum checks, execute rejects empty/duplicate content and more than
one in_progress task, narrowing the loosely-typed args into a real TodoItem[].
Both UIs render off the existing session/event: the stdio UI prints a glyphed
checklist; the ACP bridge maps the list to a `plan` sessionUpdate (todosToPlan
synthesizes the priority ACP requires; status maps 1:1). Wired into the
coding-agent, acp-agent, and snapshot example configs with a system-prompt nudge.
Tests: unit (schema, validation, append/replace, no-agent rejection, presentCall,
HMR-safety, Loader export-shape guard), full-loop integration through the agent
loop, the ACP todosToPlan mapping + stream-update arm, the stdio render arm, and
a session/load replay that re-emits the plan. New-group TS wiring added to
tsconfig.base/json/build. RFC + a doc-inventory sweep (architecture, packages
README, AGENTS layout, cookbook group list, example READMEs) ship with it.
The todo-plan ACP snapshot scenario is recorded separately (needs an API key).
Codex Phase 1 review: the event JSDoc described Phase 2 consumers (the
todo_write tool, stdio printing, ACP plan mapping) as current state, and put an
@mode tag on a SessionEventMap member. @mode is for first-class Cordis
`interface Events` entries the catalog generator reads — this event rides the
existing session/event emit and has no catalog row, so the tag was wrong.
Trim the JSDoc to the event's own contract (snapshot data shape,
last-write-wins, not-a-surface-event) and drop @mode; phrase TodoItem in terms
of its own purpose rather than a not-yet-present tool.
Add the TodoItem type and a todo/write SessionEventMap variant carrying the
whole todo list as a snapshot (last-write-wins on replay). It is NOT a
SurfaceEventType: it produces no LLM message and never reaches
deriveMessages(), so it carries no surfaceOp and stays off the surface — it is
durable, replayable UI state that rides the existing session/event emit.
Tests cover the snapshot-clone-on-append contract, last-write-wins, the
not-on-surface guarantee, and a seeded replay round-trip. Docs: session.md
gains the TodoItem type-equiv block + the event member; core.md's variant count
goes to twelve; the type-equiv manifest gains TodoItem.
Correct the filesystem-wiring claims flagged by codex: no default/example
config wires the fs tools yet (the demo agents do file ops through bash), so
the docs and RFC no longer assert that "the default product config loads
dsh-file-context". They now state the intended stance — a deployment that
loads the fs tools is expected to also load dsh-file-context for
read-before-write/edit.
Invert the tool↔policy control flow per the file-context event-gate RFC.
dsh-tool-fs becomes the executor — it reads/writes/edits through ctx.fs
directly, owns read windowing, and dispatches fs/write-expectation /
fs/edit-expectation (single-slot waterfalls) plus a contained fs/observed
emit. dsh-file-context drops its ctx.fileContext service and becomes a pure
event-gate plugin (observed-state + read-before-edit + version-guarded
write/edit, decided on those events). The provider's version guard becomes
optional so ctx.fs alone is a complete unconstrained text-storage seam:
removing the policy plugin gracefully loses the policy instead of breaking
the tool at a service-injection boundary.
- Re-validate redirect targets through validateFetchUrl before following, so a
same-origin Location carrying credentials (or a non-http(s)/over-long URL)
cannot bypass the transport hygiene a direct request enforces.
- Treat only DROPPED bytes as truncation: a body exactly at maxResponseBytes is
no longer falsely flagged truncated (which emitted a spurious footer).
- Honor the declared response charset: parse the Content-Type charset and decode
with it (rejecting unsupported labels as WEB_UNSUPPORTED_CONTENT_TYPE) instead
of always assuming UTF-8 and returning replacement characters.
- Catalog the web seam vocabulary in docs/core-data-structures/web.md with
type-equiv blocks + manifest entries, per the core-data-structures rule.
Implements the split-the-filesystem-seam RFC. ctx.fs shrinks to a text-storage
provider seam (resolve/stat/readText/streamText/writeText/editText with branded
FsTargetKey/FsVersion and an explicit FsWriteExpectation); the new
dsh-file-context package owns the model-facing policy (read windowing,
observed-state, write/edit freshness) as the concrete ctx.fileContext service.
Authorization is now freshness-based rather than full/partial view: a windowed
read records the file version and authorizes a later edit when the file is
unchanged, removing the dead-end where reading lines 100-150 of a large file
could not edit line 120. editText stays a provider primitive so version guard +
literal match + atomic rewrite remain one critical section, and the stale check
runs before matching so a stale edit reports FS_STALE_VERSION. tool-fs injects
fileContext, never reaching around to ctx.fs (the no-bypass contract).
The compaction e2e never exercised compaction: its window/fixture combo
(contextWindow 8000, thresholdRatio 0.5 → threshold 4000; four small files)
peaked at ~1389 estimated tokens, so compactIfNeeded declined every pre-step
and compact/start never landed. Shrink the window (contextWindow 2400 →
threshold 1200; retainTokens 500 + summarizationMaxTokens 300 = 800 < 1200,
convergence holds) and grow the fixture to six files so a couple of bash steps
reliably cross the threshold. Verified compaction fires and the suite passes
across repeated real-API runs.
Sync docs left stale by the landed compaction work: list compaction.e2e.ts and
keyless-smoke.e2e.ts in the coding-agent README (and fix the wrong "Both
self-skip" count), add compaction to the examples with-key inventory, and
replace the hypothetical compaction/marker / "future plugin" naming in the
session README, session types JSDoc, and the core-data-structures catalog with
the real compact/start, compact/summary, compact/end events.
Codex round 1 CBR-003: several docs still described compaction as an
`agent/request` waterfall concern, and the implemented compaction RFC
claimed "No changes to dsh-session or dsh-invariants" while the diff
changed both.
- Package READMEs / JSDoc (agent, agent-loop, system-prompt, compact,
compact-basic): compaction now lives on the serial `agent/pre-step`
seam (fired after turn/start, before step/start); the structural guard
is tool-pairing balance (`isToolPairingBalanced`), not step-alignment;
the convergence bound is strict (`>=` rejects).
- architecture.md / core-data-structures/compaction.md: same seam +
predicate + dispatch-mode updates; regenerated cordis catalog.
- Implemented compaction RFC, updated in place to describe shipped
reality: the seam is `agent/pre-step` (@mode serial) fired before
step/start; alignment is surface tool-pairing balance; the convergence
invariant rejects `>=`; and the "no dsh-session/dsh-invariants changes"
claim is corrected — dsh-session gains the tool-pairing predicate and
dsh-invariants drops its `start <= end` replace assertion (a positional
replace makes start > end normal).
Reform the compaction blueprint so a runaway turn survives and the design
stops drifting across review rounds:
- Drop in-flight-turn protection ("layer 2"). Retention is a uniform tail→head
whole-unit walk; the only structural guard is step-alignment. A single turn
that alone exceeds the window now compacts its own early closed steps instead
of being retained verbatim (the failure mode that motivated this).
- Move auto-compaction off the agent/request waterfall onto a new awaited
agent/pre-request loop seam, fired before history derivation. Compaction
mutates the surface; the loop derives once from the result — no double-derive,
and a listener structurally cannot act on not-yet-derived messages.
- Tighten compactIfNeeded to required (session, system, model, signal).
- Enforce a single-pass convergence invariant in resolveConfig: reject configs
where summarizationMaxTokens + retainTokens exceeds the threshold, so a
compaction can never immediately re-trigger.
- Document the crash vs recoverable failure taxonomy; core session repair stays
compaction-agnostic (a log-only orphaned compact/start is inert).
- Wire dsh-compact-basic into examples/coding-agent and add a with-key
compaction e2e (compaction's first real-world exercise + runaway net).
- Rewrite the RFC to encode the blueprint and move it to implemented/.
The runaway-turn snapshot is a named deferred follow-up: dsh-llm-replay cannot
yet serve the interleaved summarization model call.
Collapses the per-round review churn of the prior compact-basic branch into a
single clean baseline on top of compact-interface, so the upcoming retention
refactor lands as fresh, well-scoped commits rather than stacking on a history
of fixes that are being superseded.
Reconciles the session-surface work (surfaceOp/sourceEventSeqs provenance as
the sole derivation path) with master's worktree-subagent series (fork-seed
boundary + out-of-process subagent backends).
Semantic reconciliations beyond the textual auto-merge:
- SQLite SCHEMA_VERSION: both sides bumped 2->3. Merged to a single v3 carrying
BOTH column families — master's seed_length on `sessions` and surface's
source_event_seqs/surface_op on `events`. writeRow + both INSERT sites bind
the full set; the schema doc lists all three added columns as the v2->v3 gap.
- agent-loop runStep request: master's `sessionId: session.id` and surface's
per-append surfaceOp/sourceEventSeqs coexist (different regions).
- Fork seed + surface: a fork seeds the child from the parent's LIVE events,
which now carry surfaceOp, so the child's surface rebuilds correctly. Verified
end-to-end — the subagent-fork replay recalls the inherited "SAFFRON" codeword
through the seeded prefix.
- Subagent snapshot fixtures (recorded pre-surface) re-enriched via KEYLESS
deterministic replay: only surfaceOp/sourceEventSeqs added onto existing
recorded lines (matched by seq), no recorded value changed. Not re-recorded
against the live API.
Gates: typecheck, test (1112), test:snapshot (14), doc-sync, lint, build,
hygiene all green.
A fork subagent seeds its child session with a prefix of the parent's log, and
that seed becomes the child's persisted log — so a fork child's .jsonl begins
with the PARENT's events, including the parent's assistant/chunk events. The
snapshot replay harness derived a child's script from its whole log, which would
replay the parent's recorded responses as the child's model calls. Spawn-only
scenarios never hit it, but a fork snapshot would mis-route silently.
Record the seed boundary and skip the inherited prefix at replay:
- SessionHeader gains an optional `seedLength` (how many leading events were
inherited via a seed), threaded through CreateSessionOptions/CreateAgentOptions
meta and stamped by the fork backend (= seeded-prefix length; absent for spawn).
It is EXPLICIT, never inferred from seed.length: a resume seeds the whole stored
log, so the resume path passes the persisted boundary back.
- Both persistence backends round-trip it: JSONL header line, SQLite seed_length
column. The SQLite table change bumps SCHEMA_VERSION 2->3; per the pre-release
stance the backend rejects an older user_version on open with NO migration.
- llm-replay's parseSessionHeader reads seedLength and loadSessionScripts derives
a child script from events AFTER the boundary. seedLength is 0 for spawn, so
spawn replay is byte-for-byte unchanged.
Closes the routing-correctness gap the per-session snapshot replay RFC under-
stated; a recorded fork scenario remains a future addition but now derives
correctly. RFC: docs/rfc/implemented/testing/2026-06-22-fork-child-replay-seed-boundary.md.
Regression coverage: a fork child fixture whose seeded prefix carries a parent
chunk (derived script must exclude it, proven red without the slice); a seedLength
persistence round-trip through the shared coordinator contract (both backends);
the fork backend stamping it; resume preserving it from the persisted header.
The first OUT-OF-PROCESS subagent backend, proving the seam generalizes past the
in-process backends. @deepseek-ai/dsh-subagent-acp runs each child agent in a
spawned subprocess, driven over the Agent Client Protocol as the CLIENT — the
direction-inverted twin of the dsh-acp server bridge. Point the configured
command at the acp-agent example and the harness talks to its own process.
- Fresh process per run: start spawns, runs one ACP session (initialize →
newSession → prompt), dispose kills the subprocess and awaits its exit.
- Minimal client stub: advertises no fs/terminal; accumulates agent_message_chunk
text as the result output; auto-answers session/request_permission by a
configured policy (reject default / allow). No start-time capabilities (an
out-of-process child can't enforce the parent's depth/tool-filter); ignores
request.parent; injects only `subagents`.
- StopReason mapping (end_turn→completed, cancelled→aborted, …); result resolves
error/aborted on a child failure, never rejects (seam contract).
- Security: credential-shaped ambient env vars are scrubbed; the child's own key
is forwarded only via explicit config.env. A spawn-level error (ENOENT) is
captured and raced against the ACP drive so a bad command settles error rather
than crashing the parent.
Testing designed at every tier: keyless integration drives a scripted mock ACP
server subprocess (cancellation incl. the pre-newSession race and a
torn-pipe-after-cancel, permission auto-answer, non-message updates, spawn
failure, HMR, export shape) at 100% coverage; a with-key e2e drives the REAL
acp-agent example process (PONG + real file write, verified on disk) — the
harness driving itself. Snapshot coverage of an ACP child is deferred as
TODO(acp-subagent-replay) (each child is its own process with its own replay).
Stayed on @agentclientprotocol/sdk 0.25.1: the proposed 0.28.x bump only
deprecates the stable ClientSideConnection/AgentSideConnection API this layer
uses (33 sites incl. the server bridge), turning no-deprecated red across code
this PR shouldn't rewrite — that fluent-API migration is its own follow-up. The
backend needs nothing 0.28.x adds.
This completes the subagent seam stack (PR1 interface → PR2 in-process → PR2.5
snapshot infra → PR3 ACP); the seam RFC moves to implemented/, amended.
Reconcile the session-surface feature with master's package reorg and
simplifications:
- Adopt master's folded usage (assistant/message.usage; standalone `usage`
event dropped) and re-attach surface metadata (surfaceOp/sourceEventSeqs).
- Add surface opts to master's new max-tokens assistant/message append.
- Port surface columns onto the coordinator-refactored SQLite backend at its
new path; drop the dead v1->v2 migration (bump-and-reject, no migration per
pre-release policy).
- Move the session-surface RFC into implemented/architecture/ and refresh its
stale body (no migration, SESSION_FORMAT_VERSION=0, renamed package paths).
- Update the core-data-structures catalog SessionEvent blocks for the two new
surface fields; regenerate the cordis catalog.
- Re-harvest ACP snapshot fixtures (keyless replay) to carry surface metadata.