Repository navigation
feat(upgrade): ship qualified runtimes with resumable, observable updates - #7656
Conversation
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.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.
动机 / Motivation
需要升级已安装 LoopX 的用户,应继续使用原安装 owner 与通道,并在终端断开后知道升级是否仍在执行。过去快照升级可能重新拉取源码并在本机编译前端,观察者断开也缺少独立的进度回读;现在默认使用同一合格 revision 的运行包,并可按 operation id 重连。本轮验证了临时安装中的后台升级、重复观察不重复启动、真实 HTTP 下载与 hash 校验、激活后的 revision/channel/doctor 回读,以及失败时恢复原可执行入口。本 PR 不改变 pip/pipx 的安装归属,不授权部署、合并或真实安装升级,也不证明全球下载 SLA、原生 App 安装或模型净收益。本轮交付资格限于本地可复现的运行包与升级链路;真实 GitHub 发布、原生 Windows/macOS App 安装和跨地区网络仍由发布资格流程验证。用户的普通任务仍是一条明确的 upgrade 指令;断开后只需查看已返回的 operation id,不需要重新描述目标或重新批准同一个安装操作。
改动思路 / Approach
运行包传输、后台观察和界面展示各有明确边界,安装决策继续留在现有 self_update、installer 与原生 updater 中,因此保留这组机制是合理的。本 PR 提供预构建快照的完整本地升级与可恢复观察路径;发布到真实通道和原生平台资格仍遵循既有发布流程。stdlib 下载器在 Node 或前端构建工具尚不可用时也能取得 hash-pinned 运行包;后台 worker 持有安装生命周期锁,前台和 update status 只观察它。候选验证、数据格式升级的 backups/receipts、可执行链接切换、服务与 host materials 延续原 owner,失败不会机械回滚已迁移的数据。
| 入口 | 决策与失败边界 | 可验证结果 |
|---|---|---|
| 快照 update/upgrade | 先解析同一合格 metadata 的 version/revision;旧、分叉、未知 lineage 需明确意图 | 保留 main/stable/pinned SHA,缺 metadata 不静默转源码编译 |
| 后台执行与重连 | worker 持锁;观察者离开或重复读取不启动第二次安装 | 同一 operation id、真实阶段、最终安装身份 |
| 已有 App 面板 | 使用现有 signed updater,进度展示不触发安装 | bytes/elapsed/unknown/stalled,以及原失败恢复入口 |
具体改动 / Changes
审阅完整 28 文件、+1944/-71 的最终内容:原完整 diff 加审阅期间新增的 7 文件差异,最终 head e17ca865a5f0dc1f36043b0e0218eeb55ee35ac8,base a651a824996cce29cc69a5e7e4437b241eef963d。先读修改前 docs/guides/installing-loopx.md 的 Upgrade And Repair / Verify The Active Layers / Archive Fallback,revision a651a824996cce29cc69a5e7e4437b241eef963d。原文未编号,下列审阅标签分别对应安装归属、只读预览、激活回读和失败恢复:UP-OWNER、UP-PREVIEW、UP-READBACK、UP-RECOVERY。新增 feed 发布行为另按“latest qualified main”这个实际交付目标判断,没有拿本 PR 新写的指南反向证明实现。
关键代码讲解
prepare_target先固定 artifact revision 与 version,再检查 lineage;不可确认或较旧目标在产生安装副作用前拒绝。--allow-downgrade只表达目标选择意图,不绕过 TLS、hash、archive identity 或安装验证。start_or_observe用 installation key 对齐同一可执行入口,启动独立 worker 或回到已有 operation。真正覆盖规划、下载、安装和回读的锁由 worker 持有;read_operation将失活观察报告为 interrupted,不把旧 PID 当作重放许可。install_artifact串起有界下载、强 ETag/Range 恢复、完整 hash 和内外 identity 检查,再调用既有 installer。运行包保留技能、文档、license notices 和合格 Chat bundle;明确的--source路径仍可用。runtime-snapshots publish现在将 revision-addressed 资产放入同一 dedicated release,比较当前通道 tag 与候选 revision,只向前推进,再替换通道 metadata。transferText则只投影已收到的观察,不为未知总量伪造百分比。
最终 head 的聚焦 pytest 133 PASS / 3 平台条件 skip。完整运行包为 18,271,109 bytes;临时环境完成 clean install、upgrade、doctor、skills、license notices、每个 manifest Chat asset 的实际 HTTP hash 读取,以及禁止 npm/npx 的安装和注入激活失败后的恢复。另一个独立 installed-CLI 场景实际执行后台 update apply、断开后重连同一 operation、单次 HTTP/hash cache 下载和最终 revision/channel/hash/doctor 回读。所有安装均在一次性目录中。
对主干的风险
最终 head 没有阻塞发现。 审阅中保留了一个真实反例:旧 head 5a1a35dec42431785b1287e9efad4bcbdf07c44e 在 B 已合格后重跑 A,会把 main feed 覆盖回 A;consumer 的降级保护随即阻止已安装 B 的正常升级。新增提交已加入单向 tag 推进。同一份执行实际发布 shell、替换 GitHub provider 的固定反例:旧 head 1 FAIL / 1 PASS,最终 head 2 PASS。这验证了本地控制流,不声称已执行真实 GitHub 发布。
同样保留了通道缺陷的基线对照:相同三例在 base 为 2 FAIL / 1 PASS(main/pin 被默认 stable 取代),最终 head 3 PASS。安全和恢复用例覆盖错误 hash、Range/ETag 变化、未知 HTTP 长度、TLS 降级拒绝、危险 archive entry、旧/分叉/未知 target、Windows 原 console guard 和不完整激活;没有放宽阈值或恢复旧注入语义来使测试通过。
最终标准 premerge、仓库声明的 Ruff 范围和 configured mypy 均通过:6 direct、10 catalog、8 risk、1 boundary,0 manual hold;mypy 为 17 个配置文件。额外把 Ruff 扩大到未列入该 lint lane 的 CLI/bootstrap 文件后,仍有 1 条 E731 和 2 条 E402 style advisory,已保留为非必检的清理建议,未声称全部新增文件 lint 清零。没有查询或等待 CI。最强的剩余验证是实际发布 API、原生平台 App 安装以及真实网络尾部表现,应由正常发布 owner 完成。
语义与边界
默认快照分发从 source build 改为 qualified runtime,已在 help、指南、兼容 source smokes 和本评审明确披露;它不是 default-off 功能。运行包 manifest 和 operation progress 是安装 provider 的版本化 IO 契约,不接管 Goal、权限、quota 或 Todo 决策。pip/pipx 继续使用原环境与 index/proxy,source checkout 不被就地改写,原生 updater 的签名与安装权限没有转交给进度 UI。正常升级的一次既有授权没有被新增确认轮次取代。
我的整体评价
最终 head APPROVE,在本地安装 owner 内交付了可用且可恢复的完整切片;审阅时发现的 feed 回退已用同一反例重新验证通过。在生产 Chat bundle 的桌面和 390px 窄屏检查了中英文、长标题、未知总量、停滞和失败恢复:新增进度行可读,详情仍在高级选项,未挤出主要操作。审查对象是打开更新面板后的默认进度视图;高级选项本次未改动,没有为新进度行展开这些控制。界面使用 synthetic native IPC,不证明真实 App 安装或网络速度;画面拍于 5a1a35d,最终 head 的界面源文件未变,35 个交付资源的 hash 完全一致。额外 lint 建议和未运行的平台/发布验证已明确保留。本结论不替代真实发布资格、当前 merge readiness 或维护者合并授权。
English verdict: APPROVE at e17ca86: the isolated packaged CLI update/reconnect/readback path passes, and the same stale-workflow counterexample now preserves the newer qualified main feed. Native platform installs, live publication and regional performance remain release qualifications.
Goal And Delivered Outcome
Upgrading a snapshot currently downloads the whole source tree and can build Chat locally, while JSON callers lose progress and a reconnect can start another attempt. The new default uses a qualified, hash-pinned runtime, preserves the installation's owner/channel, and lets callers observe one durable operation through completion.
loopx-project/loopx:mainata651a824996cce29cc69a5e7e4437b241eef963d.Author Declaration
Implemented against
Existing installation ownership, Chat bundle verification, candidate activation and independent migration contracts remain the owners. The updated guide is
docs/guides/installing-loopx.mdat this head.self_update.py,update_source.pyupdate_source.pyupdate_operation.py,support_control_update_progress.pyruntime_artifact.py, existing Chat contractupdate_artifact.py,install-local.shupdate_artifact.pyScope And Continuation
--sourcepreserves the explicit source/local-build route; missing prebuilt metadata stops with recovery guidance. Older/diverged/unverified targets need--allow-downgrade.upgrade --backgroundreturns an operation id;update statusreads it after disconnect. The worker owns the installation lock. Progress refreshes every five seconds and identifies ten seconds without new progress, without fabricating bytes or a percentage.Same-head local packaging after #7651: 18,271,109 bytes for the runtime versus 96,005,151 bytes for a full
git archive --format tar.gz HEAD. These are local archive measurements, not remote Content-Length or a download SLA. Optional resources are 65,482,372 bytes.Release rollout remains explicit: publish/qualify the stable runtime before deploying the new public bootstrap. Pages checks this dependency and retains the previous deployment if it is missing. CI retains revision-addressed artifacts in one dedicated runtime-main release, then advances the qualified channel pointer without moving backwards on an older workflow rerun; release CI reuses the existing frontend/wheel lane for stable. No release or regional infrastructure is published by this PR submission.
Validation
e17ca865a5f0dc1f36043b0e0218eeb55ee35ac8.scripts/qualify_runtime_artifact.py: isolated clean install + upgrade, installed doctor/skills, every manifest-listed Chat asset served by the real installed HTTP handler, npm/npx forbiddenexamples/loopx-update-smoke.py; inherited/pinned channels, package-owner parity, explicit source and Windows original-process refusalupdate applyat5a1a35dec4: qualified-metadata fixture plus real HTTP/hash cache/installer/doctor; later changes retain notices and refine publication metadata. No live release operationexamples/desktop-update-browser-smoke.mjs: recovery, channel ownership, bilingual desktop/narrow, bytes/unknown totals, observation never restarts installationcanary premerge --from-git-diffon this exact head: all 10 selected catalog canaries, all 8 selected risk-profile smokes, direct checks, public boundary and exact quality-receipt gate; no failures or manual holdsCoverage follows the existing bootstrap/install/activation owners. PostgreSQL authority semantics, Goal rules and backend routing are unchanged; this is not an authority-store promotion or performance qualification.
Frontend / Visual Evidence
5a1a35dec4; all delivered frontend asset bytes remain identical at the final head (verified against both archive inventories). Synthetic native IPC; no software installation or transfer speed is demonstrated by these screenshots.Unknown-total fixture (native fixture version is synthetic):
Type / Area / Boundary
中文:普通 stable 用户保留 PyPI 与原安装环境;snapshot 沿用渠道,main 默认预构建,源码构建显式选择。升级增加持续进度、断线回读、校验缓存与续传、可信镜像切换和激活失败恢复。已衔接 #7651,运行包约 18.3 MB;区域部署、全球速度和正式发布尚未认定完成。