CodeNomad/dev-docs
heunghingwan 0d6629c129
Some checks are pending
Publish help / build (push) Waiting to run
Publish help / deploy (push) Blocked by required conditions
fix(transcript): keep streaming live across shared-stream reconnects (#1088)
fix(transcript): keep streaming live across shared-stream reconnects

## Problem

Long model reasoning streams stop updating the transcript roughly 30
seconds in: the visible thinking text freezes, and the display only
catches up when the thinking phase ends. Shorter streams never freeze.

A diagnostic reproduction against the real SDK/dispatcher pinned the
freeze exactly: after a mid-stream `server.connected`, ten live
reasoning deltas left the store text frozen at its pre-reconnect value
and **zero** `message.list` reads were even attempted — even
`session.reasoning.ended` and the execution terminal events could not
recover it.

## Root causes

1. **~30 s trigger** — after `CONNECTION_RECHECK_INTERVAL_MS`, any
request through the shared OpenCode service re-runs discovery; a failed
or changed probe (a busy daemon is slow to answer) invalidates the
healthy connection. The shared event stream tears down and resubscribes,
and every subscription starts with `server.connected`.
2. **Reset mid-stream** — the UI treated `server.connected` as a full
data reset (`destroyOpenCodeData`). The fresh reducer starts empty:
reasoning deltas no-op (`editReasoning` cannot find the message) and
nothing repopulates the projection until a later `session.step.started`
inserts the assistant row again.
3. **Starving recovery** — repopulation requires one `message.list` read
to return with an unchanged event revision; each arriving delta restarts
the read (the guard that never merges an obsolete page into newer
streaming messages). During continuous streaming that read cannot
complete, so any reconciliation starves until the stream pauses.

## Changes

- `stores/opencode-data.ts`: `server.connected` now runs
`reconnectOpenCodeData` instead of a bare destroy. A shared-stream
reconnect is transport recovery, not a data reset:
- The reset semantics are kept (generation fences, observer rebinding,
REST state untouched) — every existing reconnect test passes unchanged.
- Before the reset, each live session's last projected page is captured
and seeded into the fresh reducer through the existing `syncMessages`
snapshot mechanism, so post-reconnect deltas keep applying and the
display keeps streaming.
- One trailing reconciliation is scheduled per session (new non-dropping
`reconnectResync` mode): it fetches only after the stream stays quiet
for 1 s with a stable revision, then swaps in the authoritative page to
repair anything the disconnect window missed. It never drops events and
defers to dropping-mode recovery when an overflow collapse or pruning
takes over.
- `stores/opencode-data-reconnect.test.ts` (new, in the CI unit list):
three regressions — the revision-race read invariant (obsolete pages
never merge), dropping-mode admission (frozen until the stream pauses),
and reconnect continuity (deltas keep displaying; the quiet snapshot
republishes).

## Behavior compatibility

Suites covering reconnect semantics (projection reset, inbox observer
rebinding, compaction reconnect fencing, replay fences, generation
recovery, request authority) pass without modification.

Dropping-mode resync semantics (overflow, pruning) are unchanged and now
pinned by a regression test.

`opencode-data.ts` is 978 lines after this change (over the ~800
target); splitting the reconnect path into its own module is a
reasonable follow-up.

## Pre-existing failures (not caused by this PR)

Four tests fail identically on a clean checkout (verified by stashing
this change and re-running): the pinned `@opencode/client` 2.1.0 /
event-manifest contracts drifted and the fixtures were not updated:

- `opencode-transcript-replay.test.ts` × 2:
`session.compaction.started`/`ended` fixtures no longer decode (the
manifest now requires `data.recent`); both integration tests die in
fixture construction before any assertion runs.
- `opencode-data.test.ts` ("preserves SDK step, snapshot, reasoning…"):
expects the old `native` field on reasoning parts; the 2.1 reducer maps
it to `state`.
- `session-pruning-projection.test.ts`: expects a provider
`reasoningField` mapping that the current contract no longer returns.

These are fixture/expectation updates against the pinned SDK, unrelated
to the streaming machinery; CI may already be red on them. They should
be repaired in a separate PR so this fix stays reviewable.

## Validation

- Red→green: the new reconnect regression fails on the pre-fix tree at
the frozen-store assertion (`'before '` vs `'before after0 after1 '`)
and passes with the fix.
- UI unit baseline (CI file list + new file): 552 tests, 548 pass, the
same 4 pre-existing failures before and after.
- `tsc --noEmit`: the same 4 pre-existing errors before and after.
- Browser: mission-control 24/24, provider-usage 13/13,
mission-task-reader 18/18, message-scroll 128/128. `part-durations`
page-load timeouts reproduce identically on a clean tree (Vite
cold-start environment issue).
2026-10-21 23:34:37 +02:00
..
architecture.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
BROWSER_AUTOMATION.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
BROWSER_DEVICE_EMULATION.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
CACHE_REFRESH_CONVENTIONS.md fix(transcript): keep streaming live across shared-stream reconnects (#1088) 2026-10-21 23:34:37 +02:00
COMPOSER.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
DESKTOP_DATA_PROFILES.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
FILES_PANEL.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
HOTKEYS.md fix(transcript): jump between user messages with Alt+Up/Down, also from the composer (#1087) 2026-10-21 20:36:02 +02:00
INDEX.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
MISSIONS.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
MISSIONS_AUTONOMY.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
MISSIONS_RECURRING.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
MISSIONS_RECURRING_QUALIFICATION.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
NATIVE_INTERRUPTION_UX.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
OPENCODE_COMPATIBILITY.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
OPENCODE_RUNTIME_SETUP.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
PALETTE_SOURCES.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
PANEL_EXTENSIONS.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
PLUGIN_CONTROLS.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
PROJECTS.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
PROVIDER_ACCOUNTS.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
REMOTE_CONTROL.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
RUNTIME_MAINTENANCE.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
SESSION_ENVIRONMENT.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
SESSION_HISTORY_NAVIGATION.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
SESSION_HISTORY_QUERIES.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
SESSION_PRUNING_RPC.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
SESSION_PRUNING_SAFETY.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
SESSION_RECOVERY.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
SETTINGS.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
solidjs-llms.txt update blank session cleanup code for now session store logic 2025-11-27 20:18:22 -05:00
TAURI_UPDATER.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
TEXT_DISPLAY_BOUNDS.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
TRANSCRIPT.md ui: keep the message token disclosure on one row in both states (#1084) 2026-10-21 18:52:11 +02:00
UI_CONVENTIONS.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
UI_SHELL.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
USER_HELP.md docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082) 2026-10-21 16:59:43 +02:00
WORKTREE_SESSION_PLACEMENT.md fix(worktrees): omit a checkout mid-removal instead of failing the whole inventory (#1089) 2026-10-21 22:24:01 +02:00