docs(05): 锚点补记四——W28 共享 socket(含装机验收证据与两个固有代价)+ 假后端测试台 + 一条硬教训(问一句比猜一天值)+ 登录页 IME 结案

This commit is contained in:
AskaEth committed 2026-10-05 08:08:19 +08:00
1 parent 7b97440973
commit c0e78371cb
1 file changed
+49 -1
+49 -1
View File
@@ -734,7 +734,7 @@ PDF · 563 B · 24 字 · 已读入
### 待办(按优先级)
1. **W19 剩下的那一处**:**删除后抽屉里的「最后消息预览」不刷新**(本端只改了消息列表,没触发 `GET /sessions`)。① 编辑口径已由主人拍板并落地(W24)。
2. **遗留 ①:连接所有权** —— 每个会话一个 `ChatScreen` 的 VM 各建一条 `ChatSocket`,而 VM 活在 Activity 级 `ViewModelStore` 里 ⇒ 访问过的每个会话都**永久留一条连接**(含各自的 `OkHttpClient` 与连接池),**删会话也不回收**。自然解法:**共享一条 socket + 按会话路由**。
2. ~~**遗留 ①:连接所有权**~~ ✅ **已闭环(W28,`7b97440`)**:现在**进程内共享一条 `ChatSocket`**(`AppContainer` 唯一持有、`SessionsViewModel` 是唯一 owner),连接数从 **N+2 降到 1**。装机验收见「补记四」。
3. **`ChatSocket.sendInterrupt` 零调用方**(「切会话时打断」这个出口从未接线,是死 API)。
4. `observability_audit` 帧(**后端未做**,审计仍是 REST);`POST /admin/users` 仍是 `501` ⇒ 铸不出 `viewer` 账号,viewer 降级只能单测背书。
5. 子会话流程(主人已确认搁置)。
@@ -920,3 +920,51 @@ PDF · 563 B · 24 字 · 已读入
> ③ 管理页三帧(metrics/trace/log)仍到;④ 抽屉未读角标仍对;⑤ 断线重连后 `after` 补洞仍跑;
> ⑥ 登出时要断开且重登要接上(现有 `authManager.state` 那条 `connect()/close()` 逻辑要跟着搬);
> ⑦ 删会话后那条连接不再留着。
> **★ 锚点补记四(2026-10-05 上午 · 主人给了明确指示后的一批)**
>
> ### 状态
> - **`android` = `origin/android` = `7b97440`**,工作区干净,**891 测试 / 0 失败 / 0 错误**。
> - APK:`app/build/outputs/apk/debug/app-debug.apk`(就是 `7b97440` 这棵树)。
>
> ### 本批落地
>
> | # | 内容 | 提交 |
> |---|---|---|
> | W28 | **进程内共享一条 `ChatSocket`**:连接数 **N+2 → 1**,`SessionsViewModel` 为唯一 owner | `7b97440`(`276b011`) |
> | FB | **可编程假后端测试台**(`devtools/fake-backend/`,纯 Python 标准库、零依赖) | `27d5680` |
> | — | `02` 三处登记:回应回执十三 / 置顶「微信文件看不到」P0 / IM 语音消息的新需求 | `691b073` `ae49853` `a008580` |
>
> ### W28 的关键取舍(**别退回**)
> - **共享的 socket 谁都不许 close**:`onCleared` 里的 `close()` 全删了(否则切个会话就把全 App 的连接掐断)。
> 现在全仓 `connect()` / `close()` **各只剩一处**,都在 `SessionsViewModel`(它由 `ChatHomeScreen` 在进登录态时创建、活在 Activity 级 store 里,存活期正好覆盖「登录→登出→重登」)。
> - **`_events` 的 `replay` 必须恒为 0**:共享后新建的 `ChatViewModel` 会立刻订阅同一个 SharedFlow,replay > 0 就会把历史帧重放给它。
> - **两个已知的固有代价**(都在 W28 的 commit 里写明):① 晚于连接建立才创建的 VM **不再收到 `history` 帧** —— 实测无影响,新 VM 的正文由 REST `loadLatest` 给(设备上验证过:次级会话首次访问 `GET …/messages?limit=50 → 200`);② **Activity 结束不再断开连接**(进程级共享的必然结果),只在登出 / 令牌失效时断。
>
> ### W28 装机验收(**有设备证据**)
> - **连接数不随访问会话增长**:`awk` 数 `/proc/net/tcp{,6}` 里 App uid 的已建立连接 —— 只访问主会话时 **4**,**切到第二个会话后仍然 4**(改前这里会 +1)。这是 W28 全部意义所在。
> - 切会话 → 切回主会话,渲染**完全正常**(消息、来源角标、旁白都在)。
> - 管理页三个端点全到(`admin/observability/{logs,metrics,trace}`)并渲染出真实数据(总请求 205 / 平均延迟 20ms)。
> - **未验**:重连补洞(`after`)在共享 socket 下的表现、登出/重登的 owner 逻辑、viewer 分支 —— 都还没有设备证据。
>
> ### 假后端(FB)—— 它是「验不了」这个借口的解药
> `devtools/fake-backend/`(`python fake_backend.py --port 18080` + `ws_probe.py`)。它解锁三件**真后端做不了**的验证:
> 1. **`after` 补洞**:`POST /__control/messages?count=120` 免费灌 120 条,握手只回放 50 条 ⇒ 精确造出那个洞。
> (**这解释了为什么之前说「验不了」是错的**:我当时只想着用真后端灌 51 条 = 51 次模型回合。**该用假后端。**)
> 2. **打断帧**:`out/upstream.jsonl` 记录**所有上行帧** —— 这是「W25 那帧到底发没发」的**唯一直接证据**。已实跑抓到 `{"type":"interrupt","data":{"sessionId":"web","scope":"current"}}`。
> 3. **`stream_chunk` 回退路径**:能指定走旧形状,正好用来验 W29 要修的那条分支。
> - App 指过来:`adb reverse tcp:18080 tcp:18080` + 登录页服务器地址填 `http://127.0.0.1:18080`(账号随便填)。
> - ⚠️ **它的事实来源是客户端代码与协作文档,不是真后端** —— 别拿它当后端行为的依据。
>
> ### 本批的一条硬教训:**问一句,比自己猜一整天都值**
> `stream_chunk` 是我昨夜「按增量」接的(把它当 `MessageDelta` 追加)。今早问后端,他们回复:**`delta` 是整段 `res.Text`**(而且生产里已无触发源)。
> ⇒ **真到达会追加成重影**。**我昨夜那版是错的,而且如果没有问,它会以「已修好」的样子留在仓库里。**
> 同类:`viewer` 凭据我一直挂着当红验收项,问了才知**全仓没有任何铸造路径**,应当直接摘掉。
>
> ### 待办(更新)
> 1. **W29(在跑)**:`stream_chunk` 改成「整段替换 + 有结构化帧则忽略」/ 空壳消息如实说一句 /
> 分享超长内容走文件 / `F6①` 图片 URL 兜 host。
> 2. **等后端**:把 IM 入站的文件挂到消息的 `documents` 上(P0,`02` 已置顶);IM 渠道语音消息的入站 ASR。
> 3. **用假后端补三条设备验收**:`after` 补洞 / 打断帧 / `stream_chunk`(等 W29 合完)。
> 4. 子会话流程(主人说搁置);图片/工具卡实机验证(后端未配工具)。
> 5. **已结案(别再返工)**:登录页输入框被 IME 遮挡 —— **不成立**,2026-10-05 装机实测(密码框聚焦、键盘弹出时完整可见,截图 `app/build/e2e-shots/y3-ime.png`)。