8.6 KiB
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 必须精确绑定:
- Project、current snapshot、Tool Definition 和 trusted handler binding;
- action/plan/admission digest、Profile、execution class、timeout 和 Policy fence;
- reviewed adapter、redaction contract、audit contract 的 identity 与 digest;
- 可选的 exact approval request/dispatch/digest;
- 同 Project、同 Run 的 Tool StepRun;
- Trace anchor、Audit event 与 Audit receipt;
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 在一个事务中按固定顺序执行:
- 查找所有 start/replay identity,完全相同则返回
existing; - 锁定并复验 Run、StepRun、version、digest、状态、Definition 和 Project;
- 插入 SecurityAuditEvent、Trace anchor 和 Audit receipt;
- compare-and-set StepRun 为
running; - compare-and-set Run version/event sequence;
- 插入 RunEvent 和 StepRun mutation ledger;
- 插入 immutable start barrier;
- 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_runtimeSELECT, INSERT,其他产品角色无权限;- repository 使用短
SERIALIZABLEtransaction、固定 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-barriersubpath 暴露; - 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,直到后续完成:
- opaque/encrypted invocation plan 与 redacted preview Artifact;
- adapter-specific start/result/recovery contract;
- trusted composition 在观察
created|existingbarrier 后调用 adapter; - post-start 不确定结果的 inspect/manual recovery;
- 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 提供常数级重放查找,历史尝试不被覆盖。
被否决方案
- evidence 提交后再单独更新 StepRun:存在半提交窗口,拒绝。
- 先更新 StepRun,再异步写 Trace/Audit:允许观察到无证据的运行态,拒绝。
- 用 RunEvent 代替独立 barrier:无法完整绑定 admission、adapter contract 和 evidence digest,拒绝。
- 只以 StepRun ID 作为唯一启动 identity:阻止合法的新 version 重试,拒绝。
- 把调用方时间写入
committed_at_ms:混淆业务时间与数据库提交观察,拒绝。 - 在事务中直接调用 adapter:把外部 I/O 放入数据库锁窗口且仍无法原子提交外部 副作用,拒绝。
- 为 barrier 新增 workspace package:没有独立部署或依赖生命周期,拒绝。
- 把 authority 从 cluster runtime/admin root 导出:扩大可达性与数据库权限, 拒绝。
验证
- runtime-core 完整测试:309/309;
- local-sqlite 完整测试:111/111,其中 start barrier repository 5/5;
- cluster-postgres package:166 pass、1 条件 skip,其中 start barrier repository 4/4;
- 全新 PostgreSQL 18.4 arm64 六角色 integration:41 pass、1 条件 skip、0 fail;
- 真实 PostgreSQL 用例证明 migration v29、runtime 最小权限、同事务启动、exact replay、 双查询入口和数据库提交时钟;
- PostgreSQL 18.4 arm64 physical HA:physical streaming、
remote_apply、 timeline 1→2、旧主 fencing、promotion、pg_rewind、同步复制恢复和两代双 control replica,gates.passed=true; - 临时单实例与 HA Docker 资源均由 gate 清理。
后续门禁
- 设计 invocation plan/preview Artifact 的加密、脱敏与 retention;
- 实现第一类真实 built-in adapter 与 post-start recovery evidence;
- 用产品 composition 串联 snapshot publication、StepRun、start barrier 和 adapter;
- 增加 response-loss、进程崩溃、MCP/HTTP 超时和人工恢复实证;
- 完成 physical Edge idle/fault/task-scale 门后再开放 production execution。