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

7.2 KiB
Raw Permalink Blame History

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 复用单 LocalSqliteOperationAuthorityBEGIN 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 扩展。