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,148 @@
# ADR-0226:有界 Plugin Package Workflow Frontier
- 状态:Proposed
- 日期:2026-07-30
- 关联 RFCQL-RFC-0001 D-03、D-207、D-212
- 关联 ADRADR-0156、ADR-0223、ADR-0224、ADR-0225
## 背景
ADR-0223 至 ADR-0225 已把 generation-bound Workflow DAG 原子展开为一个
runtime-owned Workflow Run、全部 StepRun、Event/mutation 与 admission receipt。
准入后的 Workflow Run 固定为 `running`,而 Task root StepRun 为 `ready`、其余
StepRun 为 `pending`
首次 PostgreSQL HA 重跑暴露出一个恢复边界错误:通用 Cluster Run recovery 把没有
RunAttempt 的 Workflow 聚合 Run 识别成孤儿执行。该判断对普通 Task Run 成立,但
Workflow Run 本身是编排聚合,不应直接进入普通 Task dispatcher,也不应伪造一个
RunAttempt 或终态绕过恢复。
同时,低配 Edge 不能为每个 dormant Workflow 建立 timer、watcher 或内存队列;Cluster
多副本也不能依赖进程内 frontier。
## 决策
### 1. 通用恢复器排除 Workflow 聚合
PostgreSQL 通用 recovery source 必须排除
`trigger_type='plugin_package_workflow'`。这不是忽略孤儿工作,而是明确 ownership:
- 普通 Task Run/RunAttempt 继续由既有 Cluster recovery 处理;
- Workflow 聚合 Run 只由专属 Workflow frontier/recovery 处理;
- Workflow 的 Task Step 后续仍必须通过既有 RunAttempt/Executor fence 启动,不能把
聚合 Run 作为普通 Task 分发。
### 2. 纯 frontier 契约留在现有 runtime-core
新增显式 subpath
`@qinglong/runtime-core/plugin-package-workflow-frontier`,不新增 workspace package、
第三方依赖、进程或连接。一次 resolution 只接受:
- exact normalized immutable plan
- exact runtime-owned `plugin_package_workflow` Run fence
- 与 plan 一一对应的最多 128 个 durable StepRun
- 单调的存储 observation time。
契约对完整 StepRun 集合、Run/Project/Workflow/publication identity、Task
definition ref/digest 和 record digest 失败关闭。
### 3. 一次纯计算收敛依赖 frontier
resolution 使用确定性规则:
1. pending Step 的所有 `needs` 都为 `succeeded` 时生成 `pending → ready` mutation
2. 任一祖先为 `failed|skipped|cancelled|timed_out` 时,依赖失败可在同一 pass 传递,
下游生成 `pending → skipped`result code 固定为
`dependency_not_succeeded`
3. mutation 按 canonical Step key 顺序绑定连续 Run version/event sequence
4. frontier event ID 不超过双方言共有的 36 字节,mutation ID 由 plan、StepRun
identity/version 与目标状态做 domain-separated digest
5. resolution 返回 projected ready StepRun ID,但不把读取结果解释为执行 claim。
当全部 required StepRun 终结时,纯契约给出固定的 Run terminal 建议:
`failed/skipped → failed`,其次为 `timed_out``cancelled`,全部成功才为
`succeeded`。terminal transition 同时绑定 expected Run version/event sequence、
固定 error code、finished time 和不超过 36 字节的确定性 RunEvent identitystorage
adapter 必须把它与本轮 StepRun mutation 原子提交。
### 4. 双方言使用同一 port,不新增 package
同一显式 subpath 公开最多 64 条的 keyset candidate page 和
`listCandidates/advance` repository port
- SQLite adapter 位于既有 `@qinglong/local-sqlite`,复用单
`LocalSqliteOperationAuthority``BEGIN IMMEDIATE`candidate SQL 只返回可推进
pending dependency 或全部 StepRun 已终结但聚合 Run 未终结的 Workflow,不为 root
ready 或等待中的 Workflow 制造空写;
- PostgreSQL adapter 位于既有 `@qinglong/cluster-postgres`,由 caller 驱动有界
keyset page`advance` 使用短 `SERIALIZABLE` transaction,锁定聚合 Run 和完整
StepRun 集合,使用数据库 transaction clock,并对 serialization/deadlock 做有界
fresh-client retry
- 两者都先验证 immutable plan、Run identity 和所有 StepRun canonical JSON/mirror
再按 StepRun version/digest/status CAS;一轮只更新一次聚合 Runversion/event
sequence 按 mutation 数加 terminal event 数整体推进;
- StepRun、RunEvent、StepRunMutation 和 terminal Run/Event 同事务提交,任一 drift、
fence 或写入冲突整体回滚;已终态 aggregate 重放只读收敛为 `settled`
没有新增 migration、workspace package、第三方依赖、连接、timer、watcher 或后台
scanner。ready Task 的执行 admission 仍必须在后续原子绑定 StepRun 与
RunAttempt/Executor fencefrontier 的 ready projection 本身不是执行 claim。
### 5. admission replay 只冻结准入证据,不冻结当前执行状态
双方言 admission exact replay 继续严格验证 immutable plan、receipt、初始 RunEvent、
初始 StepRun mutation 与 admission step ledger,但不再要求当前 Run/StepRun 永远停在
准入初态。当前 aggregate 只允许合法运行/终态、identity 不变、canonical record
完整且 version/event sequence 单调;初始 Event 查询固定截止 admission final sequence。
因此合法 frontier/Executor 推进后仍可 inspect/exact replay,而历史准入证据缺失或漂移
仍失败关闭。
## 不采用方案
### 把 Workflow 聚合 Run 交给普通 dispatcher
拒绝。聚合 Run 的 `taskId` 是 Workflow IDrevision 是 publication digest,不是一个
普通 Task execution revision;直接分发会错误解释定义并把一个 DAG 当成一次 Task。
### 为恢复器伪造 RunAttempt 或把聚合 Run提前置为终态
拒绝。伪造 Attempt 会制造不存在的执行事实;提前终结会隐藏尚未执行的 ready/pending
StepRun。
### 每 Workflow 常驻 coordinator
拒绝。它给低配路由器增加空闲内存、唤醒与写放大,并让 Cluster 正确性依赖进程内
ownership。
### 新建 workflow package
拒绝。纯契约没有独立部署、重依赖或权限域,另拆会违反 D-207 的 package 粒度标准。
## 当前验证
1. 通用 PostgreSQL recovery SQL 影响评估为 LOW,新增 Workflow trigger exclusion
2. runtime-core 405/405,包含 frontier 纯契约 6/6 与后续 Attempt admission 4/4
3. `local-sqlite` 180/180;真实 SQLite frontier 覆盖 dependency ready、失败向下游
skipped、成功/失败 aggregate terminal、bounded page、drift rollback、settled 与
admission-after-progress exact replay
4. `cluster-postgres` 223 pass、1 条无测试 URL 条件 skipmock 契约覆盖
`SERIALIZABLE`、Run/StepRun row lock、单 aggregate CAS、terminal 原子写、
40001 fresh-client retry、settled 与显式 subpath
5. PostgreSQL 18.4 arm64 physical HA 在禁止 image pull 下完整通过,包含
`remote_apply`、timeline 1→2、旧主 fencing、promotion、`pg_rewind` 和两 fresh
control replicas;新增
`pluginPackageWorkflowFrontierTerminalizesAtomically`
`pluginPackageWorkflowFrontierExactlyReplays`
`pluginPackageWorkflowFrontierSurvivesPromotion` 三项 gate,总
`passed=true`
6. HA durable facts 为 1 个 Workflow admission、5 个 Workflow RunEvent、3 个
StepRunMutationpromotion 后 frontier 返回同一 `succeeded/settled` winner
admission 仍 exact replay
7. workspace 仍为 20 个 package,未新增生产依赖或常驻资源。
## 尚未关闭
1. ready Task StepRun 到 RunAttempt/Executor 的原子执行 admission
2. Workflow 取消、超时和 Task post-start recovery
3. frontier caller 与 Local application/Cluster control 生命周期装配;
4. Edge/Standalone 真实断电、资源门和产品 API/CLI/UI。