Files
qinglong/docs/adr/ADR-0158-atomic-tool-execution-start-barrier.md
T

172 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0158:同事务 Tool Execution Start Barrier
- 状态:Accepted
- 日期:2026-07-26
- 关联:ADR-0032、ADR-0133、ADR-0154、ADR-0155、ADR-0156、ADR-0157
RFC D-03/D-29/D-30/D-131/D-148/D-149
## 背景
ADR-0155 已建立 snapshot-bound handler binding 和 fresh Policy admission
ADR-0156 已建立双方言 StepRun aggregate、Run/Event fence 与 mutation ledger
ADR-0157 已能原子保存 Trace、Audit 和 receipt。
但单独 `prepare(evidence)` 仍不是“允许 adapter 开始副作用”的证明。若 evidence 提交后、
StepRun CAS 前崩溃,数据库会留下允许审计,却没有 `running` 状态;若先把 StepRun 改为
`running` 再保存 evidence,则 adapter 或恢复器可能观察到一个没有 Trace/Audit 的已启动
步骤。多副本还可能使用相同 StepRun 发起两个不同调用,或者把一次失败后的重试误判为
第一次启动的重放。
启动授权必须把 immutable plan/admission、耐久 evidence、StepRun 状态变化、RunEvent、
mutation ledger 和可恢复的 barrier 收敛成一个数据库事务,同时保持低配路由器的单连接、
零常驻后台任务边界。
## 决策
### 1. 使用纯领域 command 和低敏 barrier record
`@qinglong/runtime-core/tool-execution-start-barrier` 是现有 `runtime-core` 的显式
subpath。它定义:
- `qinglong/tool-execution-start-command@v1`canonical JSON 最大 64 KiB
- `qinglong/tool-execution-start-barrier@v1`canonical JSON 最大 16 KiB
- domain-separated `commandDigest``barrierDigest`
- `ToolExecutionStartBarrierRepository``prepare`、按 `startId` 查询和按
`(runId, stepRunId, startedStepRunVersion)` 查询。
command 必须精确绑定:
1. Project、current snapshot、Tool Definition 和 trusted handler binding
2. action/plan/admission digest、Profile、execution class、timeout 和 Policy fence
3. reviewed adapter、redaction contract、audit contract 的 identity 与 digest
4. 可选的 exact approval request/dispatch/digest
5. 同 Project、同 Run 的 Tool StepRun
6. Trace anchor、Audit event 与 Audit receipt
7. `ready``waiting_approval``running` 的 StepRun mutation 和对应 RunEvent。
admission、command 和 barrier 都不得携带 input、Secret、handler、函数、module path、
URL、execute seam、Tool output 或原始异常。
### 2. StepRun 必须先存在,barrier 只负责原子启动
产品编排先创建独立 StepRun,并把它推进到 `ready`;需要审批时则推进到
`waiting_approval` 并绑定 exact approval request。start barrier 不创建 StepRun,也不
负责解析 Workflow。
repository 在一个事务中按固定顺序执行:
1. 查找所有 start/replay identity,完全相同则返回 `existing`
2. 锁定并复验 Run、StepRun、version、digest、状态、Definition 和 Project
3. 插入 SecurityAuditEvent、Trace anchor 和 Audit receipt
4. compare-and-set StepRun 为 `running`
5. compare-and-set Run version/event sequence
6. 插入 RunEvent 和 StepRun mutation ledger
7. 插入 immutable start barrier
8. commit 后才允许 composition 调用 adapter。
任一步失败必须回滚全部事实。mutation ledger 的 `committed_at_ms` 使用数据库事务时钟,
不能把调用方的业务事件时间冒充提交时间。
### 3. 重试 identity 必须包含已启动的 StepRun version
同一 `startId`、mutation、RunEvent、Trace、Audit 或
`(runId, stepRunId, startedStepRunVersion)` 只能绑定同一 barrier。完全相同的响应丢失
重放返回 `existing`;内容漂移返回 semantic conflict。
identity 不能只使用 `(runId, stepRunId)`。一个真实 adapter 在恢复后可能把
`lost → ready → running` 推进到下一次尝试;新的 StepRun version 必须能够拥有新的
barrier,同时历史启动事实保持不可覆盖。
### 4. SQLite 与 PostgreSQL 使用同一领域协议、不同事务机制
SQLite
- `0055-tool-execution-start-barriers` 新增 `ToolExecutionStartBarriers`
- `0056-capability-v28``local-control-core` 推进到 v28
- repository 复用单一 `LocalSqliteOperationAuthority``BEGIN IMMEDIATE`
- foreign key 把 barrier 绑定到 StepRun、mutation、RunEvent、Trace 和 Audit receipt
- `committed_at_ms` 使用 SQLite 数据库时钟。
PostgreSQL
- `pg-0030-tool-execution-start-barriers``control-core` 推进到 v29
- `ql3.tool_execution_start_barriers` 只授予 `ql3_runtime`
`SELECT, INSERT`,其他产品角色无权限;
- repository 使用短 `SERIALIZABLE` transaction、固定 statement/lock/idle timeout
和最多三次可判定 retry
- Run 与 StepRun 在 evidence 写入前一起 `FOR UPDATE`CAS 失败回滚;
- `committed_at_ms` 使用 `transaction_timestamp()`
两种方言读取时都重新执行领域规范化,并逐项核对 JSON、镜像列、mutation digest、
started StepRun digest、Trace digest 和 Audit receipt digest。损坏或多行 identity
必须失败关闭。
### 5. 不新增 workspace package
虽然协议跨越领域层和两种存储实现,但它没有独立部署、依赖或版本生命周期,因此:
- 纯领域协议留在 `ql3-runtime-core`
- SQLite adapter 留在 `ql3-local-sqlite`
- PostgreSQL adapter 留在 `ql3-cluster-postgres`
- 三者只通过显式 `tool-execution-start-barrier` subpath 暴露;
- root、通用 runtime、admin、package executor 和 worker ingress 不重新导出 authority。
文件少不是拆包理由,真实部署/权限/依赖边界才是 package 边界。
### 6. barrier 仍不执行 adapter
本 ADR 只关闭“副作用开始前是否已经原子提交完整授权事实”的缺口。repository 没有调用
built-in、process、MCP、HTTP 或 Worker adapter。当前 Tool execution 仍保持
production unreachable,直到后续完成:
1. opaque/encrypted invocation plan 与 redacted preview Artifact
2. adapter-specific start/result/recovery contract
3. trusted composition 在观察 `created|existing` barrier 后调用 adapter
4. post-start 不确定结果的 inspect/manual recovery
5. receipt retention/compaction 与物理 Edge 资源证据。
## 低配与集群资源影响
- 不新增 package、第三方依赖、timer、watcher、socket、线程或常驻进程;
- Edge/standalone 继续复用一个 SQLite authority 和一次 `BEGIN IMMEDIATE`
- Cluster 每次启动只使用一个有上限的短事务,不引入队列、Redis 或新控制副本;
- 每次启动新增一条 immutable barrier,并复用既有 Audit、Trace、RunEvent 和 mutation
ledger
- 唯一索引与 exact identity 提供常数级重放查找,历史尝试不被覆盖。
## 被否决方案
1. **evidence 提交后再单独更新 StepRun**:存在半提交窗口,拒绝。
2. **先更新 StepRun,再异步写 Trace/Audit**:允许观察到无证据的运行态,拒绝。
3. **用 RunEvent 代替独立 barrier**:无法完整绑定 admission、adapter contract 和
evidence digest,拒绝。
4. **只以 StepRun ID 作为唯一启动 identity**:阻止合法的新 version 重试,拒绝。
5. **把调用方时间写入 `committed_at_ms`**:混淆业务时间与数据库提交观察,拒绝。
6. **在事务中直接调用 adapter**:把外部 I/O 放入数据库锁窗口且仍无法原子提交外部
副作用,拒绝。
7. **为 barrier 新增 workspace package**:没有独立部署或依赖生命周期,拒绝。
8. **把 authority 从 cluster runtime/admin root 导出**:扩大可达性与数据库权限,
拒绝。
## 验证
- runtime-core 完整测试:309/309
- local-sqlite 完整测试:111/111,其中 start barrier repository 5/5
- cluster-postgres package166 pass、1 条件 skip,其中 start barrier repository
4/4
- 全新 PostgreSQL 18.4 arm64 六角色 integration41 pass、1 条件 skip、0 fail
- 真实 PostgreSQL 用例证明 migration v29、runtime 最小权限、同事务启动、exact replay、
双查询入口和数据库提交时钟;
- PostgreSQL 18.4 arm64 physical HAphysical streaming、`remote_apply`
timeline 1→2、旧主 fencing、promotion、`pg_rewind`、同步复制恢复和两代双 control
replica`gates.passed=true`
- 临时单实例与 HA Docker 资源均由 gate 清理。
## 后续门禁
1. 设计 invocation plan/preview Artifact 的加密、脱敏与 retention
2. 实现第一类真实 built-in adapter 与 post-start recovery evidence
3. 用产品 composition 串联 snapshot publication、StepRun、start barrier 和 adapter
4. 增加 response-loss、进程崩溃、MCP/HTTP 超时和人工恢复实证;
5. 完成 physical Edge idle/fault/task-scale 门后再开放 production execution。