mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
# ADR-0222:按 generation 绑定的 Plugin Package 自动化发布账本
|
||||
|
||||
- 状态:Proposed
|
||||
- 日期:2026-07-29
|
||||
- 关联 RFC:QL-RFC-0001 D-144、D-145、D-147、D-175、D-207、D-211
|
||||
- 关联 ADR:ADR-0150、ADR-0151、ADR-0153、ADR-0221
|
||||
|
||||
## 背景
|
||||
|
||||
ADR-0150 已把 Package 中的 Workflow 与 Prompt 解析成同一份 immutable
|
||||
materialized revision,但“已经物化”不等于“已经发布给运行时”。如果运行时直接读取
|
||||
materialized revision,会出现以下问题:
|
||||
|
||||
- active generation 切换时,Workflow 与 Prompt 可能分别可见;
|
||||
- disable/enable 只撤出 Task 与 Tool,自动化定义仍可被新执行消费;
|
||||
- 集群多副本无法证明读取的是同一 generation、同一 publication head;
|
||||
- 路由设备若用 watcher、目录扫描或常驻缓存弥补,会增加空闲内存、I/O 和恢复状态。
|
||||
|
||||
Secret binding 具有独立授权、密文材料和审计边界,不能与只包含低敏定义的
|
||||
Workflow/Prompt publication 合并。
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. Workflow 与 Prompt 使用同一 publication
|
||||
|
||||
`qinglong/plugin-package-automation-publication@v1` 一次发布同一 Package generation
|
||||
中的完整 Workflow 与 Prompt 定义:
|
||||
|
||||
- target 精确绑定 Project、Package、installation、lock、generation、
|
||||
generation digest 与 materialized revision digest;
|
||||
- definitions 分别按 ID 排序并拒绝重复;
|
||||
- Workflow 与 Prompt 各最多 128 项;
|
||||
- canonical publication JSON 最大 12 MiB;
|
||||
- publication 使用 domain-separated SHA-256 digest。
|
||||
|
||||
同一 generation 不拆成 Workflow head 与 Prompt head。消费者只能看到完整 generation
|
||||
或旧完整 generation,不能看到半批定义。
|
||||
|
||||
### 2. append-only digest chain
|
||||
|
||||
每个 `(Project, Package)` 只有一个 CAS head,历史 publication 永不 update/delete。
|
||||
|
||||
- 每个 active Package generation 都必须拥有一条 publication;包含 Workflow/Prompt
|
||||
时状态为 `active`,不包含时状态为 `absent` 且 definitions 为空;
|
||||
- `absent` 是 generation tombstone,禁止旧 generation 的自动化 head 跨过一个空
|
||||
generation 继续可见;
|
||||
- 首条 publication 固定为 `active|absent`、version 1、无 predecessor;
|
||||
- Package generation 升级从任意 current head 追加更高 generation 的
|
||||
`active|absent` publication;允许恢复直接跨过已不再 active 的中间 generation,
|
||||
但 generation 必须严格前进;
|
||||
- lifecycle disable 追加 `active → withdrawn`;
|
||||
- lifecycle enable 追加 `withdrawn → active`;
|
||||
- `absent` 不参与 lifecycle toggle;
|
||||
- lifecycle successor 必须保留同一 target 与同一 definitions,并绑定 lifecycle
|
||||
event digest;
|
||||
- version、predecessor digest 与 `publishedAtMs` 单调递增。
|
||||
|
||||
uninstall 只能从 disabled 进入,因此 automation head 已是 withdrawn。v1 不再追加
|
||||
“retired” publication,保留 withdrawn immutable history;物理回收由独立 retention
|
||||
协议处理。
|
||||
|
||||
### 3. 双方言 durable repository
|
||||
|
||||
SQLite 使用:
|
||||
|
||||
- `QingLong3PluginPackageAutomationPublications`;
|
||||
- `QingLong3PluginPackageAutomationPublicationHeads`;
|
||||
- migration `0079-plugin-package-automation-publications`;
|
||||
- capability `local-core@40`。
|
||||
|
||||
PostgreSQL 使用:
|
||||
|
||||
- `ql3.plugin_package_automation_publications`;
|
||||
- `ql3.plugin_package_automation_publication_heads`;
|
||||
- migration `pg-0043-plugin-package-automation-publications`;
|
||||
- publication capability `control-core@42`;
|
||||
- runtime-only start guard migration
|
||||
`pg-0044-plugin-package-automation-start-guard`;
|
||||
- current capability `control-core@43`。
|
||||
|
||||
publication 表 append-only,head 只允许 CAS 推进。双方言 repository 都支持
|
||||
`findCurrent`、`findByDigest`、`publish` 与有界 keyset `listPendingPage`。pending
|
||||
source 只返回“active install 已有 materialized revision,但 automation head 缺失或
|
||||
generation 不匹配”的 Package。materialized revision fence 不匹配、fork、
|
||||
JSON/index projection 漂移或缺失 predecessor 均失败关闭。
|
||||
|
||||
PostgreSQL runtime、manager 与 package executor 可 SELECT;只有 package executor
|
||||
可 INSERT publication,并对 head INSERT/UPDATE。没有业务角色取得 DELETE。
|
||||
|
||||
### 4. 生命周期同事务联动
|
||||
|
||||
ADR-0221 lifecycle repository 不调用另一个自带事务的 repository。automation
|
||||
repository 提供显式 transaction-bound writer,由 lifecycle 已持有的 SQLite
|
||||
`BEGIN IMMEDIATE` 或 PostgreSQL `SERIALIZABLE` transaction 调用。
|
||||
|
||||
对 current automation head 为 `active|withdrawn` 的 Package:
|
||||
|
||||
- disable 要求 current automation head 精确匹配 active Package generation 且为
|
||||
active,然后在 lifecycle event/receipt、Task revision 与 Tool snapshot 的同一事务
|
||||
追加 withdrawn publication;
|
||||
- enable 要求同一 generation 的 current head 为 withdrawn,在同一事务追加 active
|
||||
publication;
|
||||
- uninstall 要求 current head 已 withdrawn,不创建新的定义版本;
|
||||
- 第二次 authorization fence 失败、外键失败、head CAS 失败或任一 capability 写失败
|
||||
时,整笔事务回滚。
|
||||
|
||||
`absent` tombstone 没有可撤出的定义,因此 lifecycle transaction 不为其追加空 toggle;
|
||||
消费者仍以 Package lifecycle/quarantine authority 作为外层准入围栏。
|
||||
|
||||
publication 的 lifecycle event 外键要求 lifecycle event 先在事务中插入,再写
|
||||
publication,最后才允许 COMMIT。这个顺序不改变原子可见性。
|
||||
|
||||
### 5. Profile 与资源边界
|
||||
|
||||
- 不新增 workspace package;
|
||||
- 不新增 daemon、timer、watcher、listener、socket 或常驻缓存;
|
||||
- Edge/Standalone 复用单 SQLite operation authority;
|
||||
- Cluster 复用 caller-driven package executor 与单连接 transaction;
|
||||
- 禁用 AI 的 Profile 不因该账本引入 AI provider、SDK 或 credential;
|
||||
- Secret material、SecretRef、Prompt 渲染输入与模型输出均不得进入 publication。
|
||||
|
||||
### 6. caller-driven publication 与恢复顺序
|
||||
|
||||
initial/next publication 已由 profile-neutral coordinator 发布,并在双方言启动链路中
|
||||
固定为:
|
||||
|
||||
`install recovery → Task materialize/reconcile → Workflow/Prompt publication → Tool snapshot → admission`
|
||||
|
||||
- coordinator 在发布前后各观察一次 active generation;换代返回 `superseded`,不把
|
||||
旧 generation 冒充 current;
|
||||
- 同 generation exact replay 返回 existing,并保留 lifecycle 已写入的
|
||||
`withdrawn` 状态;
|
||||
- conflict/invalid 进入 `manual_required`,availability 失败进入 `retry`;
|
||||
- 每页和每轮硬上限均为 64;Local Edge/Standalone 无显式配置时使用 8×8;
|
||||
- 不创建 resident reconciler,Local 复用一个 SQLite operation authority,Cluster
|
||||
复用 caller-driven package-executor Job。
|
||||
|
||||
### 7. 安全事实优先的启动门
|
||||
|
||||
publication head 只表达 generation 与正常 lifecycle,不把 quarantine 或 publisher
|
||||
revocation 重写成虚假的 `withdrawn`。启动必须通过独立、精确的
|
||||
`PluginPackageAutomationPublicationStartGuard`:
|
||||
|
||||
- 输入固定为 Project、Package 与 current publication digest;旧 digest、非 active
|
||||
publication、非 current active install、non-active lifecycle 一律返回 false;
|
||||
- SQLite 在同一 operation authority 内同时检查 current publication/install、
|
||||
lifecycle 与精确 installation/lock quarantine event;
|
||||
- PostgreSQL 只向 `ql3_runtime` 暴露
|
||||
`SECURITY DEFINER plugin_package_automation_start_allowed`,runtime 不取得
|
||||
quarantine、provenance 或 revocation 表的直接读取权;
|
||||
- PostgreSQL guard 与 publisher revocation 使用同一 transaction-level signer
|
||||
advisory lock。若启动观察先完成,它发生在撤销提交之前;若撤销先取得锁,启动等待
|
||||
后必定观察到 receipt 并返回 false;
|
||||
- pending-source 与 publication writer 同样排除 security-fenced generation,避免
|
||||
recovery 在隔离或撤销后重新发布能力;
|
||||
- 不新增 safety-state 镜像表、daemon、timer、watcher 或常驻 denylist。quarantine
|
||||
event 与 publisher revocation receipt 始终是事实源。
|
||||
|
||||
调用方必须在“记录一次自动化启动”的同一数据库事务内消费 PostgreSQL guard;把一次
|
||||
boolean 查询缓存后再异步启动仍属于 TOCTOU,不能视为生产执行门已经关闭。
|
||||
|
||||
### 8. 尚未关闭的产品门
|
||||
|
||||
本 ADR 已关闭 durable publication、lifecycle overlay、双方言 quarantine 启动收敛,
|
||||
以及双方言 publisher revocation 的即时启动收敛:Local 由文件型不可变撤销
|
||||
receipt 桥接到 SQLite 精确 installation/lock quarantine,Cluster 由 PostgreSQL
|
||||
receipt 直接参与启动判定。但不把定义解释为可执行能力。D-211 继续 Proposed,
|
||||
直到至少完成:
|
||||
|
||||
1. Workflow Run/StepRun executor 与 admission fence;
|
||||
2. Prompt 产品入口、参数绑定、Policy/Audit 与内容治理;
|
||||
3. Secret binding consumer。
|
||||
|
||||
在这些门关闭前,消费者必须同时检查 Package lifecycle/quarantine authority,不能只因
|
||||
automation head 为 active 就执行。
|
||||
|
||||
## 不采用方案
|
||||
|
||||
### 直接读取 materialized revision
|
||||
|
||||
拒绝。materialization 是 immutable source fact,不是运行时 publication head。
|
||||
|
||||
### Workflow 与 Prompt 分别发布
|
||||
|
||||
拒绝。会引入同 generation 部分可见和双 head 恢复协议。
|
||||
|
||||
### 把 Secret binding 放入 publication
|
||||
|
||||
拒绝。低敏定义发布不能取得 Secret 授权和密文材料 authority。
|
||||
|
||||
### 生命周期提交后异步撤出 automation
|
||||
|
||||
拒绝。崩溃窗口会留下 Task/Tool 已撤出但 Workflow/Prompt 仍 active。
|
||||
|
||||
### 新建 automation workspace package 或 watcher
|
||||
|
||||
拒绝。现有 runtime-core 与双方言 storage subpath 足够,额外部署边界没有独立价值,
|
||||
并会增加路由设备供应链和空闲资源。文件数量不是 package 边界;本能力没有独立部署、
|
||||
权限域、重依赖或发布责任,因此即使代码增长也继续使用现有 package 的显式 subpath。
|
||||
|
||||
## 当前验证
|
||||
|
||||
1. runtime-core publication/coordinator 定向 9/9,通过 canonical digest、跨
|
||||
generation successor、`absent` tombstone、lifecycle withdraw/restore、并发换代
|
||||
与有界 recovery 分类;
|
||||
2. SQLite repository 定向 8/8,覆盖 exact current start guard、quarantine 后
|
||||
pending-source 收敛、publication 拒绝、跨 generation `absent` tombstone 与损坏
|
||||
失败关闭;
|
||||
3. SQLite lifecycle 定向测试证明第二次 authorization 检查失败时,Task、Tool
|
||||
snapshot、lifecycle facts 与 automation head 全部回滚;
|
||||
4. Local application startup 固定执行 Task→automation→Tool,定向启动回归 14/14;
|
||||
SQLite runtime 只在调用时惰性加载 publication repository;
|
||||
5. PostgreSQL 18.4 arm64 HA fixture 中,同一 Package publication 从 v1 经四次
|
||||
disable/enable 原子推进到 v5,包含 1 Workflow 与 1 Prompt;5 条 publication 和
|
||||
active head 经 `remote_apply`、timeline 1→2 promotion、旧主 fencing、
|
||||
`pg_rewind` 及 fresh control replicas 后保持一致;
|
||||
6. PostgreSQL `pg-0044` 把 control-core 推进至 v43;start function 只授予 runtime,
|
||||
package executor/admin/manager/worker ingress 均无执行权;迁移、checksum、
|
||||
schema/readiness 与 repository 定向 43/43;
|
||||
7. HA gates
|
||||
`pluginPackageAutomationPublicationTransitionsAtomically=true`、
|
||||
`pluginPackageAutomationPublicationSurvivesPromotion=true`、
|
||||
`pluginPackageAutomationRecoverySourceConverges=true`、
|
||||
`pluginPackagePublisherRevocationImmediatelyFencesAutomation=true`、
|
||||
`pluginPackageAutomationSecurityFenceSurvivesPromotion=true`、
|
||||
`pluginPackageAutomationStartGuardIsRuntimeOnly=true` 且总
|
||||
`gates.passed=true`;撤销前 current publication 允许,revocation receipt 提交后、
|
||||
quarantine 后和 promotion 后均拒绝;
|
||||
8. 调度器 COMMIT-response-loss 同轮修复为“发送 COMMIT 后绝不透明重试”,回归 7/7,
|
||||
HA 证明只产生 1 Run、1 Attempt、2 Event、0 duplicate occurrence。
|
||||
9. Cluster caller-driven recovery 已把 automation publication 放在 Task publication
|
||||
与 Tool snapshot 之间;Cluster PostgreSQL 全量 211 pass/1 条无 DSN 条件 skip,
|
||||
最新 HA 总
|
||||
`gates.passed=true`,且未访问 registry、未拉取镜像。
|
||||
10. Local 签名 catalog 已覆盖 active install → materialized Task/Workflow/Prompt →
|
||||
active automation 的完整链路;撤销 publisher key 后,不可变 receipt callback
|
||||
以精确 installation/lock provenance 写入 SQLite `withdrawn` quarantine,raw
|
||||
automation publication 仍保持原 generation/lifecycle 事实,但 exact start guard
|
||||
立即返回 false。测试在 quarantine 与 snapshot 已 durable、文件型 trust current
|
||||
generation 尚未 promotion 的边界注入崩溃,观察到 current generation 3、
|
||||
pending generation 4、`recoveryRequired=true` 且启动已拒绝;重放同一命令收敛为
|
||||
generation 4,随后为 `existing`,SQLite 始终只有 1 条精确 quarantine event。
|
||||
Reference in New Issue
Block a user