Files
Agent-System-Documents/docs/proposals/2026-10-12-service-restart-data-recovery.md
weichen 10b442169c Distinguish historical proposal limits from current authorization
Why: old r2 and r3 draft-stage paragraphs contradicted the retained approval records and current user takeover authorization.
Evidence: independent Documents review identified the ambiguity; targeted diff and link checks are captured outside the candidate.
Impact: label historical planning limits without changing genuine NOT_RUN results, deployment boundaries, or the newer login recovery implementation record.
Refs: Documents PR #89; Dev PR #489; independent review /tmp/mq-trial489-docs-independent-review-20261014.json
2026-10-14 01:08:51 +00:00

50 KiB
Raw Permalink Blame History

title, type, date, artifact_contract, execution, product_contract_source
title type date artifact_contract execution product_contract_source
服务重启与全量数据恢复方案 fix 2026-10-12 ce-unified-plan/v1 code ce-plan-bootstrap

服务重启与全量数据恢复方案 r3

确认状态:原 r2 批准记录保留。r3 新增(R11/R12、KTD13–KTD15、U8)于 2026-10-12 已批准,当时授权连续推进到 PR 可合入;该历史批准不代替当前交付准入。合入按现行规范由智能体执行。 U8 进展(2026-10-12,未合入):Backend #183 与 Dev #482;旧记录在 ef66bfb 上记有 vet-and-test success(历史 SELF 引用,非本轮 GATE)。Dev 侧在允许 app → instanceguard 与 instanceguard → store 两条架构边后的提交是 4464255,该 HEAD 的 CI 以作业结论为准,不在本文升为 GATE。 r3.1=同日增补 D1/D2/D3(部署根治;编号避开本案 U1–U8)。映射:D1 补 U1 身份消费缺口,D2 补 U6 部署回退,D3 补 U7 真实组合门禁。确认状态:用户 2026-10-12 授权按实施计划连续推进到 PR 可合入;D1–D3 仍供人工评审,不因 U8 批准而代签。 修订记录:r1=2026-10-12 初稿并完成六角色文档自审(见同名审阅记录第一轮);r2=2026-10-12 吸收外部源码复核(U1/U3/U4/第9节四处修订,见审阅记录第二轮),需求范围与量化指标未变。 r3=同日根据 dev 8788 账户故障续查刷新源码/运行事实,保留 R1–R10、KTD1–KTD12、U1–U7;补充 R11/R12、KTD13–KTD15、U8 单实例保护及近期验证合同。原故障档和时间指标保持候选值,未宣称实测达标。上述 r3 新增已于 2026-10-12 批准。 需求来源:2026-10-12(Asia/Shanghai)用户要求“系统应该在停止并再次启动(通过 svc 或异常终止)后,应当能够恢复所有服务和数据,给我一个完善方案”。负责人:weichen;编制:Codex;实施与运维负责人在启动对应增量时指定。 主记录:本文;任务卡:Dev .task/feature-service-restart-recovery-plan.md。本文的源码路径以 Dev 根目录为基准。

1. 目标与成功边界

目标:系统停止后再次启动,用户仍能访问原有数据和服务,未完成工作进入可继续或可处理的确定状态。

采用“稳定实例配置与身份、依赖编排、进程监管、持久恢复账本、一致性文件发布、组合备份与实测验收”的方案。复用现有 PostgreSQL/SQLite、运行表、事件、命令、通知和恢复隔离机制,补齐实际接线与崩溃窗口。首期落实单机完整恢复;多实例所有权沿用已有协作存储方向,不重新设计一套集群平台。

必须区分三类恢复:

故障档 需要达到的结果 数据与时间合同
svc stop/start/restart、进程 SIGTERM/SIGKILL、应用 OOM 原配置启动;持久数据完整;安全任务继续;未知副作用隔离 存储正常、确认结果已持久化的前提下,RPO=0;建议冷启动至业务就绪≤60秒
主机重启、操作系统崩溃或断电,磁盘仍可用 数据库 WAL 恢复、文件一致性恢复、服务按既定意图启动 只有 DB 与文件落盘策略、底层存储满足耐久性且实测后,才承诺已确认数据 RPO=0;建议 RTO≤5分钟
数据盘损坏、主机丢失、误删或不可逆污染 在新目标恢复匹配的 DB、文件、配置和版本 沿用协作存储案建议 RPO≤5分钟、RTO≤60分钟;如要求此档 RPO=0,另行批准跨故障域同步复制

以上时间为待批准的验收目标,不是已测性能。大规模扫描与冷索引重建不要求全部在60秒内完成:必需数据对账及安全恢复决策完成后可开放API,派生索引按明确降级合同后台重建。故障超出资源预算时报告阻塞及原因,不能以假就绪满足时间指标。

恢复保存的是持久业务状态、步骤和已确认结果。进程内 goroutine、模型连接及未持久化 token 不可原样复活;模型回合从持久上下文和步骤边界重建。已经接受的用户消息、排队任务、审批和取消意图必须先落库再返回接受;尚未确认的结果不能事后冒充成功。

2. 当前事实及重点差距

本轮源码基线:权威 Dev 2723e452440fc11022c0f6d916cd34cc4cd17335;文档任务同步后 HEAD 26f2045e33516207959b5983acd32e1d3ea66bca(任务元数据提交);Backend 5e8ada727c358438f8ff9f94eabb697e99fe28dc;Core 89f3906c390027512ef47b5333088c3d3a019e84;Frontend cc233fec908d0f8c12193042942b94ad68508091;固定 Documents 8d0810bd35f4934e0ad37fb96d7bab6d7a732473。草案独立 Documents 基线 1048e7c6afc2bb4efc541e68575c3fb2669ee332,未推进 Dev gitlink。原 r2 对象为历史,不用于证明当前运行行为。 来源为 SELF·只读观察;原始脱敏命令和运行输出由 Dev make evidence 写入 .task/feature-svc-account-durability-plan.md。故障表现为源码推导,故障/恢复/耐久性验收 NOT_RUN。 最终 Dev 同步基线 d6d46fde6d903d0d8e37440d830fb5183d6ef5e9 已纳入主干的 svc 实例内存态修复和 Documents 指针 1048e7c6afc2bb4efc541e68575c3fb2669ee332;Backend/Frontend 未变。旧 gitlink 是启动时点,本轮仅同步权威上游,未推进本轮草案组合或重复实现该修复。

对象 已存在的基础 尚需解决的恢复缺口
数据库绑定与身份 svc 统一解析已接线;manifest/marker 和 Backend DB identity 已存在;manifest 写入已有 file/dir fsync 部分消费方仍读派生 svc.env;自定义根和 legacy/direct 启动需要完整核验;identity 不等于活动服务独占锁
服务生命周期 svc 已有 desired/autostart、recover、watchdog timer;PG 优先拉起 没有完整服务组依赖/单一进程组监管;不管理 Redis 类型;登记版本与实际二进制可能不一致
启动与停机 启动对账开放 run;优雅停机尝试中断和事件 flush 恢复通知/子代理发生在完整接线前;HTTP停机超时可跳过flush;无所有worker等待;子代理 cancelled 可覆盖 interrupted
运行恢复 runmgr.go:2046 对账、子代理派生请求/转录、pending_notices持久化、恢复hold检查 主run与HITL统一收为中断/取消;子代理先派生再写文件标记;notice消费非事务且绕过PG会话占用
执行所有权与额度 store/execution.go 有 owner/epoch/lease,终态有fencing;额度有持久表 续租只有定义未接入生产调用;15分钟租约可过期;额度无崩溃回收;不能据此宣称安全接管
命令与投影 取消/HITL持久收件箱、Surface待投递标志、事件seq与SSE续传 命令先 consumed 后执行,kill窗口丢命令;跨实例取消入队缺conversation;Surface失败仍可清pending且仅扫100条
工具副作用 Native Session已有tool intent/result转录、安全重放判定;Backend有hold表及管理员释放入口 SetRecoveryHold仅定义;Native的未知效果被写成工具错误后仍可继续模型,尚未与Backend run/owner/epoch和持久hold形成统一隔离闭环
文件与备份 blob temp+rename、SQLite 一致性快照、pg_dump 已存在;Native 转录 Write→Sync;manifest 有原子同步链 工件原文先覆写再更新 DB,无不可变版本;agent 快照缺权威 manifest,PG 和文件快照不是同一恢复点
SQLite 服务所有权 WAL、30 秒 busy_timeout、每进程一连接;数据库事务可排队 无跨进程活动服务独占;恢复扫描全部开放 run,第二个服务可打断第一服务;不具备 PG 的会话执行租约

2026-10-12 17:37:03(Asia/Shanghai)实际 dev 进程打开 runtime/dev/data/agent-system.db,DB marker 为 sqlite,运行 Backend 5e8ada7;没有登记 PG 绑定。候选 rdmcand 已停止且登记来源与实际文件版本不符。前一轮共享数据观察是历史风险线索,不冒充当前双进程事实。实际文件/marker 已核对,不等于全栈恢复或断电耐久性 PASS。

当前 PG 事件总线已有真重连、分页追赶和周期补查,Native 已接持久工具转录;这些基础不重复建设。SQLite 应用连接未显式声明 synchronous;诊断连接 PRAGMA 不能证明应用配置,见 R12。 18:14:09(Asia/Shanghai)重新实测:外部流程已将 rdmcand 拉起于独立数据根,实际 DB inode 与 dev 不同且制品/登记版本一致;dev 仍打开原库、账户最后设密时间未前进。当前共享现场已缓解;U8 的剩余任务是强制运行时防误用和隔离验收,不重复执行已完成的人工分根。完整停机/恢复和锁能力仍 NOT_RUN,本轮没有改变服务或数据。

旧报告中“DSN重启丢失”“迁移DDL与版本分开提交”“通知仅内存”等问题已有源码改进,本案只验证并复用这些基础,不把旧缺陷重新列成新建设。

3. 需求合同

ID 范围 要求与可判定验收
R1 服务 启动显式服务组时,全部已启用的必需服务按依赖就绪;可选失败明确降级;主动停止不被监管误拉起
R2 身份 连续裸启停、更换端口/程序/cwd后,DB identity、数据/工件/工作区根、实例ID、认证及插件组合一致;缺配置拒绝启动
R3 已确认数据 消息、项目、成员/权限、文档、设置、插件启用/授权、任务、审批、事件、工件原文与版本在RPO=0档零遗漏;灾难档完整恢复批准恢复点内数据,并对账/报告恢复点后的缺失和外部效果
R4 任务 已接受排队任务不丢;安全任务从持久步骤继续;HITL保留稳定gate与答案;取消意图在重启/换owner后仍生效
R5 副作用 started但无确定result的外部动作自动隔离;已确认动作不重做;恢复与发布验证当前权限和执行代次
R6 所有权 单一有效写者;旧进程迟到写状态/事件/工件被拒;过期额度回收;恢复本身可重复且可再次中断
R7 文件与派生状态 DB引用的文件内容、版本和hash一致;损坏现场保留并报告;缓存/检索索引可从权威源重建
R8 停机与就绪 停机先拒绝新工作再排空;全部worker、sidecar和工具子进程收口;错误不跳过后续清理;ready反映业务可用
R9 升级与灾难 保留上一程序/插件/资产组合及兼容合同;备份恢复到新目标并完成业务/权限/文件对账;不在普通启动覆盖现库
R10 可见与证据 用户能区分继续、等待审批、等待恢复确认、失败;运维能定位阻塞依赖;每项验收绑定精确组合、制品、配置与原始输出
R11 本地服务隔离 同一 SQLite/数据资源只允许一个活动服务运行时;直接二进制、路径别名和 legacy 入口同样受保护;账户短事务 CLI 不启动第二运行时;测试实例使用独立根
R12 耐久性档 显式设置并从应用实际连接核验 DB 耐久策略;文件和目录同步覆盖权威对象;DB/文件持久失败不能确认成功;未实测的断电 RPO 保持候选

覆盖“所有数据”需要先完成数据归属清单:除Backend业务表,还含插件自有持久库/目录和下游RDM等已启用模块。每个插件声明恢复适配器、版本、权威位置、必需程度与兼容性;无法证明可恢复的必需插件阻塞全系统签收,不能只恢复Backend便宣称全部完成。

4. 技术决策与服务结构

KTD1. 持久配置是启动权威,PID只是观察。 每实例保存完整类型化manifest:service/instance ID、配置revision、desired state、服务类型与管理权、依赖、固定程序/插件/资产摘要、DB identity与凭据引用、数据/工作区/工件根、认证、监听、必需/可选属性、恢复政策。临时环境只作显式覆盖;必需字段必须能从持久值或稳定受保护引用重新解析。机密放受保护文件/秘密服务,manifest仅存引用,不把密码或DSN写进日志。

KTD2. 配置整份提交,覆盖按字段合并。 每实例和服务组串行化生命周期操作,配置以revision做CAS;候选完整解析与校验后,临时文件落盘、原子替换、目录同步。legacy迁移同样一次提交,故障留下旧版或完整新版。更改端口不隐式更改data;数据根迁移是独立操作。恢复启动发现预期DB/marker/目录丢失时阻塞,初次初始化需要明确入口,禁止新建空库掩盖数据丢失。

KTD3. svc控制期望状态,唯一supervisor管理进程。 Linux部署推荐systemd;svc兼容既有单服务入口,增加显式stack/group控制。每服务仅一个监管者,不能nohup watcher与systemd同时拉起。分发仓已有模板要接入同一manifest合同。外部/共享PG、Redis仅探测连接,不擅自启停;managed-local只管理身份已核实且归该服务组独占的资源。pgvector是PG扩展,不另造守护进程;Redis只有配置并启用时才进入依赖图。

KTD4. 主动停止与异常退出分开。 stop先提交desired=stopped,再关闭服务;普通故障仅desired=running可重启。restart保留最终running意图并记录operation_id、执行阶段和恢复政策,不能因中途stop留下stopped。监管采用有界退避、重试预算与失败阻塞;错误配置不能无限重启。开机只恢复已登记且策略允许的服务。intent、operation、actual state必须分开,不能以pidfile存在视为运行。

KTD5. 团队业务权威沿用PG,个人档沿用SQLite。 团队PG失败保持未就绪,绝不切SQLite。普通重启让既有数据库完成自身WAL恢复,不导入备份。验证数据库identity/schema/权限和关键记录;SQLite恢复到预期文件并检查完整性,不能只看文件存在。断电承诺要求明确DB同步设置及底层存储合同,不把rename原子性当作落盘保证。依据:PostgreSQL耐久性、SQLite WAL。

KTD6. 恢复扫描与恢复执行分离。 装配Store、权限、插件/工具、工作区、预算、Workflow、命令消费者与事件投递之后,才分类认领恢复任务。扫描失败或归属未知进入blocked/hold;不能吞掉错误直接ready。恢复有持久cursor/attempt和CAS,支持分批、失败重试及再次重启;禁止全库无归属盲扫并续跑其他实例的任务。

KTD7. 持久执行账本统一主run、子run和后台任务。 保持稳定logical_run_id、request_id、parent关系;每次执行用新的attempt/boot/epoch。步骤记录输入引用、已提交输出、等待原因、调度位置、恢复决策;完成记录与业务结果原子提交。现有文件修改checkpoint不是完整执行checkpoint,不能直接拿它宣称主对话/工作流可续跑。后台任务表目前仅状态登记;只恢复明确有executor的类型,其他类型进入可见unsupported/blocked,补适配后验收。

KTD8. 副作用先登记,结果未知先对账。 复用Native runtime/session/intent.go和生产接线internal/runtime/llm_direct.go:245–301,以同一call/operation ID关联现有intent/result与Backend的run/owner/epoch/hold,不能让两个恢复器各自决定重放。DB账本裁决执行和hold,转录保存完整上下文及结果投影;本地个人档同样须有持久裁决。调用外部工具前持久化operation_id、参数摘要/安全引用、授权与gate、owner/epoch、幂等类别及started;确定结果写result/receipt。启动遇到started无确定result自动hold,不能只把“副作用未知”写成普通工具错误后允许模型继续发起动作。

目标支持可靠幂等键时按同一key重试;非幂等动作必须取得确定未执行的终态证据,并证明旧执行者及已发请求都不会再产生效果,才允许重试。“暂未查到结果”“取消请求已发送”“本地进程已退出”均不充分。否则保持hold。shell、代码执行、Git、数据库/远端写入默认按不透明副作用处理。模型/工具因失权立即取消;无法围栏的外部动作进入hold,不能靠租约到期证明旧动作停止。

KTD9. 所有工作入口共用执行所有权与额度。 前台、notice、子代理恢复、命令消费者均经过持久claim。稳定instance_id与每次boot_id分开;续租持续进行,丢租停止执行;CAS/事务围栏覆盖状态、事件、步骤、工件发布和副作用准入。execution与admission lease关联run/attempt/epoch;恢复终态和已证明失效的许可,禁止一启动就清空全库锁。父任务等待HITL/子任务让出执行额度;持久队列及跨实例等待者有有界唤醒机制。

KTD10. 命令、通知和投影处理后确认。 命令 pending→claimed(带租约)→applied/rejected,超时claim可重领;取消先从run解析已授权conversation,按逻辑run转交当前owner;HITL验证gate/version与当前权限。notice_id/旧子run的恢复claim、对应新run、消息和销项以事务与唯一约束关联,文件resumed标记仅作投影。outbox成功才ACK,失败保持pending;分批持续drain,不受单次100条限制。

KTD11. 不可变对象加DB发布记录保持文件一致性。 写新版本对象、完成必要fsync/hash校验后,在事务中发布引用与版本;失败留下可辨认未发布对象,不直接覆盖已发布版本。工作区变更另用既有overlay/CAS与副作用账本核对,不能把可变工作区伪装为不可变对象。checkpoint/JSONL尾部截断须保存原件、识别未确认尾部,已确认部分校验失败阻塞。工件缺失报告损坏,不静默返回空内容。孤立对象按清单、引用、保留期与备份需求回收。

KTD12. 业务readiness与恢复摘要分开。 liveness仅证明进程存活;readiness包含身份/认证、DB/schema、必需目录与插件、恢复决策完成和worker接线。已隔离且不影响其他会话的hold可允许API就绪,并暴露计数与处理入口;损坏权威数据或必需依赖失败不能ready。详细恢复状态只向有权owner/admin开放;公共健康响应不泄漏路径、任务ID、凭据或业务内容。

服务依赖结构(实线为必需;可选项以实例manifest为准):

flowchart TB
  M[持久实例manifest与启动意图] --> S[svc控制入口]
  S --> P[唯一进程监管者]
  P --> D[受管PG或SQLite数据]
  P --> A[应用与恢复协调]
  X[外部PG或可选Redis:仅探测] --> A
  D --> A
  F[持久文件与插件组合] --> A
  A --> W[任务/命令/通知/投影workers]
  A --> H[业务readiness]
  H --> U[Web或下游业务入口]

systemd的顺序依赖不能替代DB可用性检测;需真实探测,且明确依赖断开后的重连/恢复政策。监管策略参考systemd官方service契约与进程组停止契约。

KTD13. SQLite 活动服务锁由 Backend 强制。 在迁移和启动 run 对账之前按实际 DB/数据资源取得 OS advisory lock,持有至运行时退出;覆盖规范化路径和文件别名。marker/PID 不替代锁,SIGKILL 后由 OS 释放。合法账户 CLI 只核验同实例和兼容 schema 后做 DB 短事务,不启动 Manager;迁移独立维护。锁文件丢失或 DB 替换导致身份改变时拒绝自动续跑,不能新建可绕过的锁继续。

KTD14. 初始化与事故后缺库分开。 已登记实例启动和运维先验证预期库/marker,缺失则阻塞;显式 init 才允许创建。SQLite 可靠档明确 WAL+FULL 并核验每个连接;PG 核验实际 fsync/synchronous_commit/full_page_writes 等,不用诊断连接默认值证明应用配置。SQLite WAL、PostgreSQL WAL。

KTD15. manifest 单一权威与派生视图修复。 复用现有 temp→fsync→rename→dirsync;读侧直接消费 manifest 或验证派生 revision。manifest 提交后、svc.env 派生前崩溃可重新生成视图,不能按旧派生配置换库。未知制品能力、登记/实际版本不符报告并拒绝危险操作,不猜测。

5. 启停流程与恢复政策

5.1 启动和优雅停止

sequenceDiagram
  participant C as svc/监管者
  participant A as 应用生命周期
  participant D as 持久存储
  participant W as workers与子进程
  C->>D: 校验manifest、数据身份与操作锁
  C->>C: 启动归本组管理的依赖并探测
  C->>A: 启动固定版本
  A->>A: SQLite取得活动服务锁;PG按既有执行所有权合同
  A->>D: 校验schema、文件与恢复候选
  A->>W: 完成插件/权限/预算/工作区全部装配
  A->>D: 分类认领、对账未知效果、恢复命令与额度
  A->>W: 开放安全恢复执行
  A-->>C: readiness与恢复摘要
  C->>A: 停止请求
  A->>A: readiness=false;拒绝新写和新任务
  A->>W: 停止领取、取消或保存安全边界
  W-->>A: bounded join与子进程结果
  A->>D: 持久化恢复意图、flush、收口许可/所有权
  A->>D: 关闭存储
  A->>A: 释放本进程SQLite活动服务锁
  A-->>C: 停机结果(含未完成原因)
  C->>C: 复核进程组消失;逆序停止独占依赖

停机预算建议应用30秒、监管60秒,必须按最长合法步骤与故障实测调整;SSE连接主动结束,不能占满排空预算。某阶段出错继续运行后续可安全清理,保留聚合结果。工具无法停止或副作用未定时先持久hold并保留现场;强制结束后必须走崩溃恢复路径,不返回“已优雅停止”。

5.2 不同工作的恢复策略

停机前状态 重启处理 自动执行边界
已完成/失败/明确取消 原样加载结果及原因 不重新执行
已接受但未开始 按持久队列、权限和预算重新调度 条件满足后自动
纯读、可重建的计算 从已提交步骤边界继续 不重复已提交业务结果;模型回合可重新生成
正在执行幂等副作用 根据operation_id、目标查询/幂等合同核对 确认安全后继续
非幂等副作用结果未知 自动hold,展示原因和可核对证据 有权人对账具体operation;确认完成则记录receipt并检查剩余步骤;确认未执行须满足KTD8终态与旧请求隔离条件;可明确放弃整项任务;禁止直接“放行即重跑”
等待HITL 恢复稳定gate/version及持久答案 未回答继续等待;已回答幂等消费;撤权/版本变化拒绝并说明
子代理/后台notice DB唯一恢复claim关联旧、新attempt 不由文件标记决定唯一性,不重复派生/投递
索引与缓存 从文档/内容权威源重新排队,记录索引generation Redis可清空重建;恢复模型/维度/版本一致性后发布索引
未配置executor、缺文件或未知归属 保持blocked并提供处理原因 不丢行、不假完成、不盲续跑
stateDiagram-v2
  [*] --> pending
  pending --> running: 获得有效owner/预算
  running --> waiting_hitl: 持久gate
  waiting_hitl --> running: 当前权限下消费答案
  running --> completed: 结果与状态原子提交
  running --> recovering: 停机或失联
  waiting_hitl --> recovering: 执行者重启
  recovering --> pending: 核对可安全继续
  recovering --> waiting_hitl: 恢复原等待
  recovering --> hold: 副作用未知/权威数据不完整
  hold --> pending: 有权人对账确认安全
  hold --> recovering: operation对账结果与receipt已持久化
  recovering --> completed: 全部必要步骤与业务结果已确认完成
  hold --> cancelled: 明确放弃

recovering/hold在这里是目标业务语义,不是声称现有Core枚举已支持。过渡可用恢复账本表达;若新增公开状态,Core/Backend/Frontend/下游契约兼容必须同增量处理。已有interrupted历史记录保留,不强改成成功。

用户侧复用现有任务状态、对话卡和系统管理区,显示恢复原因、进度和动作。当前只有管理员恢复 API 基础,Frontend 对账接线待 U4 实施,不宣称已有可用界面。SSE按持久seq补发并丢弃旧事件,刷新重载权威快照;断连不推断完成。实施界面先读 DESIGN.md,遵守令牌、Collapsible和单异步状态所有者。

6. 全量数据与组合备份

数据类别 权威与恢复方法 必须对账
用户、项目、成员/权限、会话/消息、文档/版本、卡片/Surface、设置、插件授权 PG团队库或个人SQLite;包含全部已启用插件/下游的自有存储 主键、owner、权限、版本、关键内容摘要;禁止以空库替代
run/step/命令/通知/审批/取消/额度/恢复hold 持久账本与状态事务 链接、唯一请求、gate版本、owner/epoch、队列位置与外部receipt
上传、工件、文档大内容、输入附件、转录、派生请求 持久对象/文件目录,与DB清单匹配 内容hash、尺寸、引用版本、权限与可读性
执行工作区、overlay/checkpoint、任务Git现场 受管workspace与副作用记录 实际修改、基准SHA、未提交文件、执行者退出与恢复边界
程序、Web资产、插件版本、实例配置、秘密引用/密钥 不可变组合manifest;受限配置/秘密备份 摘要、schema兼容、插件签名/授权、恢复凭据可用且未泄漏
检索/向量索引、Redis缓存、临时PID/连接 从权威数据重建;PID按新boot重新观察 模型/维度/generation;不作为任务成功、审批或永久幂等权威

普通启停不从备份还原;数据库与文件继续使用同一身份及位置。灾难恢复由显式受管入口在新目标进行,禁止start自动覆盖当前数据。

当前 agent backup 只复制 svc.env、history 和 data(逐 SQLite 一致性导出),不含权威 manifest,在线非 DB 文件与 DB 没有统一快照时点;它是数据目录快照,不是完整恢复包。当前 pg_dump 默认在 runtime 子目录,完整包及副本须放在数据/runtime 生命周期之外;生成时使用受限临时目录,校验成功后原子发布完成标记。半成品不列作恢复点,完成且验证的新备份存在前不执行保留策略删除。

首期备份取得明确停写窗口:关闭准入、排空或隔离在途操作、停止全部相关写者,核对独占管理边界后备份DB及文件并生成manifest。共享PG不能仅停一个API实例便宣称一致;写者无法证明停止则阻塞此类快照。后续可用不可变对象、DB发布记录、物理基础备份+连续WAL/PITR建立在线恢复点,manifest关联source snapshot、时间/LSN、schema、源码/制品/插件组合和对象版本。

恢复到新目标首先处于隔离验证模式,不启动任务调度/副作用,也不自动连接生产外部写目标。正式切换须证明旧实例、worker、sidecar、工具子进程及已发请求的写路径已经隔离:可采用停止并验证、资源侧拒绝旧执行代次、专用执行凭据失效或网络写路径隔离;不得为此擅自轮换共享凭据。生成新的恢复权威代次,并保证实际写目标能够拒绝旧路径。两个独立恢复库各自认可的epoch不能互相围栏;旧实例不可联系不能当作停止证明。无法核实则保持副作用hold,只开放受权的数据核验。

同时对账恢复点至故障/隔离完成期间的外部效果:它们可能已执行却不在恢复后的账本里。结合独立receipt、目标日志或人工核对,将已确认动作登记到恢复账本,确认安全后逐项解锁;不能仅扫描备份里的started记录就自动重试。灾难档报告批准RPO内可能丢失的写入范围、恢复点内完整性与外部对账结果。

灾难恢复另设安全状态门禁:开放正常认证前作废备份中的全部会话和未使用登录 Key,防止已注销/消费的凭据复活。恢复点后的改密、长期 Key 撤销、角色/成员/插件授权变更与独立受信记录核对;缺记录或无法证明当前有效性时,相关主体/资源保持鉴权隔离,有权 owner/运维重新建立凭据或确认授权后开放。新执行 epoch 不代替认证撤销。普通启停/更新不执行此规则;普通账户恢复保留集成 Key 的合同也不允许灾难恢复盲目启用旧 Key。

PG逻辑pg_dump用于独立逻辑恢复;PITR须有物理基础备份和连续WAL,不能用pg_dump冒充其基础备份,依据PostgreSQL备份与PITR。SQLite使用现有一致性备份接口,不能在线只复制.db遗漏WAL。文件内容先耐久发布再提交DB引用,备份保留覆盖恢复点所需的对象版本。

备份副本及密钥与原数据同级保护:加密、专用受限目录/角色、异机或异故障域副本、保留期限与清理owner;密钥须独立可恢复。拟定每日日常备份、持续WAL、每月及重大升级前恢复演练,频率按批准的RPO与数据量固定。实际恢复须验证登录、跨项目隔离、历史文档与附件、任务/HITL、工件下载和插件业务链;恢复时间从故障触发开始计时。

部署先构建并检查完整候选目录,再切固定组合;失败仅在schema/插件/配置向后兼容时回退程序。数据回退须对账回退点后已接受业务和外部效果,不能“恢复旧库”而静默丢失新写入。

7. 实施增量与依赖

以下U-ID属于本案,与协作存储案的同名U-ID独立。每个增量完成设计细化、自检、正式验收与Review后按policy/delivery.md人工评审;本次不授权实施。新增文件名是建议,最终按实际模块边界落位。

U1. 实例与数据恢复身份

当前 manifest/DB identity 基础已在源码,既有合同见 Dev docs/recovery/u1-identity-manifest.md;本轮只规划消费缺口、派生修复和显式 init/restore,不重复建立身份层。

对应R2/R3/R9;无前置。范围:scripts/svc.sh、docs/svc.md、tests/test_svc.py、tests/test_svc_precheck.py及新增manifest/identity验证模块。 做完整配置、逐字段覆盖、原子revision与操作锁、稳定秘密引用、DB/数据目录身份marker、独立init/restore入口;保留现有dbref修复并提供旧登记一次性兼容迁移。先补现状刻画回归,再改解析器。 验收:连续两次裸启停保持同一DB与原始记录;只改端口保持data/plugins;单独data覆盖按合同生效/拒绝;配置迁移每个写边界kill后只能读到完整旧/新版;缺凭据、错DB、丢marker不得新建空系统;更换二进制路径/程序版本或以不同cwd调用svc后,DB identity、数据/工件/工作区根与插件组合保持一致(对应R2更换程序/cwd条款)。使用真实PG特有测试记录核对,桩测试只能辅助。

U2. 服务组与进程监管

当前 recover/watchdog/desired 已有首切片,余量为服务组、依赖 readiness、唯一监管与完整操作恢复,不再记为全部未实现。

对应R1/R8;依赖U1。范围:svc组编排、可选Redis适配、分发仓deploy/agent-system.service及安装接线、新增隔离服务组故障测试。 按KTD3/KTD4建立依赖图、desired state、可恢复lifecycle operation与单一监管;加入boot/start/exe身份与进程组校验。必需/可选、受管/外部资源由manifest决定。首次切监管在明确停机窗口完成,失败回到原监管且不形成双启动。 验收:全组停止后启动;先PG后应用;主动stop保持停止;kill自动退避恢复;主机重启按策略恢复;PID复用、残留子进程、端口冲突、依赖启动失败、不透明外部DB均正确拒绝/报告;不得停止其他服务组的共享依赖。

U3. 应用启动、停机与真实就绪

对应R1/R8/R10;依赖U1,可与U2并行。范围:modules/backend/internal/app/app.go、runmgr/runmgr.go、runmgr/events.go、plugins/host_v2.go、server健康接口及其生命周期测试。 统一lifecycle context、准入开关、消费者停止与worker join;恢复执行移到装配结束;不同停机原因统一条件结算;sidecar受监督并纳入收口;readiness、恢复摘要和受限诊断按KTD12实现。停机预算属预算来源变更:现状HTTP停机超时仅5秒且超时即返回、跳过后续事件清冲(app.go停机序列,SELF源码核验);“应用30秒/监管60秒”须显式替换该常量并按最长合法步骤与故障实测校准,不得沿用旧值。 验收:插件/预算/Workflow未装好不启动恢复任务;停机同时提交新run被拒;SSE与慢工具不跳过flush/清理;子run不被cancelled覆盖;sidecar崩溃可检测;DB坏/恢复扫描失败不ready;hold不影响无关会话的正常读写。

U4. 持久任务恢复与副作用隔离

对应R4/R5/R7;依赖U3,并复用U5所有权接口;U4/U5作为同一安全交付批次,完整性验收前不开放自动续跑。范围:runmgr/runtime工具入口、store运行/恢复/hold表、checkpoint适配、Core公开状态及Frontend兼容、对应恢复与真实工具替身测试。 落实KTD7/KTD8,主run、Workflow、子代理、已配置后台executor共享恢复政策;持久化步骤/输入/结果/operation;自动hold连接到工具与启动扫描。HITL保持gate、答案和权限;修复子代理恢复链唯一性。文件checkpoint仅承担其明确的文件修改范围;若新增recovering/hold为公开状态,设计阶段先交付Core枚举→Backend→Frontend及下游契约的最小状态映射表,作为设计评审前提。 Frontend 接线复用任务/对话卡和系统管理区:有权 owner/admin 查看具体 operation、未知原因和证据引用,提交已完成 receipt、未执行终态及旧请求隔离证明,或明确放弃。提交防重复,失败保留输入与原因,成功重载权威状态;Backend 按 operation 权限/版本/epoch 裁决,不以当前整 run release API 代替逐项对账。浏览器验收覆盖发现 hold→对账→剩余步骤恢复,拒绝越权和「放行即重跑」,不新增页面。 验收:排队/主run/子run/HITL/索引各类任务kill并两次恢复;工具“完成外部动作、结果未提交”只hold不重做;查询暂未发现但外部请求稍后完成时不发送第二次请求;幂等目标使用同operation key;Native pending intent只由统一账本决策,不生成普通错误后绕过hold;确认一个operation完成后执行剩余必要步骤,不假结算整run;未知工具默认隔离;撤权后禁止续跑;无executor类型明确blocked。真实模型最小任务级恢复对照单独申请测试资源,未跑标NOT_RUN。

U5. 所有权、额度、命令、通知与投影闭环

对应R4/R5/R6/R10;依赖U3;与U4共同验收。范围:store/execution.go、store/admission.go、runmgr/admission.go、runmgr/commands.go、server取消/HITL、notice、surfacesvc与PG集成/多进程故障测试。 续租及失权取消覆盖全部入口;持久队列/额度对账与共享唤醒;命令lease+处理后ACK;notice事务消费;Surface成功后ACK且持续批量drain。租约到期后的外部不确定行为仍进入U4 hold。 验收:占满额度kill后新工作可前进;长run跨租期不失权;旧owner晚到状态/事件/工件被拒;claim后kill命令可重领;取消正确解析conversation并不越权;HITL答案一次生效;notice及子代理并发恢复只建一个后继;超过100条投影事件全部处理,发送失败不清pending。两真实进程与真实PG是必要证据。

U6. 文件发布、组合备份与部署回退

对应R3/R7/R9;依赖U1/U3,恢复账本完整验收依赖U4/U5。范围:blobstore/content、workspace与checkpoint、插件恢复适配合同、svc deploy/update/backup、分发仓安装、备份manifest与restore测试。 按KTD11发布不可变版本,扫描缺失/孤立对象;登记全部插件/下游存储;按第6节生成一致恢复点、保护副本、恢复到新目标;候选完整构建后切版本,保留兼容上一组合。 验收:对象写后DB提交前kill;覆盖发布过程中kill;日志尾截断/文件缺失保留现场;备份恢复后原文hash/权限/插件链一致;恢复新目标后旧实例复活仍不能产生外部效果,未完成旧写路径隔离时新目标不调度;恢复点后已发生的外部效果对账完成前保持hold;升级ready失败可在兼容条件下回退;不兼容schema拒绝程序自动回退;数据回退不静默丢新写入。 另覆盖恢复点后注销会话、消费/撤销登录 Key、撤销集成 Key、改密与撤回角色/项目/插件权限:恢复旧包后旧会话和登录 Key 不再有效,无法证明有效的长期 Key/授权保持隔离;owner 正常确认后的新凭据可用。与普通重启原密码保持分别签收。

U7. 故障验收与交付放行

对应全部需求;依赖U1–U6。范围:Dev新增隔离tests/integration/recovery/、故障注入控制点、运行手册与需求证据表。 固定Dev/全部组件、运行制品与配置摘要;使用脱敏数据、独立端口/PG库/文件根及工具目标;记录故障点和恢复统计。先执行小规模逐点故障,再在批准规模混合运行。破坏性kill/断电测试只能在隔离环境实施。 验收:第8节每类故障均有可重放证据;RPO=0档已确认数据零遗漏,灾难档恢复点差距≤批准RPO且点内数据零遗漏、点后缺失和外部效果明确对账;零未识别重复副作用、零越权/双有效写者;时间满足批准指标;用户实际刷新、重连、登录、任务/HITL、文档和工件链通过。服务恢复与数据/任务恢复分别签收。

建议交付批次:A=U1/U2/U3(可靠启动及安全停机,自动任务续跑关闭);B=U4/U5(安全自动续跑与命令闭环);C=U6/U7(文件耐久性、灾难恢复及全系统签收)。只交付A不能宣称完成用户的全部需求。

U8. SQLite 活动服务隔离与既有存储门禁

覆盖 R2/R3/R6/R11/R12,复用 U1 身份;近期优先于自动恢复和共享库测试。范围:Backend internal/app/app.go、internal/store/store.go、internal/store/system_identity.go、新增 internal/instanceguard/(拟定);Dev scripts/svc.sh、docs/svc.md。锁只控制活动服务运行时,CLI 身份/schema 合同由账户方案 U7 负责。负责人待实施批准时指定。 依赖:既有 U1;联合确认 service/CLI/migration 模式,先刻画原例和别名冲突,再实现;不直接把当前 dev 数据迁往 PG。 测试:新增 Backend internal/instanceguard/guard_test.go,扩展 internal/store/system_identity_test.go;Dev 新增 tests/test_svc_storage_ownership.py,复用 tests/test_svc_identity.py。新增路径为计划。 T8-1:两个独立进程同时启动同 SQLite,只有一个进入迁移/恢复;第二个明确拒绝,第一个 run/HITL 不变。 T8-2:直接二进制、legacy、符号链接和可构造的文件别名不能绕过;真实 DB 替换/锁根丢失被识别。 T8-3:活动服务 SIGKILL 后合法同实例可重启,OS 释放锁;主动停止不被 watchdog 错拉起。 T8-4:服务运行期间合法账户 CLI 可提交短事务;错实例/缺库拒绝、不建目录、不 migrate;迁移不与活动服务并发。 T8-5:更换端口、cwd、程序版本保持同一资源;测试候选默认独立根,显式指定共享根也不得绕过保护。 T8-6:参数来自应用实际连接;SQLite FULL/PG 同步设置按批准合同核验,磁盘满/写失败不返回成功。 环境:独立 SQLite 根与端口、真实 PG 独立库回归;双进程和 kill 禁止在当前 dev/candidate 执行。全部 T8 当前 NOT_RUN。

近期批次 A0=U8+账户方案 U7/U5:先稳定数据归属和账户链路;另行授权后完成 dev 原账户正常登录/重启/更新验收,见账户方案 §7。A/B/C 保留;U4/U5 联合验收后才开放自动续跑,A0 不代替全量恢复。

8. 验证合同

故障/操作 最小判据 对应需求
svc stop/start连续两次、restart、地址与版本变更 原DB identity、记录/权限、data/plugins/artifacts根和固定组合一致 R1/R2/R3
SQLite 双进程、直接启动、别名、锁释放 第二活动服务在迁移/run对账前拒绝;合法 CLI 可用;kill 后同实例可重启 R2/R6/R11
实际 DB 耐久档、权威文件发布点、磁盘满 参数来自应用连接;确认结果已耐久;失败不发布成功;旧版本可读 R3/R7/R12
应用SIGTERM/SIGKILL/OOM 持久数据完整;安全工作可恢复;额度无永久占用;无残留写者 R3/R4/R6/R8
PG停止/重启、断连或提交响应丢失 不切SQLite;不假接受;按幂等记录对账;重连后可用 R2/R3/R5
主动stop与异常退出、监管/编排中途kill 主动停止不拉起;异常按running意图恢复;操作重复安全 R1/R2/R8
PID复用、重复启动、旧进程冻结后复活 不误杀其他进程;一个有效owner;旧epoch无发布/副作用权限 R1/R5/R6
排队、HITL、跨实例取消、子代理/notice恢复中途kill 接受意图不丢;审批一次生效;后继run/消息不重复 R4/R5/R6
外部效果完成而结果未落库、旧请求延迟完成 自动hold、有receipt对账和权限检查;暂未发现结果不触发非幂等重试;动作结算后继续未完成步骤 R4/R5
文件rename/发布边界kill、磁盘满、文件/日志截断 不确认未耐久结果;原发布版本一致;损坏可定位、原件保留 R3/R7
插件sidecar退出、插件存储缺失、索引重建失败 必需失败不ready;可选明确降级;数据与授权保留 R1/R3/R7/R8
SSE断开/页面刷新、>100待投影、投递持续失败 cursor补齐持久事件;旧事件丢弃;失败记录不被ACK R4/R7/R10
主机重启/断电 原数据盘恢复、全部期望服务重新就绪、耐久性指标有真实证据 R1/R3/R7/R8
完整备份在新目标恢复、旧实例复活、升级失败/回退 DB+文件+秘密/版本匹配;旧写路径隔离;恢复点后外部效果对账;灾难档满足批准RPO;权限与业务链可用;回退兼容且不静默丢新写入 R3/R5/R6/R9/R10

验证分层:svc治理桩回归→Backend单元/SQLite→真实PG及多进程→真实工具替身故障链→少量真实模型→真实浏览器与主机级故障→灾难恢复。每层证明不同合同,不相互替代。

执行时使用项目已有make test-governance、make test-backend-pg、make test、make build并增加受管隔离recovery入口。真实PG/Redis/模型/主机测试缺环境时必须明确NOT_RUN或SKIP及缺项,禁止把零用例或条件跳过当通过。按AGENTS要求验收/请求审核/合入前分别task-sync;所有precheck、同步、测试原始输出通过make evidence进入任务卡,并区分SELF/REPLAY/GATE。业务行为面的真实模型验证遵循policy/delivery.md,单测不替代。

本轮只进行源码与文档检查,业务/数据库身份查询、故障注入、模型试验、迁移、服务启停、部署均NOT_RUN。方案r2不能作为恢复能力验收结论。

9. 兼容、在途任务与批准事项

本案补充多人协作存储与分布式演进方案的R7及恢复/运维合同;不覆盖其批准记录或迁移、多实例放行条件。已存在的执行所有权、命令、outbox实现以当前代码为基础继续,不另起重复模块。

原 r2 的 svc-autorecover 二选一属于历史启动现场;当前 recover/watchdog 和 U1 身份已有源码,不再要求重新启动同一基础任务。新实施前仍核对在途 svc 监管/RDM UI/端口回收任务,避免重复修改入口。RDM/插件只有纳入兼容与恢复清单并验收后才能覆盖「所有数据」承诺。

备选:仅修svc脚本成本较小,只能覆盖启动配置/进程,无法满足在途任务和副作用恢复;另造常驻恢复daemon会增加新的监管者与恢复责任,首期无需。推荐现有svc+系统监管+Backend恢复协调的职责划分。

实施批准时需要固定:目标实例/服务组及独占管理权、实际数据归属清单、故障档与RPO/RTO、正常停机预算、既有在途任务分工。本文已给默认建议;没有这些确认不在共享运行环境做故障/迁移/监管切换。后台是否自动重做模型回合可按任务政策配置,但未知副作用不得靠默认重试放行。

r3 编制期的历史下一任务建议(不是当前批准状态)为 U8 与账户方案 U7/U5 的首批设计/实施评审:先保护 dev 原数据,补足账户一致性与登录语义;随后 U3 停机/就绪、U4/U5 任务恢复、U6/U7 文件与灾难演练。新增 U8 未批准前不实施。数据清单、故障档预算、正式组合验证仍为放行条件;PG 迁移、监管切换、备份恢复、部署和故障实验分别授权。

10. 全系统完成条件

只有在所有启用的必需服务/插件被纳入清单、R1–R12均有当前组合与运行对象的重放证据、第8节故障矩阵通过、未知副作用有可处理隔离状态、组合备份实际恢复成功、人工验收及代码Review通过并完成合入/部署后,才可声明“系统停止或异常终止后能够恢复所有服务和数据”。

编制期历史交付状态止于待审核方案;当前批准与进展见页首及第 11 节。批准不等于全系统恢复已验收,不以记录、源码能力、桩测试、健康接口或本地文档检查代替上述完成条件。

11. D1/D2/D3 部署根治补充(r3.1)

编号 D1/D2/D3 只属于本补充,避免与本案 U1–U8 重名。已合入且不再重做:Dev #474(按实例重置身份变量、停旧前候选门禁、update 走 manifest 迁移、普通启动只验证)、Backend #180(pkg/identity)、RDM #276(identity 子命令与未知位置参数退出)、Dev #477(backend pin 到含 #180 的 acabb85)。

11.1 现场快照与 rdmcand 数据归属(SELF,2026-10-12T13:39:21Z)

快照目录(在线 sqlite3 .backup / RDM VACUUM INTO,未停服务):

  • runtime-backups/{dev,rdm1,rdmcand}/20261012T133921Z
  • runtime-backups/{dev,rdm1,rdmcand}/domain-20261012T133921Z

只读核对副本,未移动任何业务数据。rdmcand 在 2026-10-12T06:51:14Z–10:02:40Z 使用 runtime/dev/data。该窗口内:

  • dev 库的会话、消息、事件均不落在窗口内。最后一条消息时间 06:50:43Z,早于 rdmcand 挂上该目录。会话与用户属于 dev(owner weichen、genui-smoke)。
  • 窗口内仅有 13 条 command_audit,全部是 rdm.inspect.*,owner weichen,没有领域事件。
  • 三份 rdm.db 的 events 均为 0。dev 的领域库更大(含权限表,schema_version=6),rdm1/rdmcand 的领域库只有字典与 schema 行。
  • rdm1/rdmcand 的 agent-system.db 只有 2026-10-12T13:19:47Z / 13:20:12Z 由候选门禁写入的身份行,没有业务行。

处置(未执行,需逐项授权):不把 dev 的会话或领域库迁到 rdmcand。没有一份应属于 rdmcand 的历史领域数据需要回迁,因此不触发身份 adopt 重审。rdm1/rdmcand 当前库是空壳加已建立的身份行。

11.2 D1 身份流程收尾

对应 U1 的消费缺口。完成条件:

  • 身份 CLI 只打开既有库:不建文件、不建目录、不迁移。agent_system_identity 表(v27 起,列冻结为 instance_id/dbref/schema_version/created_at)缺失则拒绝并提示受控迁移。比本二进制更旧或更新的 schema,只要该表仍在,verify 仍可读。
  • verify 输出带 schema_version。DSN 走 AGENT_SYSTEM_DSN 或权限恰好 0600 的 --dsn-file;--dsn 保留并告警。
  • 能力文档一份 JSON,经 version --json 与 identity capabilities 输出。探测只跑 version --json,在空目录、清空环境、超时内执行。非 JSON 视为无能力。
  • 顶层未知子命令 exit 2,不以 - 开头才拦截,避免落入 serve。
  • manifest identity.db_marker:fresh | pending | established。新实例首次部署目标目录必须为空,启动建库后 ensure 变为 established。领养时有能力且库已存在则 ensure,否则 pending。pending 只在库文件存在时补齐,不迁移。established 缺行或不匹配时,start/restart/deploy/update/recover/init 全部拒绝;唯一修复入口是显式 adopt --confirm <instance_id>,并先打印库大小、关键表行数和最近写入时间。
  • svc_lifecycle 是 start/restart/deploy/update/recover 的共同入口。update 与 recover 逐服务以 env -i 启动子进程,不传入 AGENT_SYSTEM_*。serve 使用显式环境,并写入 0600 的 serve.env 供裸启动与 recover 复用。

11.3 D2 切换事务

对应 U6 的部署回退。制品进入 runtime/<name>/releases/<rid>/,current 用临时符号链接加 mv -T 切换。bin/plugins/htdocs 改为指向 current/ 的稳定链接。切换日志 deploy-journal.json(0600)先写 phase 再执行。失败回到上一版本并恢复原期望运行态,不覆盖业务库。schema 不兼容时在停旧服务前拒绝。前进迁移必须 deploy --migrate-schema --confirm <instance_id> 且先 backup,日志标记 forward-only。能力文档增加 schema.target 与 schema.compatFloor;每条迁移登记是否破坏旧版读取,漏登记则测试失败。

11.4 D3 真实组合门禁

对应 U7 的故障验收入口,范围限于部署生命周期。Dev 用 tests/integration/lifecycle/rdm.pin 固定 RDM SHA(当前、上一版、无 identity 的旧版)。CI test-pg 按 pin 取固定 SHA,白名单加入 RDM 及其 pins。make test-lifecycle 缺 PG、源码或制品即失败,不静默跳过。桩测试不能代替该门禁。RDM 登录入口与账户 CLI 的 DSN 退出命令行依赖账户方案 U7/U5;未就绪的项保持 NOT_RUN,D3 不得标完成。

11.5 验收

缺一项则对应增量保持未完成:双向混合批次身份不串用;无 manifest 时 deploy/update/recover 一致;无能力、缺行、不匹配在停旧前拒绝且不建库;两次裸重启及换端口、换版本、换目录后身份不变;构建失败、启动即退、健康超时、工作台失败可回滚;切换中断后登记与制品一致;真实 PG 绑定与多实例检查通过且凭据不进命令行、日志和证据;RDM 页面、登录、领域命令和原有数据可验证,不能只看 HTTP 200。