docs(05): 锚点补记三十 —— W52 五项落地(已合并推送/101 suite 1270 tests 真跑绿)+ 真机验收结果

- 五项各自的机制与判据要点(S4 一格一份 ViewModelStore 与四条豁免、S5/S9 纯账本与既有 interrupt 出口、
  登出条目、降级气泡屏蔽服务端动作、保活开关与 Start/Stop/Leave 判据)。
- 真机验收:设置页两行 / 抽屉「正在思考…」/「停止」发既有 interrupt 帧 / 降级气泡无删改入口 **通过**;
  **首屏失败「重试」未通过**(断网冷启动下按钮没出现,根因未定位,写了下一步的诊断办法)。
- 保活开关→服务、S4 回收 未验(时间不够),如实标注。
- 三条教训:① 判据第一步要防「别的 App 抢前台」(gms 账号页把我们的 dump 全吃了);
  ② 更正 INTERNET 那条:release 的 INTERNET 来自 okhttp-android 传递依赖,建议显式声明;
  ③ 构建锁协议有洞(有人整文件删除导致别人的行没了)。
This commit is contained in:
AskaEth committed 2026-10-06 16:10:37 +08:00
1 parent 579eccdfd1
commit 126a706422
1 file changed
+29
+29
View File
@@ -1609,3 +1609,32 @@ S2 我第一次测出「没生效」,差点去改代码——真因是**设备
- **不准用 `WaitFor` 等子代理**(哪怕动机是「本回合内把它收回来」):它会把整个回合锁住,**主人在这期间无法插话**——主人当场打断了我的 `WaitFor` 并明确说「纪律是不准用 WaitFor」。
- **正确做法**:**派完就结束回合**(子代理完成会自动唤醒我);等待期间**只做与它们不冲突的并行工作**,或者干脆停手。多支并行时,谁先回来就先收谁。
- 备注:这条在后端仓库 `Cyrene_v2/AGENTS.md §10` 里原本就有(「等待子代理:不许用长 WaitFor 阻塞回合」),**本项目文档此前没写、我因此重犯了一次**,现补上。
---
## ★★ 锚点补记三十 · W52 五项落地(主人拍板的那五条)——**已合并推送;真机验收部分通过、一项未通过**
### 1. 代码(五支全部合并 + 真跑 + 已推)
合并到 **`579eccd`**(五次 `--no-ff`,其中 W52A 与 W52B 在 `deleteSession` 的同一锚点冲突一次,两侧都留)。主树全量 `--rerun` **真跑**:**101 suite / 1270 tests / 0 失败 / 0 error**(基线 1205 ⇒ +65,全为新增)。五支的 worktree/分支已清、构建锁已放。
- **W52A(S4)**:`core/session/ChatSessionSlots.kt` + `ChatSessionEviction.kt` —— **一格会话一份 `ViewModelStore`**(这样「交出一格」就是那份 store 自己的 `clear()` ⇒ 那一格的 `onCleared` + `viewModelScope` 取消,订阅/补拉/在飞请求一起停);「最近」口径 = **最后一次成为当前会话**;判据四条:**主会话永不回收** / **屏幕上那一格永不回收**(它一死那一段就不再更新)/ **有草稿或未上路发送的格子永不回收**(回收会抹掉用户唯一一份内容)/ 其余按最旧先走、`keep=3`,**被豁免的那些占名额**。删会话定点回收、登出全清。槽位挂在 Application 级容器上 ⇒ 存活期改由「≤3 格 + 回收/登出」界定(行为变化,已记在 KDoc)。
- **W52B(S5+S9)**:`ui/sessions/SessionBusy.kt` 纯账本(**只认活帧、绝不外推**;`session_idle` 无条件收、`session_interrupted` 仅在 `activeTurns=0` 时收、`turn_end` 只收认得的那一路、`stream_end` 刻意不参与;**WS 掉线当场作废账本**)+ 抽屉行内第三行「正在思考…」+ **「停止」**(用**既有** `WsOutbound.Interrupt`,恒发 `scope=current`,从不发 `all`)。⚠️ 「停止」**顶掉**了该行的置顶按钮(宽度硬上限),灯灭后置顶回来 —— 主人如需并列请说。
- **W52C(登出条目)**:`BasicComponent(title="退出登录", error 色, startAction=Icon(MiuixIcons.Remove))` —— 图标是穷举 Miuix 图标集后选的(`Basic` 只有 7 个、`extended` 156 个里没有 exit/logout 字形)。
- **W52DE**:`ui/chat/MessageServerActions.kt` 纯判据(`proactive-local:` 前缀 ⇒ 不做服务端动作,选字/复制不受影响)+ 空态拆两种真相(「还没有消息」/「消息没能加载出来」)+ `retryFirstPage()`(**一行**转调 `loadFirstPage()`)。
- **W52K(保活)**:`core/keepalive/{KeepAliveService,KeepAlivePolicy}.kt` + 设置页 `KeepAliveRow.kt` —— **默认关**、低优先级静默常驻通知、`dataSync` 类型、`onTimeout` 自停(不自己停会 ANR);判据 `keepAliveAction(enabled, sessionKnown, loggedIn, notificationsAllowed) → Start/Stop/Leave`(`Leave` 是「登录态未定,此刻不该有主张」)。
### 2. 真机验收(`emulator-5554` + 假后端)
| 项 | 结果 | 证据 |
|---|---|---|
| 设置页两行(W52C/W52K) | ✅ | dump:`后台保持连接`(带**如实**代价文案)+ `退出登录`(带文字);截图确认开关渲染正常 |
| 抽屉「谁在跑」(S9) | ✅ | `POST /__control/busy?session=web` ⇒ 抽屉里 与昔涟 行出现 `正在思考…` |
| 「停止」入口(S5) | ✅ | 点它,客户端发出 `{"type":"interrupt","data":{"sessionId":"web","scope":"current"}}`(**既有帧**;`out/upstream.jsonl` 为证) |
| 降级气泡不给删改入口(D) | ✅(弱对照) | 注入 `--without-id` 气泡后屏幕上该气泡**没有**「消息操作」入口;但同屏没有服务端行可对照(假后端主会话为空)⇒ 这条主要由那 6 条单测承担 |
| **首屏失败「重试」(E)** | ❌ **未通过** | 断网冷启动后:同屏有错误横幅「无法连接到服务器」+「重连中…」,但中央是「**还没有消息**」且**没有重试按钮**(dump + 截图双证)。⇒ 判据里的 `hasError` 在实际那一刻为假,而那条横幅确实来自 `state.error?.let` ⇒ **我的状态模型与现场不一致,根因未定位**。下一步:在 `ChatScreen` 那一支加一条一次性诊断日志(打出 `loading / hasError / items.size`)再定;另一种可能是判据该直接问「首屏有没有失败过」而不是 `error` 这个代理(W52DE 自己也登记过这个洞:横幅上的 X 会清掉 `error`,按钮跟着消失)。 |
| 保活开关 → 前台服务 | **未验** | 只验到「开关渲染正确」;服务是否真起、通知观感、13+ 权限流都没验 |
| S4 回收 | **未验** | 只有单测 + 走查(VM 生命周期在 JVM 上起不来) |
### 3. 这一轮踩到 / 学到的
1. **`mCurrentFocus` 之外还要防「别的 App 抢前台」**:Google Play 服务的账号核验页(`gms PreAddAccountActivity`)在我们操作期间抢了前台,我的 dump/截图**全打在它身上**,白跑好几轮。⇒ 判据第一步永远是 `dumpsys window | grep mCurrentFocus`;**看到不是本 App 就先清掉它**(`keyevent 4` × N + `keyevent 3`,再 `am start` 拉回来)。
2. **`INTERNET` 权限的更正**(我先前说「release 可能没网络权限、得单独验」):W52K 跑了 `:app:processReleaseMainManifest` —— release 合并清单里**有** `INTERNET`,来自传递依赖 **`com.squareup.okhttp3:okhttp-android:5.5.0`**(不是 AGP 只给 debug 注入)。**结论:release 现在能联网,但那是搭在传递依赖上的** —— 换掉/排除 okhttp-android 会静默失去网络权限。建议无条件显式写进源码清单(一行,**还没做**)。
3. **构建锁协议有洞**:这批有切片是**整文件删除**而不是只删自己那行 ⇒ 别人的占用行一起没了(W52B/W52K 都提到过)。下次按「一行一个占用者、只删自己那行、收工前先读一遍」写死。