Skip to content

fix(device-link): 减少重复远程查询并补齐同步诊断 - #4205

Merged
MagicLizi merged 4 commits into
mainfrom
dash/remote-sidebar-idle-sync
Sep 10, 2026
Merged

fix(device-link): 减少重复远程查询并补齐同步诊断#4205
MagicLizi merged 4 commits into
mainfrom
dash/remote-sidebar-idle-sync

Conversation

@dashhuang

@dashhuang dashhuang commented Sep 9, 2026

Copy link
Copy Markdown
Member

这次改了什么

摘要

独立侧栏隐藏后仍每 10 秒查询远程任务列表,连接状态重复发布 online 时也会重新启动查询。本次暂停隐藏侧栏的周期扫描,过滤重复 online,并阻止明确离线后继续启动查询;保留首次加载、缓存、实时推送和真实重连恢复。

同时补齐手机同步分阶段耗时、后台退订与停止记录、JS 构建身份和携带频道的 push/invoke 帧 ACK 超时分类,便于区分连接等待、读取等待、提交和后台挂起。诊断仅记录阶段、耗时、短标识及白名单频道,不记录正文、参数或原始 topic。

手机回到前台和首页重新获得焦点继续复用现有静默列表刷新。本次同时修复简介的新鲜度选择:历史视图已更新正文时,不再让旧消息镜像覆盖新的 Host 简介;首页、设备页和抽屉统一使用同一选择入口。实时正文只在对应列表元数据尚未变化时优先,错过完成事件后新的列表状态可以接管。同步标记变化会通知对应行,普通流式 token 不唤醒首页根层。

变更类型

  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 测试

范围

  • 关联 Issue / 需求:fix(device-link): 合并重复同步查询并统一两端流式恢复 #4116 合并后的远程连接日志排查。
  • 本 PR 包含:侧栏周期查询的显隐控制、重复在线通知与启动查询收敛、两端诊断、手机列表简介新鲜度和回归测试。
  • 明确不包含:历史 fresh 读取合并的新机制、后台发送策略调整、wire / ACK / 1013 预算变更、原生配置与指纹变更。
  • 用户可见变化:减少隐藏侧栏和重复在线通知造成的额外请求;回前台后简介跟随新列表状态;保留缓存和实时正文,实时标题、已读与离线提示语义保持不变。
  • 是否存在 breaking change:无。

UI 变化

  • 引用的设计规范:不涉及:只修查询接线和列表简介的数据新鲜度,没有布局、样式、动效或文案变化。

怎么验证的

自动验证

pnpm test:unit:related — 通过(Desktop/Mobile 全量及 device-link 相关测试)
统一 run-unit-gate.sh — 0e732e51c 全仓通过,GATE_EXIT=0;ba21d1120 小修由最终根 related 复验
pnpm --filter desktop --filter mobile run typecheck — 通过(更新主干后)
pnpm --filter @cindy/device-link run build — 通过
pnpm --filter mobile run test:scope — 通过
pnpm check:dco — 通过

定向覆盖隐藏/显示期间的在途查询与退避、初始状态查询失败且没有后续推送、重复 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 超时的具体流量来源仍需安装新版本后交叉对照,不能宣称全部同步停滞已解决。

风险

风险分类

  • 其他:异步连接生命周期和诊断覆盖。

影响与回滚

  • 影响范围:桌面远程任务镜像接入、手机同步与后台生命周期诊断、共享连接包超时日志,以及手机首页/设备页/抽屉的简介选择。桌面列表沿用已有 Host preview;SSH 执行与远端文件操作不受影响。
  • 不变量:未知连接状态允许原有首次加载回退,明确离线才阻止 bootstrap;真实重连恢复订阅,重复在线不重复;显隐不重置退避或在途请求;旧代诊断不混入新代;诊断失败不阻止 stop/connect。单设备超时不新增其他设备或 relay 的恢复动作,也不改变 heavy/light topic 释放规则。
  • 简介不变量:复用现有消息同步标记和预览缓存,不新增轮询、请求、持久缓存或重试;预览单独比较 Host preview,不改变历史加载的新鲜度门槛。保留原始正文预览 getter 用于同步提示判断。
  • 回滚 / 降级方式:回退本提交即可,无存储迁移、协议或配置升级。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名
  • UI 变化已注明不涉及视觉或交互的原因
  • 未提交凭证、令牌或授权文件
  • 已补充必要说明(本 PR 的诊断与验证边界)
  • 已确认测试结果或说明未执行原因

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
@dashhuang
dashhuang requested a review from a team as a code owner September 9, 2026 23:15
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T01:05:01.764179Z abdb9b3 New commits
🔒 Security Review Completed 2026-09-10T01:01:16.306096Z abdb9b3 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@greptile-apps

greptile-apps Bot commented Sep 9, 2026

Copy link
Copy Markdown

Greptile Summary

本 PR 收敛桌面端远程会话查询生命周期,并补充移动端同步、后台连接与可靠消息超时诊断。

  • 独立侧栏隐藏时暂停周期对账,保留在途请求与失败退避状态。
  • 过滤重复 online 通知,并在明确离线时阻止新的 bootstrap。
  • 增加移动端同步阶段、后台退订/停止和运行时构建身份日志。
  • 在可靠 ACK 超时日志中加入经过白名单过滤的频道。
  • 当前主要问题集中在两个诊断准确性缺口:挂起停止事件代号错位,以及 history 失败缺少阶段记录。

Confidence Score: 4/5

该 PR 整体可合并,但建议修正两处非阻塞的诊断准确性问题,以免新增日志在挂起恢复和 history 失败场景中产生误导或缺失。

核心查询门控、重复 online 收敛和离线 bootstrap 防护未发现确定的功能回归;已确认的问题仅影响诊断事件的代际归属和失败阶段完整性,不改变连接、停止或同步控制流。

Files Needing Attention: apps/mobile/src/device-link/backgroundConnection.ts, apps/mobile/app/sessions/[sessionId].tsx

Important Files Changed

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]
Loading
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

Comment thread apps/mobile/src/device-link/backgroundConnection.ts Outdated
Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread packages/device-link/src/client.ts
@MagicLizi MagicLizi added status:awaiting-bot-review 等外部审查机器人表态(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) and removed status:awaiting-bot-review 等外部审查机器人表态(review-pr 自动维护,仅展示) labels Sep 10, 2026
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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=passclient-ci / pr-design-basis / PR #4205 均 success);mergeable=MERGEABLEbehindBy=0
  • 源码对照:
    • 侧栏 useDeviceLinkRemoteProjects(windowVisible, 'sidebar');reconciler isActive 为 false 时直接 return,不重置 inFlight/backoff。
    • linkOnline: boolean | nullcanBootstrap 仅在 linkOnline === false 阻止;重复 onlinewasOnline 跳过;迟到 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)

@MagicLizi
MagicLizi merged commit 16a414b into main Sep 10, 2026
20 of 21 checks passed
@MagicLizi

Copy link
Copy Markdown
Contributor

合了。隐藏侧栏还在空转远程列表这件事终于收住了,简介也不会再被旧镜像盖回去。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants