触发与范围
桌面聊天侧栏的 REST 刷新与 inbox 实时事件并行。新的事件已把聊天更新为待审批,旧 REST 响应随后返回时,useConversationThreads.refresh 仍整份替换 threads,把待办数从 1 覆盖为 0。实时流去重水位仍保持新值,重放同一已接受事件不会将侧栏恢复。
按 P2 侧栏状态一致性追踪。2026-09-30 只在原 hook 的隔离合成响应中确认;没有真实 Electron 操作、REST/SSE 连接、审批、工具或用户数据丢失。范围仅为 desktop-owned 聊天侧栏的异步读取与状态 admission,现有共享 Web 文件用于定位,不要求本轮修改 Web 或后端。
原 hook 证据与保护对照
fixture 原样转译并执行 useConversationThreads、conversationInbox 与 threadAttention,使用 React test renderer。apiFetch 仅记录输入并返回内存 deferred,EventSource 和重连定时器均为内存对象;无真实网络、票据、账号、localStorage 或 profile。未使用的主聊天/错误/回执依赖被替换为一调用就抛错的边界。
- 挂载触发的旧 REST 保持 pending;合成 inbox snapshot 后接收更新事件,侧栏待办数为 1。随后旧 REST 完成,列表回到旧状态、待办数变为 0。
- 再送同一合成事件,原去重逻辑拒绝它;待办仍为 0,回调总共只接受一次。这是去重工作的对照,也显示 REST 没有遵守同一状态版本。
- 两个手动 refresh 乱序完成,同样由后返回的旧列表覆盖先返回的新列表。
- 顺序 refresh 的对照正确保留新列表和待办数。
完整输入、列表、回调次数、假请求与源码哈希只保存在本地 ignored artifact。这里没有修改存储或后端;不能把丢掉待办呈现写成真实审批消失、消息/草稿丢失或错误工具执行。
根因与影响
refresh 直接执行 setThreads(await fetchConversationThreads()),没有请求 generation 或跟已接受事件状态协调。inbox event 则有独立 appliedSeq/event-ID admission,再用 upsert 更新列表。REST 路径可绕过这份版本保护,造成界面与已接收状态倒退。
用户可能在侧栏看到过时的待办、未读、运行或标题状态,难以判断哪些聊天需要关注。主聊天 view 的线程归属与服务端审批仍有独立保护;本轮只确认侧栏内存呈现,不宣称授权或后端状态被改变。
桌面优化与验收
- 将列表读取、实时 snapshot 和逐条事件纳入统一的侧栏状态合同;迟到读取按请求范围、generation 与有效版本处理,不能用旧快照无条件覆盖新状态。
- 如果响应无法证明比已接收状态更新,保留当前状态并进行受控重取;使用显式协议版本/水位,而不是根据文案猜测新旧。
- 重跑挂载 REST、并行 refresh 与新事件交错序列:已接收的新待办不回退;事件重复仍只接受一次。顺序刷新、新连接与旧连接回调保护保持正确。
- 在独立可弃置桌面环境补实际延迟响应验收,只用合成聊天,不连接审批或工具;未完成前不宣称生产事故或全平台保证。
去重与源码
#37 是进度/恢复提示的 typed notice;#38 是停止与完成竞争;#67 是审批决定的多视图投递;#78 是服务事件入口的身份验证。本条是消费端列表状态被旧读取覆盖,根因独立,关联 Epic #31。
固定公开源码只供导航,本次证据以当前源文件哈希为准:useConversation、conversationInbox、threadAttention。
触发与范围
桌面聊天侧栏的 REST 刷新与 inbox 实时事件并行。新的事件已把聊天更新为待审批,旧 REST 响应随后返回时,
useConversationThreads.refresh仍整份替换 threads,把待办数从 1 覆盖为 0。实时流去重水位仍保持新值,重放同一已接受事件不会将侧栏恢复。按 P2 侧栏状态一致性追踪。2026-09-30 只在原 hook 的隔离合成响应中确认;没有真实 Electron 操作、REST/SSE 连接、审批、工具或用户数据丢失。范围仅为 desktop-owned 聊天侧栏的异步读取与状态 admission,现有共享 Web 文件用于定位,不要求本轮修改 Web 或后端。
原 hook 证据与保护对照
fixture 原样转译并执行
useConversationThreads、conversationInbox与threadAttention,使用 React test renderer。apiFetch 仅记录输入并返回内存 deferred,EventSource 和重连定时器均为内存对象;无真实网络、票据、账号、localStorage 或 profile。未使用的主聊天/错误/回执依赖被替换为一调用就抛错的边界。完整输入、列表、回调次数、假请求与源码哈希只保存在本地 ignored artifact。这里没有修改存储或后端;不能把丢掉待办呈现写成真实审批消失、消息/草稿丢失或错误工具执行。
根因与影响
refresh 直接执行
setThreads(await fetchConversationThreads()),没有请求 generation 或跟已接受事件状态协调。inbox event 则有独立 appliedSeq/event-ID admission,再用 upsert 更新列表。REST 路径可绕过这份版本保护,造成界面与已接收状态倒退。用户可能在侧栏看到过时的待办、未读、运行或标题状态,难以判断哪些聊天需要关注。主聊天 view 的线程归属与服务端审批仍有独立保护;本轮只确认侧栏内存呈现,不宣称授权或后端状态被改变。
桌面优化与验收
去重与源码
#37 是进度/恢复提示的 typed notice;#38 是停止与完成竞争;#67 是审批决定的多视图投递;#78 是服务事件入口的身份验证。本条是消费端列表状态被旧读取覆盖,根因独立,关联 Epic #31。
固定公开源码只供导航,本次证据以当前源文件哈希为准:useConversation、conversationInbox、threadAttention。