# ADR-0152:Generation 绑定的原子 Plugin Package Task Reconciliation - 状态:Accepted(双方言原子仓库、ownership、receipt、最小权限入口与真实 PostgreSQL 门已实现;生产 materialization coordinator、故障注入级 HA receipt 证明和引用感知 retention 尚未实现) - 日期:2026-07-26 - 关联:ADR-0089、ADR-0091 至 ADR-0093、ADR-0149 至 ADR-0151、 QL-RFC-0001 D-144/D-145/D-146 ## 背景 ADR-0151 已能按 `generationDigest` 持久化一份完整、不可变的 Package semantic revision,但 TaskDefinition consumer 仍有三个未关闭的错误窗口: - 一代包含多个 Task 时,逐条调用通用 Task repository 会暴露部分 generation; - upgrade 删除一个 Task 时,直接删除历史会破坏 Run、execution revision 和审计引用, 不处理又会让旧任务继续可调度; - Package publisher 与人工/其他 publisher 竞争同一个 `taskId` 时,只有 revision CAS 不足以表达长期 ownership。 此外,active pointer、materialized revision、Task head 和 execution revision 位于不同 耐久边界。publisher 必须在同一提交内证明“正在提交的仍是当前 active generation”, 并保存可精确重放、可恢复、可审计的 generation receipt。 ## 决策 ### 1. 不新增 workspace package 纯规划与共享 repository contract 放入既有 `@qinglong/runtime-core/plugin-package-task-reconciliation`。SQLite adapter 放入 `@qinglong/local-sqlite/plugin-package-task-reconciliation` 显式 subpath; PostgreSQL adapter 只从 `@qinglong/cluster-postgres/package-executor` 导出。 该切片没有独立部署、依赖、版本或供应链生命周期,因此不创建只有一两个文件的新 workspace importer。它不新增第三方依赖、timer、watcher、socket、连接池或常驻缓存; `packages/` 仍保持 21 个 importer。 ### 2. generation 是唯一批次,receipt 是提交事实 新增 `qinglong/plugin-package-task-reconciliation@v1` receipt。每份 receipt 精确绑定: - Project、Package、generation、lock digest 和 previous lock digest; - active `generationDigest` 与 materialized revision digest; - 该 generation 的完整 Task item 集合; - 每项最终 `taskId`、revision、content digest 与 disposition; - domain-separated receipt digest 和数据库提交时间。 任务 identity 固定为 `pkg::`。单次 reconciliation 最多处理 512 项;输入 facts 必须与“当前 materialized Task 集合 ∪ 上一代 receipt item 集合” 完全相等,少项、多项、重复项或外来项均失败关闭。 disposition 只有: - `created`; - `retained`; - `updated`; - `disabled`; - `already_disabled`。 upgrade 中消失的 Task 不删除,而是以新不可变 revision 设置 `enabled=false`;已禁用 任务不重复写 revision。语义未变的 Task 保留原 revision,不因 generation 变化制造 写放大。 ### 3. 规划是纯函数,提交重新复验全部 fence runtime-core planner 只接受规范化 materialized revision、上一代 receipt 和数据库 观察到的 Task/ownership facts,产生确定性 mutation ID、TaskDefinition writes、 execution plans 与 receipt。它不取得数据库、文件、网络或 active pointer authority。 adapter 在提交前和事务内都要复验: 1. materialized revision identity; 2. active install 的 installation、generation、lock 与 previous lock; 3. 上一代 receipt chain; 4. Task head revision/content digest; 5. Package ownership; 6. 完整 item/write/execution-plan coverage。 任何 fence 漂移都必须整体回滚。相同 generation、相同 receipt 是 exact replay;同一 generation 绑定不同 receipt 是 conflict。 ### 4. Task ownership 是长期数据库事实 双方言新增 `plugin_package_task_ownerships`,以 `Project + taskId` 为主键并保存 `packageName`、首次 claim generation digest 与时间。 ownership 不随 disable、upgrade 或安装历史回收而删除。 首次 create 必须在创建 Task head 的同一事务 claim ownership;后续只有相同 Package 可以更新。其他 Package、人工 publisher 或通用 Task repository 不得接管。SQLite 和 PostgreSQL 的通用 TaskDefinition append 入口都在事务内拒绝已被 Package claim 的 Task,避免绕过专用 publisher。 ### 5. SQLite 使用一个 `BEGIN IMMEDIATE` SQLite 新增: - `QingLong3PluginPackageTaskOwnerships`; - `QingLong3PluginPackageTaskReconciliations`; - `QingLong3PluginPackageTaskReconciliationItems`。 `0047-plugin-package-task-reconciliations` 安装三表、索引、CHECK、FK 和 owned trigger; `0048-capability-v24` 把 local capability 推进至 v24。catalog 当前为 43 张 owned table。 repository 复用既有单 operation authority,在一个 `BEGIN IMMEDIATE` 中完成: - active generation 与上一代 receipt 复验; - Task head/revision CAS; - command Task 的 local execution revision; - ownership claim; - receipt 和 item 明细。 事务中任一步失败都不得留下 Task、execution plan、ownership 或 receipt 的部分状态。 ### 6. PostgreSQL 只允许 Package executor 调用受审函数 PostgreSQL `pg-0026-plugin-package-task-reconciliations` 增加同义三表,并把 `control-core` capability 推进至 v25;migration stream 为 26 条,catalog 为 41 张表。 `ql3_package_executor` 没有对 Task head/revision、execution revision、ownership 或 receipt 表的原始 INSERT/UPDATE/DELETE authority。唯一写入口是: ```sql ql3.commit_plugin_package_task_reconciliation( char(64), char(64), jsonb, jsonb, jsonb ) ``` 该 `SECURITY DEFINER` 函数固定 `search_path`、复验 `session_user` 属于 `ql3_package_executor`、锁定 active install/Task heads,并在一个数据库事务内执行与 SQLite 同义的 fence、CAS、ownership、execution revision 和 receipt 提交。 runtime、admin、package-manager、worker-ingress、PUBLIC 均无函数执行权和新表写权; manager 不能把公开管理请求提升为 execution authority,常驻 runtime 也不能直接发布 Package Task。 ### 7. 当前只关闭 TaskDefinition 发布缺口 本 ADR 不注册 Tool handler,不激活 Workflow/Prompt,不绑定 Secret,也不把 materialized revision 自动接入生产启动。生产 coordinator 仍须按以下顺序显式装配: 1. 读取 active generation; 2. 精确读取 immutable materialized revision; 3. 调用专用 reconciliation repository; 4. 再次观察 active generation; 5. 只有 receipt 与当前 generation 一致时才允许后续 consumer admission。 在 coordinator、startup recovery 与用户可见状态完成前,该能力保持显式 subpath、 默认不可达。 ## Profile 影响 - edge/standalone:没有后台 reconciliation;每次显式安装/恢复只使用一个 SQLite writer transaction。未变化 Task 不新增 revision,适合低性能路由设备。 - cluster:多个 executor 可竞争,但数据库锁、CAS、ownership 和 generation receipt 使同一代只产生一个结果。executor 无原始表写权限。 - worker:不导入 planner/repository,不读取 receipt,不取得 Package 发布 authority。 ## 被否决方案 1. **为 planner、receipt 或双方言 adapter 分别新建 package**:没有独立生命周期, 会继续把 `packages/` 拆成单文件包;现有 package 的显式 subpath 已满足隔离。 2. **逐 Task 调用通用 repository**:一代中途失败会产生部分可见状态,也无法原子保存 generation receipt。 3. **删除 upgrade 中移除的 Task**:会破坏历史 Run/execution/audit 引用;禁用新 revision 保留审计连续性。 4. **以 namespace 字符串代替 ownership 表**:字符串约定不能阻止通用 publisher 或 并发 Package 抢占。 5. **给 executor 原始表写权限**:应用层校验可被绕过,无法证明 item/write/receipt 同成同败。 6. **把 Task revision 直接嵌入 materialized revision 当作已发布**:语义事实不等于 当前 Task head,也不能给 scheduler/executor 提供既有 CAS 和 execution revision。 ## 验证 - runtime-core:两代 create/retain/update/disable 规划、receipt digest、完整 coverage、 ownership collision、head drift 和确定性 mutation identity; - SQLite:真实 `DatabaseSync` 共享 contract、exact replay、第二代 retain/disable/create、 active generation 漂移整体回滚、通用 Task publisher 旁路拒绝; - PostgreSQL 18.4 arm64 真库:26 条 migration 后两代 reconciliation 与 exact replay 通过,完整集成测试 34 pass、0 fail、1 个环境能力相关 skip; - PostgreSQL package:146 pass、0 fail、1 个真库条件 skip; - PostgreSQL physical HA:timeline 1→2、`remote_apply`、旧主先 fencing、standby promotion、旧主 `pg_rewind` 只读重入以及 promotion 前后 package-executor v25 readiness/ACL 均通过,最终 `gates.passed=true`。 上述 HA 结果证明 schema、函数 identity 和最小权限契约可随 WAL/promotion 保持 ready, 但本轮没有在分区或 COMMIT-response-loss 窗口中提交 reconciliation receipt,因此不能 宣称已经取得 Task reconciliation 事务级故障注入证明。 ## 后续 1. 实现唯一 production materialization coordinator 与 bounded startup recovery; 2. 把 reconciliation receipt 纳入 PostgreSQL HA 的 COMMIT-response-loss 与 promotion durable inspection matrix; 3. 建立 immutable Tool registry generation snapshot; 4. 建立 Workflow/Prompt 版本仓库和 activation contract; 5. 在 Run/execution 引用图完整后设计 Package Task 与 materialized revision 的 retention/GC;此前禁止删除 ownership、receipt 或历史 revision。