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

5.5 KiB
Raw Blame History

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|rollbackstaged → 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。