Repository navigation
fix(desktop): reject unusable release CLIs during automatic startup - #6774
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
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)
动机
普通桌面用户重新打开 LoopX 时,可能遇到已保存的 CLI 文件还在、解释器却已损坏的情况,结果一直进不了工作区。 旧实现仅看安装清单就接受这个候选;新实现先执行现有安装诊断,失败便让自动发现继续寻找可用 runtime。 本轮独立验证了损坏候选被拒绝、正常 Core 身份保持一致,以及真实服务的启动、重开和错误身份拒绝。 本 PR 不交付新的版本选择界面,不改变显式开发者 pin、安装所有权或 Goal 权限;正式安装、完整升级矩阵和持续使用验收仍有各自边界。 父级启动与升级交付仍缺完整的较新/较旧版本、离线失败、签名安装、Windows 交互及持续使用证据。
改动思路
保留现有 Core 资格判断和 native 进程边界,删除第二套仅凭 manifest 放行的规则,改动与损坏候选造成的重复启动失败直接对应。 当前 PR 的边界是让现有桌面启动入口只接受真正能回答安装诊断的 CLI,并维持已有自动发现、显式 pin 和服务身份核验。
入口仍是现有桌面 supervisor:先取显式 pin 或保存的发现候选,再调用 Core 的安装诊断,得到身份后才进入已有版本/官方提交关系判断和两个 HTTP 服务的身份围栏。清单是版本信息来源,不能证明解释器还能执行。与不改相比,这消除了可重复的错误放行;再加 native 健康缓存或另一套清单验证,只会保留第二个可能过期的判断。这里直接复用现有 owner,既没有新设置,也没有额外的用户选择或确认轮次。
下表是源码路径说明,绑定本次 head 67eb7ceac06b55b770e6ee2d27bec0564eb9543c,不是截图或执行证据;未核验 Mermaid 渲染,因此使用表格。
| 触发 | base | 当前 head |
|---|---|---|
| 清单还在,CLI 已坏 | 仅凭清单接受 | doctor 失败后拒绝候选,交还既有发现/恢复路径 |
| 正常 managed CLI | 清单身份 | doctor 返回的同一公开身份 |
| CLI 挂起并带子进程 | 清单分支不执行探测 | 既有约 10 秒进程组边界终止、回收 |
具体改动
我阅读了全部四个文件及周边调用,范围是 +65/-49,含测试、README 和两张 PNG。判断依据是改动之前的 apps/desktop/loopx-control-plane/README.md,spec_revision 105c4bc15091afac832495167cb98a1389951a04,criterion_id Updates And Recovery:只有合格 runtime 进入自动启动,保留显式 pin/安装所有权,并在打开工作区前让两个服务匹配同一身份。该项在当前边界 implemented。另已检查同一基线的 task-first frontend delivery:这次改变既有开窗结果,没有发明一条 CLI-only 产品旅程。README 本身被修改,所以新文字没有被用来给实现自证。
关键代码讲解
runtime_identity_for_executable_with_path把目标解析到真实可执行文件,然后运行已有doctor --installation-only。删除的 manifest 直接返回分支是这次缺陷根源。非零退出、无效 JSON、探测超时都会得到无身份,由调用方决定既有恢复。安装探测仍为 10 秒,轻量版本探测仍为 1 秒。identity_from_installation_doctor只接受成功、安装限定范围和既有 schema;managed release 必须有 release id,普通 wheel 仍走原 fingerprint 规则。返回值重新投影公开字段,独立夹带私有字段的响应也被剥离。这个身份只用于本地服务复用,不能证明发布者或替任意程序背书。resume_runtime的版本、官方 ancestry、缓存归属和显式覆盖逻辑没有改动。现在损坏的保存候选真正返回无身份,已有自动发现才有机会继续;显式环境 pin 失败仍报错,不能静默换成其他版本。安装和失败恢复调用也复用了同一个资格入口,没有新增生命周期 owner。
两张图片均已查看完整首屏:before 是既有恢复面板下的连接失败,after 是合成空工作区。PR 已把 native/package 测试版本、真实 Core 来源和排除的早期隔离失败说清楚;图片无私人对话、路径或凭据。本轮没有独立重做 native 窗口自动发现,因此把这部分保留为作者 QA;我独立执行的真实资格检查及两个 HTTP 后端另列如下,没有混称正式安装。
对主干的风险
最强反对理由是原来几乎免费的 managed 清单读取变成真实进程诊断,正常打开会增加成本,多个挂起候选也可能各耗一个 deadline。我编译引用未改动的 base/head 完整 Rust services 模块,并对同一实际可执行文件进行反例:退出失败、非 JSON、doctor 未就绪、错误 scope、错误 schema 和空身份,base 全部错误放行,head 全部拒绝。挂起并生成子进程的 managed 候选在 head 约 10.002 秒返回拒绝,父子 PID 都已消失;正常返回仍剥离多余字段。这证明了与旧缺陷区分,而不只是新 helper 的 JSON 测试。
真实 Core 使用隔离副本,版本 2.0.0a1、来源提交 6da1b467a19346ec5c60b2dd3646fc64417b5688;本轮两次 warm 资格探测为 0.213/0.193 秒,身份与 base 完全一致,不能拿它当冷启动保证。还独立运行了生产 supervisor 的 real-Core opt-in 测试:真实 status/Chat 两次启停重开、零 Goals/Sessions、错误预期身份拒绝、所有拥有的监听器停止。它使用独立注册表/数据目录并关闭使用上报,不包含模型请求,也不证明 native 自动发现或签名更新。
本轮 cargo test --lib 为 135 passed、5 个现有 opt-in ignored;与本改动直接相关的 real-Core 测试另行运行 1 passed。cargo fmt --check、标准 premerge 的 17 项选择检查及 4 项直接检查通过。标准选择没有覆盖 native 行为,所以这些结果没有替代上述 Rust/进程/后端验证。
语义与 CI 对齐
复用既有 installation_only 和 runtime identity 词汇,没有新 Enum/协议。差异 advisory 未找到支持的 Python/TS 候选,但它不分析 Rust,Rust 分支已人工核对。这里是对 managed 候选默认路径的明确变更,不是 default-off 功能,也没有假装通过关闭开关证明兼容性。安装清单不得代替诊断是强制资格条件,README 没有将它称为建议。
额外必需模块预算检查仍失败:tests/architecture/test_top_level_module_budget.py::test_top_level_module_count_stays_at_the_pinned_budget,base/head 均 1 failed/5 passed,146 对 145,指向 workflow_skill_install.py。我比较了完整错误(仅去掉末尾运行时间)、全模块列表、预算、检查器和该模块源码,均一致;当前 PR 没改 Python 因果路径。故这是已有独立合并限制,不要求本 PR 扩修,也没有放宽预算。依当前契约没有查询、等待或引用 CI 作为证据。
我的整体评价
APPROVE,未发现当前修复的阻断问题。 交付判断是 justified_increment:损坏可执行文件不再被静态清单放行,是可独立验证的有用改进;父级升级验收保持开放。long_horizon 为 improved,理由是消除重复选择坏候选,并验证同一真实身份的服务重开;user_experience 为 improved,理由是复用已知候选和恢复路径,增加零个必填交互/确认轮次,而不要求用户选择版本。这些判断不声称已验证持续 Agent 成果或官方安装采用。
比例和兼容性判断是保留该方案:已经应用删除重复 converter 的同域小重构,正常 managed 身份和既有 wheel 规则保留;Core 与 App 独立升级的边界也保留,没有新的配置同步或协议版本分支。记忆建议仅帮助我检查从启动到可用工作区/后续重开的结果,没有继承历史批准,也没有将一次建议采用称为受控效益证据。现有模块预算失败仍单独限制合并;本结论不授予 merge、bypass、安装或发布权限。若 head 改变,需重新核验变化后的完整边界。
English verdict: APPROVE at 67eb7ceac06b55b770e6ee2d27bec0564eb9543c. The manifest-only shortcut is removed in favor of existing bounded Core qualification. Exact base/head process counterfactuals, 135 Rust tests and an isolated real-Core supervisor/reopen test cover the repair. The identical pre-existing module-budget failure remains a separate merge hold. No merge or installation is authorized by this review.
Goal And Delivered Outcome
A remembered managed CLI could retain a valid
release.jsonafter its interpreter or executable broke. Desktop treated that metadata as a usable runtime, kept choosing it, and repeatedly failed to open the workspace. It now asks the same Core installation-only doctor used for wheel installs before accepting any discovered CLI. A broken remembered version is skipped automatically; a qualified existing CLI keeps its installation owner.This is a bounded S4/S12 startup-recovery fix against
mainat105c4bc15091afac832495167cb98a1389951a04. It adds no runtime setting, version chooser or new lifecycle owner.Author Declaration
Written by: model_agent · OpenAI Codex, GPT-6 family; exact runtime model identifier was not exposed in this turn.
Implemented against
apps/desktop/loopx-control-plane/README.md, Updates And Recovery, anddocs/development/frontend-delivery.md, Task-first delivery, at105c4bc15091afac832495167cb98a1389951a04.Scope And Continuation
The executable-qualification defect is complete within this proposal. The isolated macOS newer/older/offline and upgrade-failure matrix below is now qualified. The parent startup/upgrade program remains open: sustained use, signed official installation and interactive Windows qualification remain separate evidence. Existing #6770 and #6772 retain their independent startup-copy and Chat-recovery scopes. Existing #6768 owns the unrelated top-level module placement failure.
The small refactor removes the duplicate manifest identity converter and reuses one existing doctor projection; it does not recreate package hashing in Rust. PEP 440 ordering, official ancestry, cached-installation ownership and explicit
LOOPX_BINsemantics are unchanged. A normal managed-release launch now incurs the existing bounded doctor probe; measured QA startup was 4.565 s cold and 1.762 s on normal reopen, observations rather than a latency guarantee.Validation
Tested native/package revision:
85d9720642cda8bf4b9ce0d880b0f631521dc655; current head67eb7ceac06b55b770e6ee2d27bec0564eb9543cadds only the two screenshots, with identical production Rust blobs. Run state: finished. Inputs: synthetic, public fixture. The native selection drill used the installed Core2.0.0a1at public source6da1b467a19346ec5c60b2dd3646fc64417b5688; both owned HTTP children returned the same artifact identity.cargo test --manifest-path apps/desktop/loopx-control-plane/src-tauri/Cargo.toml --lib: 135 passed, five existing opt-in tests ignored; includes runtime selection/ancestry and supervisor/update failure pathscargo fmt --check, Rust release build, changed-file public/private scan and diff hygienereal_private_installer_keeps_the_default_cli_owneropt-in test: real installer, qualified release identity, default CLI bytes preserved99.0.0is skipped; both services become ready; normal quit/reopen works. Explicit broken pin remains an explained failure. No model request was sentmainfailure; this PR adds no Python module. Retained without waiver; #6768 proposes repaircqr_009ca990b826b460fcccis non-passing because of that required failure; no merge readiness or approval is claimed. CI and independent review remain requiredCurrent premerge and receipt verification are explicitly non-passing. An earlier non-detached QA launcher was reopened by the UI tool without its isolation environment; it was exited, excluded from isolated acceptance and supplied no published screenshot. The final detached drill verifies both parent PIDs, the explicit private root and zero-Goal readback. Official App installation was not replaced.
Supplemental macOS Upgrade Matrix
Native shell
67eb7ceac06b55b770e6ee2d27bec0564eb9543c, with unchanged production code, was exercised against genuine Core1.4.0(public source323c3053128e) and2.0.0a1(bundled source85d9720642cdor independently installed source6da1b467a193).LOOPX_BINpoints to 1.4.0Every case was observed in the native App and through the real status/chat services over an empty isolated registry. All QA processes exited normally; the user's default CLI target and bytes remained unchanged. The private QA bundle's original resources were restored afterward. No model requests or live Goal writes were made.
Qualification boundary: unsigned, isolated QA distribution with the current native shell. The older bundled Core was an exact public-source export with its matching packaged frontend, independently checked against 487 source inputs and 19 asset digests, then qualified by the existing archive owner. This is not a signed historical App/update-feed installation. Fault injection was limited to a private Python transport adapter rejecting other discovered CLI doctor probes, corruption of the QA archive/preference, and closed outbound proxies with loopback allowed. The final keep-newer-CLI case allowed the real independent CLI through its doctor. Versions, Core, installer and HTTP were real. These are single-machine functional observations; unchanged source checks were not rerun, and the existing required quality/review/merge holds remain.
Frontend / Visual Evidence
UI impact: changed startup outcome; layout, copy and responsive behavior are unchanged. Source data: synthetic. macOS desktop viewport. No live preview is kept running after QA.
Before: historical QA shell with the unchanged manifest-only discovery path repeatedly fails on a broken remembered CLI. The independent base regression proves this predicate separately.
After: packaged native code at
85d972064automatically skips that candidate and opens the empty isolated workspace. The screenshot shows the actual existing Core frontend; it does not claim this PR changed the model configuration or installed that frontend for the user.Attention review: removes the need for runtime-choice/retry interaction on this ordinary recovery path. Existing explicit-pin failures remain visible and retain their recovery controls. Mobile is not affected by this native launch-provider change.
Areas And Boundary
Shared-authority RFC fixture impact: N/A; no shared-state/provider promotion is proposed.