Skip to content

feat(upgrade): ship qualified runtimes with resumable, observable updates - #7656

Merged
huangruiteng merged 8 commits into
mainfrom
codex/reliable-upgrade-experience
Oct 29, 2026
Merged

huangruiteng merged 8 commits into
mainfrom
codex/reliable-upgrade-experience

Conversation

@huangruiteng

@huangruiteng huangruiteng commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

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.

  • Basis: the accepted upgrade-experience request summarized here; no separate written specification or new RFC.
  • Intended base: loopx-project/loopx:main at a651a824996cce29cc69a5e7e4437b241eef963d.
  • Related: chore(docs): retire 77 MB of historical captures and define retention #7651 is already in that base. This PR consumes its cleaned tree and retention policy; it does not duplicate its removals or archive retired captures.

Author Declaration

  • Written by: OpenAI Codex (GPT-6 model family), with maintainer-directed scope.

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.md at this head.

Accepted criterion Disposition Owning path Decisive evidence
Stable defaults and source compatibility implemented self_update.py, update_source.py Pip/pipx parity; inherited channel/pin and explicit downgrade tests
Target chosen before snapshot side effects implemented update_source.py Same qualified metadata supplies version/revision; no mutable-branch version probe
Progress and reconnect implemented update_operation.py, support_control_update_progress.py Detached-worker/readback and no-second-install tests; final JSON stdout, NDJSON stderr
Prebuilt runtime with retained capabilities implemented runtime_artifact.py, existing Chat contract Exact archive clean install, upgrade, doctor, skills and installed Chat HTTP
Verified resumable transport and recovery implemented update_artifact.py, install-local.sh Real interrupted HTTP, Range/ETag/hash/cache/redirect cases and post-switch failure injection
Regional distribution support implemented in transport update_artifact.py Publisher/operator-trusted sources, measured stall/low-throughput switching, identical-byte verification
Actual regional hosting and global latency targets deferred to release/infrastructure operation existing release owners No regional deployment or cross-country latency claim

Scope And Continuation

  • Stable: regular installs remain PyPI wheels through their existing pip/pipx environment and configured index. LoopX does not replace these with an archive, invent another package resolver, or reset their proxy settings.
  • Snapshots: inherit stable/main/pinned SHA. Main means the latest qualified main artifact, which may lag the branch while CI runs. --source preserves the explicit source/local-build route; missing prebuilt metadata stops with recovery guidance. Older/diverged/unverified targets need --allow-downgrade.
  • Observation: upgrade --background returns an operation id; update status reads 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.
  • Contents: keep runtime, skills/references, text docs, LICENSE/NOTICE/historical MIT notices, the verified frontend and an audited inventory. Frontend development sources and named presentation media are optional resources. Node remains required for the TypeScript runtime; npm/local frontend compilation is absent from the normal prebuilt install.
  • Activation: reuse existing candidate validation, migration receipts/backups, host-material updates and service ownership. Failed activation restores owned executable links. It does not mechanically roll back migrated data. The original Windows console self-uninstall guard remains in the original process.
  • App: existing signed native updater actions remain; the existing panel adds byte/rate/elapsed/unknown/stalled observations. A CLI-only upgrade does not install an App.

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

  • Tested revision: e17ca865a5f0dc1f36043b0e0218eeb55ee35ac8.
  • Run state: local qualification finished and passed; remote CI is pending.
  • Input classes: synthetic, public_fixture.
Check kind Result Evidence / limitation
unit / integration passed Focused upgrade/transport/owner pytest: 133 passed, 3 platform-specific skips
real_entrypoint / real_backend passed 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 forbidden
regression_parity passed Injected failure after executable switch; old executable restored and installed doctor passed; data rollback is not claimed
static passed Dashboard TypeScript + Vite build; exact frontend source/revision contract; declared Ruff and configured mypy (17 configured Python files)
regression_parity passed examples/loopx-update-smoke.py; inherited/pinned channels, package-owner parity, explicit source and Windows original-process refusal
real_entrypoint / real_backend passed Additional isolated installed-CLI update apply at 5a1a35dec4: qualified-metadata fixture plus real HTTP/hash cache/installer/doctor; later changes retain notices and refine publication metadata. No live release operation
integration passed Exact final publication shell with a synthetic gh provider: forward/equal promotion, retain a newer channel, reject divergence/unavailable lineage; immutable files never clobbered
real_entrypoint passed Real detached CLI worker finishes/readbacks after observer exit; unsupported checkout is the safe negative case
manual / regression_parity passed examples/desktop-update-browser-smoke.mjs: recovery, channel ownership, bilingual desktop/narrow, bytes/unknown totals, observation never restarts installation
static passed Public-boundary scan of all 28 changed paths with disposable empty registry; semantic advisory, RFC bilingual and diff checks
manual passed Exact-scope change-quality receipt verified; one bounded repair pass, no blockers/warnings; regional measurement remains advisory
integration passed Final standard canary premerge --from-git-diff on 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 holds
integration not_run Native Windows/macOS App installation and cross-country networks are not exercised by the synthetic browser fixture; remote platform CI remains separate

Coverage 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

  • UI impact: changed, within the existing LoopX updates panel.
  • Before: base DesktopUpdate source, rebuilt with the same public fixture shell.
  • After: production Chat bundle captured at 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.
  • Desktop: 1486×880. Narrow: 390×844. The existing fixture includes long/truncated work text and mixed unavailable/retry states.
  • Source data: public fixture + synthetic updater state. No live Goal, conversation or operating context is used.
  • Attention/craft review: one compact transfer line adds information needed to judge waiting; actions stay grouped, advanced choices stay collapsed, and unknown totals remove the percentage bar. The panel fits the narrow viewport without horizontal overflow. The surrounding navigation and error actions retain their existing hierarchy.
  • No hosted preview is published; the committed browser smoke reproduces the packaged preview locally. Captures are attached, not committed, following chore(docs): retire 77 MB of historical captures and define retention #7651.
Before After
Before: desktop upgrade panel After: desktop transfer observation
Before: narrow upgrade panel After: narrow transfer observation

Unknown-total fixture (native fixture version is synthetic):

Unknown total: bytes shown without a percentage

Type / Area / Boundary

  • Bug fix, feature, documentation and focused validation
  • Build/packaging/installer/CI, CLI integration and existing presentation
  • No private state, credentials, raw traces, internal links or local paths in the diff or public evidence
  • No benchmark work or new Goal/authority semantics
  • No paired RFC changed; bilingual diff check passed
  • Visual evidence attached; every commit is DCO signed
  • Shared-authority RFC fixture impact: N/A; no authority-store or routing change.
  • Merge/release: requires normal exact-head review, required CI and maintainer action; this submission does not merge or publish.

中文:普通 stable 用户保留 PyPI 与原安装环境;snapshot 沿用渠道,main 默认预构建,源码构建显式选择。升级增加持续进度、断线回读、校验缓存与续传、可信镜像切换和激活失败恢复。已衔接 #7651,运行包约 18.3 MB;区域部署、全球速度和正式发布尚未认定完成。

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 loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

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.

动机 / 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.

@huangruiteng
huangruiteng merged commit b280b94 into main Oct 29, 2026
15 of 16 checks passed
@huangruiteng
huangruiteng deleted the codex/reliable-upgrade-experience branch October 29, 2026 11:31
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.

2 participants