# 私聊中群互动行：原群消息已读状态

## 需求与展示

- 范围是私聊内容区穿插的群互动单行，不是第二栏联系人卡片。
- 在摘要末尾、跳转箭头前显示状态，不新增卡片、不增加行高。
- 未读为小蓝点；已读复用私聊消息及 @ 标识的圆圈对勾。
- 鼠标悬浮或键盘聚焦立即显示说明，区分“对方已阅读群内原消息”和“你已阅读群内原消息”。仅有明确逐条阅读时间时显示时间。
- 状态未知保持空白，不能当作未读。原行点击打开详情的行为保持不变。

## 数据语义与接口

新增插件宿主只读操作 `source.interwoven-read-receipts`，参数为 `sourceRef` 和最多 20 个宿主签名的 `momentRefs`。

| 场景 | 状态依据 |
| --- | --- |
| 我在群里 @ 当前私聊联系人 | 原群消息 `/api/v1/chats/read-receipts/detail` 中该联系人唯一的成员回执，不使用全群已读数量 |
| 对方在群里 @ 我 | 原群 `/api/v1/chats/detail` 中自己的有效 `current_cursor` / `unread_snapshot`，与原消息 `seq` 比较 |
| 原消息不可定位、已撤销、失去权限、对应成员缺失或响应异常 | 未知，不显示蓝点或对勾 |

不会根据私聊行是否可见来确认已读，不调用群已读上报接口，不修改未读数量。
浏览器只传签名引用；账号、私聊对象、群、消息、发送者、阅读者均在宿主校验。退出/切换账号丢弃旧请求结果。

## 旧服务副本兼容

实际联调发现：World 交织列表的近期记录使用 `chat_legacy_receive_...` 副本 UID，不能直接交给 Chat 的原消息定位接口。直接调用 `/api/v1/chat/timeline/around` 会返回业务码 2001，导致近期互动全部未知。

- 复用 `/api/v1/chats/interwoven/occurrences/query` 获得当前私聊双方互动的 canonical 群、消息、关系、owner 和 seq。
- 按 Subject 源码 `buildLegacyReceiveRecordEdgeUID` 的既有规则核对副本身份：`chat_legacy_receive_` + `sha256(groupUid + ':' + relationUid)` 的前 32 个十六进制字符。
- 必须同时匹配私聊、群、消息 owner、发送者及方向；重复身份对应不同原消息则拒绝。不是按正文、时间或名称猜测。
- 兼容查询每次最多 3 页、每页 50 条，携带结果版本；版本变化/覆盖不完整则不接收映射。精确映射在签名引用有效期内复用。
- 普通旧 UID 则通过 exact group + owner + record 的 `timeline/around` 校验并取得 seq。
- 超出兼容窗口或仍无法准确对应的记录保持未知；不宣称所有历史互动均有回执。后续后端若直接返回 canonical locator，可移除这一兼容映射。

## 请求控制

- 只查询可视行（含 80px 预取范围），100ms 合并，每批最多 20 行，宿主上游最多 3 个并发。
- 存在未读时每 15 秒检查；全未知时每 60 秒重试；网络错误退避至最多 60 秒。
- 全部已读后停止轮询；实时失效事件、重新聚焦或重新进入可视区域时刷新。
- 隐藏页面、切换会话/账号会取消请求并清理状态。没有已读状态的无浏览器测试环境安全跳过监听。

## 验证结果

- 10 个相关测试文件共 **713 项通过**，覆盖宿主权限边界、原群 cursor、指定成员回执、旧副本映射、版本变化、分页上限、取消/轮询、账号隔离、行末位置、提示及原点击行为。
- `pnpm typecheck`、`pnpm build`、`git diff --check` 通过。
- 3100 独立 Web 预览真实会话：抽查最近 10 条互动，9 条获得明确已读状态，1 条未知并保持空白。出站回执带对方阅读时间，入站 cursor 不伪造逐条阅读时间。
- 浏览器实际看到摘要行末圆圈对勾，箭头位置保留；聚焦后立即出现正确阅读者提示，Escape 可关闭。当前窄/常规宽度未产生额外行高。
- 没有为测试发送新消息或上报群已读。仅重新部署独立的 3100 预览，未更改安装的桌面客户端。
- 控制台仍有运行时重启后的旧控制流断开记录及 TRTC 预加载 AbortError；本次未修改通话模块，不将这些记录描述为已修复。
- 尚未做双账号实时收发的端到端人工验收，也未推送或合并分支。
