Skip to content

product(runtime-host): make Host handoff automatic when safe and actionable when blocked #4792

Description

@M4n5ter
English

Problem

Maka already has strong Runtime Host lifecycle primitives: compatibility and protocol negotiation, Host/Root/process fencing, authoritative activity snapshots, retirement admission, owner-scoped replacement transactions, and recovery. The user-facing handoff is still expressed through several mechanism-shaped paths such as restartable conflict, non-restartable conflict, startup recovery, managed-service repair, and remote operator action.

That makes one user problem — “an existing Host prevents this client from continuing” — harder to understand and risks a sequence of different dialogs or generic failures as the observed state changes. The user should not need to understand compatibilityEpoch, generation, lifecycle mode, deployment ownership, or whether a particular Host implements the current retirement operation before choosing a safe action.

PR #4781 improves one important branch: an incompatible local ephemeral Host with an OS-verified process identity can now offer an explicit Stop Host and Continue action. Remote Hosts, managed services, unknown lifecycle registrations, and unverifiable process identities correctly remain non-destructive. The remaining problem is to turn all of these branches into one coherent handoff decision and recovery experience without weakening their different authority boundaries.

This should not be solved by copying opencode v2's process-replacement semantics. Its current daemon reuses only an exact-version registration; otherwise it sends SIGTERM, escalates to SIGKILL, and starts a successor without consulting active sessions, connected clients, or durable work (daemon implementation). That produces a simpler UI because interruption is accepted as the default, not because the hard handoff cases have been resolved.

Desired outcome

Define one product-level Runtime Host handoff decision model that consumes authoritative lifecycle facts and yields a small, closed set of user outcomes. Desktop, TUI, and future clients should render those outcomes without independently deciding lifecycle policy.

For one handoff attempt:

  • continue automatically when the existing Host is compatible;
  • transition automatically only when an authorized replacement transaction can prove the exact Host is idle and close admission races;
  • show at most one blocking decision surface when work may be interrupted;
  • keep that surface current as the Host exits, becomes replaceable, changes identity, or enters recovery instead of opening consecutive dialogs;
  • state plainly when Maka lacks the authority or evidence to complete the handoff, and provide an actionable operator step rather than a generic startup failure;
  • preserve the safest action as the default whenever interruption or process termination is possible.

“One surface” means one semantic handoff journey per attempt, not necessarily identical visual components in Desktop and TUI.

Decision matrix

Observed condition Expected behavior
Existing Host is compatible Connect; no prompt.
Exact local/managed owner and a complete activity snapshot prove idle Run the fenced replacement transaction and continue; no prompt.
Active operations, other clients, or durable residencies remain and interruption authority exists Show the activity facts. Default to waiting/cancelling; offer Interrupt/Stop and Continue only as an explicit destructive choice.
Activity is unavailable, but an exact local ephemeral process lifetime is verified Use the #4781 path: never claim idle; allow explicit Stop Host and Continue, plus non-destructive alternatives.
Activity or process identity cannot be verified Do not infer idleness and do not terminate by PID. Offer wait, retry, diagnostics, or manual action as applicable.
A managed deployment can be repaired by the current operator Offer one repair/retry flow backed by the existing operator transaction; do not expose raw service-manager steps unless repair cannot proceed.
The Host is remote or owned by another operator Do not present a local stop/restart action. Explain where the Host must be updated or retired, provide copyable instructions/diagnostics, and allow retry or cancel.
The observed Host/owner identity changes during a decision Invalidate the prior decision and re-evaluate; never apply stale consent to a successor.
A transition is incomplete or recovery is required Resume the persisted transition when possible; otherwise show one truthful repair-required outcome with no implied automatic recovery.

Waiting must also be truthful. If recurring residency or a managed supervisor means the Host will not naturally exit, the product should not present passive waiting as though it will eventually converge.

Product contract

The handoff outcome supplied to a Surface must carry enough information to explain and act without reconstructing policy:

  • the exact observed Host and State Root identity;
  • whether activity is complete, active, idle, or unavailable;
  • lifecycle/deployment authority available to this caller;
  • whether safe replacement, explicit interruption, waiting, repair, or operator action is available;
  • the expected effect on active work and durable state;
  • transition progress and a typed terminal or recovery outcome;
  • bounded diagnostics suitable for display or copying.

The exact type names and UI layout are not prescribed here. The requirement is one authority-owned decision with thin Surface adapters.

Safety invariants

  • Never run two Runtime Host writers for one State Root.
  • Bind every mutation to the exact Root, Host Epoch, process lifetime, deployment owner, and supervisor identity available for that path.
  • Never silently interrupt active or unverifiable work.
  • Never terminate from a registration or PID alone.
  • Ordinary Client credentials must not acquire remote or managed-service operator authority.
  • Retirement must fence new admission before claiming an idle or prepared transition.
  • Durable intent and unresolved external effects must retain their documented recovery semantics.
  • Cancelling a handoff must leave the current Host and ownership state truthful and usable.
  • A failed replacement must recover the previous lifecycle where the persisted transaction proves that safe; otherwise it must stop at an explicit repair-required state.

Acceptance criteria

  • One shared handoff decision model covers compatible, idle replaceable, active, unknown-activity, managed, remote, owner-changed, and recovery-required states.
  • Desktop shows no more than one blocking handoff dialog for one attempt; state changes update or resolve that dialog instead of chaining another modal.
  • TUI and CLI consume the same decisions and safety defaults without duplicating replacement policy.
  • Compatible connection and provably idle authorized replacement complete without prompting.
  • Any action that may interrupt work or terminate an unverifiable Host requires explicit consent and is never the default action.
  • Remote/unmanaged cases provide actionable operator guidance and retry without claiming local replacement authority.
  • The fix(desktop): stop stale ephemeral Host at startup #4781 OS-process-identity path remains the only direct incompatible-ephemeral termination fallback and retains its final identity recheck.
  • Identity changes, concurrent startup, transition failure, cancellation, and recovery are covered by decision-model and Surface tests.
  • Released-version coverage includes at least one legacy Host that cannot report a complete activity snapshot.
  • English and Simplified Chinese copy explain consequences in user language rather than exposing internal lifecycle terminology as the decision.

Scope and related work

This issue owns the product-level handoff decision and cross-Surface presentation contract. It consumes rather than redefines the underlying work:

Useful opencode ideas remain versioned executable staging, persistent reconnect credentials, atomic instance registration, identity rechecks before signaling, and bounded waits. Exact product-version matching, unconditional TERM/KILL, last-writer-wins daemon election, and health without activity/identity are explicitly not the target semantics.

Non-goals

  • Zero-downtime transfer of arbitrary in-flight model or tool execution.
  • Parallel Runtime Host revisions writing one State Root.
  • Silent remote or managed-service mutation through ordinary Client authority.
  • Treating every product-version difference as protocol incompatibility.
  • Replacing the existing owner, retirement, deployment, or recovery transactions with Surface-owned logic.

AI assistance disclosure

Codex assisted with the opencode v2 comparison, current Maka lifecycle analysis, and issue drafting.

简体中文

现在的问题

Maka 在底层已经做了很多该做的保护:协议兼容性、Host/Root/进程身份校验、活动快照、安全退场、owner-scoped replacement transaction 和失败恢复都不是空白。现在欠缺的是把这些能力整理成一套用户能看懂的交接流程。

用户启动 Maka 时碰到旧 Host,真正关心的无非三件事:

  1. 能不能直接继续,不用我管;
  2. 继续会不会打断别处正在跑的工作;
  3. 如果当前 Maka 没权限处理,我到底要去哪里、做什么。

目前代码需要区分 restartable、non-restartable、startup recovery、managed service、remote operator 等路径。这些技术区别必须保留,但不应该原样变成好几套弹窗和前后不一致的按钮。一次启动过程中如果 Host 状态发生变化,也不该让用户连续处理多个看似不同、实质相同的提示。

PR #4781 已经补好了一个具体场景:遇到不兼容的本地 ephemeral Host,只要操作系统能够证明它仍是刚才观察到的那个进程,就可以让用户明确选择 停止 Host 并继续。对于远程 Host、托管服务、身份不明的进程和无法确认生命周期的旧注册,仍然不会贸然结束进程。这条 issue 不重复 #4781,而是要把它和其他交接路径收敛到同一套产品判断里。

opencode v2 的做法只能当作对照,不能直接照搬。它发现版本不同后,会尝试 SIGTERM,超时再 SIGKILL,随后拉起新 daemon;交接前不看其他客户端、活动 Session 或 durable work。界面之所以简单,是因为它默认接受“旧工作可能被打断”,并不是这些边界已经被解决了。

希望做到什么

Runtime Host 层或 lifecycle owner 给出一份完整、类型化的交接结论,Desktop、TUI 和 CLI 只负责把它呈现出来,不再各自猜测该重启、等待还是修复。

一次交接最多只有一个需要用户决定的界面。旧 Host 退出、变为空闲、身份改变或进入恢复状态时,原界面直接更新或自动结束,不要再弹第二个框。

具体体验应当遵循下面这套判断:

实际情况 用户应该看到什么
Host 本来就兼容 直接连接,不提示。
能确认是自己管理的 Host,并且完整活动快照证明它已空闲 后台安全替换,完成后继续启动,不打扰用户。
还有任务、其他客户端或长期驻留,但当前 owner 有权中断 把影响说清楚;默认等待或取消,只有用户主动选择时才中断并继续。
旧 Host 不会报告活动,但本地进程身份可以可靠确认 #4781:不假装它是空闲的,只在用户明确同意后停止,同时保留等待和取消。
活动和进程身份都无法确认 不猜、不按 PID 硬杀;按实际能力提供重试、复制诊断、等待或人工处理。
托管部署由当前 operator 管理且可以自动修复 给出一个 Repair/Retry 流程,由现有 operator transaction 执行,不让用户先去研究 systemd、LaunchAgent 或 Task Scheduler。
Host 在远端,或者属于另一个 operator 不显示虚假的本地“停止/重启”按钮;告诉用户应该在哪台机器处理,提供可复制命令或诊断,然后允许重试或退出。
用户确认后 Host/owner 已经换了 之前的确认立即作废,重新判断,绝不能把对旧进程的授权套到 successor 上。
上一次 replacement 没完成 能按持久化事务恢复就继续恢复;不能证明可恢复时,明确停在“需要修复”,不要装作稍后会自动好。

“等待”也必须讲真话。如果 Host 因为 scheduled task、Goal 或 supervisor 会长期驻留,就不能让用户误以为等一会儿自然会结束。此时应该明确需要 operator 操作,或者提供经过授权的中断选项。

交接结论至少要带什么

Surface 不应该重新拼装 lifecycle policy,因此上游给出的结论至少要包含:

  • 当前观察到的精确 Host 与 State Root 身份;
  • 活动信息是完整、空闲、繁忙还是根本拿不到;
  • 当前调用者到底拥有哪一种 lifecycle/deployment authority;
  • 可以安全替换、明确中断、等待、修复,还是只能找 operator;
  • 每个动作会不会影响正在运行的工作和 durable state;
  • 当前进度,以及成功、失败或需要恢复的类型化结果;
  • 可以直接展示或复制的有界诊断信息。

这里不预先规定 TypeScript 类型名,也不要求 Desktop 和 TUI 长得一样。重点是交接决策只能有一个 authority,Surface 保持薄。

不能为了省弹窗牺牲的底线

  • 一个 State Root 任何时候只能有一个 Runtime Host writer。
  • 所有 mutation 都必须尽可能绑定精确 Root、Host Epoch、进程实例、deployment owner 和 supervisor。
  • 活动状态不明时按“可能有工作”处理,不能静默中断。
  • registration 和 PID 不能单独作为结束进程的依据。
  • 普通 Client credential 不等于远程机器或托管服务的 operator 权限。
  • 声称“可以安全替换”前,必须先封住新的 admission race。
  • durable intent 与结果不明的外部 effect 继续遵守现有恢复契约。
  • 用户取消后,原 Host 和 ownership 状态必须仍然真实、可用。
  • replacement 失败时,只有现有持久化事务能证明安全才恢复旧 lifecycle;否则明确进入需要修复的状态。

验收标准

  • 一套共享决策覆盖:兼容、可安全替换、存在活动、活动未知、managed、remote、owner 改变、需要恢复。
  • Desktop 的一次交接最多出现一个阻塞式交接窗口;状态变化在原窗口内更新或直接收敛。
  • TUI/CLI 使用相同的结论与安全默认值,不复制 replacement policy。
  • 兼容 Host 和“已证明空闲且有 authority”的替换不弹窗。
  • 任何可能中断工作或结束不确定进程的操作都需要明确同意,而且不能作为默认按钮。
  • 远程或无管理权限的场景给出真正可执行的下一步,并支持 Retry,不冒充本地 replacement authority。
  • fix(desktop): stop stale ephemeral Host at startup #4781 的本地旧 Host 兜底仍以 OS process identity 为前提,并在真正发信号前做最后一次身份复核。
  • 测试覆盖并发启动、Host/owner 换人、取消、replacement 失败和恢复,不会重复弹窗或沿用过期授权。
  • 至少用一个无法提供完整活动快照的已发布旧版本做真实交接验证。
  • 英文和简体中文都直接说明“会发生什么、是否会打断工作、接下来怎么办”,不要把内部术语丢给用户自己理解。

和已有工作的分工

这条 issue 负责产品层的交接判断与跨 Surface 展示契约,不重新发明底层机制:

opencode 值得借鉴的是版本化 executable、可持续使用的重连凭据、原子 registration、发信号前复核身份和有界等待;不应借鉴精确产品版本相等、无条件 TERM/KILL、last-writer-wins daemon 选举,以及只返回布尔值的 health check。

不打算做的事

  • 承诺任意模型调用或工具执行都能零停机续跑;
  • 允许两个 Runtime Host revision 同时写一个 State Root;
  • 用普通 Client 权限静默修改 remote Host 或 managed service;
  • 把产品版本不同直接等同于协议不兼容;
  • 把 owner、retirement、deployment、recovery transaction 搬到 Desktop/TUI 里重写。

AI 辅助声明

Codex 协助对比了 opencode v2 的 daemon replacement,核对 Maka 当前 lifecycle 边界,并参与起草本 issue。

Activity

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions