Repository navigation
fix(desktop): distinguish update transport and signature failures - #6985
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
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.
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.
Validation
Tested source:
cbe5f0e0218168222432bcf351431707167fb5b5; base:4e6baa51afdc2ad522012c1f691ae166c9d67e7b.cargo fmt --checkremains failed in unchangedservices.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:
After — actionable signature failure:
After — download timeout on narrow layout:
Startup recovery — the same bounded timeout cause:
The existing panel keeps the cause and retry visible, with advanced diagnostics collapsed; no additional control or permanent explanation is added.