Skip to content

fix(mobile): 补齐历史窗口空洞,跨空洞不再折成一条工作组 - #1210

Merged
MagicLizi merged 12 commits into
mainfrom
dash/mobile-history-gap
Jul 31, 2026
Merged

fix(mobile): 补齐历史窗口空洞,跨空洞不再折成一条工作组#1210
MagicLizi merged 12 commits into
mainfrom
dash/mobile-history-gap

Conversation

@dashhuang

@dashhuang dashhuang commented Jul 31, 2026

Copy link
Copy Markdown
Member

这次改了什么

摘要

手机端打开长会话时,中间的对话会整段消失,只剩首条消息、一条「已工作 142m 32s」和最后一条回复。

原因是两层:

  1. 窗口本身有洞。手机端的消息窗口由冷开缓存的旧页、listMessages 的最新页和 local-db:messages:created push 的尾部拼成,几者之间没有连续性保证 —— 断连期间漏收的 push 不会补回,而 setLatestMessageWindow 只要求缓存旧页与最新页有交集就整段保留。于是窗口成了「首段 + 尾段」的孤岛,中间几百行从未加载。
  2. 渲染层没有兜底。桌面早有 HISTORY_GAP_SPLIT_MS(30 分钟)双层守卫(groupWorkRuns + tool_segment 都按它切),手机端走的那份共享分组 messageRender.ts 一直没有。user 行是唯一的 turn 边界,窗口里缺了它,跨空洞的动作就被折进同一个「已工作 Xs」,时长也从某段真实工作谎报成整场跨度。

实测现场:一个 445 行的会话(8 条 user 行、跨 2 小时 22 分),手机上只渲染出 3 个 item,中间 6 轮对话("给我总结下我们解决了什么问题?"、"怎么改的跟我说一下"、"好的,提交pr" 等)连同各自的回答全部不可见。

本 PR 三层一起修:

  • 阈值上移:新增 packages/maker-shared/src/historyGap.ts 作两端共用正本,desktop 侧 lib/historyGap.ts 改为 re-export(桌面既有引用与文档指向不变)。
  • 渲染兜底:共享 messageRender.ts 的工作组分组与 tool_group 段内双层按阈值切开,对齐桌面 MessageStream。锚点取结束时刻并取已见最大值 —— 为此新增 settledAt(配对 tool_result 的落库时刻,由 shared pairing 新增的 resultCreatedAtFor 提供、mobile normalize 填充),所以「一次跑 40 分钟的工具」「想了 40 分钟的 thinking」「并行工具乱序完成」都不会被误判成空洞、把连续工作切碎。
  • 窗口连续性(从源头,Closes bug: 手机端 30 分钟内产生的历史窗口空洞不会被检测到 #1222setLatestMessageWindow 原本只要求缓存旧页与最新页有交集就整段保留 —— 交集不等于连续,两段之间仍可能隔着服务端有、本地从未加载的行。现在调用方把结构信号 moreBeyondWindow(本页满页或被 device-link 裁行 = 上沿之外还有服务端历史)传进 store,为真时早于本页的缓存段一律丢弃,窗口于是始终是「某点 → 最新」的连续区间。这条不依赖时间阈值,所以覆盖了「断连期间漏收几十上百条、但它们在 30 分钟内快速产生」那类检测不到的孤岛(一个长 turn 里的连续工具调用就是)。判据与「加载更早」入口同源(shouldKeepOlderMessagesAffordance),本就该一致。唯一的例外是已验证连续的那种旧段(整窗替换的那页、用户一路「加载更早」翻出来的、订阅未断时收到的 push):它由一段显式的覆盖区间 [since, until] 标出,与本页首尾相接时保留,否则用户正在看的历史和滚动锚点会在每次满页重连时凭空消失(详见下方不变量 8)。
  • 窗口自愈:新增 apps/mobile/src/session/historyWindowGap.ts。找窗口里最靠尾部的跳变后,先花一次 limit=1 探测确认服务端两行是否本来就相邻 —— 时间跳变不等于空洞,"用户隔夜回来继续聊"同样会留下几小时的间隔,拿时间阈值直接触发翻页会让这类会话每次打开都白翻几页。确认有洞才沿既有 before 游标向上翻页补齐,判定只看本页真正取回的行(拿合并后的窗口判定,随便一页都会让判定成立,空洞永远补不回来)。

变更类型

  • feat 新功能
  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联 Issue / 需求:无(2026-07-31 实机发现)
  • 本 PR 包含:共用阈值常量上移 + 共享渲染分组的空洞守卫(含 settledAt 结束时刻锚点)+ store 层的窗口连续性保证(从源头消除孤岛,Closes bug: 手机端 30 分钟内产生的历史窗口空洞不会被检测到 #1222 + 手机端窗口空洞探测与补齐 + 回归测试
  • 明确不包含:
    • 不改服务端侧 local-db:messages:list 的分页语义(连续性完全由客户端的结构信号判断)
    • 不做「服务端已删行」的对账(bug: 手机端离线期间被桌面删除的历史消息,重连后仍留在窗口里 #1237):覆盖区间的事实自检只能证伪「区间内我缺行」,证伪不了「区间内我多行」——离线期间桌面 /clear/rewind 删掉的行,重连后若早于最新页最旧行会继续留在窗口里。实测 origin/main 同样复现,是既有缺口而非本 PR 回归;修它要引入方向相反的新信号(_count 总数对账),误判方向是清空用户正在看的历史,需要单独设计与验证
    • 不给空洞加"这里有未加载历史"的可见提示(桌面也没有;补齐成功后用户不该看见断裂)
    • 不动 device-link wire protocol,不加 after 方向游标
  • 用户可见变化:手机端长会话不再"中间掉一段";跨空洞时折叠条会切成多条、时长不再谎报
  • 是否存在 breaking change:无

状态模型与不变量(补齐流程)

三轮 review 全部集中在补齐的状态形状上,所以把不变量写在这里当锚点。每条在代码里只允许有一个判据,所有对称路径复用它;historyWindowBackfillWiring.test.ts 逐条对着断言。

状态backfillGapStateRef{ sid, epoch, contiguous, backfilled, failed })、backfillInFlightRun{ sid, seq } | null,可观察 state)、backfillRunSeqRef / backfillLatestRunSeqRef(单调序号)。

事件:窗口对账完成、messages 变化、切会话、换连接代、补齐 settle(6 态)。

不变量

  1. 一轮补齐的身份是单调的。每次启动分配只增不减的 runSeq;「是否已被取代」「飞行标记的清除」「结论的写入」全部对着 seq 比。凡是「当前状态是否仍等于启动时状态」的判据都不可靠——会话 id 会摆回来(A 在飞 → 切到 B → 快速切回 A),那种判据会把取消撤销掉,于是同一会话并发翻页、旧轮收尾还误清新轮的标记。切会话时占掉一个序号(不启动新轮)即作废在飞的那一轮,作废是终态。

  2. 同一会话同一时刻最多一轮在飞。互斥按 inFlightRun.sid === sessionId;别的会话残留的那一轮不连坐当前会话,它自己会在下一次 isCancelled 上收手。

  3. 同步门槛按 session + 连接代判定。屏实例会被原地复用,屏幕级 lastSyncedAt 在切会话后仍是上一个会话的非空值;复用仓内既有的 readAckSyncedKey

  4. 每个 settle 结局有独立的遗忘条件与预算归属

    结局 跳过后续检测 占翻页额度 遗忘条件
    contiguous(服务端本来相邻,是事实) 本次访问内不忘
    covered / budget / exhausted(真翻过页) 本次访问内不忘
    failed(请求异常) connectionEpoch 变化即清空
    cancelled 不记(两个触发条件本身就不会招来立刻重试)
  5. 两道预算闸:考察总次数 HISTORY_GAP_MAX_CONSIDERED_PER_VISIT(防跨数百天的会话里几十上百处正常停顿打出上百次 limit=1 探测)、翻页段数 HISTORY_BACKFILL_MAX_GAPS_PER_VISIT(防一路翻整场历史)。前者统计三类之和,后者只统计真花了翻页请求的那些。

  6. 窗口连续性是一段显式的「已验证覆盖区间」,不是一个下界[since, until] 断言「这个闭区间内的所有服务端行都在窗口里」,另带一个 liveTailTrusted 位表示「自 until 建立以来实时推送链路没断过」。

    建立 / 扩展 整窗替换(区间即那一页)、最新页对账(与旧区间相接则求并,否则收敛到本页)、「加载更早」(沿 before 从最旧端连续翻页 → 下界前移)、订阅内到达的实时 push(上界后推)。冷开缓存 hydrate 与空洞补齐 mergeMessages 刻意不登记
    尾部可信的条件 liveTailTrusted权威页落库那一刻订阅是否已 ACK 决定。屏幕侧的 openAndSubscribestartFocusedTopicSubscription 都是 void subscribe(...)(订阅只管之后的推送,不该挡数据读),所以「页比订阅先到」是常态;这个空窗里被控端写下的行既不在这一页、也不会被推来,之后一条 push 就会把 until 抬过它们,等尾部涨过一页后最新页已不含那几行、自检也发现不了。反过来重连补齐(rehydrate)是 await subscribe 之后才拉页,尾部可信。ACK 本身不点亮既有区间——ACK 之前的空窗里可能已经漏了行,只有此后落库的权威页能重新确定尾部
    失效 /clear、rewind、会话回收、服务端行被清空 → 删除整个区间;断流清 liveTailTrusted 与 ACK 记录(socket 掉线 → 全部会话;退后台释放 session:<id> 订阅、离开会话取消订阅 → 对应会话),区间本身保留——断流不会让断流前验证过的那段变假。生效与失效必须成对:ACK 记录若不随断流作废,断线后才落库的在途页会重新把尾部标成可信,绕一圈回到同一个孤岛
    采纳 保留旧段要求「旧行落在区间内」「本页最旧行不晚于 until」(两段首尾相接)。只记下界不够:下界断言的是「到窗口最新端连续」,而窗口最新端会被断流期间漏收的行悄悄作废——旧窗 1–80、漏收 81–200、重连后先到一条 push 201、最新页 122–201,此时「有交集」仅靠那条 push 成立,旧下界却还在给孤岛背书
    自检 最新页若在区间内带来窗口没有的行,说明断言已被服务端事实推翻(桌面侧改写历史、迟到落库),当次按「未知」处置
    记账时机 在相等早退之前。权威页与缓存逐行相同是冷开常态;被早退跳过则这次响应白来——之后会话靠 push 涨过一页、再遇一次满页重连刷新,本已被权威页确认过的行会被当成来源不明全丢,用户当前历史与滚动位置一起消失。setMessagessetLatestMessageWindow 两条早退路径口径一致

    保留判据与区间记账共用同一个「是否相接」的判断结果:两处分开算,早晚会出现「保留了旧段却不声明覆盖它」(下次同步照丢)或「声明了覆盖却已经丢掉」(凭空背书出一个孤岛)。

  7. 身份不确定的信号不进判据。同毫秒组内的行 id、按邻接位置猜出来的 tool_result 归属,都不足以确定身份:空洞的身份用两侧时刻对historyWindowGapKey),连上/相邻判定按时刻比而不是 id 精确匹配,工具的结束时刻(settledAt)只认 toolUseId 精确配对。内容可以猜(猜错最坏是显示串了,live 中按 id 到达即自愈),进入判据的时刻不能猜——猜错会让空洞守卫在最需要它的场景失效。

  8. 补齐永不写用户可见的加载态或错误。它是静默自愈,失败由渲染层的 HISTORY_GAP_SPLIT_MS 守卫兜底(不谎报时长),用户仍可用「加载更早」自己往上翻。

UI 变化

不涉及:没有新增或修改任何组件、布局、样式、动效或文案。唯一的视觉差异是消息流里的「已工作 Xs」折叠条在窗口不连续时会切成多条(既有组件、既有样式,条目数与时长由数据决定),与桌面现有行为一致。

  • 引用的设计规范:不涉及:改动命中 UI 路径(apps/mobile/app/apps/mobile/src/session/apps/desktop/src/renderer/)但确无视觉/交互/文案变化 —— 全部是消息流分组逻辑、窗口分页与共享常量,没有新增或修改任何组件、布局、样式、动效与 UI 文案,也没有新增颜色或 token。唯一的观感差异是既有的「已工作 Xs」折叠条在窗口不连续时会切成多条(既有组件、既有样式,条数与时长由数据决定),与桌面 MessageStream 的现行行为一致。

怎么验证的

自动验证

pnpm --filter mobile typecheck                      ✅
pnpm --filter desktop typecheck                     ✅
pnpm --filter @cindy/maker-shared exec vitest run   ✅ 51 文件 / 736 测试
pnpm --filter mobile exec vitest run                ✅ 271 文件 / 2756 测试
pnpm --filter mobile run test:scope                 ✅ mobile-scope-guard passed
pnpm --filter mobile run test:smoke                 ✅ 2 文件 / 2 测试
desktop src/renderer/__tests__/workGroup* 定向      ✅ 27 测试(确认 re-export 与 max 锚点未破坏桌面守卫)
仓库根单测门禁(run-unit-gate.sh)                   ✅ GATE_EXIT=0,pass 315 / fail 0
pnpm check:dco                                      ✅ 11 commits signed off

新增回归测试 48 个:

  • packages/maker-shared/src/__tests__/messageRenderHistoryGap.test.ts(8):跨空洞切成两组且时长不横跨、空洞落在两次工具调用之间时 tool_group 也切开、40 分钟工具/40 分钟 thinking 不算空洞、空洞前那段按动作结束时刻结算(不低报)、无 nextItem 时取全体子项结束时刻的最大值(并行乱序完成)、缺 settledAt 时退回发起时刻、窗口连续时分组不变。
  • apps/mobile/src/__tests__/historyWindowGap.test.ts(20):跳变检测(取最靠尾部一处、严格大于阈值、跳过 mobile-system-* 合成卡、时间不可解析的行不参与)与补齐结局(contiguous / covered / exhausted / 游标不前进 / 请求数预算 / 行数预算 / cancelled / failed)。
  • apps/mobile/src/__tests__/historyWindowBackfillWiring.test.ts(11):屏幕侧前置条件不许被删(不变量 1–6 逐条断言:单调 seq 与三个作废入口、同会话互斥、按 session + 连接代的同步门槛、六条 settle 结局各自的遗忘条件与预算归属、两道预算闸、补齐不写 error/loadingEarlier),外加实时流生效/中断通知的四个入口(订阅被远端 ACK 时按真正记进 ACK 表的 topic 生效;socket 掉线 → 全部会话;退后台释放 session:<id> 订阅、离开会话取消订阅 → 对应会话)。
  • apps/mobile/src/__tests__/remoteSessionStore.test.ts(+14):窗口连续性覆盖区间——moreBeyondWindow 为真时丢弃来源不明的缓存段、「加载更早」翻出来的历史在满页重连时保留、rewind 后结论失效、丢弃旧段时仍保留更新的 live push 与本地系统卡、断流后先到的 push 不再让旧段被背书、断流后到达的 push 不推进上界(本页被裁到只剩它时信任位是唯一守卫)、断流按会话隔离、订阅未断时 push 推进上界后满页刷新仍保留更早历史、两条相等早退路径都登记连续性、最新页在区间内带来窗口没有的行时旧结论作废、订阅 ACK 之前落库的权威页尾部不算可信断流时 ACK 记录一并作废(socket 掉线与按会话释放两种形态各一例)。
  • packages/maker-shared/src/__tests__/messageNormalize.test.ts(+2):settledAt 只认 toolUseId 精确配对,邻接猜出来的时刻不进判据。
  • apps/mobile/src/__tests__/messageRenderModel.test.ts(+1):手机端整链路上的孤岛形状回归。

用真实现场数据(本机 445 行会话)跑过修复前后对比:

修复前 修复后
孤岛窗口(首段 12 行 + 尾段 8 行) 3 个 item,一条 work_group durationMs=8552s(= 142m 32s) 8 个 item,切成 2s / 16s / 0s / 40s 四段
完整 445 行 24 个 item,6 轮 user + 各自工作组,时长 934s/5s/7s/7s/17s/370s/55s/175s 不变

手工验证

在 iPhone 17 Pro 模拟器(iOS 26.5、cn 版 dev client com.xd.cindycn v0.1.0 build 2026072301)上跑本分支:pnpm mobile:sim:whoami -- --region=cn✓ PASS,Metro :8081 归属本 worktree,源码指纹 dash/mobile-history-gap@cd31ce92a+3775274060。启动到登录页与主流程正常,无红屏、无回归。

未执行的验证

  • 孤岛症状的实机复现未做:那条「已工作 142m 32s」需要"冷开缓存旧段 + 最新页尾段"拼出来的窗口,干净安装的模拟器首开拿到的是连续的最新一页,无法自然构造。因此实机只验证了"打开长会话正常、没有回归",空洞两侧的切分与补齐由上述真实数据回归与单测覆盖。
  • 未在 Android 上验证:本改动是纯 TS 数据层与渲染分组,无平台分支。

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:

影响与回滚

  • 影响范围:手机端消息流分组与会话打开时的一次条件性补齐;桌面端只是常量换了个来源文件(值与语义逐字不变,桌面既有守卫测试仍绿)。
  • 协议兼容:不涉及。全程复用既有 local-db:messages:listbefore 游标(与「加载更早」同一通道,已在 device-link allowlist 内),没有新增 IPC channel、push 事件或 wire protocol 字段。
  • fingerprint / OTA:不涉及。未触及 app.jsonapp.config.jsapps/mobile/package.jsoneas.json production 段、plugins/modules/fingerprint.config.cjs不触发冷更
  • 网络成本:连续窗口零额外请求;窗口存在 30 分钟以上跳变时,每处空洞每次打开最多一次 limit=1 探测(真安静的会话到此为止),确认有洞才翻页,上限 400 行 / 12 请求 / 每次打开 3 处。补齐失败静默,由渲染守卫兜底并保留「加载更早」入口。
  • 用户数据:补齐只读不写。取消判据同时看会话镜像 ref 与"空洞较新侧那行是否还在窗口里",所以 /clear、rewind 或整窗替换之后不会把刚被移除的历史(含 clearedAt 之前的消息)merge 回窗口。
  • 回滚 / 降级方式:整体 revert 即可,无数据迁移、无持久化格式变化。回滚后回到原行为(窗口仍可能有洞、跨空洞折成一条)。

远程 / 手机适配(docs/dev-rules/remote-and-mobile-adaptation.md

  1. SSH 远程工作区:本改动只读会话消息,走既有 maker.listMessages(origin-aware 路由,远程会话自动经 listMessagesFor),不碰 workdir 文件与 agent 进程,无本机 fs 读取。
  2. IPC / push 白名单:无新增 channel 或推送事件,复用已在 allowlist 内的 local-db:messages:list
  3. 手机版入口 / UI:本 PR 本身就是手机版修复,桌面侧同款能力早已存在(backfillHistoryUntil + HISTORY_GAP_SPLIT_MS)。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI 则跳过)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档(阈值与补齐策略的完整理由写在 historyGap.ts / historyWindowGap.ts 的文件头注释,与既有规则文档指向一致)
  • 已确认测试结果或说明未执行原因

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 31, 2026 10:01
@dashhuang
dashhuang requested a review from a team as a code owner July 31, 2026 10:01
@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Greptile Summary

本次变更修复手机端历史消息窗口空洞及跨空洞工作组错误合并:

  • 通过单调运行序号和按会话隔离的飞行状态,确保切换会话、重连及手动加载时旧补齐轮次不可恢复且不能误清新轮次。
  • 自动探测并补齐窗口内部缺失历史,同时为请求次数、补齐行数及每次访问处理的空洞数设置预算。
  • 使用显式连续覆盖区间管理缓存页、最新页和实时推送之间的连续性。
  • 在共享渲染层按统一时间阈值切分跨空洞工作组,并以工具实际结束时间计算分组边界。

Confidence Score: 5/5

当前实现已关闭此前报告的跨会话补齐阻断和取消撤销路径,看来可以安全合并。

当前代码在会话切换、连接代变化和手动加载入口统一推进单调序号;旧轮在异步请求返回后会先检查取消状态,结论写入及飞行标记清理也都按运行序号校验,因此此前两个线程描述的阻断、并发补齐和误清新轮状态均不再可达,没有剩余阻塞性故障。

Important Files Changed

Filename Overview
apps/mobile/app/sessions/[sessionId].tsx 接入窗口补齐流程,并以单调序号、按会话互斥和同步代门槛管理补齐生命周期;此前报告的跨会话阻断与取消撤销路径已关闭。
apps/mobile/src/session/historyWindowGap.ts 新增空洞检测、服务端邻接探测、分页补齐、取消检查和请求预算控制。
apps/mobile/src/session/remoteSessionStore.ts 使用显式覆盖区间及实时尾部可信状态维护最新窗口连续性。
packages/maker-shared/src/messageRender.ts 为共享消息分组加入历史空洞切分,并按已见动作的最大结束时间确定边界。
packages/maker-shared/src/messageNormalize.ts 通过精确 toolUseId 配对提供工具结果落库时间,避免长任务或乱序完成被误判为空洞。
apps/mobile/src/device-link/DeviceLinkContext.tsx 将实时订阅确认和断流事件传递给消息窗口连续性状态。

Sequence Diagram

sequenceDiagram
  participant Screen as 手机会话页
  participant Store as remoteSessionStore
  participant Desktop as 桌面消息服务
  Screen->>Desktop: 同步最新消息窗口
  Desktop-->>Screen: 最新页 + moreBeyondWindow
  Screen->>Store: 对账连续覆盖区间
  Screen->>Screen: 检测最靠尾部的时间跳变
  Screen->>Desktop: "limit=1 邻接探测"
  alt 服务端本来相邻
    Desktop-->>Screen: 较旧侧相邻行
    Screen->>Screen: 记录 contiguous
  else 确认存在空洞
    loop 请求与行数预算内
      Screen->>Desktop: 按 before 游标加载更早页
      Desktop-->>Screen: 历史消息页
      Screen->>Store: 合并补齐结果
    end
  end
Loading

Reviews (12): Last reviewed commit: "fix(mobile): 尾部可信改由订阅 ACK 决定,页比订阅先到不再算可信" | Re-trigger Greptile

Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1fbf6a6a4f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated
Comment thread apps/mobile/app/sessions/[sessionId].tsx
Comment thread packages/maker-shared/src/messageRender.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

本 PR 修复手机端长会话在“历史窗口不连续(有空洞)”时中间对话整段不可见、且跨空洞被折成一条「已工作 Xs」导致时长谎报的问题:将桌面既有的空洞阈值与切分逻辑对齐到共享层,并在手机端增加窗口空洞探测与按需补齐。

Changes:

  • 新增 @cindy/maker-shared 级别的 HISTORY_GAP_SPLIT_MS 单一来源,并在桌面侧通过 re-export 保持既有引用路径不变。
  • 共享渲染层(messageRender.ts)补齐“跨空洞切分”兜底:工作组与 tool_group 段内按阈值切开,并新增 settledAt(工具结束锚点)以避免长工具/长 thinking 被误判为空洞。
  • 手机端新增 historyWindowGap:对窗口尾部跳变先 limit=1 探测确认是否真空洞,再沿 before 游标回填补齐;同时补充覆盖该行为的回归测试。

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/maker-shared/src/messageRender.ts 增加空洞阈值切分与 settledAt 结束锚点,避免跨空洞合并导致时长谎报
packages/maker-shared/src/messageNormalize.ts tool_use/tool_result 配对新增“结果 createdAt(结束时刻)”查询能力,供渲染层锚点使用
packages/maker-shared/src/historyGap.ts 新增两端共用的 HISTORY_GAP_SPLIT_MS 常量单一来源
packages/maker-shared/src/tests/messageRenderHistoryGap.test.ts 新增共享渲染层历史空洞切分的回归测试
packages/maker-shared/package.json 暴露 ./history-gap 子路径导出
apps/mobile/src/session/messageNormalize.ts mobile normalize 填充 settledAt(来自 tool_result createdAt)
apps/mobile/src/session/historyWindowGap.ts 新增手机端窗口空洞探测与补齐实现
apps/mobile/src/tests/messageRenderModel.test.ts 增加手机端渲染链路在“孤岛窗口”形状下不跨空洞折叠的回归用例
apps/mobile/src/tests/historyWindowGap.test.ts 覆盖空洞检测与补齐的核心分支与预算/取消/失败结局
apps/mobile/src/tests/historyWindowBackfillWiring.test.ts 锁住 SessionScreen 接线层的关键前置条件(互斥、去重、取消判定等)
apps/mobile/app/sessions/[sessionId].tsx 在会话屏幕中接入后台空洞补齐流程(探测 + 回填 + 取消条件)
apps/desktop/src/renderer/lib/historyGap.ts 桌面侧常量改为从 shared re-export,保持桌面引用路径稳定

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/mobile/src/session/historyWindowGap.ts Outdated
Comment thread apps/mobile/src/session/historyWindowGap.ts Outdated
Copilot AI review requested due to automatic review settings July 31, 2026 10:30

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 20a4387fb0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated
Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Comment thread packages/maker-shared/src/messageRender.ts Outdated
Copilot AI review requested due to automatic review settings July 31, 2026 10:54
Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe34cc5a06

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (1)

apps/mobile/app/sessions/[sessionId].tsx:3812

  • 这里注释把 cancelled 归到 failed 一类,但下面实际实现刻意对 cancelled 不落地(outcome !== 'cancelled' 才写 backfilled,且 cancelled 不写 failed)。建议把这段注释口径改成与实现一致,避免后续维护者误以为 cancelled 会进入 failed 集合。
   *  - `failed`:请求异常(断线等)。跳过是为了防抖(messages 每变一次就重试会打成请求风暴),
   *    但**绑在连接代上**:`connectionEpoch` 变化即清空,重连后同一处可以再试。不占额度。
   *    `cancelled` 也归这里 —— 会话切走 / 锚点行被 /clear、rewind 拿掉,都属于"这次没做成"。

Copilot AI review requested due to automatic review settings July 31, 2026 11:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (1)

packages/maker-shared/src/messageRender.ts:1326

  • itemEndTimestampwork_group 目前是“从后往前找到第一个有时间戳的 child 就返回”。但 work_group 的 children 是按发起/出现顺序组织的,且你在 workRunFallbackEnd 的注释里也明确提到并行动作会乱序完成;因此这里应该取 所有 child 结束时刻的最大值,否则会把锚点回退(例如 A 先发起跑很久、B 后发起很快结束,最后一个 child 是 B),进而把后续本来连续的 item 误判成空洞并错误切段。
  if (item.type === 'work_group') {
    for (let index = item.children.length - 1; index >= 0; index--) {
      const childEnd = itemEndTimestamp(item.children[index]);
      if (childEnd !== null) return childEnd;
    }

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f4cf3687f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/src/session/historyWindowGap.ts Outdated
Comment thread apps/mobile/app/sessions/[sessionId].tsx
Copilot AI review requested due to automatic review settings July 31, 2026 11:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (1)

packages/maker-shared/src/messageRender.ts:1326

  • itemEndTimestampwork_group 的结束时刻目前是“从后往前找到第一个有时间戳的子项就返回”,这等价于取“最后一个子项的结束”。但 work_group.children 的排序是按动作发起时刻,不保证结束时刻单调(并行 Agent/Task、长 thinking + 短 tool 等场景会出现较早发起的子项结束更晚)。在这种情况下锚点会被低估,导致后续 item 被误判为跨空洞而被切段/切组。建议对 work_group 也按所有子项结束时刻取 max(与 workRunFallbackEnd 的口径保持一致)。
  if (item.type === 'work_group') {
    for (let index = item.children.length - 1; index >= 0; index--) {
      const childEnd = itemEndTimestamp(item.children[index]);
      if (childEnd !== null) return childEnd;
    }
    return null;
  }

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 702f3d9536

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/src/session/historyWindowGap.ts
@MagicLizi

Copy link
Copy Markdown
Contributor

@dashhuang 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/mobile/src/session/historyWindowGap.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

Copilot AI review requested due to automatic review settings July 31, 2026 12:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

上一个 commit 的收紧太粗:它只看"最新页是否满页",于是把用户一路「加载更早」翻出来的历史也当成
不可信段丢掉 —— 一次普通断线重连(最新快照恰好满 80 行)就会让正在看的历史与滚动锚点消失,而且
自动补齐不会拉回(裁完窗口里已经没有内部跳变可发现),只能手动重翻。#1210 review 指出。

窗口连续性因此分成两档,由 store 的 `sessionContiguousSince`(每会话「已验证连续」的下界)标出:

- 建立下界的只有"服务端一次给出的连续段":整窗替换 `setMessages`、最新窗口 `setLatestMessageWindow`、
  以及沿 before 从窗口最旧端连续翻页的「加载更早」—— 后者走新入口 `mergeEarlierMessages`。
- 冷开缓存 hydrate 刻意不建立(它与最新页的关系无从确认,正是 #1222 要丢的那类)。
- /clear、rewind、error-persisted 整窗失效、会话回收一律重置为未知,下次最新窗口同步重建。
- 判据本身以 hasOverlap 为前提:下界断言的是"到**窗口最新端**连续",本页与窗口完全不重叠时
  (陈旧窗口)那个断言对并集不再成立,不能让旧结论赖着不走 —— 既有的
  "replaces a stale cached window" / "keeps live-pushed tail" 两条测试正钉这一点。
- 空洞补齐的 `mergeMessages` 刻意不下移下界:补完虽然更连续,但要算准新下界得先确认窗口里没有
  别的洞,而那正是补齐在解决的问题。保守不动的代价是补回的段可能在下次满页同步时被丢弃
  (下次打开会重新补),方向上是安全的那一侧。

于是三种段各归其位:已验证连续的保留,来源不明的按 moreBeyondWindow 丢弃,比本页更新的 live push
行与本地系统卡照旧保留。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Dash <dashhuang@gmail.com>
Copilot AI review requested due to automatic review settings July 31, 2026 13:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

apps/mobile/src/session/remoteSessionStore.ts:1332

  • setLatestMessageWindow 在“更早段被丢弃时收敛下界”的判断里用的是 next.some(item.createdAt < latestOldestCreatedAt)。但 next 会包含 mobile-system-* 本地系统卡,它们的 createdAt 可能早于本页最旧行,从而让这里误以为“仍有更早历史被保留”,导致 sessionContiguousSince 不被重置。这样旧的连续性下界可能继续背书后续窗口,进而在 moreBeyondWindow=true 时把来源不明的更早缓存段错误当成“已验证连续”而保留。建议这条判断排除 mobile-system-*
    // 本页自身是连续段;若更早的段被丢弃,下界就收敛到本页最旧行。用前移语义合并:既有下界更早
    // 且那些行仍在窗口里(已验证连续)时保持不动。
    if (!next.some((item) => item.createdAt.localeCompare(latestOldestCreatedAt) < 0)) {
      sessionContiguousSince.delete(sessionId);
    }
    lowerContiguousSince(sessionId, latestOldestCreatedAt);

Comment thread packages/maker-shared/src/messageRender.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: acee484bae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated
第七轮 review 的两条,并把这两个不变量的消费点穷举补齐,不等下一轮再被指出。

1. 手动翻页与自动补齐仍会短暂并发(codex P2)。上一轮把 loadingEarlier 加进作废 effect 的依赖,
   但 effect 是**被动**的:loadEarlierMessages 在 setLoadingEarlier(true) 之后同步就发请求,若自动
   补齐在 effect 执行前返回,它照样通过 isCancelled、merge 并继续下一页。作废因此抽成
   abandonInFlightBackfill(),手动入口在发请求**之前**同步调用;被动 effect 只留给切会话与
   连接代变化(重连 —— 在飞请求走的是旧连接,早收手比等超时干净,这条是穷举时补的第三个入口)。

2. 嵌套工作组的结束时刻取的是"最后一个 child",不是全体最大值(copilot)。children 按发起时刻
   排列,并行动作乱序完成时真正的结束可能落在更靠前的 child 上。shared 的 itemEndTimestamp 与
   桌面同款函数 renderItemEndMs 都改成全量取 max,与两处已有的 Math.max 锚点(groupWorkRuns 的
   prevEndMs、workRunFallbackEnd)口径一致。

如实说明:这条在**当前调用图下不可达** —— 进入 end 计算的 run/items 都来自 buildLinearItems 的
线性输出,不含 work_group(内层组只出现在外组的 children 字段,不参与外组时长)。所以改的是口径
一致性与防御性,写不出能区分新旧实现的测试,也就没有为它硬造用例(试写过一条,新旧实现都通过,
已删掉——留着会假装钉住了什么)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Dash <dashhuang@gmail.com>
Copilot AI review requested due to automatic review settings July 31, 2026 14:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.

Suppressed comments (1)

apps/mobile/src/session/remoteSessionStore.ts:1331

  • 这里用 next.some(item.createdAt < latestOldestCreatedAt) 来判断“更早的段是否仍在窗口里”并据此决定是否 delete sessionContiguousSince,会被本地系统卡(mobile-system-*)干扰:系统卡会被刻意保留且 createdAt 可能远早于 latest page,导致条件恒为 true,从而无法在丢弃更早服务端缓存段时把 contiguousSince 收敛到本页最旧行。这样会让 sessionContiguousSince 继续背书一个实际上已不在窗口里的“已验证连续”下界,后续最新页同步可能错误地把更早的缓存段当成 verified contiguous 而保留。建议此处只检查非本地系统卡/真实服务端行。
    // 本页自身是连续段;若更早的段被丢弃,下界就收敛到本页最旧行。用前移语义合并:既有下界更早
    // 且那些行仍在窗口里(已验证连续)时保持不动。
    if (!next.some((item) => item.createdAt.localeCompare(latestOldestCreatedAt) < 0)) {
      sessionContiguousSince.delete(sessionId);
    }

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7854016706

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/src/session/remoteSessionStore.ts Outdated
Comment thread apps/mobile/src/session/remoteSessionStore.ts Outdated
`sessionContiguousSince` 只记「已验证连续」的下界,而下界断言的是「从它到**窗口
最新端**连续」——窗口最新端会被断流期间漏收的行悄悄作废:旧窗 1–80,断线漏收
81–200,重连后先到一条 push 201,再收到最新页 122–201,此时「有交集」仅靠那条
push 成立,旧下界却还在给「1–80 + 122–201」这个孤岛背书,而这些行若在 30 分钟内
产生,自动探测也发现不了。

改成显式的覆盖区间 `[since, until]` + `liveTailTrusted` 位:

- 采纳旧段要求「旧行落在区间内」**且**「本页最旧行不晚于 `until`」(两段首尾相接);
- `liveTailTrusted` 表示自 `until` 建立以来推送链路没断过,只有这时 push 才能后推
  上界。断流的三个入口都通知 store:socket 掉线(全部会话)、退后台释放
  `session:<id>` 订阅、离开会话取消订阅(对应会话)。区间本身保留——断流不会让
  断流前验证过的那段变假;
- 事实自检:最新页若在区间内带来窗口没有的行,说明断言已被服务端事实推翻,当次
  按「未知」处置;
- 记账移到相等早退**之前**(`setMessages` 与 `setLatestMessageWindow` 口径一致):
  权威页与冷开缓存逐行相同是常态,被早退跳过则这次响应白来,之后会话涨过一页、
  再遇一次满页重连刷新,本已确认过的行会被当成来源不明全丢,用户当前历史与滚动
  位置一起消失。

顺带把「这一行在不在那一批里」收敛成一份身份索引,交集探测与覆盖区间的事实自检
共用它,不再各写一遍迁移档判据。

新增 7 个 store 用例 + 3 个断流接线断言,全部经变异验证能区分新旧实现;`coverLiveRow`
里跳过本地系统卡那一档写不出能区分的用例(当前调用图下要叠两个改坏点才暴露),
按防御性保留、不硬造用例。

Signed-off-by: Dash <f9dftwf5tj@privaterelay.appleid.com>
Signed-off-by: Dash <dashhuang@gmail.com>
Copilot AI review requested due to automatic review settings July 31, 2026 14:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.

Suppressed comments (1)

apps/mobile/src/session/remoteSessionStore.ts:1571

  • remoteSessionStore 里既有一个模块级 helper noteLiveStreamInterrupted(...),又新增了同名的 store 方法 noteLiveStreamInterrupted(...),方法体里再调用 noteLiveStreamInterrupted(sessionId) 依赖“method 名在函数体内不成 binding”这一语义才能命中外层 helper。这里对读者非常不友好,也很容易在后续重构(例如把 helper 移到别处、或把方法改成函数表达式)时不小心变成递归/死循环。建议把外层 helper 或 store 方法改名(例如 helper 改成 markLiveStreamInterrupted / setLiveTailUntrusted),并同步更新此处调用点,避免同名遮蔽带来的维护风险。
  noteLiveStreamInterrupted(sessionId?: string): void {
    noteLiveStreamInterrupted(sessionId);
  },

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e1e219c30

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/src/session/remoteSessionStore.ts Outdated
@MagicLizi

Copy link
Copy Markdown
Contributor

@dashhuang 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/mobile/src/session/remoteSessionStore.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

屏幕侧的 `openAndSubscribe` 与 `startFocusedTopicSubscription` 都是
`void subscribe(...)`(订阅只管之后的推送,不该挡数据读),所以「权威页比订阅
ACK 先到」是常态。上一版把每个权威页都标成 `liveTailTrusted: true`,于是这个空窗
里被控端写下的行——既不在这一页、也不会被推过来——会被之后一条 push 抬过去:
`until` 越过漏收的行,等尾部涨过一页后最新页已不含它们,`joinableWindowCoverage`
的事实自检也发现不了,孤岛就此固化,而它们若在 30 分钟内产生,时间空洞补齐同样
不触发。

改成:

- `liveTailTrusted` 由**权威页落库那一刻订阅是否已 ACK** 决定。device-link 在订阅
  ACK 记账处通知 store,且只按 `markHeldRemoteTopicsSubscribed` 真正记进 ACK 表的
  topic 生效(中途被释放的不算)。重连补齐路径(`rehydrate`)是 `await subscribe`
  之后才拉页,因此那条路径拿到的仍是可信尾部。
- ACK 本身不点亮既有区间:ACK 之前的空窗里可能已经漏了行,只有此后落库的权威页
  才能重新确定尾部。
- 断流两种形态都把 ACK 记录一并作废(socket 掉线 → 全部会话;退后台释放
  `session:<id>` 订阅、离开会话取消订阅 → 对应会话)。生效与失效不成对时,断线后
  才落库的在途页会靠旧 ACK 重新把尾部标成可信,绕一圈回到同一个孤岛。

新增 4 个用例(ACK 之前落库的页不可信、两种断流形态各一例 ACK 失效、ACK 接线断言),
并给原有 4 个依赖 push 续推的用例补上「订阅已 ACK」前置;全部经变异验证能区分新旧
实现。

Signed-off-by: Dash <f9dftwf5tj@privaterelay.appleid.com>
Signed-off-by: Dash <dashhuang@gmail.com>
Copilot AI review requested due to automatic review settings July 31, 2026 15:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f94d36e36f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/mobile/src/session/remoteSessionStore.ts
@MagicLizi

Copy link
Copy Markdown
Contributor

@dashhuang 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/mobile/src/session/remoteSessionStore.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-review: standard-tier 审查通过。18 文件 / 2274 行。

  • 修复 Mobile 历史窗口空洞导致不相关消息段被折成一个工作组(显示错误时长)
  • 共享 30min 阈值拆分 + coverage 追踪 + 自动回填(预算限制 6 probe / 3 fill / 400 rows)
  • Desktop 同步修复:renderItemEndMs 取所有子项最大值(并行任务可能乱序完成)
  • 无冷更触发、无安全/凭证变更,测试 700+ 行

零 P0/P1。

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review passed — all invariants verified, gap detection logic is correct, tests comprehensive. No P0/P1 findings.

@MagicLizi
MagicLizi merged commit a2aca9e into main Jul 31, 2026
14 of 15 checks passed
@MagicLizi
MagicLizi deleted the dash/mobile-history-gap branch July 31, 2026 16:56
@MagicLizi

Copy link
Copy Markdown
Contributor

历史窗口空洞的修复做得很扎实——用单调 seq 代替易摆动的 session ID 做取消判据是个巧妙的设计,六条不变量的接线守卫让后续重构也很难无意打破这些约束。

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: 手机端 30 分钟内产生的历史窗口空洞不会被检测到

3 participants