bug: 手机端离线期间被桌面删除的历史消息,重连后仍留在窗口里
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 现象
手机端离线(退后台、断网、切端)期间,桌面侧删除了消息(`/clear` 清理某时刻之前的历史、rewind 回撤、单行删除),删除 push 收不到。重连后即使拉了最新页对账,**窗口里早于本页最旧行的那些已删除消息仍会留在界面上**,直到整窗替换(`options.replaceMessages`)或会话被回收。
## 复现(单测形态,实测输出)
```
窗口 = t10, t20, t30(一页权威页拉回,覆盖区间 = [t10, t30])
手机离线 → 桌面删掉 t10(删除 push 收不到)
重连最新页(满页,moreBeyondWindow: true)= t20, t30
实际: t10, t20, t30 ← t10 已被服务端删除,仍在窗口里
预期: t20, t30
```
同一形状在 `origin/main`(#1210 之前)实测同样复现,所以这是既有缺口,不是 #1210 引入的回归。#1210 只是把它的触发条件从「缓存旧页与最新页有交集就整段保留」收窄到「旧行落在已验证覆盖区间内、且本页与该区间首尾相接」。
## 根因
`setLatestMessageWindow` 保留旧段的依据是「这段历史与本页连续」。#1210 加的事实自检能证伪**一个方向**——最新页在覆盖区间内带来了窗口没有的行 ⇒ 断言为假;但证伪不了**反方向**:窗口里有服务端**已经删掉**的行。满页时最新页到不了那些更早的行,页本身不构成证据。
## 修法方向
需要一条客户端能独立核对的「多行」信号,最现成的是**总数对账**:`session._count.messages` 对上窗口里已加载的服务端行数(仓内 `hasOlderMessagesAfterReopen` 已经在用这个比较,可复用它的过滤口径)。窗口里的服务端行数 > 服务端总数 ⇒ 窗口里有已删除的行 ⇒ 作废覆盖区间、丢弃早于本页的旧段(甚至整窗重来)。
落地前要先核实的坑:
- `_count.messages` 与「窗口里被算作服务端行」的口径是否严格一致(本地系统卡、临时流式行、tool 行的取舍);
- 远程(SSH)会话与旧版被控端是否稳定给 count;不给时必须退化为「不对账」,不能当成 0;
- 误判方向是**清空用户正在看的历史**,比现状(多显示几条已撤回的消息)更糟,所以需要偏保守的判据与专门的回归测试。
## 影响
用户可见但范围有限:只影响「离线期间桌面删过消息」+「重连时最新页是满页」这一组合,且被删的行必须早于本页最旧行。下一次整窗替换或重新打开会话(触发 `replaceMessages` 的路径)即恢复。
发现于 #1210 的 review([codex 的 thread](https://github.com/makecindy/cindy/pull/1210#discussion_r3691496916),它给出的例子形状不复现——`isOlderLoadedPage` 还要求「旧行早于本页最旧行」——但指出的方向有上面这个真变体)。
Contributor guide
Research direction
Start at setLatestMessageWindow and trace how session._count.messages is compared with loaded service-side rows. Read the existing hasOlderMessagesAfterReopen logic and the #1210 changes, then inspect the replaceMessages path and related message-window tests. Done means a regression test covers offline deletion before the oldest full-page row without incorrectly clearing local or unsupported-count rows.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100