跳转至

20 · 可靠性、恢复与可观测性

RecoveryHot RestartWatchdogsOrphan ReapingOpenTelemetry

一句话结论: Paperclip 把故障当作持久化状态机的一部分:run、lease、workspace operation、wakeup、watchdog 和 activity 都能被扫描、分类、重试或转成显式恢复路径,而不是依赖“进程应该还在”。

1. 可靠性目标

对多 Agent 控制面而言,可靠性不是“API 返回 200”,而是:

  • 不重复执行同一任务;
  • 不留下无 owner 的 in_progress;
  • 不让终态被晚到事件覆盖;
  • 不让成本/预算越过硬停后继续启动;
  • 不让 workspace/lease/runtime service 永久泄漏;
  • Server 重启后能解释每一条 active run;
  • 人可以看到失败、证据和下一步。

2. Recovery Service 的视角

flowchart TB
  T["periodic tick / startup / mutation"] --> SCAN["load durable facts"]
  SCAN --> RUN{"run state coherent?"}
  RUN -->|no| R1["terminalize / retry / mark lost"]
  SCAN --> ISSUE{"issue has live path?"}
  ISSUE -->|no| R2["release / wake / recovery issue"]
  SCAN --> WS{"workspace/lease settled?"}
  WS -->|no| R3["finalize / cleanup / expire"]
  SCAN --> WATCH{"watchdog / monitor due?"}
  WATCH -->|yes| R4["create idempotent review wake"]
  R1 --> ACT["activity + notice + metrics"]
  R2 --> ACT
  R3 --> ACT
  R4 --> ACT

Recovery 不是一个总开关,而是一组按问题分类的 reconcile service。

3. Startup Reconciliation

启动时至少需要检查:

  • hot-restart intent/report;
  • DB 中 running/queued/scheduled retry run;
  • active process/provider metadata;
  • Issue checkout/execution locks;
  • environment leases;
  • workspace operations;
  • runtime services;
  • plugin workers/jobs;
  • stranded issues;
  • task watchdog/issue graph liveness。

每项都应有分类结果,不能笼统改成 failed:adopted、finalized while down、lost、skipped、retryable、needs operator 等。

4. Hot Restart Hand-off

旧进程收到重启请求时写入 hot-restart-intent.json,包含:

  • previous PID/identity/start time/version;
  • requestedAt、reason、drainRequired;
  • requestedByRun;
  • preflight active run IDs;
  • shutdown snapshot(run、adapter、process、issue、status)。

新进程启动后生成 report:

adopted
finalized_while_down
lost
skipped

Intent/report 采用原子写入和 stale lock 处理,避免两个重启流程覆盖对方证据。

5. Orphan Run 处理

Run 有 process PID、group/provider reference 和 heartbeat timestamps。恢复时:

  1. 判断 process identity 是否仍属于本实例/run;
  2. 检查 provider 是否还能查询状态;
  3. 若已结束,读取结果并 finalization;
  4. 若仍活跃,adopt/继续监控;
  5. 若无法确认,标记 lost/failed 并创建 recovery path;
  6. 清理 Issue lock/lease 或保留明确等待状态。

不能简单以 PID 是否存在判断:PID 可能已被系统复用。

6. Stranded Issue Recovery

Issue liveness scanner 定期找:

  • Agent-owned + nonterminal;
  • 没有 queued/running/scheduled retry;
  • 没有有效 wakeup/interaction wait;
  • 没有合法 review/blocked path;
  • 没有 active workspace finalization。

处理策略是保守的:

  • 保留 owner 证据;
  • retry 一次或 enqueue continuation;
  • 留 recovery notice/action;
  • 无法安全推进时 escalation/人工可见;
  • 不因为“看起来卡住”就随意改 Agent/删除 workspace。

7. Task Watchdog

Task watchdog 关注配置 Issue 及其非 watchdog 子孙。当整棵子树停止且没有 live continuation 时:

  1. 计算 stop fingerprint(叶状态、blocker、interaction、配置);
  2. 与 lastReviewedFingerprint 比较;
  3. fingerprint 未变化则抑制重复;
  4. 创建/复用一个 originKind=task_watchdog review Issue;
  5. wake watchdog Agent;
  6. scope enforcement 限制它只能操作 monitored subtree。

它不是 silent-run monitor,也不是全局“看一切”。

8. Silent Active-Run Watchdog

另一个 scanner 关注仍为 running 的 process 是否长时间无输出。它使用:

  • last event/log/progress;
  • timeout threshold;
  • provider/adapter classification;
  • process health。

可能结果:发 ping、创建 stop decision、interrupt/retry、转 recovery。不要把它和 task watchdog 合并,否则 active silence 与 stopped subtree 会产生错误动作。

9. Task Watchdog 的 scope enforcement

Watchdog run context 携带 watchedIssueId/watchdogIssueId/companyId。路由层拒绝:

  • 修改 watched subtree 外 Issue;
  • 跨 Company mutation;
  • 修改自身 watchdog 配置;
  • 创建另一个 watchdog;
  • 处理 Board-only approval/security-sensitive interaction;
  • 绕过 typed reviewer/execution policy。

安全约束在 Server 执行,而不是写在 watchdog Prompt 里。

10. Recovery Action 与 Notice

恢复动作要成为可见资源:

  • recovery action kind/status;
  • source run/issue;
  • recommended/allowed operation;
  • actor/result/error;
  • resolved time。

Notice/comment 应说明:

  • 发现了什么证据;
  • 系统采取了什么动作;
  • 哪个 lock/lease/run 被影响;

  • 还需要谁做什么。

这样 Board 不会看到任务“莫名其妙”回到 todo。

11. Provider Failure Classification

Adapter/provider 错误要分类:

类别 例子 恢复
transient 429、短暂网络、provider busy scheduled retry
setup binary/key/config 缺失 fail fast + operator fix
auth token 过期、grant revoked reauth/approval,不盲重试
workspace provision/sync/finalize operation retry/recovery
policy budget/permission/approval blocked/skip,等待治理
logic contract bug、invalid result failed + visible escalation

重试次数和 backoff 按类别,而非所有 Error 一样。

12. Scheduled Retry Promotion

Retry 记录 next attempt time、retry-of、reason、generation。Server tick 到期时:

  • 检查 Company/Agent/budget/policy 是否仍可执行;
  • 处理 Issue lock/workspace compatibility;
  • 把 run/wakeup promotion 到 queued;
  • 若条件仍不允许,重新 schedule 或 skipped with reason。

不要在 Worker 内 setTimeout 直到几小时后;进程重启会丢失。

13. Health 与 Runtime Service

Environment、connection、plugin worker、runtime service 都有 health read model。健康失败不一定等于直接删除:

  • 记录 last check/message;
  • 触发 degraded/error event;
  • 阻断新 acquire 或 tool call;
  • 保留外部 reference 以便修复;
  • 背景 sweep 再检查。

14. Observability:结构化日志

Server middleware 对 request/run/plugin/tool 做结构化 logging 和 redaction:

  • request ID/actor/company/run;
  • route/action/status/latency;
  • provider/adapter/error class;
  • cleanup/recovery classification;
  • secret/token/body redaction。

日志用于诊断,不替代 activity/cost/run event 的业务事实。

15. OpenTelemetry

当前 OTel 集成是 opt-in、traces only:

  • 未设置 OTEL_EXPORTER_OTLP_ENDPOINT 时不加载可选依赖;
  • 支持 grpc、http/protobuf、http/json exporter;
  • HTTP、Express、PG 等 auto instrumentation;
  • fs/dns/net 默认关闭以避免噪音;
  • 不在此集成中导出 metrics/logs。

这表示结构化业务事件与 OTel trace 是互补的:trace 看请求/跨服务时间,DB event 看可审计业务事实。

16. 可靠性中的不可变证据

以下记录最好 append-only 或只允许受控状态推进:

  • heartbeat run event/log;
  • activity log;
  • cost event;
  • document revision;
  • approval comment/resolution;
  • tool invocation/audit;
  • recovery action;
  • plugin job run。

当前状态可以修正,历史证据不应被“恢复成功”覆盖。

17. 失败注入的关键问题

可靠性测试应问:

  • 在 Issue checkout 后、Adapter 启动前崩溃会怎样?
  • Adapter 已启动但 PID 尚未保存怎么办?
  • Run terminal 写入后 lease release 失败如何恢复?
  • cost 写入成功但 budget pause 失败会否继续启动?
  • WebSocket 断线 30 秒,UI 是否能对账?
  • plugin worker 在 action 中途退出,job 是否能重试?
  • pipeline automation ledger 写入后进程崩溃,会不会重复创建 Issue?

这些问题比“HTTP 500 是否被捕获”更接近控制面可靠性。

18. 当前边界

  • live event bus 进程内,无跨节点 replay;
  • local process/runtime service 依赖持久主机;
  • provider 状态可不可查询影响 adoption 质量;
  • recovery scanner 复杂且可能产生保守的人工升级;
  • OTel 不提供现成业务 metrics dashboard;
  • plugin UI/Worker 的信任边界仍不同。

19. 可复用经验

  1. 把启动对账、运行扫描、lease sweep 和 watchdog 作为正式服务。
  2. 持久化 intent/report,让重启交接可解释。
  3. PID/Provider reference 要尽早记录,且按 identity 验证,不只看 PID。
  4. liveness scanner 按“没有下一跳”找问题,而不是按静态 status 猜。
  5. Retry 需要 failure classification、next time 和 generation。
  6. 恢复动作要变成可审查资源,不能只写 debug log。
  7. 业务 audit/event 与 trace/log 分层,分别服务事实与诊断。

源码入口

  • server/src/services/recovery/service.ts
  • server/src/services/recovery/issue-graph-liveness.ts
  • server/src/services/recovery/run-liveness-continuations.ts
  • server/src/services/issue-liveness.ts
  • server/src/services/task-watchdogs.ts
  • server/src/services/task-watchdog-scope.ts
  • server/src/services/hot-restart.ts
  • server/src/services/managed-resource-drift.ts
  • doc/TASK-WATCHDOG.md
  • doc/observability.md

可靠性必须被测试和发布流程持续验证:21 · 测试矩阵、CI 与发布。