Files
qinglong/docs/adr/ADR-0398-pre-activation-plugin-package-candidate-qualification.md
T

76 lines
5.5 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-0398Plugin Package 激活前候选资格校验与自动保留旧版本
- 状态:Proposed
- 日期:2026-08-14
- 关联 RFCQL-RFC-0001 D-306B2
- 关联 ADRADR-0153、ADR-0394、ADR-0396
## 问题
现有 Local 与 Cluster 启动恢复先发布 active pointer、把安装记录推进为
`active`,随后才读取 staged bytes、物化 Package Task/Workflow/Prompt/Tool 资源。
因此一个摘要正确但资源语义无效的升级可能先替换健康版本,再在任务发布阶段失败,
迫使整个启动门失败。安装状态机虽然保留 `previousActiveLockDigest`,却没有在指针切换前
使用该事实形成真正的失败隔离。
“先切换、失败后再把指针写回去”也不安全:回写会与并发发布竞争,Kubernetes
ConfigMap 和数据库 head 之间会出现第二次分布式提交窗口,并且历史 generation 可能被
静默重新激活。自动恢复应避免制造需要补偿的外部事实,而不是依赖补偿事务。
## 决策
1. 已存在旧 active 的 `upgrade|reinstall|rollback``staged → activating` 之前必须依次通过所有前置条件。Secret binding/transition
receipt 先完成;随后从 staged install、immutable lock 和 content-addressed bytes
构建目标 resource generation,读取有硬上限的 Manifest/资源并执行完整语义物化。
generation 1 没有可回退的旧指针,并且 Secret-aware 首次安装仍需 ADR-0395 的
post-activation B1 binding ceremony,因此不进入本 ADR 的候选物化门。
2. 候选 materialized revision 以既有 `generationDigest` repository key 在激活前发布。
相同 revision exact replay 返回 existing;不同事实冲突。该 revision 尚不构成 active
Task reconciliation、Automation publication 与 Tool snapshot 仍只消费 active generation。
3. 确定性的候选语义错误或 durable revision 冲突把当前安装从 `staged` 原子推进为
`failed(reason=activation_fact_conflict)`。状态机必须保留
`activeLockDigest=previousActiveLockDigest`,并且 activation publisher 调用次数为零。
4. OCI、文件、SQLite/PostgreSQL 或 reader close 的瞬时不可用不写失败事实,安装保持
`staged` 并由既有有界 recovery 重试。不得在不可区分时把可用性故障伪装成坏包。
5. active pointer 发布成功后,既有 Task publication recovery 复用预先持久化的 revision
只执行 generation-fenced reconciliation;它继续承担响应丢失与并发 superseded 检查。
6. Local 与 Cluster 必须使用同一个 runtime-core prerequisite sequence 和候选物化实现。
Local 复用单 SQLite authority 与本地 staging readerCluster 复用 caller-driven recovery
Job、单 PostgreSQL Pool、OCI reader 与 Kubernetes CAS publisher。
7. 本决策不增加 workspace package、migration、表、第三方依赖、daemon、timer、watcher、
listener、连接池或常驻 cache。Edge/Standalone 只在已有启动恢复遇到 staged install 时
按需读取候选字节;没有待恢复安装时只创建少量短生命周期对象,不增加后台 cadence。
## 接受条件
- 共享测试证明有效候选在激活前发布且 exact replay 不重复写;语义无效候选不发布 revision。
- 升级恢复测试证明 rejected 候选进入 failed、旧 active lock 保留且 publisher 未调用。
- Local 与 Cluster 组合测试证明恢复顺序一致,既有 active publication/reconciliation 不回归。
- 完整 18-package、backend、package/dependency/deployment/edge/import 审计通过。
- 真实 PostgreSQL/Kubernetes 门证明失败升级没有移动 active ConfigMap/headphysical HA 门通过。
- 固定物理低配设备证据仍由 ADR-0396 单独阻断,不能用开发机观测替代。
## 影响与替代方案
- 失败候选可能留下一个不可达、不可变的 materialized revision。它按 generation 有界,保留
失败取证事实;物理清理由独立 retention/GC receipt 决定,不在失败路径同步删除。
- 不把 materialization 塞入 Kubernetes publisher。Publisher 只拥有 pointer CAS 与投影
evidence;让它读取 OCI/PostgreSQL 会聚合执行和发布 authority。
- 不新增 `rolling_back` 状态。指针从未移动时,健康旧版本本来就仍是 active;新增补偿状态
只会扩大恢复矩阵并让低配设备承担无收益的持久化协议。
## 当前验证
- Runtime Core 定向 21/21 通过,覆盖前置条件顺序、候选预物化、exact replay、无效语义拒绝、generation 1 B1 兼容和升级失败保留
`activeLockDigest`;拒绝路径的 activation publisher 调用次数为零。
- Local Application 47 pass/4 条件 skipCluster Admin 347 pass/3 条件 skip。Cluster 首次在
沙箱内运行的 32 个失败均为回环监听 `EPERM`,在允许本机监听的环境重跑后全部通过。
- 18-package clean build/test 退出 0Runtime Core 548/548backend 1196 项为
1194 pass/2 条件 skip/0 fail。package boundary 保持 18 个 package 且
`singleSourcePackages=[]``shallowSourcePackages=[]`cluster dependency、cluster deployment
与 edge import 审计均无 finding。
- PostgreSQL `18.4` arm64 physical HA 通过 125 项门,timeline `1→2`,报告 SHA-256
`75d7a52be75c22b2aacf32f2d7e2c432a467ebaab4d639668ff3a4b98767a17e`,临时 Docker
资源已清理。真实 Kubernetes 失败升级未移动 active pointer/head 的现场门与固定物理低配设备
证据仍待完成,因此本 ADR 保持 Proposed。