返回 DeepSeek-Reasonix
TRANSCRIPT_PROJECTION.md
根目录 / docs / TRANSCRIPT_PROJECTION.md
1 # Transcript projection
2
3 [简体中文](TRANSCRIPT_PROJECTION.zh-CN.md)
4
5 Local Desktop and current Serve sessions share `internal/transcript`. The
6 controller appends each durable envelope, commits its display projection, then
7 publishes the event outside the commit lock. Planner output, executor output,
8 prompts and terminal events use this boundary. Provider history remains the
9 input to model requests; display records never enter those requests.
10
11 The Desktop surface uses the Electron host contract. New transcript methods
12 and ownership metadata are generated together; DTO names cannot shadow the
13 TypeScript helpers used by that contract.
14
15 ## Identity and recovery
16
17 The controller reserves a user message ID before planning. The frontend keeps a
18 local submission echo outside the durable transcript until the canonical row is
19 installed. `submissionId` correlates one send attempt, `messageId` identifies the
20 durable message, and the presentation key only preserves a mounted DOM node
21 during handoff. Durable user rows always use `m:<messageId>`; history mutation,
22 content lookup, and persistence never use the presentation key. Equal bodies are
23 allowed and are not duplicate identities. A sampling attempt reserves its
24 assistant message ID before its first delta; successful persistence uses that ID,
25 and discard removes only that attempt's owned records. Tool cards use tool call
26 IDs.
27
28 Local submission handoff is independent of the resident history window. The
29 follower sends formal user identities before window filtering alongside the
30 visible projection in one reducer action. A record outside a reader's window
31 retires its matching echo without inserting a row or requesting tail follow.
32 Matching prefers an already bound message ID; only unbound echoes may match a
33 submission ID. Conflicting bindings and different formal message IDs never merge.
34
35 The same transaction records `visibleSubmissionHandoffs` for formal users in the
36 resulting window. ChatSource uses these hints to inherit a previously mounted
37 echo's key even when React skips the intermediate binding render. User, process,
38 and tail nodes share that stable turn key. Hints are pruned to resident formal
39 users; presentation maps are pruned to mounted groups. Neither survives session
40 replacement or restores a reclaimed echo. A missing send anchor is not permission
41 to prepend an echo to unrelated history: placement requires its original boundary
42 or explicit turn identity.
43
44 A late message-ID binding first reconciles resident records. If the formal user
45 is no longer resident, the owning follower may use the existing exact-message
46 history query solely to confirm identity, without installing that page. Reads
47 are generation-fenced, coalesced per unresolved submission, and retried only after
48 committed coverage advances or reconnect. An inconclusive read retains the echo.
49 An identity event alone never starts a read: committed coverage must have advanced
50 since the follower first observed the unresolved submission. This watermark is
51 pruned with the submission. The synchronization engine loads on first follow,
52 and stopping before its module loads cancels that start.
53 RPC acceptance, failure, and unknown outcome belong to the local submission;
54 request ownership separately controls runtime state. Only a new explicit send
55 advances the scroll submission revision.
56 Snapshots retain unresolved submissions for the same canonical session ID;
57 a different snapshot owner clears echoes and handoffs and advances the session
58 generation. Remote callbacks read the state owned by their tab, never another
59 tab's last rendered state.
60
61 History window and body I/O load on demand. Their routing identity is captured
62 before the deferred import, and Store generation checks still discard stale
63 completions. This keeps startup bytes within the existing bundle budget.
64
65 `bench/submission-handoff.mjs` exercises the real follower, bounded Store, reducer,
66 Composer, and Transcript using 1,000 deterministic turns and 20 paging round trips.
67 Run it with `--electron` for the isolated Electron host. Its JSON evidence includes
68 DOM continuity, native selection, reader displacement, viewport writes, resident
69 entries, and presentation-map sizes. Scripted Electron input is not OS-native input
70 qualification; the report records that evidence separately.
71 In Linux CI, `--electron --native-input` runs inside Xvfb and uses X11 XTest
72 wheel, keyboard and scrollbar input through `xdotool`. The evidence includes
73 scroll extents and blank-frame samples as well as confirmation-time reader
74 stability. Run this mode only in an isolated graphical session.
75
76 Terminal projection checkpoints live in the session's
77 `.transcript-projection.json` sidecar. The checkpoint records the provider prefix
78 count and digest, session head/rewrite identity, rows, runtime, and covered event
79 sequence. A failed checkpoint write keeps the WAL projection acknowledgement
80 pending. Recovery restores a matching checkpoint and replays its retained
81 suffix without executing tools. It does not seed an autosaved in-flight tail
82 and then append that same tail again. Existing display sidecars remain readable;
83 only their legacy migration path may use the old user-hash/occurrence mapping.
84
85 Terminal records retain protocol-recovery tokens, incomplete-read/readiness
86 details, accepted partial-read receipts, cancellation and failure diagnostics.
87 Starting a new turn retires earlier recovery actions. Checkpoint capture copies
88 mutable metadata while sharing immutable body strings; disk encoding stays
89 outside the projection lock.
90
91 ## Snapshot protocol
92
93 Desktop exposes `TranscriptSnapshotForTab`, `TranscriptPageForTab`,
94 `TranscriptContentForTab`, and `TranscriptReplayForTab`. Serve exposes GET
95 `/transcript/snapshot`, `/transcript/page`, `/transcript/content`, and
96 `/transcript/replay` behind its existing authentication and host checks.
97
98 `ResumeTranscriptSessionForTab` and `OpenChannelTranscriptSessionForTab` return
99 switch-phase diagnostics after adoption, without building a legacy history page.
100 The frontend records these only after the matching snapshot commits, including
101 a separate snapshot-install duration. An older host returning no diagnostics is
102 reported as unknown (`duplicateLoadCount: null`), never as proof of zero repeats.
103 Legacy page callers reuse a preload only if it matches the controller's captured
104 history digest; a changed, fully persisted runtime triggers a fresh durable read.
105
106 Each snapshot includes protocol version, snapshot ID, session/head/rewrite/runtime
107 identity, projection revision, covered-through sequence, records, active owners,
108 and pending runtime prompts. Page and content requests carry the same snapshot
109 ID. Replay carries the identity and the last committed sequence. An expired cut
110 returns `stale`; a replay identity or retained-range mismatch requires a new
111 snapshot. Tab and remote-client generations are checked after asynchronous reads.
112
113 The frontend suspends ordered ingress before requesting the snapshot. Mutable
114 owners' referenced prefixes resolve before rows, runtime and attempt state commit
115 in one reducer transaction. Only then does event coverage advance. Queued and
116 replayed events share one commit entry. Status polling supplies replay hints and
117 ancillary job counts; it cannot install a second transcript/runtime prefix.
118
119 Older pages merge by record/item identity and backend order, including an active
120 user retained before the newest page. Delayed content patches check both the cut
121 and intervening item mutations. A page cannot resurrect a discarded attempt.
122
123 Content resolution uses durable message identity. The presentation layer may
124 carry a local echo's key across canonical handoff, but that key never replaces
125 the item's `m:<messageId>` identity. Delayed patches target the current durable
126 item and reject intervening mutations before replacing its preview; a reclaimed
127 row cannot be recreated by a late patch.
128
129 ## Bounds and compatibility
130
131 Pages default to 120 records and 512 KiB, with a 2 MiB response ceiling. Large
132 string fields use 4 KiB previews and UTF-8-safe 64 KiB content chunks. Chunk reads
133 traverse typed fields directly instead of serializing the entire payload.
134 The same 2 MiB limit covers complete replay responses, including JSON escaping
135 and envelope overhead. Oversized replay pages request a fresh snapshot; the
136 covered data remains accessible through content chunks without advancing an
137 unreceived event cursor.
138 Snapshots retain at most three cuts under a 64 MiB estimated budget. The current
139 cut is pinned: a larger session remains readable and evicts older cuts. Settled
140 strings are shared, while immutable metadata and active prefixes are retained.
141
142 The client retains unresolved previews only; successful expansion releases that
143 record cache. Active/running tabs are pinned. Inactive caches have a three-tab,
144 32 MiB budget. Live gap buffers have a 1,024-event/8 MiB bound; overflow retains
145 the high-water mark and recovers the suffix from the ledger.
146
147 An old Serve returning an unsupported endpoint/protocol or its HTML index stays
148 connected through the legacy path. The UI states that synchronization is limited
149 and reconnects may have missing or repeated content. Capability discovery never
150 upgrades or restarts Serve. Modern hydration uses metadata-only ancillary reads,
151 so it does not also download the old full `/history` payload.
152
153 ## Validation
154
155 Root-module tests cover attempt identity, checkpoint write failure, autosave
156 recovery, immutable cuts, nested content references, large sessions, and HTTP
157 session binding. Desktop tests cover planner cancellation, old-Serve discovery,
158 late remote responses, and metadata-only reads. The frontend snapshot client
159 tests exercise prefix/suffix races, optimistic keys, page ordering, tool pairs,
160 discard tombstones, and stale content writes through the real reducer.
161
162 Run the root and Desktop Go suites separately. Run frontend `test:typecheck`,
163 `test:transcript`, `test:remote`, `test:stream`, and the production build. Browser
164 transcript and app-memory checks, together with native Electron replay,
165 remain separate acceptance gates; unit tests or cross-compilation do not replace
166 native evidence.
167
167 lines MARKDOWN