docs(05): 锚点补记十三——W41 三级阶梯(未连接先探 health,通就回落 REST 发出去、不通才进队列),含 Offline 为何不探测、gapCursor 那个被补掉的洞、以及图/附件走不到这条路的边界
This commit is contained in:
1 parent
eb22521269
commit
97992bdb6b
1 file changed
+42
@@ -945,3 +945,45 @@ curl -s -X PATCH "$BASE/api/v1/sessions/$SID" -H "Authorization: Bearer $TOK" \
|
||||
> | 发送按钮 | **(950, 2170)** |
|
||||
> | 抽屉(左上菜单)| 按 1080 宽重算,约为 **(86, 188)**(未实测,下次用前先截图确认) |
|
||||
> 注意:**软键盘仍是禁用状态**(`ime disable` 写在 userdata 里,跨重启有效),所以布局不会因 IME 位移;这是刻意的(见补记八第 1 条坑)。
|
||||
|
||||
> ## ★★ 锚点补记十三(2026-10-06 凌晨 · W41 三级阶梯:未连接时**先探 health,通就发出去**)
|
||||
>
|
||||
> ### 状态
|
||||
> `android` = `origin/android` = **`eb22521`**(W41 合并提交;分支提交 `9f77fb4`);主树 **1135 测试 / 0 失败 / 0 错误**。W41 worktree 已清。
|
||||
> ⚠️ **本批只有编译 + 纯判据单测证据,没有真机回归**(子代理按任务书禁碰 adb)。
|
||||
>
|
||||
> ### 主人给的思路(比原来那版好)
|
||||
> 我原先只做了「未连接 ⇒ 进队列」。主人指出:**「未连接状态你就不能拿 httpAPI 尝试吗?」→「可以走 health 端点验证连通性」**。
|
||||
> 关键理由(我原来那份评估的漏洞):我曾担心「REST 分不清有没有送达,所以不敢重发」——
|
||||
> 而这条**已被 `clientMsgId` 幂等解掉**(服务端按 `(session_id, client_msg_id)` 唯一索引仲裁,重复到达只回 `chat_duplicate`)。**既然重发安全,就没有理由不试。**
|
||||
>
|
||||
> ### 三级阶梯(`ui/chat/SendFallback.kt` 的 `sendRouteFor`)
|
||||
> 1. **WS 已发出** ⇒ `Ws`(连接状态与探测都不再参与;**成功路径零改动**)
|
||||
> 2. **`WsState.Offline`** ⇒ `Queue`,**连探测都不做**。理由:该档语义是「握手 401 且续期也失败」,会话已知是死的;而 `/api/v1/health` **不鉴权**(`routes.go:51`、`rest.go:219-221` 只回 `{"status":"ok"}`)⇒ 探它必然 200 ⇒ 会发一次注定 401 的 REST,把「已排队不会丢」换成一次回滚 + 一句误导人的鉴权错误。
|
||||
> 3. **未就绪(Idle/Connecting/Reconnecting)+ 探测通** ⇒ **`Rest`**:回落 `POST /api/v1/chat` 把消息**当场发出去**,不进队列。
|
||||
> 4. **其余一切**(不通 / 超时 / 抛异常 / **还没探过**)⇒ `Queue`(**fail-closed**,`null` 与「不通」同路)。
|
||||
>
|
||||
> **超时**:探测 **2500 ms**(`callTimeout` 覆盖 DNS+连接+写+读);回落 REST 另加 **10000 ms** 上限。
|
||||
> **无网时的上界**:WS 就绪 = 0 次探测;`Offline` = 0(不探);完全无网 ⇒ **≤ ~2.5 秒**进队列且**一次 REST 都不会发**;探测通但 REST 卡住 ⇒ **≤ ~12.5 秒**后进队列(气泡全程「发送中…」,不是无响应)。
|
||||
>
|
||||
> ### ✅ 它顺手补掉了一个真洞(值得记)
|
||||
> 「回落 REST 之后回复怎么回来」要求核实 —— 结论是**能回来**(`catchUp` → `loadAfter` → `absorb`,`ChatViewModel.kt:923/924-932/2091/2106` + WS 握手 `history` 兜底),
|
||||
> 但发现 **`gapCursor` 只在「离开 Connected」那一刻记**:若本 VM **从没连上过**(进会话时就是断的)它为 `null`,重连那一步**什么都不会拉**,而回落那一轮的回复恰好只能靠它。
|
||||
> ⇒ 新增 `armGapCursorForFallback`(`:1614-1615`),只在回落被受理后、且游标还是 `null` 时补记一次(**绝不改写掉线时记下的起点**)。
|
||||
> ⚠️ **仍缺的一段(它如实写了,没假装补上)**:本端一条消息都没有时(`newestKnownAt == null`)游标补不上 —— `after=0` 会从**最旧**方向拉,20 轮上限停在最旧 1000 条上,正好错过要看的那条。主会话由 `history` 的 50 条兜住;**次级会话 + 首屏为空 + WS 从未连上过**这个组合下要用户自己刷新。
|
||||
>
|
||||
> ### 边界(它核实后写的,别记错)
|
||||
> - **有图 / 有附件的消息走不到这条阶梯**:`ChatSendRouting.channelFor` 对「有图或有附件」恒回 `Rest`,而只有 WS 那一支才进阶梯 ⇒ 进阶梯的**必然是纯文本**。
|
||||
> ⇒ 不存在「本地 URI 授权失效」「图片重编码」被这条新路踩到的可能;有图/有附件的行为与 W40 之前**逐字相同**。
|
||||
> - **REST 失败分档**:`null`(超时/映射不出)与网络族(NoNetwork/Dns/Timeout/ConnectFailed)⇒ **进队列**;Server/Unauthorized 与配置族 ⇒ 仍回滚 + 文案(服务端给过裁决、等下去不会变好)。
|
||||
> - **队列仍不持久化**(进程被杀就丢)——W40/W41 一致,未改。
|
||||
> - ⚠️ **「任何 REST 失败都进队列」没做**(主人若指这个,需先设计「补发时怎么安全重放图片/附件」):本次只覆盖**阶梯第 2 步那次回落 REST**。
|
||||
>
|
||||
> ### 顺带改了两处(任务书外,有 KDoc/commit 说明)
|
||||
> `devtools/fake-backend` 补上 `GET /api/v1/health`(否则本机跑假后端时探测吃 404、阶梯退化成直接排队);补发由 W40 的「同步无挂起点」改成**单飞协程 + 串行出队**(阶梯要挂起,必然)。
|
||||
>
|
||||
> ### 待办(本节的)
|
||||
> 1. **W41 的真机回归**:用假后端 `--hold-replies` + **断 WS 但留着 HTTP**,看是否真走 REST 出去、重连后回复是否补齐。
|
||||
> 2. 「任何 REST 失败都进队列」若要做,先设计图片/附件的安全重放。
|
||||
> 3. `gapCursor == null` 且本端为空那条残留洞:可加「重连时 `loadLatest` 一次」兜底。
|
||||
> 4. **W42(图片按钮改多选)已派单**(`feat/W42-image-multipick`,基线已追到 `eb22521`)。
|
||||
Reference in new issue
Block a user