Skip to content

feat(cli): reconcile externally replaced npm Runtime Hosts - #4069

Open
me2seeks wants to merge 6 commits into
apache:mainfrom
me2seeks:feat/cli-external-runtime-host-reconciliation
Open

feat(cli): reconcile externally replaced npm Runtime Hosts#4069
me2seeks wants to merge 6 commits into
apache:mainfrom
me2seeks:feat/cli-external-runtime-host-reconciliation

Conversation

@me2seeks

@me2seeksme2seeks commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • reuse the installed-update target activator as the single Ready/durable-settlement guard
  • let the exact durable source package retire its own compatible ephemeral Runtime Host
  • reconcile an externally replaced npm-global installation through the existing owner handoff transaction
  • retain a bounded idle-only fallback for releases that predate the source retirement helper
  • explicitly scope external reconciliation to the same durable npm-global installation owner

The installed npm slot is already changed in this flow, so this PR does not run npm, add an update scheduler, or introduce another owner, journal, or lock. Cross-owner transfer, remote Hosts, and managed service Hosts remain under their respective owner/operator paths.

Safety invariants

  • stage and verify the exact installed registry target before authority mutation
  • open the exact durable selected source from the same owner local package store; do not reconstruct historical evidence from the registry
  • reject a different durable source owner before registry resolution, staging, or Host observation
  • fence retirement by State Root, confirmed Host epoch, lifecycle mode, and the inherited owner-authority lease
  • preserve active work by default and commit ownership only after exact target Ready evidence
  • make the activator read the durable owner record as the sole release-versus-retire verdict; coordinator success, failure, and disconnect are only settlement triggers
  • retain the launch barrier and authority lease while durable record reads are uncertain
  • revalidate the npm slot before owner commit and reject external downgrades before staging or retirement

Validation

  • npm run build --prefix packages/cli
  • CLI full suite: 617/617
  • focused Runtime Host update/reconciliation suite: 42/42
  • release file and artifact policy suite: 15/15
  • scoped Biome and git diff --check

Refs #3243
Refs #3231

中文说明

本 PR 处理同一 durable npm-global installation owner 已被用户通过 npm 外部替换、但旧 Runtime Host 仍在运行的情况。不同 owner 的转移不属于本切片,会在解析 registry、stage 或观察 Host 前 typed 拒绝。

新 CLI 不直接假装兼容旧 Host,而是从持久化部署中启动旧版本自己的 retirement helper;旧版本负责按自己的协议安全退休旧 Host,新版本再通过共享 activator 启动并验证精确目标。父 coordinator 的成功、失败或断线都不再决定 target 的关闭与保留;activator 统一读取 durable owner record 后裁决。读取不确定时保留 barrier 与 authority lease,读到 exact committed 才 release,否则才 retire。

不在本 PR 范围内:跨 owner transfer、远程 Host、systemd 等托管 Host、npx 临时运行语义、自动检查周期与 TUI 文案。

@me2seeks

Copy link
Copy Markdown
ContributorAuthor

Post-implementation dual audit — GO

First-principles correctness

  • Authority stays singular: the existing owner record, handoff transaction, writer fence, and inherited lease remain the only durable coordination mechanism.
  • User intent stays explicit: this path runs only after the local restart choice; it does not turn external npm mutation into automatic Host interruption.
  • Compatibility is owned by the source: the exact durable old package speaks to and retires its own Host, including a different protocol/compatibility epoch.
  • Process identity is fenced twice: the coordinator re-observes under the authority transaction, and the source helper requires the exact State Root and confirmed Host epoch.
  • A Host that changes after confirmation is not retired. Active work rolls the handoff back; failures after retirement begins retain the handoff for recovery; target ownership commits only after exact Ready evidence and a final npm-slot check.
  • Managed service and remote Hosts remain under their operators. Downgrades fail before staging or retirement.

Simplification / deletion proof

  • Reused the feat(cli): coordinate npm-global Runtime Host updates #3925 target activator and removed the weaker direct TUI launch path.
  • Deleted the proposed old-version registry reacquisition: a durable selected deployment must open and validate its exact local package store; missing evidence fails closed.
  • Added no scheduler, journal, lock, owner record, forwarding API, or parallel recovery state.
  • The three participating processes are irreducible at an epoch boundary: current coordinator owns the transaction, exact source helper owns old-wire compatibility, exact target activator owns Ready/commit guarding. Only the existing owner record is durable.

Validation at 6eeb7eb7ba789c02da1b48a47b2c495dd4cbadea: CLI full suite 614/614, focused lifecycle suite 35/35, release policy suite 15/15, CLI build, scoped Biome, and diff-check all pass. Hosted checks are currently queued, so this verdict does not pre-claim CI success.

中文审计结论

结论:GO。实现仍只有一份 owner/handoff/lease authority。旧版本通过其自身 helper 兼容不同 epoch,新版本通过共享 activator 等待精确 Ready 后才提交;用户确认后的 Host 若已经换了 epoch,不会被误关闭。活跃工作会回滚,退休开始后的故障保留 handoff 供恢复。实现还删除了重新联网下载旧版本的路径,直接验证持久化的精确旧包;没有新增 scheduler、journal、lock 或第二份恢复状态。远程与托管 Host 仍由各自 operator 更新。

@me2seeks

Copy link
Copy Markdown
ContributorAuthor

Maintainer findings independently confirmed and fixed at 4b3368e46.

  1. Ambiguous durable settlement: removed the parent committed / abort verdict. Every parent outcome now sends only one settlement trigger. The activator reads the durable owner record and is the sole release-versus-retire authority. An exact committed record releases the launch barrier; a readable non-committed record retires the guarded target; read failure retains the barrier and inherited authority lease and retries. Coordinator disconnect uses the same path. This closes both commit-before-ack retirement and committed-on-disconnect barrier leaks.
  2. Source owner contract: this slice is now explicitly same-owner npm-global replacement. A durable owner / handoff from that differs from the resolved npm-global installation returns typed source_owner_mismatch before registry resolution, staging, package-store lookup, or Host observation. Cross-owner transfer remains with its owner-specific adapter.

Regression coverage includes an injected authority read failure followed by exact committed ownership (release, never retire), readable uncommitted handoff retirement, uncertain coordinator commit settlement, and Desktop-to-CLI owner mismatch with no staging or Host observation.

Validation: CLI build; focused 42/42; full CLI 617/617; release policy 15/15; scoped Biome and diff-check.

中文修复说明

两条结论均确认属实并已修复。父进程不再发送 committed/abort 裁决,只触发结算;activator 统一读取 durable record 决定 release 或 retire,读取不确定时继续持有 barrier/lease。外部 npm reconciliation 明确只支持同一 durable npm-global owner;跨 owner 在任何 registry、stage、store 或 Host 操作前返回 typed 拒绝。

@me2seeks
me2seeksforce-pushed the feat/cli-external-runtime-host-reconciliation branch from 4b3368e to e288ae4CompareAugust 30, 2026 08:40
@me2seeks

Copy link
Copy Markdown
ContributorAuthor

Maintainer P1 independently confirmed and fixed at e288ae45d.

The authority lease previously supervised Candidate lifetime only; it did not fence Runtime Host admission. A Ready target could therefore accept ordinary TUI/Desktop work before the durable owner commit, and a later finalization failure could retire that target underneath newly admitted work.

The fix makes pre-commit admission part of the existing launch-owner guard:

  • connectOrSpawn binds the supervised Candidate to the activator's opaque, per-run Client identity.
  • While the inherited launch-owner lease is held, the Host handshake admits only that activator identity. Other local, Desktop, WebSocket, and peer Clients receive draining and cannot acquire work.
  • The existing release IPC opens ordinary admission only after the activator has read the exact durable committed owner record.
  • Coordinator loss/non-commit still retires the Candidate, but it can no longer interrupt ordinary work because none was admitted. Activator loss remains fail-closed through the existing guard.

This adds no protocol field or compatibility epoch and no second durable state. Remote and service Hosts are unaffected because the gate exists only on the inherited local launch-owner path.

Real-process regressions now prove both sides of the boundary: a second ordinary Client is rejected during the finalization window and the Candidate exits safely when its launch owner is lost; after release, an ordinary Client connects and executes host.diagnostics.query against that exact PID.

After rebasing onto current main, validation passed: dependency-ordered builds, Runtime Host 1414 pass / 9 skip, CLI 649/649, focused 34/34 plus the two launch-owner integration cases, scoped Biome, diff-check, and a clean current-main merge tree. Final simplification and first-principles audits are GO; no additional simplify finding.

中文修复说明

已确认该 P1。旧实现只用 launch-owner lease 约束候选进程寿命,没有限制 Host 接入,所以 durable commit 前普通 TUI/Desktop 可能取得工作,随后 finalization 失败会把正在工作的 target 直接退休。

现在候选在 lease 持有期间只允许 activator 的一次性 Client 身份完成握手;其他本地、Desktop、WebSocket 或 peer Client 都得到 draining,无法取得工作。activator 读取到精确 durable committed owner 后,复用既有 release IPC 开放普通接入;未提交或发起者丢失时仍 fail closed。该修复没有新增协议字段、epoch 或第二份持久化状态,也不影响 remote/service Host。

真实进程测试覆盖了 commit 前第二客户端被拒绝且 finalization 失败可安全关闭,以及 commit/release 后普通客户端能够连接并对精确 PID 执行诊断。rebase 后完整 Runtime Host、CLI、构建、Biome、diff 与 merge-tree 门禁均通过;双重审查结论 GO。

@me2seeks
me2seeksforce-pushed the feat/cli-external-runtime-host-reconciliation branch from e288ae4 to 631a0bfCompareAugust 30, 2026 12:25
@me2seeks

Copy link
Copy Markdown
ContributorAuthor

CI failure independently diagnosed and repaired at 631a0bf47.

The failed test job did not fail Runtime Host behavior. Its Desktop renderer-architecture check compared a merge against base 8c491e64b and found the newly merged use-session-collaboration-dialog.ts absent from that base's ledger. Current main contains the dedicated ledger repair in #4255.

I rebased onto current main and also reconciled the overlapping #4114 launch-owner work without conflating authorities:

  • closeOnLauncherExit retains its process-lifetime-only Desktop behavior and does not create an admission gate.
  • Only a Candidate carrying inheritableAuthorityLeaseFd requires the activator Client identity and receives the pre-commit admission gate.
  • Both paths reuse the same launch-owner guard and release IPC; no parallel guard or durable state was added.

Exact-head validation: renderer architecture 62/62; Runtime Host 1434 pass / 12 skip; CLI 659/659; dependency-ordered builds; focused launcher-loss and updater-admission integrations 3/3; focused reconciliation set 34/34; scoped Biome, diff-check, and current-main merge tree all pass. Final simplification and first-principles audits remain GO.

中文说明

失败的是 Desktop renderer architecture 清单,不是 Runtime Host 行为。CI 使用的旧 base 尚未包含新 collaboration 文件对应的 ledger;最新 main 已由 #4255 修复。现已 rebase 最新 main,并正确合并 #4114:普通 Desktop closeOnLauncherExit 只约束进程寿命,不启用 admission gate;只有携带 updater authority lease 的候选才要求 activator Client 身份并限制 commit 前准入。完整门禁和双重审查均通过。

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for consolidating installed-update target release/retirement behind the durable owner record and the shared activator. I reviewed exact head 631a0bf4727d8aeb006c958f3fecdba9cb85bef5; the exact target, owner, epoch, active-work, and pre-commit admission guards are strong. I left one P1 recovery-path suggestion inline. The required test is green, while the installed-CLI Windows validation currently fails and should be rerun or resolved after revision. Please do push back if there is an external supervisor/deadline around this child that I missed.

中文摘要

谢谢把更新目标的 release/retire 决策收敛到 durable owner record。当前主要问题是 record 永久损坏或持续 I/O 失败时,activator 会无限重试,父更新流程、barrier 与 authority lease 都无法退出;建议区分永久/暂时错误并提供有界、可观察的恢复结果。若外层已有我未追到的 supervisor/deadline,欢迎 push back。

AI-assisted review disclosure: Codex ran an independent analysis lane; Astro-Han independently verified the exact head, production path, and severity, and owns this review.

Comment threadpackages/cli/src/runtime-host-installed-update-activator.ts Outdated
@me2seeks
me2seeksforce-pushed the feat/cli-external-runtime-host-reconciliation branch from 631a0bf to f323d1dCompareAugust 30, 2026 15:34

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for tightening the durable-owner recovery and distinguishing permanent record corruption from transient I/O. One part of the earlier bounded-recovery concern still remains at the in-flight read boundary. This is a suggestion from an outside review, so please feel free to push back if the authority reader has a stronger completion guarantee.

AI-assisted review disclosure: Codex ran independent runtime-owner and recovery/test analysis lanes; Astro-Han is the contributor of record for this review.

let record: Awaited<ReturnType<typeof input.readRecord>>;
for (;;) {
try {
record = await input.readRecord(input.expectedRootId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] (category ② — reasonable recovery path)

Thanks for adding the 10-second settlement deadline. The deadline is only checked after readRecord() rejects, so a single authority read that hangs indefinitely never reaches the timeout branch. In the normal external npm replacement recovery path, the old Host is already retired and the candidate remains behind the launch barrier while the parent awaits the activator’s closed promise without its own bound, leaving the Runtime Host unavailable until the hidden process is terminated manually. Could the parent own a hard activator-settlement deadline and terminate/wait for the child so its lease and launch-owner guard converge? A regression with readRecord: () => new Promise(() => {}) would cover the remaining boundary; a child-only Promise.race may still leave an uncancelled fs request keeping the process alive. Please push back if every production authority read is itself guaranteed to settle within this deadline.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XLOver 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@me2seeks@Astro-Han