运行时架构 · 所有者发起 · macOS v1

对话内自主更新

用户在 OpenProgram 中要求 Agent 修改 OpenProgram。Agent 在隔离 worktree 中完成修改、测试和提交;独立于 worker 的控制进程构建并安装完整 App;新 worker 启动后自动创建一次验收任务,依据原始目标检查结果。系统检查或目标验收失败时恢复旧 App。

核心约束:正在被替换的 worker 不能负责完成替换,重启前持久化交接与其他任务的暂停检查点,重启后创建带来源标记的新验收任务。验证完成后按检查点继续兼容任务,并为发起会话创建后续任务。
Platform
macOS 默认 App
/Applications/OpenProgram.app · :18100 · ~/.openprogram
Candidate
本地 Git commit
独立 worktree,禁止 dirty candidate
Activation
完整 App bundle
不在首版维护 wheel/asar 增量分支
Concurrency
单一活动更新
安装锁 + update id + 幂等恢复

Question

怎样让 owner 在聊天中授权 Agent 修改框架,并在修改完成后自动构建、停止旧实例、替换 App、重启、触发验收 Agent 和处理失败?

Audience

Runtime、Desktop、Job/DAG、权限和本地发布维护者。

Exclusions

Windows/Linux、远端 stable Release、后台无授权改码、多机部署、无限自动修复循环。

01

当前实现与缺口

自更新控制协议、重启后验收、受控修正及状态界面已进入组合候选。完整组合检查与默认 App 的成功、激活前失败、激活后回退验收仍是发布条件;代码存在不等于已安装可用。

复用模块
原有行为
自更新实现职责
Agent 修改与 worktree
owner 会话可使用文件、shell 和 worktree 工具;写入受 permission、sandbox 和 read-before-edit 约束。
新增专用 self_update_prepare,只接受已提交 candidate,不允许通用 shell 隐式获得部署授权。
完整 App 构建
package-and-install-app.sh 已构建 runtime、Electron App 并运行 packaged smoke。
把构建与激活交给 one-shot supervisor;构建阶段不停止当前 worker。
事务安装与恢复
install-app.sh 已验证候选、保留 previous.app、失败恢复旧 App,并恢复原来的 App/worker/launchd 状态。
允许延迟删除 previous.app,直到重启后的系统检查和 Agent 目标验收都完成。
源码升级探测
openprogram upgrade 已有 cold start、doctor、restart 和 expected revision 校验。
抽取公共 probe;候选来源改为本地不可变 commit,而不是 origin/main。
Session / Job 持久化
会话历史持久化;重启时普通 running turn 变为 interrupted,running Job 变为 worker_lost。
不能复用 running Job。必须在新 worker 中依据 update record 创建新的验证 Job。
Scheduler / JobRunner
Scheduler 面向 once、recurring、monitor 用户任务,按时间触发;JobRunner 提供持久化 Job、并发限制和资源治理。
自主更新不创建 Scheduler task;startup recovery 认领待验收记录,再通过 JobRunner 提交一次性验证 Job。
Updater detection
openprogram/updater/ 识别 managed release、source checkout 和 unsupported 安装方式,并禁止 worker startup 直接应用更新。
复用安装方式识别;startup hook 只检查、认领和调度验收,App 替换仍由外部 supervisor 执行。
跨重启交接
self_update/ 保存独立请求、状态、控制程序和验收证据,不复用 upgrade-state。
持久化 turn-end release、startup claim、稳定验证 Job、原会话结果投递及 Desktop reopen;真实重启验收独立记录。
02

对话行为

用户通过普通对话提出工作请求,消息和工具结果描述该工作。安装状态由更新子系统维护,不由独立的对话组件维护。重新打开 App 会恢复对话,不显示更新历史面板、更新恢复提示或临时验证对话框。

03

目标架构

控制进程与 candidate runtime 分离。当前 worker 只负责准备和交接;supervisor 决定激活与恢复;新 worker 只负责运行固定系统检查和新的验收 Agent。

OpenProgram 对话内自主更新架构 Owner 对话驱动 Agent 在隔离 worktree 产生候选提交。一个由 launchd 托管的 supervisor 构建和探测完整 App,等待 turn-end handoff 后停止旧实例并安装候选。新 worker 运行系统验证并创建新的验收 Agent;通过后提交安装,失败则 supervisor 恢复旧 App。 Owner chat目标 + 更新授权当前 session / agent Isolated worktree修改 · 测试 · review不可变候选 SHA Update record会话 · 轮次 · 条件atomic state + lock launchd supervisor单次运行 · worker 外部可信的当前控制器 Build + cold probecomplete App bundle打包冒烟测试 · doctor Turn-end handoff最终响应已持久化runtime quiescent 激活事务stop App / workerretain previous.app New workerexpected identitystartup recovery 系统验证版本 · 健康状态 · doctor单次分派认领 Verification Agentsame agent profile新的 self_update_verify 轮次 commit install rollback + report passfail / timeout
1 · 准备
Agent 在隔离 worktree 修改、测试、复核并形成 candidate commit。
2 · 交接
owner 批准;update record 记录 session、turn、candidate 和验收条件;supervisor 由 launchd 托管。
3 · 构建
旧 worker 仍运行时构建完整 App,并完成 packaged smoke 与 cold probe。
4 · 激活
当前回复已持久化且其他任务已暂停或结束后,停止 App/worker,保留 previous.app,激活候选。
5 · 验收
新 worker 校验 revision/doctor,再创建新的 self_update_verify Agent turn 检查原始目标。
6 · 终态
通过后删除 previous.app;失败或超时则恢复旧 App,并把证据写回原会话。

候选构建允许执行 candidate code,但必须在 sandbox 中拒绝写入默认状态目录、安装目录和 update record。激活与恢复只由当前已安装版本生成的 supervisor 执行。

04

Workflow 与模型职责

准备阶段复用普通聊天 Agent 的计划、实现和评审过程;提交后由 self_update/ 的持久化控制逻辑转换状态,不新增通用 Workflow 引擎。验收、诊断、修正使用冻结的阶段 prompt 与结构化结果;模型输出不能直接停止进程、安装、回退或改变事务状态。

阶段与触发
LLM 工作
确定性系统动作
持久化输出
PLAN
owner 提出目标
澄清目标;列出修改范围、风险和可观测验收条件。
记录会话合同;prepare 时冻结 checkout、base SHA、authority、预算和允许路径。
会话记录;提交后为
request.json 的 goal/assertions
IMPLEMENT
计划合同已接受
在隔离 worktree 修改源码和测试;说明未处理项。
限制文件、命令、网络和凭据;记录 diff、命令和退出码。
worktree
change manifest
REVIEW
实现完成
检查正确性、安全、回退兼容性、schema、测试覆盖和无关改动。
执行计划约定的检查并保存证据;prepare 重新核对 clean Git 和 HEAD,不把 Git 检查当作评审证明。
会话/评审记录
candidate SHA
AUTHORIZE
candidate 可构建
生成面向用户的变更、验证、中断和恢复说明,不代替用户授权。
强制 owner 审批后冻结 candidate/scope/iteration envelope 及阶段配置;启动 supervisor。
request.json
各阶段 config 与迭代授权
STAGE / ACTIVATE
授权与 gate 通过
不调用 LLM。
构建完整 App、cold probe、maintenance、turn-end handoff、事务替换和重启。
artifact manifest
事务日志
VERIFY
新 worker 系统 gate 通过
调用冻结检查 ID;逐条给出 pass/fail/inconclusive 和证据。UI 计划要求真实默认 App。
startup recovery 原子认领;JobRunner 创建确定 ID 的受限 Job;监督超时和 lease。
verifier-result-{attempt}.json
签名 receipt 包含结构化结果
DIAGNOSE
目标验收未通过
区分实现、测试、环境和目标问题;提出下一轮最小修改。
先恢复旧 App;检查迭代预算、授权范围和停止条件。
diagnosis-result-{attempt}.json
next-attempt plan
FINALIZE
成功或停止
报告源码、artifact、live revision、验收和回退的实际状态。
成功时提交安装;失败时保留证据和 candidate;更新原会话。
terminal state
session result
Restart dispatch

不使用普通 Scheduler task

Scheduler 的时间触发和用户任务 authority 不适合作为更新控制协议。新 worker 在 JobRunner、session store 和 owner-auth 可用后执行 recover_pending_updates()。

Exactly once

稳定 Job ID + 原子 claim

Job ID 使用 self-update:{update_id}:verify:{attempt}。claim 保存 owner、lease、attempt 和 job_id;重复启动发现已有 Job 或终态时不再提交。

Prompt contract

阶段上下文与工具隔离

实现 Agent 不能绕过安装审批;验收 Agent 没有源码写工具;诊断与修正 Agent 只读并提出结构化结果。verifier_config.py、diagnosis.py、source_repair.py 保存各阶段配置。新计划共享 goal、assertions、verification plan、迭代策略和期限,继承上下文不扩大工具权限。

启动顺序

  • 加载并迁移 self-update state schema,损坏时 fail closed。
  • 完成 Job orphan reconciliation。
  • 启动 JobRunner,确认 admission/resource governance 可用。
  • 完成 revision、owner-auth、Web/WS 和 doctor 系统 gate。
  • CAS 认领 attempt,提交一次验证 Job,然后启动普通 Scheduler。

普通 Scheduler 启动失败不阻止自主更新验收;JobRunner 或系统 gate 失败则请求 supervisor 回退。

受控迭代策略

  • 默认 approve_each_activation:失败后回退并诊断;新 candidate 激活前重新授权。
  • 可选 bounded_auto:owner 明确授权最多 3 次、总时限、允许路径和测试集合。
  • 新增依赖、修改权限/审批、安装器/supervisor、扩大路径或改变目标时立即暂停并重新授权。
  • 每轮使用新 SHA、attempt 和 Job ID,不覆盖上一轮证据。
  • 连续相同失败、rollback 失败、环境型 inconclusive 或预算耗尽后停止。
04a

失败后的执行流程

恢复服务、诊断原因、修改源码和批准激活是不同阶段。诊断结果不直接授权安装。

阶段
执行与持久化
限制与后续动作
恢复完成后自动诊断
新请求在 owner 授权时同时冻结诊断模型、profile、只读工具和输出格式。可信控制器确认 ROLLED_BACK 及旧版本系统检查通过后,在解除维护前发布本 attempt 的诊断请求。worker 使用稳定 Job ID self-update:{update_id}:diagnose:{attempt} 创建新任务。
复用 JobRunner,不创建 Scheduler task;不延续原 turn,不移动原会话 head。仅允许 read、glob、grep、list,不允许源码写入、shell、MCP、安装或再次发起更新。
固定期限与恢复
诊断请求绑定原 update、candidate、attempt、失败证据和回退系统证据;期限最多为回退终态后的五分钟,有 bounded_auto 总期限时取更早值。重复 startup 复用原请求与 Job ID,已有终态或 orphan Job 不重跑。
worker 内的有界等待负责收集结果和取消超时任务,不依赖普通 Scheduler。新更新取代待诊断更新、owner 取消、模型缺失、期限届满或证据变化均停止诊断;不改变原 ROLLED_BACK 结论,也不重新限制已恢复的服务。
结构化诊断
记录实现、测试、环境、目标不清或证据不足的分类、原因、原证据引用及修正建议。结果附加到原会话的独立 Job,并保存为本 attempt 的诊断记录。
模型的分类和建议是待验证内容,不是测试证据或新的 owner 授权。环境问题、目标不清及证据不足不得自动修改或激活。
生成并应用源码修正
仅当新请求冻结修正配置、诊断属于 implementation 或 test、旧版本已恢复且预算有效时,创建稳定 ID 的独立 repair Job。模型使用原模型、profile 和只读工具,返回结构化文件修改;控制器在新建的隔离 worktree 校验原文本并应用修改,不让模型执行 shell 或 Git。
默认限制到原 changed_paths;bounded_auto 使用原 allowed_paths。权限、审批、更新控制器、安装脚本、依赖声明及 Git 配置等保护文件均不自动修改。路径越界、符号链接、重复替换或源文本不匹配均停止;不修改用户原 worktree。
新 candidate 与测试证据
控制器创建本地新 commit,记录与原 base 的完整 diff,再在无网络、无 App 写权限的沙箱执行冻结的 required_tests;记录命令、candidate SHA、退出码和有界日志。测试后再次检查 HEAD、Git 绑定及 tracked 文件;测试修改源码则证据无效。
修正 Job 和测试共享回退后的十分钟期限,原总期限更早时取更早值。新更新、owner 取消或超时终止该阶段及其测试进程组。终态、失败和部分写入保留证据与隔离 worktree;重启不重复编辑、提交或测试,不延长期限。
再次授权与激活
候选记录绑定原请求、诊断、修正 Job、完整 diff 和测试结果;控制器调用迭代策略重新核对次数、期限、路径、依赖和连续失败。approve_each_activation 等待 owner 对新 SHA 授权;bounded_auto 仅在原 envelope 内自动提交下一次更新。
候选记录不是安装授权。缺少必需测试或测试失败不得激活;环境、目标或 inconclusive 诊断不自动修正。下一 candidate 仍经过完整构建、系统 gate、新验收 Job 和失败回退。源码修正与测试阶段的完成不代表后续激活流程已实现。

未包含冻结诊断配置的旧更新保持原有恢复行为,不从当前可变 profile 补造配置。诊断请求、claim 和结果位于原更新目录,唯一待处理指针位于 self-updates 根目录;新更新在同一存储锁下撤销旧待处理指针。执行入口再次核对持久化 Job、当前 worker claim、冻结输入与未过期期限,拒绝伪造 source 或恢复旧队列时的输入漂移。owner 通过现有 Job 取消入口停止诊断,不使用只对激活前更新有效的 self_update_cancel。诊断记录不得覆盖原 verifier verdict、签名验收结果或回退结论。

修正阶段同样要求原请求中的独立冻结配置;旧请求不自动获得源码写入能力。默认首次授权说明包括“回退后在隔离 worktree 修正和执行所列测试”,不包括再次安装;只有显式选择 bounded_auto 且批准下述完整迭代授权的新请求,才允许范围内再次安装。修正输出最多 32 处文本编辑、总计 1 MiB;每处 old_text 必须在目标普通 UTF-8 文件中唯一匹配,null old_text 仅用于创建不存在的文件。new_text 为 null 时要求 old_text 等于整个文件才允许删除。修正 Job 使用 wait=true、不产生普通后续 Agent、不移动原会话 head。模型 Job 完成后,owner 在原会话调用 self_update_repair_cancel(update_id),取消仍在执行的候选测试。新 SHA、完整 diff、全部已配置测试和测试后检查通过才记录 candidate_ready;未配置 required_tests 时记录 awaiting_tests。任何已配置测试缺失、失败或测试后检查失败均记录 failed;主动取消与期限届满分别记录 cancelled、expired。这些状态均不代表已安装。

04b

修正版本的授权与更新

每次更新保留独立请求和证据;同一迭代序列共用原始目标、授权范围、次数和总期限。

约定
控制器行为
拒绝与恢复
原始授权与兼容
首次请求冻结 root update、owner、session、goal、assertions、原 base、model/profile、模式、路径、测试、max_attempts 和可选总 deadline。bounded_auto 必须有完整的总期限、路径和非空测试集合,审批说明明确包含限次再次安装;授权记录的摘要写入不可变请求。
旧请求没有该授权记录时,即使已有 policy.mode=bounded_auto,也不自动获得再次安装权限。当前 profile、模型建议或 candidate_ready 状态均不能补造授权。改变目标、扩大路径或增加依赖需要新的 owner 请求,不修改已有序列。
重新核验证据
候选来自本 attempt 的已完成修正 Job;请求、诊断、Job 输出、生成 intent、candidate manifest 和测试日志摘要一致。重新读取真实 Git:专用 worktree 仍归原 session,HEAD 等于待批准 SHA,工作区干净,SHA 是上一 candidate 的新后继,与原 base 的完整 diff 满足范围及保护路径检查。审批展示前和批准返回后、预留 child 之前均检查;supervisor 构建及激活前继续执行已有漂移检查。
必须存在非空 required_tests,且每项命令、SHA、退出码和日志匹配;不接受模型声明或只检查 manifest 的成功字段。缺失、漂移、失败或超出范围时停止提交并保留原因,不以重新批准绕过。此前未配置测试的 awaiting_tests 候选须由 owner 发起带明确测试的新请求。
两种批准方式
默认通过原会话中的 self_update_retry(update_id, candidate_sha) 对这一精确 SHA 强制单次审批,显示完整变化、测试和剩余预算;不能保存为长期 allow 规则。bounded_auto 由控制器按原授权和真实证据自动提交,无需模型再次调用工具。
owner 拒绝或审批超时不创建后继请求。两种方式都核对当前 owner、原 goal/assertions、回退成功、剩余次数及总期限。只有实现或测试类失败可以继续;环境、目标不清、inconclusive、回退未证实或连续相同失败均停止。失败指纹取可信失败类别和失败 assertion/check 标识,不取模型解释、时间戳、SHA 或随机 ID。
次数与不可变后继
第一次更新计为 attempt=1;后继使用新的 update_id、SHA、attempt 和各阶段 Job ID,并记录 root/parent 关系。原 goal、assertions、base、owner、model/profile、policy 不变;只重新绑定本次身份及输出 schema。最多 3 次包含首次更新。
在同一个存储锁下预留唯一后继和下一 attempt;一次预留消耗一次预算,提交失败、取消、重启均不退还或重置次数。同一 parent 不产生第二个后继。每次构建/验收保留原单次超时,且不得超过原总 deadline;达到期限后只允许必要的安全回退及恢复,不再修正或安装。
中断、并发与完整更新
candidate 结果之后、移除修正 pending 指针之前,持久化下一阶段请求。startup 从同一请求恢复等待审批或提交;先保存固定 child ID/预算预留,再发布 child 请求和 active 归属,再调用既有外部 supervisor。重复恢复只能完成这一提交,不能重新编辑、测试或扩大授权。
其他 active update 或 maintenance 阻止新的提交;新 owner 更新撤销旧序列待处理项。默认审批请求在批准 turn 持久化后 release;自动提交以已落盘授权、结果和提交记录作为 release 条件,不伪造聊天 turn。后继仍执行完整打包、cold probe、系统 gate、新 verifier、commit 或回退;提交失败保留可查记录,不跳过阶段。
取消与可见状态
self_update_iteration_cancel(update_id) 在原 owner 会话持久化停止标记,停止整个序列尚未提交的审批或自动提交,并取消该序列仍运行的诊断、修正 Job 和候选测试。已创建但尚未激活的 child 使用现有取消路径;不影响其他序列或普通任务。状态显示 root、parent、当前 child、attempt/max_attempts、总期限及停止原因。
已进入激活/验收的事务不能通过删除请求强行中断;禁止后续 attempt,并由现有事务流程完成验证或安全回退。待提交阶段区分 awaiting_approval、submitting、submitted、stopped;submitted 只表示请求已交给 supervisor,不能显示为安装成功。成功仍以 child 的 SUCCEEDED 和验收证据为准。

该阶段复用 SelfUpdateStore 的进程锁、JobRunner、现有强制审批、可信 supervisor 和事务安装器,不创建第二套 Scheduler 或通用 workflow 执行器。self_update/iteration.py 保持无副作用策略判断;新的提交协调模块负责证据核验、预算预留、状态持久化和恢复。源码中的职责划分不能代替实际的重启、失败回退及多次尝试验收。

05

设计结论

首版只增加完成目标所必需的边界,不引入第二套 Job 系统、插件系统或更新依赖。

Controller

更新控制必须在 worker 外

使用 macOS launchctl submit 启动 one-shot supervisor。它不属于 worker 进程树,worker/App 停止后仍可完成激活、验证超时和恢复。

Candidate

只部署不可变 commit

candidate 必须来自专用 worktree、状态干净、base 明确。构建期间再次读取 HEAD;变化则废弃 artifact,不尝试部署工作目录快照。

Artifact

首版始终构建完整 App

复用完整 runtime、Electron builder 和 packaged smoke。暂不根据 changed paths 选择 wheel、asar 或完整 App,以免维护多套完成语义。

Handoff

工具调用不直接停止 worker

self_update_prepare 只持久化请求并启动 supervisor。dispatcher 在当前 assistant final、session commit 和变更归因完成后写入 turn_released。

Continuation

startup recovery 创建新验收 Job

新 worker 原子认领 update,再复用 JobRunner 创建 source=self_update_verify 的后台任务。普通 Scheduler 不创建、不轮询也不重试这个 Job。

Rollback

目标失败也恢复旧 App

previous.app 保留到系统检查和目标验收均通过。系统错误、Agent verdict=fail/inconclusive 或超时都会恢复旧 App;candidate worktree 和证据保留。

06

状态机与持久化

一个 update id 对应一个目录。每次状态变更使用临时文件、fsync 和 replace;active.json 只保存当前 update id。

PREPARING请求、授权、candidate 固定
STAGING完整构建与隔离 probe
READY等待 turn release 与空闲
ACTIVATING停止并替换,保留 previous
VERIFYING新 worker + 验收 Agent
SUCCEEDED提交安装,清理 previous
激活前终止态

ABORTED

授权拒绝、构建失败、candidate 漂移、取消或等待空闲超时;旧 App 从未被替换。

Recovered terminal

ROLLED_BACK

候选激活后系统或目标验收失败,旧 App 已恢复且旧 revision 已重新验证。

Operator terminal

NEEDS_MANUAL_RECOVERY

新旧 App 状态无法自动确定或 rollback 失败;停止自动动作并保留全部 transaction。

~/.openprogram/self-updates/
  active.json                 # schema + update_id
  maintenance.json            # admission ownership
  <update_id>/
    request.json              # immutable intent
    state.json                # current phase + owner
    events.jsonl              # append-only evidence
    verifier-config.json      # frozen model / tools / optional plan
    diagnosis-config.json
    source-repair-config.json
    iteration-root.json       # frozen root authority
    iteration-next.json       # bound proposal + unique child reservation
    iteration-status.json     # submission / stop evidence
    supervisor.sh             # trusted copied controller
    supervisor.log
    artifact.json             # path + hashes
    verifier-result-{attempt}.json
    diagnosis-result-{attempt}.json
    source-repair-result-{attempt}.json
    controller/               # trusted runtime and installer
    bootstrap.json
    recover.sh
/Applications/.openprogram-app-install.*/
  previous.app                # transaction path bound in state.detail

request.json 的稳定字段

  • schema、update_id、created_at
  • session_id、origin_turn_id、origin_assistant_id、agent_id
  • repo、worktree_id、base_sha、candidate_sha
  • changed_paths、pre_update_evidence
  • goal、assertions、timeout_seconds;验收目标直接保存在 request,不另建 acceptance.json
  • iteration_policy:mode、max_attempts、deadline、allowed_paths、required_tests
  • pre_update_evidence 绑定迭代记录摘要;记录含 root/parent update、原授权摘要、全序列 attempt 和唯一 child ID。每个后继有独立目录,原请求和历史终态不改写。
  • app_path 固定为规范化的 /Applications/OpenProgram.app

state.json 的可变字段

  • phase、revision、updated_at、detail
  • attempt、dispatch.job_id、dispatch.claimed_by、dispatch.lease_until
  • last_event:状态先落盘而事件 append 中断时,用于确定性补齐最后一条事件

不接受 URL、任意 installer 路径、任意 shell 命令、state dir 或端口覆盖。candidate 不能选择控制进程。

07

完整时序

构建占用主要时间,但不影响旧实例;只在激活和重启阶段进入短暂不可用状态。

1 · Agent 准备 candidate独立 worktree 内修改;执行受影响测试、完整 gate、spec review 和 quality review;提交后确认 worktree clean。owner worker
2 · 创建请求self_update_prepare 从 Git 重新计算 base/candidate/changed paths,保存 acceptance,并显示精确授权卡。拒绝当前主 checkout、dirty candidate、版本不一致和第二个 active update。owner worker
3 · 启动 supervisor当前可信 runtime 生成固定 supervisor 文件,权限 0700;用 launchctl submit 启动 one-shot job。只传 update id 和状态目录。launchd
4 · 构建和 probesupervisor 在 candidate worktree 调用完整 App build;使用当前可信 smoke/probe 合同验证 manifest、assets、启动、doctor、/chat 和 expected revision。candidate build 被 sandbox 限制。supervisor
5 · turn-end releaseAgent 输出“候选已提交,更新已安排”。dispatcher 在 final node、session status、turn commit 和变更归因全部持久化后设置 turn_released=true。dispatcher
6 · 等待空闲拒绝新的写入 turn,等待其他 foreground turn 与 running Job 自然结束。默认最多十分钟;超时则在停止 worker 前终止本次更新,不取消其他任务。maintenance gate
7 · 激活记录 App/worker/launchd 原状态;停止 App 和 worker;移动旧 App 到 transaction/previous.app;移动 staged App 到规范路径;重新注册 Launch Services;恢复原启动状态。supervisor
8 · 系统验证新 worker 必须证明 owner identity 与 candidate revision,并通过 doctor、Web 和 WS gate。失败或超时由 supervisor 停止新实例并恢复 previous.app。supervisor
9 · 自动触发 AgentJobRunner ready 后,startup recovery 对当前 attempt 做原子 claim,用稳定 Job ID 创建 source=self_update_verify 的受限 Job。普通 Scheduler 不参与。new worker
10 · 提交、恢复或迭代pass 时提交安装;其他 verdict/timeout 时先恢复旧 App。默认等待新授权;bounded_auto 在原授权范围和剩余预算内创建下一 attempt。结果都回写原 session。supervisor
08

两层验收合同

确定性系统检查先证明新实例可识别、可启动、可服务;Agent 再判断用户要求的行为是否成立。任何一层都不能替代另一层。

层
执行者
必须检查
失败结果
Pre-update evidence
修改 Agent + review
RED/GREEN、affected/full tests、docs/checklinks、clean candidate、两个独立 review verdict。
不创建更新请求。
Artifact gate
supervisor
完整 runtime manifest、Electron bundle、version match、packaged smoke、isolated cold start。
旧实例保持运行。
Live system gate
supervisor
默认 18100、owner-auth identity、expected candidate revision、doctor、Web/WS 基础连接。
自动恢复旧 App。
Goal acceptance
verification Agent
逐条读取原始 assertions;检查真实公共入口。UI 改动必须操作规范 App;API/CLI 改动必须调用真实入口。
verdict=fail/inconclusive,自动恢复。
Evidence binding
controller
每条 assertion 保存命令/页面/节点、观察值、artifact revision 和时间;verdict 必须覆盖所有 required assertion。
缺字段即 inconclusive。
Verifier authority

读取和操作验收入口

工具白名单为 read/glob/grep/list 与 self_update_observe。检查 ID 只能执行冻结的本地 API、CLI/test 或 UI 动作;不提供通用 Browser/Computer Use、shell、源码写入、提交、部署、外部消息或第二次更新权限。

Semantic limit

Agent verdict 是证据判断

candidate runtime 无法独立证明自身可信。系统 gate 由 supervisor 判断;Agent 只对用户目标给出结构化 pass/fail/inconclusive 与可检查证据。

Timeout

没有无限等待

系统启动 90 秒、Agent 验收默认 10 分钟、整体更新默认 30 分钟。超时不会被解释为通过。

08A

真实 UI、API 与测试证据

验收 Agent 使用同一个 JobRunner 和冻结配置。新增的是有限的验收能力,不是第二个 Agent、Scheduler 或通用浏览器控制服务。

验收入口
执行合同
不能替代的证据
默认 App 界面
受限适配层连接 /Applications/OpenProgram.app 的实际主窗口。绑定 App 进程、window、owner、update_id、attempt 和本次 worker identity;操作前后复核身份。模型不能指定 CDP 地址、任意 target、JS 或文件路径。
保存实际截图、可访问性树、当前路由、窗口标识和时间。只收到 /chat HTML、DOM 文本或内置 Web tab 的截图,不能证明 App 主界面的视觉和交互要求。
界面交互
冻结原会话主窗口检查:截图与可访问性树、聊天区滚动、session/dag 切换。新安装包按能力声明 UI protocol 1/2/3;旧 protocol 4 仅保留读取与回退兼容,不接受临时测试对象操作。
成功须绑定原会话、worker、Job 和目标窗口,并完成自有位置/视图清理;用户输入中断时不覆盖用户选择。任意点击、消息、设置、外部导航、文件传输、剪贴板及执行代码均不授权。
本地 API
保留 owner-auth 和 loopback 限制;按冻结的 method、路径、参数和响应上限执行。禁止重定向、模型传入认证信息和任意 URL。调用前后核对同一 worker 与 candidate identity。
真实响应必须与 assertion 相关。健康检查通过不证明功能正确;HTTP 200、页面标题或模型自述均不能单独判定通过。
受限测试与 CLI
固定 CLI version/help 调用已安装 App 的 runtime;冻结 test:python 在 candidate worktree 执行。不接收模型 shell。原生验收沙箱限制为单进程,禁止 fork、posix_spawn 及子进程;这不改变独立 build/repair-test 的执行策略。
源码测试不能替代安装后验收,CLI help 不能证明任意功能。最小环境与无网络隔离,拒绝默认数据和 App 写入;超时、取消、输出超限、身份变化或清理失败不判 pass。不启动第二套 profile/App。
冻结的验证计划

能力在更新前确定

每条 assertion 绑定 evidence kind、检查 ID、允许目标/动作和预算。计划摘要进入 request evidence 和 verifier config 的摘要绑定。prepare 必须检查依赖与权限是否可用;旧配置不自动获得新增 UI 或 test 权限。

可信证据回执

由执行适配层保存证据

沿用 controller 的受限观察与签名 receipt 协议,增加证据类型而不接受模型自签结果。每份证据绑定 update、attempt、SHA、worker、检查 ID、时间和内容摘要。截图及输出限量保存,认证信息不进入日志、截图导出或模型上下文。

Failure and cleanup

缺失证据不判通过

窗口关闭、身份变化、权限拒绝、对象消失、输出截断影响判断、provider 失败或超时均记录具体原因。适配层释放自有连接和测试资源,不关闭用户窗口;清理失败须记录并阻止 pass。总时限不因重连或重启重置。

页面内容、测试输出和候选代码均是不可信数据,不能扩大工具权限。对允许动作的约束必须同时覆盖 UI、IPC 和后端写入口;仅凭 DOM selector、按钮文字或“只读”提示词不足以阻止副作用。自动验收不接触其他会话和个人浏览器资料;用户正在操作目标窗口时停止自动交互并保留已有证据,等待不得超过原验收时限。

09

失败语义与恢复

失败状态必须说明候选是否被激活、当前运行哪个 revision、旧 App 是否保留,以及验收任务是否执行。

失败位置
系统动作
用户看到的结果
授权拒绝、dirty candidate、无 commit
不启动 supervisor,不改变 worker/App。
原会话显示稳定错误和修复条件。
构建、测试、清单、打包冒烟测试、冷启动探测
终止 staging;旧实例继续运行;清理未验证 artifact。
同一会话直接收到失败步骤和日志摘要,不发生重启。
其他任务在等待期限内未结束
退出 maintenance gate,终止更新;不取消其他 turn/Job。
显示 quiescence-timeout 和仍运行的任务列表。
旧 App/worker 无法停止
安装目录不变;恢复先前 maintenance 状态。
显示精确 PID/launchd 状态,不报告已激活。
App move、Launch Services 或新 worker 启动失败
移动新 App 到 failed.app,恢复 previous.app 和原启动状态。
App 重新打开旧版本;结果卡标记 ROLLED_BACK。
revision/doctor/Web/WS gate 失败
不创建 Agent 验收;立即恢复 previous.app。
原会话收到系统 gate 证据。
验收 Agent fail、inconclusive 或超时
保存 verifier-result,先恢复旧 App;具备冻结配置的新请求自动诊断。允许的实现/测试失败继续修正和测试;默认只在再次安装前等待授权,完整 bounded_auto 授权可在范围内自动提交。inconclusive、环境或目标问题停止再次安装。
显示逐条 assertion、恢复 revision、attempt 和剩余预算。
JobRunner、provider 或 model 不可用
不使用 Scheduler 延后执行;标记环境型 inconclusive,并恢复旧 App。
显示缺失依赖和可重试条件,不误报代码缺陷。
rollback 自身失败
停止所有自动迭代,保留 previous/candidate/transaction,进入 NEEDS_MANUAL_RECOVERY。
显示实际 App、revision、进程和只读恢复说明,不报告已经恢复。
supervisor 自身崩溃或机器重启
下一次 worker/App 启动前运行 recovery:依据 phase、规范 App、previous.app 和 revision 做幂等修复。
不新增 RECOVERED phase;恢复后依据真实结果保留 SUCCEEDED、ROLLED_BACK 或 ABORTED,无法确定时为 NEEDS_MANUAL_RECOVERY。恢复事件与恢复证据单独保存。

独立于 App 的恢复入口

激活候选前,可信当前版本除保存 controller/runtime/installer,还生成本次更新专用的 ~/.openprogram/self-updates/<update_id>/recover.sh,并在本机用户的 ~/Library/LaunchAgents/ 写入对应的一次性登录任务。脚本、状态目录和 runtime 都位于 App 外;不依赖 PATH 中的 OpenProgram、待修复 App、普通 worker 或 Scheduler。没有完整可验证的保存副本时,不生成新的控制代码,也不尝试猜测恢复。

入口
行为
约束
recover.sh status
不传参数也只查状态
由保存的 runtime 读取本次更新、维护状态和人工恢复结果;返回固定恢复入口路径。
不执行模型、不修改 App、不新建更新;拒绝其他参数、任意命令、路径或 profile。
recover.sh repair
复用下述人工确认流程。App 或普通 CLI 无法启动时,owner 仍可在终端检查计划并授权恢复。
仍需交互式确认和原十分钟期限;原验收、journal、身份与权限约束不变。
用户下次登录
或 recover.sh resume
重新校验保存的 bundle、请求和固定脚本,调用原 supervisor,按持久化 phase 继续已有交接或恢复;不合成 turn release。
沿用原更新、rollback、commit 和 repair 的期限与结果。没有人工授权的 manual 状态保持维护;不批准新 candidate,不创建第二个验收 Job。

登录文件名为 ai.openprogram.self-update.recovery.<update_id>.plist,在本用户每次登录时最多执行一次,完成后清除:RunAtLoad=true,不设置 KeepAlive、周期或常驻 daemon。写入 plist 不立即加载另一份控制进程;正常更新仍使用原 one-shot supervisor 和同一 controller lock。当前登录会话中,若 App 与控制进程都已不可用,终端恢复脚本是显式入口;机器重启后需本用户登录才触发自动恢复,不声称能在未登录或磁盘未解锁时执行。

发布恢复入口、固定参数和 plist 的绑定记录必须发生在原 supervisor 被提交、候选被激活之前。只操作本用户拥有的规范目录和本次更新的确切文件;已有同名但内容不匹配、符号链接、非普通文件或不安全权限均拒绝,不覆盖。重复准备校验原副本,不从已替换 App 重新复制。启动错误留下独立诊断,绝不解除维护或延长授权。

原更新已终止且不再拥有维护状态后,只清除内容仍与绑定记录一致的本次登录 plist;保存的 runtime、脚本、原更新证据和恢复结果继续保留,便于查询。清理失败保留诊断,不改变已验证的原结果;残留一次性任务再次运行也不得恢复已完成更新。该入口本身损坏、权限变化或原证据不足时仍需人工处理,不提供任意文件修复或强制清除维护的能力。

任务暂停、重启和继续执行

更新进入维护后,worker 使用现有 canonical pause 命令让正在执行的任务到达持久化检查点。每个更新保存明确的 execution id、pause command id 和观察版本;只恢复由本次更新暂停的任务。原本暂停、等待用户输入、取消和已经完成的任务不自动继续。排队且尚未执行的 Job 保持原有队列身份,不参与活动执行等待。

supervisor 只在 canonical 活动 attempt 和旧版运行任务均结束后替换 App。新 worker 在更新提交或验证回退且解除维护后,通过原来的 continue 命令和 Job 资源队列继续任务。命令使用固定 id,重复恢复不创建重复执行。暂停失败或不可暂停任务在期限内没有完成时中止更新,恢复已暂停任务。

检查点恢复保留运行合同校验;工具实现或权限改变时禁止重放旧操作,保留旧检查点。只有本次更新发起的 continue 因合同变化被拒绝时,才从原任务和历史创建固定身份的新规划 Job,保留原工具选择和权限规则,并在执行前重新校验原执行及更新归属。未解决的副作用、用户取消或其他会话活动阻止自动重新规划。任务恢复失败不更改安装验收结论。发起更新的会话在终态产生一个持久化后续任务,读取原目标、历史和更新结果继续检查;它不替代固定 verifier,也不获得再次激活更新的交互授权。旧记录不追溯创建后续任务。

验收入口是 supervisor 等待、worker reconciliation 和真实 canonical pause/continue。必须覆盖排队任务、检查点恢复、重复 worker 恢复、用户暂停/取消、维护未解除、更新失败,以及原会话后续任务只创建一次。实现状态:实现与针对性测试已完成,独立评审、完整测试和实际 App 验收待完成。

显式人工恢复

openprogram self-update status [update_id] --json 在不执行模型、不创建更新、不修改 App 的情况下读取维护归属、原终态与恢复结果。openprogram self-update repair <update_id> 是本机 owner 的独立控制入口,不依赖普通聊天或 Scheduler;必须在交互式终端确认显示的 update id、目标 revision、恢复动作与计划摘要,不提供跳过确认参数。确认时重新比较原状态、维护归属、安装 journal 和可信 controller;发生变化则拒绝旧确认。

原事务证据
获准动作
通过条件
尚未不可逆提交,previous 身份及旧 revision 可确定
由原可信安装器幂等恢复旧 App;不激活新的 candidate。
规范旧 App 身份匹配,重启后的旧 revision、owner、doctor、Web 和 WS 检查全部通过。
已经 committing / committed,且原已接受的签名验收决定完整
只完成原不可逆提交;不能用本次确认补造 goal pass,也不能在 activated 阶段重新授权首次 commit。
原 grant/result/decision/journal 身份一致,规范 candidate 与重启后的实际运行版本通过新检查。
ABORTED 且仍为 prepared
只验证未变的旧 App 并恢复运行;不执行安装或回滚。
prepared journal 不变,运行的不是 candidate,系统检查全部通过。
身份不明、证据缺失、可信 controller 损坏或检查失败
保留维护限制和全部恢复文件,记录具体失败;不猜测路径、不删除旧 App、不继续自动迭代。
显示仍需人工处理,不将原目标标记为已达成。

每次确认生成独立、持久化的恢复请求,绑定原更新与事务身份,限时十分钟;重试和进程重启不延长这次期限。只复用已保存的 controller/runtime/installer,通过包含恢复请求 id 的 one-shot launchd label 启动;不从待修复 App 重新复制控制代码。恢复进程使用原安装事务校验规范 App 后重启默认 worker,再验证新进程,使启动检查和普通 Scheduler 按正常启动顺序恢复。中断时允许在同一期限内重复幂等恢复及 worker 重启,但不能创建 verifier Job 或第二次更新。

原终态、原验收结论与旧期限保持不变;人工恢复写入独立 resolution receipt,区分“旧版本恢复”“已接受版本恢复”和“未激活更新恢复”。只有该 receipt 与新系统证据匹配,才原子解除本次维护归属。失败、过期或未确认时继续禁止普通任务;后续再次尝试必须重新确认。此入口不是任意 shell、文件修复或强制清除 maintenance 的接口。

10

权限与信任边界

“允许改代码”和“允许替换当前运行产品”是两个授权。第二个授权只授予一个 candidate SHA,或一个显式有界的多轮 envelope。

离线构建输入

控制器从已保存的可信完整 runtime 取得固定版本的 uv、managed Python 和依赖。复用前逐字节核对 candidate 的 uv.lock 与 product-runtime.json;不匹配或输入缺失时在激活前失败,不能悄悄沿用不匹配依赖或允许候选联网。依赖变化需要先提供匹配的完整 runtime 输入,不由候选自选下载源或继承 owner 凭据。

runtime 和既有 npm/uv/Electron-builder/node-gyp 构建缓存复制到本次私有 build-home;macOS 使用独立的 copy-on-write 副本,不共享可写缓存。Electron 平台 zip 由可信控制器按固定版本、架构和发布 SHA-256 选择,复制到 candidate 不可写的 update 输入目录并通过 electronDist 使用,不因缓存命中而跳过完整性校验。候选仍重新构建 Web、文档、wheel 和 Desktop archive,仅把新 wheel 安装到私有 runtime;正式 Release 的常规构建入口保持原有行为。复制、构建及检查共用原期限,缓存命中不是验收证据。

构建进程不访问外部网络、默认 App 或用户状态。packaged worker smoke 只获准绑定并访问控制器选择的单个私有 loopback 端口,明确排除默认端口 18100;其余网络访问继续被 sandbox 拒绝。需要宿主图形服务的固定检查不向 candidate sandbox 扩权:控制器使用固定参数调用系统 sips 检查四个固定图标源,并由固定的可信 runtime Python 执行 Playwright Chromium probe;执行前后都要求 candidate 浏览器资源与已保存可信 runtime 的资源树 hash 一致。worker、图标和浏览器检查都完成并写入 manifest/receipt 后才允许进入激活阶段,不能通过开放默认 worker 或跳过检查取得通过结果。实际打包及已安装 App 验收状态见文末。

Trusted controller

控制逻辑来自当前已安装版本

supervisor 和安装器在 candidate 激活前从当前 runtime 复制并 hash 固定。candidate 不提供 supervisor、App path、state path、命令或恢复策略。

  • one-shot launchd label 包含 update id
  • 0700 controller,0600 状态
  • 固定默认 profile、18100 和规范 App path
Candidate code

构建可执行,权限受限

candidate 的构建和 smoke 会执行源码,因此在 sandbox 内运行,并使用最小环境。拒绝访问 provider 凭据、默认 session state、安装目录和 update record。

  • 只写 candidate worktree 与 staging temp
  • 不继承 token/API key
  • 网络默认关闭;依赖必须来自 lockfile/cache
User authority

激活必须处于 owner 批准范围

self_update_prepare 加入不可由 bypass 跳过的强制审批集合。默认绑定 candidate;有界自动迭代绑定 goal、允许路径、最大次数、时限和禁止变化项。

  • cron/scheduler/sub-agent/MCP 不能发起
  • 审批不持久化为通用 allow rule
  • scope、依赖或权限变化后失效
Verification

验收 Agent 权限单独收窄

自动验收不能修改源码或再次更新。它只能读取证据、运行受限检查和操作本地 UI;需要额外副作用时返回 inconclusive。

  • 固定 agent/profile/model snapshot
  • 每个 assertion 必须有 evidence
  • verdict 通过 supervisor 的一次性 token 提交
11

公共入口与内部边界

用户仍然只需要聊天。工具参数保持结构化,不向模型开放安装命令。

变更工具 · 强制审批

self_update_prepare

{worktree_id, candidate_sha, goal, assertions[], iteration_policy?, verification_plan?}。强制审批后验证 candidate、冻结配置、写 request、启动 supervisor。返回 update_id、phase、candidate_sha、base_sha 和 changed_paths,不返回安装成功。

Read-only tool

self_update_status

{update_id?}。返回 phase、steps、current/candidate revision、active App、rollback availability 和 verifier verdict。

激活前控制

self_update_cancel

只在 PREPARING/STAGING/READY 生效。ACTIVATING 之后拒绝 cancel,由 supervisor 完成 commit 或 rollback。

Repair control

self_update_repair_cancel

{update_id}。原 owner 会话取消该次回退后的源码修正或候选测试,不改写原验收和回退结论。

重试 · 强制单次审批

self_update_retry

{update_id, candidate_sha}。在原会话批准通过真实证据核验的修正 SHA;审批前后均复核身份、候选和预算。返回固定 child ID 与提交状态,不返回虚假的安装成功。

Iteration control

self_update_iteration_cancel

{update_id}。停止整个迭代序列及其待执行/正在修正的任务。已激活事务继续必要验证或恢复,但不能创建后续 attempt。

# Job result; controller wraps it in a signed receipt
{
  "schema": 1,
  "update_id": "su_...",
  "candidate_sha": "...",
  "attempt": 1,
  "verdict": "pass | fail | inconclusive",
  "assertions": [
    {
      "id": "acceptance-1",
      "status": "pass",
      "entry": "default App",
      "observation": "...",
      "evidence_refs": ["observation-id"],
      "observed_at": 1.0
    }
  ]
}

DAG 表达

发起更新的 assistant node 记录 self_update_id。重启后的验证 branch 使用:

  • source=self_update_verify
  • caller=origin_assistant_id
  • predecessor=null,不改变用户主分支 HEAD
  • 结果通过现有 Job completion/follow-up 机制回到主分支

UI 将其渲染为“更新后自动验收”,不渲染成用户消息。

11A

更新记录与普通对话结果

安装、验证和回滚保留各自的持久更新记录。对话仅显示普通消息和工具结果。消息列表不挂载软件更新历史组件、不发起更新历史轮询请求,也不提供更新管理表单。Desktop 恢复保留其路由和确认协议,不向对话添加更新恢复提示。

投影字段
写入者与来源
读取含义
缺失或过期
update_id、root/parent、origin session、attempt
已校验 request 与迭代关系。
定位同一次更新、原会话及父子 attempt;身份只由服务端提供。
归属不一致时拒绝读取;不能通过 renderer 传值改绑。
phase、state_revision、updated_at
SelfUpdateStore 的持久化 phase、递增 revision 和时间。
事务进度;state_revision 是状态版本号,不是 Git SHA。
损坏/未来 schema 返回明确错误,不转为“没有更新”。读取不得初始化、修复或推进事务。
candidate_revision、changed paths
prepare 从 Git 核验并冻结的 request。
目标源码版本与变更范围,不表示已安装或正在运行。
不能用 candidate SHA 填充运行版本。
last_verified_runtime
按 phase 选择并校验 controller 的 system_gate、committed_system_gate、restored_system_gate;激活前旧版本来自 previous_system_gate。人工恢复使用对应已校验恢复 receipt。
最后验证的 SHA、worker PID、验证时间及证据来源。SUCCEEDED 选提交证据,ROLLED_BACK 选恢复证据,VERIFYING 选候选证据。安装 journal 的 committed 不是另一个 update phase。
中间切换、ABORTED 或人工恢复状态没有匹配证据时为 unknown;不取“最近一个看似有效”的旧 gate。此字段不是持续在线证明。
target_app、rollback 状态
request 的规范 App 路径、事务清单和对应恢复状态。
区分安装目标、可恢复副本、已恢复和需要人工恢复。
目标路径不命名为“实际活动 App”;不能因曾经保存 previous.app 就一直显示可回退。
验收、诊断、修正与下一次提交
对应已校验 result/receipt 及 next-candidate summary。
逐条 verdict 和证据、修正 SHA、待授权状态、预算及 child ID。verifier pass 不等于安装已提交;安装提交完成后 update phase 才是 SUCCEEDED。
还未产生结果显示 pending;无 evidence 显示 inconclusive。回退后继续修正不能覆盖原失败结论。

共享读取合同

openprogram/self_update/control/projection.py 提供专用工具、Web 与 Running 的共享只读投影。owner-auth 路由为 GET /api/self-updates?session_id=… 与 GET /api/self-updates/{update_id}?session_id=…,并校验 origin session;历史列表使用有上限的稳定游标分页,不依赖 active 指针。证据经同一更新的 /evidence?session_id=…&evidence_id=… 路由再次验证,只返回签名结果引用的观察记录。

投影只包含明确允许的字段、有限摘要和不透明 evidence ID,不返回配置、token、签名密钥、环境变量或任意日志路径。证据读取必须再次核验 owner、session、update 和 receipt 绑定;不支持任意文件读取。状态及关联结果在同一一致性边界内读取,无法形成一致快照时返回可重试错误。读操作不触发 claim、repair、Job 或 Scheduler。

更新投影 API 保持只读并限定于会话作用域,用于显式工具读取。聊天不轮询这些 API。已存储的更新状态不依据窗口断连推断成功或失败。

Conversation

Normal results

工具结果和持久结果交付使用现有对话消息流程。聊天中不挂载独立更新历史、分页、证据查看器或更新请求表单。

持久结果交付

结果不依赖窗口在线

controller 先持久化终态,worker 通过已有 Job completion/follow-up 投递机制把有限结果写回原会话。以 update、attempt 和结果类型形成稳定投递键;重复启动或 ACK 丢失不产生重复消息。终态早于 verifier 创建、原会话未打开时也必须投递;会话删除则保留未投递状态,不改投其他会话或创建新的用户任务。

No false completion

分别显示执行与安装结论

自动验收任务完成、目标通过、安装提交、回退完成和后续修正分别显示。通知由确定性代码生成,不另起 LLM 来编写状态,也不因此触发再次安装。DAG 保留 origin caller 与验证 Job 的关系,不改写用户主分支 HEAD。

Desktop 重启定位原会话

复用 apps/desktop/worker-start-url.js 的 owner-auth 启动机制与现有 /s/{session_id} 路由。supervisor 在停止 App 前保存本次更新的受信任 reopen 意图,绑定 update、attempt、origin session、owner、期限和启动动作。Desktop 只接收不透明 update ID;由已认证服务解析经过核验的会话,再构造同源路由。候选、模型、CLI 参数和 renderer 不能指定任意 URL、文件路径或认证 fragment。

只在这次更新触发的重新打开中消费该意图,不改变普通启动、用户当前窗口、中心标签和稳定 Release updater。完成认证并确认原会话加载后才 ACK;ACK 前崩溃可重试定位,不能重新激活更新或重建 verifier Job。多窗口只由指定主窗口消费;重复消费是幂等定位,不重复建标签。用户主动离开原会话后不再强制跳回,也不插入专用恢复提示。

owner 改变、session 不存在、意图过期、身份不匹配或恢复接口不可用时,保持普通启动,在内部状态中保留原因;不跳到其他会话,不自动创建会话,也不把定位失败判为安装成功。App 原本未打开时遵守保存的启动状态;若验收必须操作可见 App,应在 prepare 时显示并获得打开窗口的授权。更新与回退双方均须支持所需的最小读取/恢复协议,协议不兼容在激活前拒绝。

12

参考实现与采用方式

参考只用于确定进程、更新和 worktree 边界;OpenProgram 的候选来源、权限和验收仍由本设计定义。

采用

Git worktree

使用 linked worktree 隔离 candidate 的 HEAD/index/工作目录;serving checkout 和用户现有 dirty checkout 不作为构建输入。

修改采用

Apple launchd agent

采用用户会话内独立进程边界。首版使用 one-shot launchctl submit,不安装常驻 updater daemon。

修改采用

Electron 更新生命周期

采用 staged artifact、restart 和明确状态;候选来自本地 commit,不使用远端 update feed。

不采用

Electron autoUpdater

macOS 自动更新要求签名并围绕发布 feed 工作;不能覆盖本地未发布 candidate、Agent 验收和当前 unsigned 开发 App 合同。

复用仓库实现

正式版本自动更新

复用完整 artifact、manifest、runtime probe 和平台安装边界;不改变 stable Release 的发现、下载和用户安装行为。

复用仓库实现

Agent worktree

复用 worktree 归属、commit、merge/discard 和 approval 语义。自主更新只读取 candidate,不自动合并用户主分支。

13

分阶段实施计划

每个任务独立 review;前一任务通过 specification 与 quality review 后才开始下一任务。所有生产文件名是目标位置,实施时先核对现有共享 primitive。

任务 A · 持久合同

状态模型与单实例锁

新增 openprogram/self_update/,只使用 stdlib JSON、fcntl 和 atomic replace。

  • types.py:phase、request/result schema
  • store.py:create/load/transition/claim/event journal
  • RED:损坏 JSON、并发 create、非法 transition、重复 claim、future schema
  • 门禁:单元测试 + 组件状态恢复
任务 B · 暂存与事务

完整 artifact 与延迟提交安装

复用现有 Desktop 构建/安装脚本,不新增 updater dependency。

  • package-and-install-app.sh:分离 build-only 输出
  • install-app.sh:prepare/commit/rollback 或等价 defer-commit 接口
  • upgrade.py:抽取公共 identity/doctor probe
  • RED:build 失败不停止旧 worker;激活后失败恢复旧 App
TASK C · CHAT HANDOFF

专用工具、审批与 turn-end release

工具不执行停止动作,只提交持久化意图。

  • programs/tools/system/self_update/:prepare/status/cancel
  • agent/permissions/approval.py:forced owner approval
  • agent/dispatcher/:final durable 后 release
  • RED:拒绝 dirty SHA、非 owner source、并发 update、final 未落盘
任务 D · 外部监督器

launchd one-shot 控制进程

当前可信版本生成固定 controller,candidate 只能提供 commit 内容。

  • self_update/supervisor.py 与固定 shell handoff
  • 构建沙箱、维护门禁、静默等待超时
  • 状态/日志/transaction 目录生命周期
  • RED:worker 被停止后 supervisor 仍完成;重复启动幂等
任务 E · 重启验证

startup claim、JobRunner 与受控迭代

不恢复被中断的执行栈;重启后创建新 Job,普通 Scheduler 不参与控制。

  • self_update/recovery.py:system gate、lease、稳定 Job ID 和 exactly-once dispatch
  • worker/runner.py:JobRunner ready 后、普通 Scheduler 前的启动 hook
  • agent/job/runner.py:source/authority/tool/model snapshot
  • self_update/iteration.py:attempt budget、scope diff 和停止条件
  • RED:重复启动、lease 过期、orphan verifier、provider 缺失、三轮耗尽和 scope 扩大
任务 F · UI 与完整验收

结果交付与 App 验证

验证和恢复使用应用服务;对话保留普通消息和工具结果。

  • 只读状态工具与持久结果交付
  • Desktop reopen 定位 origin session
  • 受限真实 UI/API/CLI/test evidence
  • Python/Web/CLI/Desktop/full gate、文档与默认 App 三场景

Task F 的依赖与验证范围

子任务与目标位置
交付内容
必须通过的验收
F1 · self_update/projection.py、Web self-updates 路由、现有 status 工具和 Running 路由
先实现同源白名单投影、分页历史及权限合同;不引入新的控制入口。
公共 API/工具:跨 session 拒绝、无凭据泄漏、终态可读取、查询不写盘、损坏状态不伪装为空、候选/运行版本不同、旧恢复证据不能覆盖新状态。
F2 · 现有 Job 结果交付
普通持久消息交付;不设聊天专用更新控件或历史轮询。
结果先于订阅、窗口关闭、投递后崩溃、重复恢复、切换会话时迟到响应、待授权/回退/人工恢复、终态历史、键盘与窄屏。运行真实组件行为测试,不以源码字符串检查替代。
F3 · apps/desktop/main.js、worker-start-url.js 与 startup handoff
依赖 F1/F2;绑定更新的单次 reopen、原会话加载 ACK,普通启动保持原行为。
真实路由加载、错误 owner/session、过期意图、认证失败、ACK 丢失、多窗口、手动导航优先;不产生第二个 verifier 或自动修改会话。
F4 · self_update/verifier_config.py、现有受限观察/签名协议与 App/测试适配层
依赖已评审的重启验证与 F3;冻结验收计划并提供真实 UI/API/CLI/test 证据。各 Workflow 阶段 prompt 使用同一目标、assertion 与预算,工具权限由执行层强制限制。
真实 App 主窗口定位与截图、批准的交互、实际 CLI 检查;拒绝任意 JS/CDP/URL/shell、越权写入、跨会话、身份切换、伪造 receipt 和重放;旧配置不获得新权限,缺证据/清理失败不判 pass。
F5 · 主线集成、产品文档与发布验收
依赖 F1–F4;同步本页及英文/中文产品文档,保留并检查现有架构图;逐项核对整个目标与当前实现。
完整 Python、contracts、Web、CLI、Desktop、打包、docs/checklinks 和组合提交验证。随后仅用 scripts/refresh-local-app.sh 更新默认 App,实际验证成功、激活前失败、激活后回退;分别记录 local commit、main、remote 和 installed identity。

每个子任务开始前从已评审的不可变 commit 创建隔离 worktree,写明 exact 文件、RED 和完整测试命令。通过相关检查的独立改动及时提交;评审通过不等于发布通过。已证实的基线失败保留为未通过状态,不能用较小测试集合替代完整 gate。文档机器检查、浏览器交互与桌面/窄屏视觉检查分别记录,不新增另一份设计文档。

14

产品级完成条件

以下条件全部成立后,才能把“对话内自主更新”标记为 implemented。

真实聊天入口

owner 在默认 App 中提出修改;Agent 完成 worktree 修改、测试、提交并调用专用工具,不依赖开发者手工执行脚本。

不可变 candidate

request、artifact 和 live revision 绑定同一 SHA;dirty worktree、HEAD drift、version drift 均在停止旧实例前失败。

进程边界

旧 worker/App 停止后 supervisor 仍运行;当前 turn 已完成持久化,不产生永久 running node。

无并发损失

存在其他运行任务时只等待或中止更新,不隐式取消;maintenance gate 不丢失已提交用户输入。

完整 artifact

App bundle、embedded Python、Web/docs、Programs、Browser assets 和 capability manifest 都来自 candidate,并通过 packaged smoke。

live identity

重启后默认 18100 的 owner-auth challenge 证明 candidate revision;单独的匿名 health 结果不能满足该条件。

自动验收

新 worker 正好创建一个 self_update_verify Job;每条 required assertion 均有结果和证据,缺失即 inconclusive。

调度隔离

普通 Scheduler 停止、延迟或重复 tick 不影响验收触发;worker 重启、lease 接管和 orphan reconciliation 后,每个 attempt 仍至多一个验证 Job。

受控迭代

默认每次重新授权;bounded_auto 严格执行最大次数、总时限、路径和禁止变化项,任何越界都暂停。

自动恢复

系统 gate、目标验收和超时失败均恢复 previous.app,恢复后的 worker revision 可验证,并在原会话显示结果。

崩溃恢复

在 READY、ACTIVATING、VERIFYING 各阶段模拟进程/机器重启后,recovery 得到确定终态,不重复激活或重复发 Agent turn。

仓库与可见状态一致

设计、测试、review、candidate commit、App source marker、运行 worker identity 和 UI verdict 指向同一实现状态。

15

实现状态附录

本节描述主线及已安装产品的集成状态。功能分支实现与评审证据单独记录,不等于产品已经可用;目标合同不因当前缺口而降低。

已实现的基础

Agent 修改与 worktree

文件/shell/worktree 工具、approval、sandbox、read-before-edit、变更归因和 session DAG 已存在。

已实现的基础

完整构建与 packaged smoke

Desktop runtime preparation、Electron builder、runtime manifest 和隔离 smoke 已存在。

已实现的基础

App 安装失败恢复

现有 install-app transaction 已保存 previous App,并在激活或启动失败时恢复原状态。

已实现的基础

Updater、Scheduler 与 JobRunner

复用安装方式识别、source probe、Job 持久化及 orphan reconciliation;自更新启动认领使用 JobRunner,普通 Scheduler 不参与控制或兜底验收。

已集成候选 · 待发布验证

持久自主更新协议

组合候选包含状态机、单活动锁、durable release、maintenance、外部 supervisor、延迟提交与回退,以及 owner repair 和 App 外登录恢复入口。离线私有构建输入、固定 SHA 的 Electron 分发、单端口 packaged worker smoke、固定图标渲染和可信 runtime browser probe 已通过修复后的完整实际打包;browser probe 只执行候选无权写入且已重新校验的保存 controller runtime,构建用可写 runtime 不会由控制器执行。独立评审、最终组合门禁、主线发布和实际安装/恢复验收尚未完成。

已集成候选 · 待发布

自动验收与会话恢复

startup claim、稳定 self_update_verify Job、签名结果、目标失败 rollback、持久化结果投递和 Desktop reopen 均已集成。旧 App 缺少打包协议时必须先显式完整刷新;不能以源码测试证明已安装 App 的重启、原会话 ACK 或自动验收。

已集成候选 · 待发布

诊断与自动修正

回退后诊断、受限编辑提案、隔离 worktree/commit、冻结测试、再次审批与 bounded_auto 已集成。重启不重置次数和期限;完整组合检查与真实更新序列仍待验收,不覆盖原失败结论。

已集成候选 · 待发布

结果投递与受限观察

普通结果投递、原会话定位,以及有签名的 HTTP、CLI/test、截图、滚动和视图检查已集成。聊天不挂载更新管理界面;不支持临时测试对象操作。能力严格限于冻结检查;任意 UI 交互和真实数据修改不在授权内。默认 App 三场景及实际目标证据完成前,整体保持未完成。

openprogram/programs/tools/system/self_update/owner 工具、prepare/retry 审批与观察入口 openprogram/self_update/control/supervisor.py外部事务控制、系统检查与回退 openprogram/self_update/verification/verifier_config.py冻结配置、阶段 prompt 与工具白名单 openprogram/self_update/verification/verification_plan.py固定检查、资源预算与 UI protocol 能力 openprogram/self_update/repair/next_candidate.py下一候选授权、证据、预算与提交 openprogram/self_update/control/projection.py工具、Web 和 Running 的共享只读状态 tests/component/self_update/控制协议、验收、恢复与迭代行为测试;非实际 App 发布证明 apps/desktop/scripts/package-and-install-app.sh完整 App build、smoke 与安装入口 apps/desktop/scripts/install-app.sh事务安装、previous App 和失败恢复 scripts/release/smoke-packaged-runtime.shpackaged runtime 与 Web smoke apps/cli/.../commands/upgrade.pysource checkout probe、restart 与 revision verify openprogram/worker/lifecycle.pyworker 停止、启动与重启 openprogram/agent/job/store.pyJob 持久化与 worker-lost 恢复语义 openprogram/agent/job/runner.py一次性验证 Job 的执行和资源治理 openprogram/scheduler/service.py普通 Scheduler 合同;自主更新不依赖此入口 openprogram/updater/__init__.py安装方式识别和 startup 不应用更新的现有边界 apps/server/.../_exec_dag.py启动时协调中断的运行 openprogram/agent/permissions/approval.pyowner、source、工具审批与硬限制

对话边界

打开、切换或重新连接对话不查询软件更新历史。普通工具输出和现有结果交付仍可用。聊天 UI 不显示软件更新加载状态、状态信息、重试表单或恢复提示。生产 App 不挂载临时验证用重命名对话框。验证在任何 UI 请求之前拒绝 test_object 交互;仍支持截图、有界滚动和视角检查。

验收:普通聊天和 App 外层界面挂载时不包含软件更新 UI 或历史请求;桌面会话确认仍正常工作;安装与回滚行为保持不变。