Files
qinglong/docs/adr/ADR-0223-generation-bound-plugin-package-workflow-execution-plan.md
T

146 lines
7.2 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-0223:按 generation 绑定的 Plugin Package Workflow 执行计划
- 状态:Proposed
- 日期:2026-07-30
- 关联 RFCQL-RFC-0001 D-12、D-144、D-211、D-212
- 关联 ADRADR-0001、ADR-0150、ADR-0156、ADR-0222、ADR-0224
## 背景
ADR-0222 已经让 Workflow 定义以 immutable、generation-bound publication 发布,并让
quarantine、publisher revocation 和正常 lifecycle withdrawal 立即阻止新启动。但定义
可见不等于 Workflow 可执行。现有系统仍缺少一个不可变输入,把以下事实绑定为同一次
运行意图:
- exact active automation publication
- exact materialized revision 与 Workflow 定义;
- Workflow 中每个 Task 的 immutable source digest
- DAG 依赖、初始 ready frontier、Run ID 与每个 StepRun ID。
如果执行器临时读取多个 current head,再逐项创建 StepRun,会留下三个问题:
1. Package upgrade、disable 或安全撤销可在读取之间切换 generation
2. 进程崩溃可能只展开部分 DAG
3. 响应丢失后无法区分 exact replay 与使用新定义重新执行。
这些问题不能靠进程内缓存、定时扫描或“再次读取最新定义”修复。
## 决策
### 1. 先冻结纯执行计划,不提前开放执行副作用
`@qinglong/runtime-core/plugin-package-workflow-execution-plan` 提供 profile-neutral
纯契约,不新增 workspace package、第三方依赖或进程:
- schema 固定为 `qinglong/plugin-package-workflow-execution-plan@v1`
- 最多 128 个 Step,编码后最多 256 KiB
- plan 绑定 Project、Package、installation、lock、generation、materialized
revision、publication、Workflow definition、Run 与所有 StepRun identity
- 每个 Step 绑定 Task ID、generation-bound reference、原始 source digest、依赖和
`pending|ready` 初态;
- root Step 才能为 `ready`,有依赖的 Step 必须为 `pending`
- plan 使用 domain-separated canonical SHA-256,可严格 normalize 与复算。
本 ADR 的切片只创建不可变 plan。后续 ADR-0224 已在 SQLite 中原子创建
Run/StepRun/receipt,但仍不调用 Executor、不解析 Secret、不读取输入 Artifact
也不把 durable admission 解释为产品 Workflow 已开放。
### 2. v1 执行面严格等于现有 Package Workflow 语义
Plugin Package Workflow v1 只包含有界、无环的 Task DAG:
- Step 只引用同一 materialized revision 中的 Package Task
- disabled Workflow 或 disabled Task 拒绝规划;
- publication 与 revision 的 Project、Package、installation、lock、generation 和
digest 必须完全一致;
- publication 中的 Workflow semantic digest 必须与 revision 中的同名 Workflow
一致;
- 调用者必须一次提供与 Step key 精确相等、无重复的 StepRun ID 集合。
RFC 15.1 中的 Model、Tool、Approval、条件、表达式、Subworkflow、动态重试和模板输入
不是这个 v1 plan 的隐式能力。它们必须在各自的 Policy、Secret、Artifact、Trace 和
start barrier 契约完成后按版本扩展,不能通过任意 JSON 字段提前进入执行面。
### 3. durable admission 必须是双方言单事务
存储切片必须新增专用 Workflow admission authority,而不是让应用串联通用
RunRepository 与 StepRunRepository。一次 admission transaction 必须:
1. 取得与 publisher revocation 相同的安全序列化边界;
2. 在事务内验证 exact publication start guard
3. exact replay 优先检查 immutable plan/receipt
4. 原子创建 Workflow Run、全部 StepRun、顺序 RunEvent/StepRun mutation 与
admission receipt
5. 任一 collision、generation 漂移、非 current publication、quarantine、
lifecycle withdrawal 或权限失败全部回滚;
6. COMMIT response loss 只能 inspect durable receipt,不得重新生成 ID 或重新规划。
SQLite 复用单 `LocalSqliteOperationAuthority``BEGIN IMMEDIATE`。PostgreSQL 使用
受审 `SECURITY DEFINER` admission function 或等价的单一 runtime transaction,使
start guard 和 Run/StepRun 写入共享同一 transaction-level signer lock。runtime
不取得 Package provenance、quarantine 或 publisher receipt 表的直接读取权。
### 4. executor 使用 durable frontier,不使用常驻全量扫描
后续 executor 只能从已 admission 的 plan 与 StepRun 状态计算 frontier
- 只有全部 `needs` succeeded 的 pending Step 才能原子推进为 ready
- 任一 required dependency failed/cancelled/timed_out 时,下游收敛为 skipped
Workflow Run 按固定聚合规则终结;
- 已 durable running 的 Step 在 Package 后续 withdrawn/quarantined 时按原 revision
完成或恢复;安全事实只禁止新的 Workflow/Step start
- Edge/Standalone 使用既有 application lifecycle 的有界单页调用,默认分别限制为
8/32 个候选,不新增 daemon、watcher 或 per-Workflow timer
- Cluster 使用 caller-driven worker/executor cycle 与数据库 keyset claim,不创建
每 Workflow 常驻 Job 或内存队列。
具体 Task Attempt 启动仍必须复用现有 Run/Attempt/Executor fence,不能由 Workflow
planner 直接 spawn。
## 不采用方案
### 每次执行重新读取 current Workflow 和 Task head
拒绝。会让一次 Run 混合多个 generation,也无法 exact replay。
### 先建 Run,再逐项创建 StepRun
拒绝。崩溃会留下半展开 DAG,恢复只能猜测未创建步骤。
### 把 Workflow executor 放进新的 workspace package
拒绝。纯领域契约没有独立部署、权限域、重依赖或 Profile 替换价值;它属于
runtime-core subpath。双方言 authority 分别留在既有 local-sqlite 与
cluster-postgres。
### Edge 为每个 Workflow 启动 timer 或 watcher
拒绝。低配路由设备不能为 dormant Workflow 支付常驻内存、唤醒和闪存写放大。
### publication withdrawn 后终止所有 running Step
拒绝。正常退役和安全隔离都不等于已启动副作用可以安全热杀;running execution 继续由
Attempt cancellation/recovery authority 裁决。
## 当前验证
1. runtime-core 定向 6/6active publication + exact revision 生成两步 DAG
root 为 ready、dependent 为 pending,并绑定两个 Task source digest
2. 完整 plan、JSON round-trip 与无摘要字段输入得到相同 canonical digest
3. withdrawn publication、跨 revision 漂移、缺失或重复 StepRun ID、plan digest 和
generation-bound Task reference 漂移均失败关闭;
4. planner 只经显式 runtime-core subpath 发布,root 不暴露 authority
5. runtime-core 完整测试 394/394 通过;
6. ADR-0224 已完成 SQLite v41 单事务 admission、Run/StepRun/event/mutation/receipt
exact replay 与 readinessworkspace 仍为 20 个 package
7. 未新增生产依赖、timer、watcher、listener 或常驻进程。
## 尚未关闭
1. PostgreSQL durable admission repository、migration、readiness、权限与 HA
2. SQLite 真实断电和固定 Edge 资源预算;
3. 有界 ready-frontier coordinator 与 Task Attempt 启动;
4. Workflow terminal aggregation、取消、超时和恢复;
5. 产品级强认证/Policy/API/CLI/UI 入口;
6. Prompt、Secret binding 以及 Model/Tool/Approval/Subworkflow 扩展。