fix(device-link): 减少重复远程查询并补齐同步诊断 - #4205
Conversation
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
| Filename | Overview |
|---|---|
| apps/desktop/src/renderer/features/device-link/useDeviceLinkRemoteProjects.ts | 增加显隐门控、三态连接状态和重复 online 过滤;未确认到会破坏正常重连恢复的具体路径。 |
| apps/mobile/src/device-link/backgroundConnection.ts | 增加后台生命周期诊断,但挂起恢复触发的 stop 被记录到错误的下一代 generation。 |
| apps/mobile/app/sessions/[sessionId].tsx | 增加同步阶段耗时诊断,但 history 读取失败缺少与其他资源一致的失败阶段。 |
| packages/device-link/src/client.ts | ACK 超时日志增加白名单频道分类,并将未知或无频道的 payload 安全标记为 unknown。 |
| apps/desktop/src/renderer/components/layout/SidebarWindowLayout.tsx | 将窗口可见状态接入远程会话周期对账,没有布局或交互变化。 |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[侧栏可见性变化] --> B{窗口可见?}
B -- 否 --> C[暂停周期 sessions:list]
B -- 是 --> D[按既有退避继续周期对账]
E[连接状态变化] --> F{首次进入 online?}
F -- 是 --> G[重新订阅、bootstrap 并 reseed]
F -- 重复 online --> H[跳过重复启动]
I[移动端进入后台] --> J[记录 background / release-settled]
J --> K{宽限期或挂起恢复}
K -- 宽限期结束 --> L[记录 stop: grace]
K -- 长时间挂起 --> M[记录 active 与 stop: suspended]
N[会话快照同步] --> O[link-ready]
O --> P[history / pending / projection / active]
P --> Q[complete]
Prompt To Fix All With AI
### Issue 1
apps/mobile/src/device-link/backgroundConnection.ts:100-105
**停止事件代号错位**
应用从超过 `suspendMs` 的后台挂起恢复时,`active` 事件使用当前代 N,但代码随后先递增 `generation`,再将同一后台周期的 `stop(reason=suspended)` 记录为 N+1。按 generation 关联诊断时,这次停止会被归入尚未开始的下一代,无法与对应的 background 和 active 事件对齐。
```suggestion
clearTimers();
// A timer reference cannot tell whether the socket was actually stopped:
// JS can be suspended while the final unsubscribe is still awaiting ACK.
if (elapsed > options.suspendMs) {
report("stop", { reason: "suspended", elapsedMs: elapsed });
}
generation += 1;
```
### Issue 2
apps/mobile/app/sessions/[sessionId].tsx:3427
**History失败缺少诊断**
history 读取只有成功时才记录 `history-settled`。如果 history refresh 或 `listMessages` 最终失败,Promise 会直接拒绝,既没有 `outcome: failed` 阶段,外层也没有补记等价诊断;相比之下,另外三项读取都会记录失败。这会让整批同步失败时的日志无法区分 history 失败和读取尚未完成。
```suggestion
messageRead
.then(() => reportSyncPhase('history-settled'))
.catch((error) => {
reportSyncPhase('history', { outcome: 'failed' });
throw error;
}),
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "fix(device-link): 减少重复远程查询并补齐同步诊断" | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0e732e51c4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ba21d11205
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
MagicLizi
left a comment
There was a problem hiding this comment.
Findings
无 P0/P1。
Rule coverage
AGENTS.md/docs/dev-rules/development-workflow.md:fix + 测试/诊断范围与 PR 描述一致;DCO trailer 四枚 commit 均有;未夹带无关重构或原生指纹改动。docs/dev-rules/remote-and-mobile-adaptation.md:无新 IPC/allowlist;超时诊断只加白名单 channel,不改变 peer/relay 恢复半径。PR 已写明故障半径(单设备超时不放大、不改 topic 释放)及三端结论。隐藏侧栏只停周期对账,保留 push/缓存/在途/退避。docs/dev-rules/protocol-compatibility.md:未改 wire 语义、relay kind 或 payload 形状;channel 仅写入本地超时日志。docs/dev-rules/mobile-development.md:无原生配置/指纹输入;EXPO_PUBLIC_XDT_GIT_COMMIT仅 JS 诊断。docs/dev-rules/electron-security-and-process-boundaries.md:Renderer 仍走既有 deviceLink IPC;无新特权、无 Node 下沉。docs/dev-rules/engineering-conventions.md/log-upload-and-redaction.md:新增日志为 debug 阶段/短 id/白名单频道;未知频道记unknown;测试断言正文/伪造续行不进入 warn。docs/dev-rules/credentials-and-local-storage.md:softHit 为packages/device-link/src/__tests__/client.test.ts:1613测试桩secret字段,用于证明脱敏,非正式凭证。docs/design-rules/DESIGN.md:命中 UI 路径但是接线/数据新鲜度,无布局、token、组件或文案变化。format.uiEvidenceMissing=true为非阻断提醒。docs/product-rules/core-product-principles.md:收敛重复查询与简介过期,不把组织流程塞进 Core。- 讨论 issue:无。已 resolve thread(bootstrap loading 清理、history 失败诊断、suspended generation、invoke-result channel 覆盖限制)已在当前 head 落实或由作者明确收敛为已知限制。
Verification
- 本 worktree 无
node_modules,未在本地重跑pnpm test:unit:related/ typecheck(避免在审查 worktree 执行未获准安装)。 - 实时 context:
gate.ci.status=pass(client-ci/pr-design-basis/PR #4205均 success);mergeable=MERGEABLE,behindBy=0。 - 源码对照:
- 侧栏
useDeviceLinkRemoteProjects(windowVisible, 'sidebar');reconcilerisActive为 false 时直接 return,不重置 inFlight/backoff。 linkOnline: boolean | null;canBootstrap仅在linkOnline === false阻止;重复online用wasOnline跳过;迟到getState非 online 且无 push 时markAllDisconnected()。- 简介选择:
getSessionListMessagePreview在 marker 与 Host preview 对齐且窗口已同步时用镜像,否则 Host 元数据优先;live 文本以liveSession === session绑定当前元数据;isSessionMessageWindowSynced仍不因 preview 单独失效。 - ACK 超时 channel 经
PUSH_FORWARD_ALLOWLIST/REMOTE_INVOKE_ALLOWLIST;未知为unknown。
- 侧栏
- 作者声称的 related/typecheck 由绿 CI 交叉验证,不把未运行的本地命令写成通过。
Overall
pass(没有 P0/P1)
|
合了。隐藏侧栏还在空转远程列表这件事终于收住了,简介也不会再被旧镜像盖回去。 |
这次改了什么
摘要
独立侧栏隐藏后仍每 10 秒查询远程任务列表,连接状态重复发布 online 时也会重新启动查询。本次暂停隐藏侧栏的周期扫描,过滤重复 online,并阻止明确离线后继续启动查询;保留首次加载、缓存、实时推送和真实重连恢复。
同时补齐手机同步分阶段耗时、后台退订与停止记录、JS 构建身份和携带频道的 push/invoke 帧 ACK 超时分类,便于区分连接等待、读取等待、提交和后台挂起。诊断仅记录阶段、耗时、短标识及白名单频道,不记录正文、参数或原始 topic。
手机回到前台和首页重新获得焦点继续复用现有静默列表刷新。本次同时修复简介的新鲜度选择:历史视图已更新正文时,不再让旧消息镜像覆盖新的 Host 简介;首页、设备页和抽屉统一使用同一选择入口。实时正文只在对应列表元数据尚未变化时优先,错过完成事件后新的列表状态可以接管。同步标记变化会通知对应行,普通流式 token 不唤醒首页根层。
变更类型
fix缺陷修复refactor/perf重构或性能优化docs/test/chore测试范围
UI 变化
怎么验证的
自动验证
定向覆盖隐藏/显示期间的在途查询与退避、初始状态查询失败且没有后续推送、重复 online、真实重连、离线异步尾巴、后台计时器与迟到退订、诊断回调抛错、未知频道脱敏。保持消息可先显示、控制状态未完成时不提前发送已读的生产回调测试。
最新回归:迟到 getState 确认离线时复用断线清理(订阅/快照等待两例旧代码失败、修后通过);suspended stop 与 active 归属同一后台代;history 失败记录阶段后原样抛出。Desktop 定向 66 项、Mobile 38 项、最终根 related 和两端 typecheck 均通过。
简介回归:已打开任务后历史视图与旧镜像分离、后台漏 final/done 后全量列表接管、正常 done/status 不倒退、仅 preview 字段变化、同步标记变化通知、离线/删除失效、旧 Host 无 preview 回退、真实流式优先及静态 streaming 标记不可信。Mobile 定向 298 项、typecheck、scope/smoke 通过。首次根门禁发现抽屉旧用例缺少同步标记,补充未同步/已同步断言并保留清空回退后,根 related 通过。随后合并 main 5381eb2,最终根 related 再次通过(两端全量及受影响共享包),两端 typecheck 通过。
手工验证
已完整源码复核,独立只读审查无 P0/P1。没有把自动测试或源码复核视作实机验收。
未执行的验证
诊断覆盖限制:invoke-result 响应载荷没有频道,ACK 超时仍记录 channel=unknown。现有短 request ID 可在相应控制端慢请求/超时或 Host 慢执行/出站队列日志存在时关联;快速完成、缺日志或仅 ACK 丢失时不能保证还原频道。本次后台样本是 push;本 PR 不新增 request-to-channel 映射或本地 pending 元数据机制。
未安装日常实例、未启动 Metro、无本分支 DEV build label 或实机弱网证据。目标分支为 dash/remote-sidebar-idle-sync,worktree 为 cindy-remote-sidebar-idle-sync。Light/Dark 未实机目检,本次无视觉改动。后台 ACK 超时的具体流量来源仍需安装新版本后交叉对照,不能宣称全部同步停滞已解决。
风险
风险分类
影响与回滚
提交前检查