Skip to content

fix(desktop): distinguish update transport and signature failures - #6985

Merged
loopx-agent merged 1 commit into
mainfrom
codex/native-recovery-qualification-20261021
Oct 21, 2026
Merged

loopx-agent merged 1 commit into
mainfrom
codex/native-recovery-qualification-20261021

Conversation

@loopx-agent

Copy link
Copy Markdown
Collaborator

An update download failure currently loses its cause behind one download/signature message. This keeps the native updater's safe, actionable cause through both the startup recovery page and the workspace update panel.

  • Distinguish timeout, interrupted network request, unavailable package and signature rejection using the pinned updater's typed errors. Keep unknown failures on the existing fallback.
  • Show the retry action in Chinese and English. Reading an error never retries, installs or restarts; explicit Check discovers a fresh candidate.
  • Reuse the existing transfer, background preparation and diagnostics owners. Download deadlines, signature checks, cache publication and install authority are unchanged.

Validation

Tested source: cbe5f0e0218168222432bcf351431707167fb5b5; base: 4e6baa51afdc2ad522012c1f691ae166c9d67e7b.

  • Native library: 155 passed, 11 explicit ignored tests. Separately ran the real header/body idle timeout case: passed in 60.06s total.
  • Real loopback HTTP and the shipped updater/verifier: valid signed bytes + cache readback, tampering, truncated body and HTTP 404; background failure preserves the cause and local-service availability.
  • The same signature regression test fails on immutable base with the old generic code. The new packaged timeout UI assertion also fails on base.
  • Production Chat build/typecheck, exact-commit desktop runtime bundle and full workspace/boot browser smoke passed; eight boot diagnostics tests passed. Browser tests use synthetic status and a native IPC double; they do not install software.
  • Standard managed premerge passed: 4 direct checks and 19 selected checks, with no failures or manual holds. Exact-scope change-quality receipt passed. Diff/RFC bilingual checks, changed-Rust formatting and public-boundary scan passed. Three error codes extend only the existing native diagnostic vocabulary; the semantic advisory cannot analyze these Rust/string-map carriers.
  • Optional whole-crate cargo fmt --check remains failed in unchanged services.rs. Independent clean base/head runs have identical complete normalized failure, fixture and toolchain digests. Both changed Rust files pass formatting. This is disclosed rather than relabeled as a passing formatter.

The bounded refactoring pass retained the direct local error match; background preparation already preserves these causes, so no new state or abstraction is needed. This proposal does not establish a WebView white-window root cause or installed adoption.

Visual evidence

Actual first viewports from the production frontend at the tested source, with public synthetic data/native IPC. Before is immutable base; after source bytes are those committed above. Desktop 1280×900; narrow 390×844. No live preview service is retained.

Before — generic cause:

Before: generic update failure

After — actionable signature failure:

After: signature failure

After — download timeout on narrow layout:

After: download timeout on mobile

Startup recovery — the same bounded timeout cause:

Startup recovery timeout

The existing panel keeps the cause and retry visible, with advanced diagnostics collapsed; no additional control or permanent explanation is added.

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | reasoning_effort=max

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

未发现阻塞性问题。当前结论绑定 cbe5f0e,是对这份诊断修复的作者自评;正式发布采用和白屏根因仍未证明。

动机

使用桌面 App 检查或安装更新的用户,会在下载失败时进入现有恢复面板。以前签名被拒绝与网络中断都显示同一句失败提示;现在可以直接看到原因,再选择检查更新。已验证四类安全错误原因、现有重试入口和只读查看无安装副作用。本次不改变下载期限、验签、缓存发布或安装权限,也不宣称修复白屏根因。新错误提示尚未通过正式发布进入已安装 App;白屏根因仍属于现有桌面恢复工作的剩余问题。

改动思路

错误分类属于原生更新 IO 边界,两个既有界面只消费安全码;保留既有下载与恢复 owner,避免第二套生命周期。当前 PR 一并交付原因分类、启动恢复与工作区提示、真实传输反例及首屏证据。先由原生更新器完成真实下载和签名校验,再把失败枚举变成安全错误码;界面只决定如何描述,不能因此重试、安装或重启。只改文案无法恢复已经被抹掉的原因,增加一套重试管理器又超过了问题所需,因此直接扩展既有 owner。

在这个 head 上,超时对应下载超时,截断对应网络请求失败,HTTP 非成功响应对应安装包不可用,签名类错误对应校验失败。未知错误仍走原来的通用提示。后台准备已经保留错误,外层映射只处理阻塞任务的 JoinError,没有必要改其并发规则。桌面与手机整屏已检查,原因与检查更新保留在既有浮层,高级信息仍折叠。

具体改动

规范依据为 apps/desktop/loopx-control-plane/README.md,采用变更前的不可变版本 4e6baa51afdc2ad522012c1f691ae166c9d67e7b,没有用本 PR 新增说明反过来定义验收。逐条映射:signature-before-publication 由实际 minisign 拒绝、正确签名缓存读回及后台拒绝发布验证;bounded-network-policy 由真实响应头和正文停滞、原有 feed 短期限验证;local-first-recovery 由后台失败后仍可 recover_connected、pending 为空和原生状态测试验证。这三项均 implemented。

关键代码讲解

  • download_error(apps/desktop/loopx-control-plane/src-tauri/src/maintenance.rs:406)匹配锁定 SDK 的 Error 枚举及 is_timeout。读取 SDK 实现确认 Network 在 archive HTTP 非成功时产生;不猜测错误字符串,也不输出 URL 或签名内容。
  • download_update(同文件 709 行)仍由手动安装和后台准备共用,保持原有连接、读取和完整下载期限。唯一行为差异是 map_err 保留原因;校验未过时拿不到可发布的字节。
  • codeText / safeCode(apps/desktop/loopx-control-plane/static/boot.js:127)和 updateErrors / failedUpdate(apps/presentation/dashboard/src/features/personal-workspace/desktop-update.tsx:12)是两个现有加载边界的投影,新增原因后仍经过白名单、当前语言和通用回退。渲染没有新的权限或动作。

变更共 11 个文件、165 行增加和 9 行删除:原生分类与两个展示字典、既有传输/启动/浏览器测试、双语说明和四张合成数据首屏图。浏览器测试还修正了变更前就过时的启动文案断言,并将缺失的 Node 观测明确保留为 null;没有去掉原有恢复断言。生产 bundle 与精确提交的桌面资源已构建验证,未提交构建产物或本机状态。

对主干的风险

最强反例是界面 mock 自己提供了正确错误码,而真实原生代码仍然全部抹掉。为排除它,本次实际执行了锁定 SDK、真实 HTTP、签名和后台 Maintenance 路径:正确签名仍可读回缓存,篡改、截断和 404 分别拒绝;失败不能进入发布闭包,pending 保持空,本地服务恢复仍可用。响应头及部分正文停滞分别被现有 30 秒界限终止,合计 60.06 秒。

完整原生库 155 项通过、11 项显式忽略;变更涉及的长停滞项已另行执行。生产 Chat/boot 浏览器验收和八项启动诊断测试通过,19 项标准 canary、4 项直接检查及精确范围 CQR 通过。相同测试在不可变 base 上以旧通用错误码及缺少超时提示明确失败。中英文、通用回退、窄屏无横向溢出、只读无动作、显式 Check 仅产生 check 均已验证。

全仓 cargo fmt 仍在未改动的 services.rs 失败。干净 base/head 的完整归一化失败、工具链和配置摘要完全一致,两个改动 Rust 文件单独格式检查通过;该失败保留为 pre_existing_unrelated,而非记作通过。按当前 Goal 策略未查询或等待远端 CI。HTTP/签名后端是真实的,窗口 runtime 与浏览器 IPC 是测试替身,因此不把这些结果说成已安装新版或真实 WebView 白屏已修好。

语义与 CI 对齐

三个字符串只扩展既有 App 本地诊断语义,未注册新 Core 状态或形成第二个权限 owner。diff 语义 advisory 对 Rust/字符串字典不具备覆盖能力,已补以枚举源、完整调用方检查和真实反例;完整语义漂移检查通过。校验与安装拒绝条件未削弱,未知原因仍保留安全回退。范围判断、配额和 Agent 义务均未改变。

我的整体评价

这是 justified_increment:长程持续工作维持 preserved,依据是失败没有抢占服务或制造重复安装;用户体验 improved,依据是同一入口和同一次检查就能读懂原因,没有新增必答或确认轮次。可见结果不等于收益或正式采用,剩余发布及白屏诊断边界已明确。

代码量与问题相称,保留直接匹配,未引入额外状态、配置或框架。原生 wrapper 与 Chat runtime 可独立升级,因此保留旧通用码和未知错误回退是有实际调用方的兼容需要。本结论允许这份精确提交进入既有合并门,不能替代 ready 检查、维护者授权或尚未完成的整个启动验收。

English verdict: APPROVE — cbe5f0e. Typed native causes reach both existing recovery surfaces without altering verification, deadlines or effect authority. Real HTTP/native and packaged UI counterexamples passed; the unchanged full-crate formatter failure is separately attributed. Installed adoption and WebView root cause remain outside this increment.

@loopx-agent
loopx-agent merged commit 5706ef0 into main Oct 21, 2026
30 of 44 checks passed
@loopx-agent
loopx-agent deleted the codex/native-recovery-qualification-20261021 branch October 21, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant