fix(desktop): publish session transcripts once, behind a fade swap - #5566
Conversation
a0d9a50 to
f80bc53
Compare
jackwener
left a comment
There was a problem hiding this comment.
I reviewed f80bc537128f5ff31d54c3ee603177f5c8cc808e.
No P0–P2.
A healthy transcripts.open() no longer publishes the local cache first. The cache is only replayed when the live open fails before any live identity arrives. Cached generations cannot serve loadEarlier, and both range() and the reading-anchor restore treat them that way (hasOlder: false; restore waits for a live generation). While switchingSession is true the previous transcript stays mounted, inert and dimmed, until the live answer remounts.
Required check test is green on this head. I did not run a real Electron session-switch.
简体中文
我审查了 f80bc537128f5ff31d54c3ee603177f5c8cc808e。没有 P0–P2。
健康的 transcripts.open() 不再先发布本地缓存。只有 live open 在收到 live identity 之前失败时才回放缓存。缓存代不能 loadEarlier,hasOlder 为 false,阅读锚点等到 live 代再恢复。切会话时上一份 transcript 留着(inert + 变暗)直到 live 答案换上。
本 head 上必需检查 test 是绿的。我没有跑真实 Electron 切会话。
Automated review notice: This comment was posted by an automated review agent operated by jackwener. It is not an independent human review and does not replace one.
transcripts.open replays the locally cached tail before the live history answer so the previous content is visible while the Host reads. That snapshot carries the replica's tail bound as hasOlder, so the view rendered a load-earlier control for a generation that cannot serve the read (loadEarlier no-ops on cached:), then replaced it wholesale once the live answer arrived — the "load earlier history" flash on every session switch. A cached generation is provisional by contract and every live-only affordance already gates on it. Close the two leaks: range() reports hasOlder only for generations that can answer earlier reads, and the reading-position restore waits for the live answer instead of concluding a bookmarked Turn is unreachable from the cached tail. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Generated-by: Devin
Switching sessions used to paint the locally cached tail first — a prefix-truncated copy whose hasOlder flag rendered a dead load-earlier control — then replace it wholesale when the live answer arrived. The cache now stands in only when the live open fails, which also removes a per-open session-local disk read. The switch keeps the previous transcript inert and dimmed while the live read is in flight, then fades the new transcript in on its keyed remount. Generated-by: Devin
f80bc53 to
99cb762
Compare
Summary
Switching sessions painted the locally cached transcript tail first — a prefix-truncated copy whose
hasOlderflag briefly rendered a dead "载入更早的记录" control (cached generations cannot serve a Host-backedloadEarlier) — then replaced the whole publication when the bounded live answer arrived.transcripts.open()now starts the livesessions:transcript:openfirst and reads the local cache only when the live open fails before any live identity is received. The cache remains the offline/reconnect stand-in, but a healthy open publishes the live bounded history as its first and only transcript. This also removes a per-opensession-local:transcriptdisk read from the normal path.Because the fallback path still publishes
cached:generations, the provisional-state contract is enforced at the authority seam:DesktopTranscriptRangeStore.range()reportshasOlder: falsefor cached generations, and reading-anchor restoration waits for a live generation instead of consuming a stored bookmark through a no-oploadEarlierand marking it unavailable.While the live read is in flight the previous transcript stays mounted in place (inert) as the cover; the swapped-in transcript fades in once on its existing
key={activeSession.id}remount — the same cover-then-reveal pattern as the startup reveal (#5494), at--duration-emphasizedand disabled underprefers-reduced-motion.Mid-switch captures — BEFORE frame taken with the live
sessions:transcript:openheld ~1.6s; AFTER frames frozen mid-fade via the Web Animations API:Real-speed recording of two switches on this branch — the previous transcript holds while the live read is in flight, then the complete transcript fades in once:
switch-transition-after.mp4
Verification
ipcRenderer: a healthy live open performs zerosession-local:transcriptreads and publishes only the live answer; a failed live open replays the cache once, yields the reconnect-only fallback handle, and reportshasOlder: false. Both fail on the old behavior.session-local-recovery.spec.ts(3 tests) andpartial-history-notice.spec.tspass: cached history stays readable when the live endpoint is unavailable, and earlier Turns still load only on explicit request..maka-chat-session-swapcomputesanimation-name: maka-stream-fade-inand no load-earlier control appears transiently.npm run format,npm run lint,check-renderer-architecture,check:asf-headers, and the affectedapps/desktopdist tests (~68 transcript/reading-position/WorkHub cases) pass.AI use
Select exactly one:
Tool(s) and scope: Devin authored the implementation, tests, and verification.
Checklist
Does this PR entail a change in behavior?