From c0e78371cb729708ad265f212d995a1393d87b43 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Mon, 5 Oct 2026 08:08:19 +0800 Subject: [PATCH] =?UTF-8?q?docs(05):=20=E9=94=9A=E7=82=B9=E8=A1=A5?= =?UTF-8?q?=E8=AE=B0=E5=9B=9B=E2=80=94=E2=80=94W28=20=E5=85=B1=E4=BA=AB=20?= =?UTF-8?q?socket=EF=BC=88=E5=90=AB=E8=A3=85=E6=9C=BA=E9=AA=8C=E6=94=B6?= =?UTF-8?q?=E8=AF=81=E6=8D=AE=E4=B8=8E=E4=B8=A4=E4=B8=AA=E5=9B=BA=E6=9C=89?= =?UTF-8?q?=E4=BB=A3=E4=BB=B7=EF=BC=89+=20=E5=81=87=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=8F=B0=20+=20=E4=B8=80=E6=9D=A1=E7=A1=AC?= =?UTF-8?q?=E6=95=99=E8=AE=AD=EF=BC=88=E9=97=AE=E4=B8=80=E5=8F=A5=E6=AF=94?= =?UTF-8?q?=E7=8C=9C=E4=B8=80=E5=A4=A9=E5=80=BC=EF=BC=89+=20=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=A1=B5=20IME=20=E7=BB=93=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devdocs/05-开发进度与交接.md | 50 +++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/devdocs/05-开发进度与交接.md b/devdocs/05-开发进度与交接.md index 6cb2aa8..8e80df1 100644 --- a/devdocs/05-开发进度与交接.md +++ b/devdocs/05-开发进度与交接.md @@ -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`)。