feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,171 @@
# 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。