触发与范围
桌面聊天主流的同 thread 历史定位 helper 同时处理两个不同 target:先请求 A,再请求 B;B 先完成并安装窗口后,迟到的 A 仍会安装其旧窗口。两次原 ensureMessageVisible 均返回 ok,最终 around_message_id 和 messages 窗口却回到较早的 A。
按 P2 历史定位状态归属追踪。2026-09-30 仅由原 hook 的内存分页 fixture 确认;没有实际桌面点击、SSE、滚动或私有聊天,没有永久消息/引用丢失、工具或权限事故。范围仅为 desktop-owned 聊天历史定位的请求 generation/窗口提交,共享 Web 文件作定位,不要求本轮修改 Web 或后端。
原 hook 证据与已有保护
fixture 原样转译并执行 useConversation,使用 React test renderer。REST/page、EventSource 与 timer 全为内存边界;当前同 thread 有可用 tip,两目标都不在当前窗口。依次调用原 ensureMessageVisible,再控制 B page 先返回、A page 后返回。
- B 返回后,原 helper 返回 ok,view.messages 与 around_message_id 指向 B。
- A 迟到后也返回 ok,原 setter 将窗口替换为 A,较新的 B 不再属于当前窗口。
- 若 A pending 期间调用原 jumpToLatestMessages,epoch 递增;tip 更新后 A 被返回 cancelled,无法再替换窗口。
- 若切换 thread,A 返回 missing,当前另一聊天窗口保持不变。
完整分页内容、helper 返回值、view、假请求/timer 和源码哈希只存本地 ignored artifact。此证据是 hook 的数据窗口,不是原生 viewport 已滚到 A 的实测。
根因与影响边界
ensureMessageVisible 捕获 aroundEpochRef,但新的 around 请求自身不推进该 epoch。两同 thread 请求因此具有相同有效标识,晚完成的旧请求仍通过 isLive 和 setView 检查。jump-to-latest 的 epoch 保护工作,不能据此推断两次 around 之间也按最新用户目标取消旧结果。
Timeline 的 anchorRequestRef 与 Shell 深链 cancelled guard 仍可阻止旧任务继续执行高亮/滚动;它们没有作为取消身份传入 hook,无法阻止 helper 内的窗口提交。本轮静态核对这些 caller,未执行真实 Timeline/滚动,不能声称其最终视觉必然错误。
确定的缺口是:较早的页面请求可改变较新定位已使用的数据窗口。分页原文仍在服务中,后续可重新读取;这里不是记录被删除,也未测试引用发送或真实命令。
桌面优化与验收
- 为每次明确 around 目标分配新的请求身份,或传入调用方取消范围;提交窗口、pending-around、返回状态都绑定到最新有效目标。
- 失效请求返回 cancelled,不替换较新窗口。保留 jump-to-latest、thread switch 与 supersession 的已有保护。
- 重跑原 hook 两目标乱序:B 完成后 A 不能替换 B,旧任务返回 cancelled;顺序请求、返回最新、线程切换与失效分支目标继续正确。
- 在可弃置桌面环境补连续历史/深链定位及真实滚动验收,仅用合成消息;未完成前不宣称实际视口事故或完整恢复保证。
去重与源码
本条为两个同 thread around 任务之间的身份,已有 jump-to-latest epoch 对照通过,不把它另报为失效。不同 thread 的旧响应和 supersession 保护也保持成立。#80 是侧栏旧列表,#81 是 inbox 服务重建游标;均非此定位窗口根因。关联 Epic #31。
固定公开代码仅用于导航,隔离证据保存当前源码哈希:useConversation / ensureMessageVisible、Timeline caller、Shell 深链 caller。
触发与范围
桌面聊天主流的同 thread 历史定位 helper 同时处理两个不同 target:先请求 A,再请求 B;B 先完成并安装窗口后,迟到的 A 仍会安装其旧窗口。两次原 ensureMessageVisible 均返回 ok,最终 around_message_id 和 messages 窗口却回到较早的 A。
按 P2 历史定位状态归属追踪。2026-09-30 仅由原 hook 的内存分页 fixture 确认;没有实际桌面点击、SSE、滚动或私有聊天,没有永久消息/引用丢失、工具或权限事故。范围仅为 desktop-owned 聊天历史定位的请求 generation/窗口提交,共享 Web 文件作定位,不要求本轮修改 Web 或后端。
原 hook 证据与已有保护
fixture 原样转译并执行
useConversation,使用 React test renderer。REST/page、EventSource 与 timer 全为内存边界;当前同 thread 有可用 tip,两目标都不在当前窗口。依次调用原 ensureMessageVisible,再控制 B page 先返回、A page 后返回。完整分页内容、helper 返回值、view、假请求/timer 和源码哈希只存本地 ignored artifact。此证据是 hook 的数据窗口,不是原生 viewport 已滚到 A 的实测。
根因与影响边界
ensureMessageVisible 捕获 aroundEpochRef,但新的 around 请求自身不推进该 epoch。两同 thread 请求因此具有相同有效标识,晚完成的旧请求仍通过 isLive 和 setView 检查。jump-to-latest 的 epoch 保护工作,不能据此推断两次 around 之间也按最新用户目标取消旧结果。
Timeline 的 anchorRequestRef 与 Shell 深链 cancelled guard 仍可阻止旧任务继续执行高亮/滚动;它们没有作为取消身份传入 hook,无法阻止 helper 内的窗口提交。本轮静态核对这些 caller,未执行真实 Timeline/滚动,不能声称其最终视觉必然错误。
确定的缺口是:较早的页面请求可改变较新定位已使用的数据窗口。分页原文仍在服务中,后续可重新读取;这里不是记录被删除,也未测试引用发送或真实命令。
桌面优化与验收
去重与源码
本条为两个同 thread around 任务之间的身份,已有 jump-to-latest epoch 对照通过,不把它另报为失效。不同 thread 的旧响应和 supersession 保护也保持成立。#80 是侧栏旧列表,#81 是 inbox 服务重建游标;均非此定位窗口根因。关联 Epic #31。
固定公开代码仅用于导航,隔离证据保存当前源码哈希:useConversation / ensureMessageVisible、Timeline caller、Shell 深链 caller。