d546f31755d5568af67c5ed27970883edfd98519
为什么要把「原因」也带出来 - 结果只有 Ws / Rest / Queue 三档,而「为什么是这一档」在设备上完全看不见。W45 那轮真机回归 就卡在这里:那条消息进了补发队列、网络侧一条请求都没有,而「零请求 + 进队列」在判据里只可能 来自 Queue 那一档;可顶栏同时显示「重连中…」。两者都只是结论,事后无法分辨「判据读到 Offline」与「判据读了别的值」。 - 于是把判据抽成 sendRouteDecisionFor(wsDispatched, connection) -> (route, reason):reason 就是 原来那三条规则各自的分支标识(FrameDispatched / ConnectionOffline / ConnectionNotReady)。 sendRouteFor 现在读它的 route,所以两处不可能分叉。判据、优先级、行为逐字未改。 那一行日志(tag = CyreneRoute,Log.d,只在 DEBUG 构建打) - 打点:ChatViewModel.sendThroughLadder 一进入就一笔,覆盖阶梯的三个分支(帧交出去 / 试 REST / 直接进队列),一行 key=value、空格分隔: wsDispatched=... socket=<ChatSocket.state.value,权威值> mirror=<顶栏读的那个镜像> wsObject=<present|absent> route=... reason=... outcome=... fromQueue=... - socket 与 mirror 必须同时打出来:mirror 是 collect 过来的一格,只会滞后、不会超前,两者不一致 就说明顶栏那一刻显示的是上一格 —— 这正是「顶栏重连中…」与「这条进了队列」能同时出现的形态。 - 这是开发期诊断仪,不是产品行为:字段增删不构成兼容问题、不为它写测试(要钉的是纯函数那张全表)。 不打 token / 请求体 / 正文 / localId。 顺带(同一轮的可观测性) - ChatSocket 新增只读属性 hasLiveSocket(webSocket != null),只喂日志,不是产品逻辑的输入。 - 复核 WsState.Offline 的写点:ChatSocket 里只有两处(runConnectionLoop 的 ConfigMissing,以及 endWithAuthFailure),close() 写的是 Idle;非 401 的握手失败(代理回 503)走 Closed -> Reconnecting,进不了这一档。 测试 - SendFallbackTest 新增两例:五档连接状态 × wsDispatched 真假的全表逐个钉住结果 + 原因,并钉住 sendRouteFor 与判定同值;另一个钉住三个 reason 都可达、没有第四种组合。全量 1155 例, 0 失败 0 错误。
Releases
5
Alpha_v0.1.0_4
Latest