fix(desktop): fence stale transcript range failures - #3406
Conversation
Bind range reads to the registration and Host source generation that started them, so teardown and replacement errors cannot leak as failures of the current consumer. Preserve real errors while preventing stale controllers from presenting feedback on a newer surface. Generated-by: Codex
jackwener
left a comment
There was a problem hiding this comment.
Automated review of exact head 2a57d69547d943f1f7c479f482a8f71efb9e33f3.
I confirmed the current main@efe381f4fd registry still returns physical transcript range promises directly, so a close or Host-source replacement can leak a stale rejection through IPC. The patch fences each operation by both transcript-registration object identity and source object identity, while preserving failures from the still-current pair. The renderer guard uses both active Session and controller identity, which correctly handles A → B → A rather than relying on Session ID alone. The deterministic regression covers consumer close, source replacement, and propagation of a real current-source failure. I found no P0-P2 defect.
Required CI is green on this exact head. Merge readiness: not ready yet because the PR is draft and still needs independent human review.
Keep range failures scoped to the consumer, replica, and generation that started them. Same-source subscription recovery can replace the physical replica without changing the outer registry lease, so stale operations must settle at the observer boundary while current failures continue to propagate. Generated-by: Codex
40aae49 to
d146f12
Compare
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for tracing this to stale operation ownership rather than reopening or reusing a closed transcript consumer. I reviewed exact head d146f122b373ce25eada0c4c5e260fa5512335cb, including the latest same-source replica recovery change. The Registry, Observer, and renderer fences each protect a distinct existing ownership boundary, preserve failures from the current owner, and avoid introducing a parallel lifecycle authority. The deterministic regressions cover consumer close, Host-source replacement, replica recovery, and current-owner failures. I found no P0–P3 findings, and required CI is green.
AI-assisted review disclosure: Codex performed the code analysis. The human reviewer reviewed the analysis and made the final approval decision for this exact head.
English
Summary
Fence Desktop transcript range reads to the exact renderer registration and Runtime Host source generation that started them. Closing a session or replacing its Host now cancels stale range failures at the registry boundary, while failures from the current registration and source still propagate normally. Same-source subscription recovery is fenced independently by the exact consumer, replica, and generation owned by the observer.
Also prevent an obsolete transcript controller from presenting a history-load error after the user has moved to another session or returned through an A → B → A navigation.
This keeps teardown immediate and preserves delivery ACK/backpressure semantics; it does not add consumer reuse, request replay, string-matched cancellation, or a second lifecycle state machine.
Fixes #3394
Verification
npm run lintnpm run format:checknpm --workspace @maka/desktop run typechecknpm --workspace @maka/desktop test— 1,019 tests passedRoot cause
RuntimeHostSessionObservationRegistryalready fenced transcript open/restore work by registration and source identity, butloadTranscriptBeforeandloadTranscriptAroundreturned the physical observer promises directly. A close or Host replacement could therefore reject an operation that no longer belonged to the current logical consumer, leaking it through Electron IPC and the renderer toast path. Same-source subscription recovery can also replace the observer replica without changing the outer Registry identities, so the Observer now fences range completion by consumer, replica, and generation.AI use
Select exactly one:
Tool(s) and scope: Codex investigated the lifecycle race, developed the registration/source fencing design, implemented the change and regression coverage, and prepared this draft PR. The human contributor remains responsible for review and submission.
Checklist
Does this PR entail a change in behavior?
中文
概述
将 Desktop transcript range read 绑定到发起它的精确 renderer registration 和 Runtime Host source generation。会话关闭或 Host replacement 后,Registry 会在生命周期边界取消旧 range operation 的失败;当前 registration 和 source 的真实错误仍然正常传播。同一 source 内部发生 subscription recovery 时,Observer 也会用精确 consumer、replica 和 generation 独立 fence 旧操作。
同时,过期 transcript controller 在用户切换会话或经历 A → B → A 后,不再有权提交历史读取错误。
该方案保持 teardown 立即执行,也保留 delivery ACK/backpressure 语义;不增加 consumer 复用、请求重放、错误字符串匹配或第二套生命周期状态机。
对应并关闭 #3394。
验证
npm run lintnpm run format:checknpm --workspace @maka/desktop run typechecknpm --workspace @maka/desktop test— 1,019 项测试通过根因
RuntimeHostSessionObservationRegistry已经用 registration/source identity fence 保护 transcript open 和 restore,但loadTranscriptBefore、loadTranscriptAround直接返回物理 observer Promise。因此 close 或 Host replacement 可以拒绝一个已经不属于当前逻辑 consumer 的 operation,最终通过 Electron IPC 和 renderer toast 泄漏为当前错误。同一 source 内部的 subscription recovery 也可以在不改变 Registry 外层 identity 的情况下替换 observer replica,因此 Observer 现在按 consumer、replica 和 generation fence range completion。AI 使用
工具和范围:Codex 排查生命周期竞态、设计 registration/source fencing、实施修改与回归覆盖,并准备本 draft PR。最终审查与提交责任仍由人类贡献者承担。
Checklist
本 PR 是否改变行为?