Files
qinglong/docs/adr/ADR-0165-trusted-tool-terminal-failure-completion.md
T

6.7 KiB
Raw Blame History

ADR-0165Trusted Tool 失败与超时终态完成协议

  • 状态:Accepted
  • 日期:2026-07-26
  • 关联:ADR-0156、ADR-0158、ADR-0162 至 ADR-0164RFC D-152 至 D-154

背景

ADR-0163/0164 已关闭 Trusted Tool 的成功持久化与 Profile-neutral 成功协调, 但 adapter 明确失败和 deadline 到期仍会让 StepRun 停留在 running。如果每个 Profile 自行解释异常,低配路由设备和集群节点会产生不同的终态、重试和敏感信息 持久化语义;若把异常对象、stack 或未知 output 写进 RunEvent,又会扩大泄露面。

失败终态还必须与成功 completion 竞争收敛。仅在应用层先查一次成功记录不能证明 互斥:并发调用、事务提交响应丢失和 PostgreSQL 主库切换都可能让调用者观察到未知 结果。

决策

1. 失败完成只保存固定低敏事实

runtime-core 新增显式 subpath @qinglong/runtime-core/tool-execution-failure-completion。v1 只接受:

  • failedtool_adapter_failed / Trusted Tool execution failed
  • timed_outtool_deadline_exceeded / Trusted Tool execution deadline exceeded

completion 必须绑定 exact start、Project、Run、StepRun、Tool、adapter、Run version、 event sequence、mutation 和 event identity。它不保存 raw error、stack、adapter output 也不创建 Result Artifact 或取得 result key authority。

2. 成功与失败 completion 必须原子互斥

SQLite 与 PostgreSQL 各增加 append-only failure completion 表和 repository。失败提交 必须在一个事务内完成:

  1. 复验 start barrier、当前 running Tool StepRun、非 terminal Run
  2. 拒绝同一 start 已存在的成功 completion
  3. 将 StepRun 原子迁移到 failedtimed_out,保持 outputRef=null
  4. 推进 Run version/event sequence
  5. 写入固定低敏 RunEvent、StepRunMutation 与 failure completion。

成功 repository 同样必须在事务内拒绝同一 start 的失败 completion。SQLite 使用 BEGIN IMMEDIATE 串行化双方竞争;PostgreSQL 使用 serializable transaction,并锁定 exact StepRun/Run。两侧都只允许完整命令 exact replay,任何部分身份复用、跨表镜像 漂移或同时存在成功/失败记录都失败关闭。

SQLite capability 推进到 v32,共 55 张受管表;PostgreSQL control-core capability 推进到 v33。PostgreSQL runtime role 对 failure completion 只取得 SELECT, INSERT admin、Package manager、Package executor、Worker ingress 与 PUBLIC 默认拒绝。

3. 一个协调器裁决所有 Tool 终态

runtime-core 新增显式 subpath @qinglong/runtime-core/trusted-tool-completion,由 executeAndCompleteTrustedTool 固定编排:

  1. 执行 adapter 前同时读取 durable success/failure winner
  2. 只有两者均不存在才调用既有 Trusted Tool execution
  3. adapter 抛错后再次读取 durable winner,先收敛并发或未知提交结果;
  4. 只把明确的 TrustedToolExecutionFailedError 归类为 failed
  5. 只把明确的 TrustedToolExecutionDeadlineExceededError 归类为 timed_out
  6. Artifact、key、binding、snapshot、storage 等缺失或损坏继续保持非终态并失败关闭;
  7. failure commit 响应丢失后只读取 durable winner,同一次调用不得重新执行 adapter。

返回值是 succeeded | failed | timed_out 的判别联合。固定 system actor、start-derived dedupe、mutation 和 event identity 均由协调器生成,不接受 transport 注入。

4. 保持双 Profile 的资源边界

现有 LocalSqliteTrustedToolStorageClusterTrustedToolStorage 增加 failureCompletions port,不新增 workspace package或第三方依赖:

  • edge/standalone 仍使用首次请求才动态加载的 SQLite 单例 bundle,不新增连接、 timer、watcher 或 socket
  • cluster-control 仍只通过受审 @qinglong/cluster-postgres/runtime 组合入口, 在现有单一 Pool 上装配同构 bundle
  • runtime-core root、PostgreSQL root/admin/ingress 和生产 transport 不获得执行 authority;生产 allowlist 仍只有 run.getrun.cancel

被否决方案

  1. 把异常序列化进 Artifact 或 RunEvent:扩大敏感信息面,且失败终态不需要解封。
  2. 只有失败 repository 检查成功记录:晚到的成功事务仍可穿透,无法证明互斥。
  3. 用任意异常自动终态化:会把 key 丢失、binding 漂移或数据库不可用误报为业务失败。
  4. 协调器捕获后重跑全部流程:会重复 adapter 副作用。
  5. 为两个 repository 各拆一个 package:没有独立部署或依赖边界,只会加重 路由设备安装、构建与审计成本。
  6. 因终态闭环而开放生产 route:result key 生命周期、人工恢复和专属故障注入 尚未完成。

验证

  • runtime-core331/331,覆盖成功、失败、超时、固定事实、并发 winner、响应丢失、 adapter-call-count=1、前置条件缺失保持非终态和显式 subpath;
  • local-sqlite119/119,真实 SQLite 覆盖原子失败提交、exact replay、成功/失败双向 排斥、固定事实与 lazy storage bundle
  • cluster-postgres181 pass / 1 条件 skip,覆盖事务 SQL、双向排斥、typed schema、 migration checksum、readiness、六角色权限和 composition entrypoint
  • cluster-control139 pass / 2 条件 skip,覆盖单 Pool storage 装配与依赖边界;
  • 21 个 QL3 workspace package 全量 build/test 为 0 failpackage 数量和第三方依赖 均未增加;edge import、cluster dependency、cluster deployment 审计无 finding
  • 全新 PostgreSQL 18 六角色真库 integration42 pass / 1 条件 skip / 0 fail,直接 覆盖 timed_out 原子提交、exact replay、晚到成功拒绝、固定低敏事实与无 output 明文;
  • PostgreSQL 18.4 arm64 物理 HA 总门 passed=trueremote_apply、timeline 1→2、 旧主 fencing、双 control replica 重新 activation、pg_rewind 只读同步重加入及 既有领域 COMMIT-response-loss 门全部通过。该 HA fixture 尚未注入 Trusted Tool failure completion 专属 COMMIT-response-loss,不能用通用事务门替代该证据。

后续门禁

  1. 在真实 PostgreSQL 上执行新增 terminal completion integration,并给 Tool completion 注入 COMMIT-response-loss、主库晋升后 durable winner 和 adapter-call-count=1
  2. 给 SQLite 增加 adapter 返回后、failure/success 事务提交前后的进程 crash 矩阵;
  3. 建立 result key catalog、rotation、retention/rekey 与 key-lost 人工恢复;
  4. 按 Project current snapshot 构造短生命周期 adapter registry,再评审产品 use-case/transport 入口。