| 1 | # Desktop agent notes |
| 2 | |
| 3 | Desktop Go is a separate module; root Go tests do not cover it. |
| 4 | |
| 5 | For changes affecting transcript viewport, scrolling, loaded history, or |
| 6 | delayed geometry work, read the |
| 7 | [transcript scroll and history contract](../docs/TRANSCRIPT_SCROLL_CONTRACT.md) |
| 8 | ([中文](../docs/TRANSCRIPT_SCROLL_CONTRACT.zh-CN.md)). |
| 9 | It preserves single-writer ownership, generation isolation, reader intent, |
| 10 | bounded rendering, the bounded reading window, and deterministic regression |
| 11 | requirements. |
| 12 | |
| 13 | Other Desktop work does not require the scroll-specific procedure. |
| 14 | |
| 15 | ## Natural-flow chat |
| 16 | |
| 17 | The transcript uses ChatSource and ChatScrollController. It has one natural-flow |
| 18 | implementation for local and remote sessions. Do not restore the retired |
| 19 | window adapter, measurement ledger, geometry revision loop or logical selection. |
| 20 | |
| 21 | - Stable node keys derive from message/call identities, never array positions. |
| 22 | Streaming and settlement update the same assistant host; unchanged node and |
| 23 | order snapshots retain their references. |
| 24 | - Business state remains in the controller/history owners. ChatSource is a |
| 25 | reconstructable view projection. Structural changes batch in microtasks; |
| 26 | existing controller frame batching owns stream publication. |
| 27 | - Only frontend/src/lib/transcriptViewportWriter.ts writes the chat viewport. |
| 28 | ChatScrollController owns programmatic follow, reader anchoring and navigation. |
| 29 | Native input is never synthesized or prevented to keep the tail pinned. |
| 30 | - A small upward reader movement releases follow even inside the 24px bottom |
| 31 | threshold. Prepend and resize preserve a stable node plus viewport offset. |
| 32 | Old observers, requests and callbacks cannot act on a replaced session. |
| 33 | - Markdown, tables and loaded history use document flow. Parsing may be lazy, |
| 34 | but must not create a nested virtual vertical scroller. Collapsed |
| 35 | process/tool bodies are mounted on demand. |
| 36 | - History is a bounded reading window, not an ever-growing list. The resident |
| 37 | store keeps a small number of adjacent pages (`windowMaxPages`, default 3 of |
| 38 | 32-message pages) per session, including the active one: paging past that |
| 39 | reclaims a page from the end the reader is moving away from and re-fetches |
| 40 | it on demand. Nothing is deleted — the persisted session is authoritative — |
| 41 | and the reclaimed direction stays reachable through its cursor. Do not add a |
| 42 | path that holds every loaded page resident, and do not treat "all history is |
| 43 | mounted" as a correctness property; assert reachability and bounded |
| 44 | residency instead. |
| 45 | - Paging is bidirectional. `loadOlder`/`loadNewer` reclaim from the opposite |
| 46 | end and hand the caller the ids to drop; a caller that ignores them will |
| 47 | render rows the store has already released. Window cursors pin a fixed |
| 48 | snapshot: appends keep them valid, a storage replacement answers |
| 49 | `stale_cursor`, and a cursor the server cannot read is that same typed |
| 50 | answer rather than a transport error. |
| 51 | - History reads route by the tab's binding identity, never by the result of a |
| 52 | failed call: a local error must not be answered by a remote service holding |
| 53 | a different session. A remote service that never negotiated |
| 54 | `history-window-v1` answers with the typed `unsupported` status and keeps |
| 55 | its protocol-7 pages. |
| 56 | - No geometry snapshots are stored in React state. Layout observers must |
| 57 | converge without a render/measurement feedback loop. |
| 58 | - Native selection is browser-owned. No cross-window selection overlay or |
| 59 | clipboard interception belongs to the chat. |
| 60 | - Keep draft input, approvals, questions, model controls and the session bridge |
| 61 | outside the presentation refactor. Do not change persisted/provider bytes. |
| 62 | - Run pnpm test:transcript and the applicable browser suite. The primary cases |
| 63 | are small reader gestures, stream growth, prepend, disclosure, session change, |
| 64 | stale callbacks and unchanged-node render isolation. Do not weaken performance |
| 65 | gates to hide regressions. |
| 66 |