Skip to content

[Desktop/Chat][P1] 跨视图延迟整桶写回覆盖不同聊天的草稿、历史与暂存 #75

Description

@jiayuqi7813

触发与范围

桌面聊天两个同服务视图在 debounce 期间分别编辑不同聊天,各自从存储读出整桶并保留自己的 dirty 快照;之后按顺序提交整桶,后写者会覆盖前一个视图的新内容。这个问题不要求 storage 写入失败,也不要求双方编辑同一个聊天。草稿、提示词历史和手动暂存存在同类整桶竞争,统一在本条处理。

按 P1 条件性本地数据丢失追踪。2026-09-30 仅使用原存储模块的独立 VM 与全成功内存存储复验;没有实际打开多个 Electron 窗口、读取真实 profile、删除用户草稿或触发认证/发送/工具。范围仅为 desktop-owned 聊天本地持久化协调,现有共享 Web 文件作证据定位,不要求本轮修改 Web 或后端。

原模块证据与对照

fixture 原样 TypeScript 转译并加载 composerDraftStore、composerRecallStore;两个 VM 拥有独立模块状态,共享合成 StorageLike。原测试 override 接入该存储,定时器仅记录在内存,使用原 flush 函数确定提交顺序。所有 get/set/remove 均成功,无 quota、HTTP、Provider 或真实 localStorage。

合成条件 原存储结果
A、B 分别写不同 thread 草稿,先 A flush 后 B flush A 曾成功持久化,随后从持久桶和 fresh read 消失,B 保留
A 清一个 key,B 持旧桶编辑另一个 key,A 后 B flush A 的已清 key 被 B 旧桶带回
不同 history session 交错 flush 前写 session 消失
两个手动 stash 交错 flush 前写 stash 消失
两 realm 顺序写入并各自 flush 两份草稿均保留
同一 realm 在 debounce 期写两 thread dirty 桶合并正确,两份均保留

完整快照、成功存储轨迹、定时器记录和源文件哈希保存在本地 ignored artifact。这里证明指定存储的合成数据被覆盖;实际发生频率、UI 表现及外部备份未测,不能声称所有内容在任意环境永久不可恢复。

根因与桌面边界

draft 的 readBucketFromStorage 在 memoryDirty 时仅返回本 realm 快照,300ms 后 writeBucketToStorage 全量替换 drafts;recall 的 history/stash 同样在 dirty 时返回本地快照,200ms 后全量替换。提交时没有针对当前存储合并独立 key/entry 或校验版本。单 realm 的 dirty 保护正确,但不能协调多个 writer。

现有桌面同 origin 子窗使用相同 partitionFor(origin),因此共享偏好的多视图前提有源码依据;本轮未操作真实双窗口。不同 origin 的 partition 分离仍有效。逐个 setItem 的成功并不保证这些跨读取、延迟、写回操作构成同一个事务。

桌面优化与验收

  • 为同 service/profile 的 draft/history/stash 使用统一 desktop persistence owner;按具体 key/entry 提交更新和删除,协调 pending 写入与 flush,而非用陈旧全桶替换。
  • 保留独立聊天内容与删除语义;同一 key 的并发修改给出明确的版本/冲突合同,不能静默覆盖用户的更新。
  • 重跑全部合成序列:不同 thread/session/stash 的交错保存均保留双方;删除不会被旧快照恢复。单 realm 与顺序对照继续通过,存储失败保持真实归因。
  • 在可弃置桌面 profile 补双窗口、刷新、重连/关闭前 flush 验收,只使用合成内容;未经此测试不宣称真实桌面事故或全平台保证。

去重与源码

#58 是写盘失败后旧值覆盖内存;本条全部写入成功。#61 是迟到发送结果清理已变引用;本条不发送。#74 是通知设置旧字段覆盖;这里是 draft/recall 整桶并发导致不同聊天的用户数据丢失。#69 的认证主动清理策略另行处理。旧版 v1 的重新迁移也作为独立主因跟踪。本条不按各存储或场景拆计数,关联 Epic #31。

固定公开提交仅用于导航,当前源文件哈希为本次证据:composerDraftStore、composerRecallStore、ConversationShell、main.cjs。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions