7.3 KiB
ADR-0136:SQLite Plugin Package 安装仓库与 Head CAS
- 状态:Accepted(首建/替换 head、完整 lock、mutation ledger、版本与 record digest CAS、bounded recovery、migration/schema/readiness lockstep 已实现;ADR-0137 已补 PostgreSQL parity,ADR-0138 已补本地 activation publisher/组合;具体产品 consumer、 startup lifecycle 与生产管理入口仍未开放)
- 日期:2026-07-24
- 关联 RFC:QL-RFC-0001 D-08、D-09、D-130、D-132、D-133、D-134
- 关联 ADR:ADR-0063、ADR-0076、ADR-0132、ADR-0134、ADR-0135
背景
ADR-0134 定义了 profile-neutral PackageLock、安装状态机、CAS commit 和恢复动作,
ADR-0135 又证明离线 bundle 可被确定性检查并私有 staging。但没有 durable adapter
时,queued、staging receipt、activation receipt、上一代 active pointer 和
mutation replay 都只存在于内存,进程重启后无法安全裁决。
本地 edge/standalone 必须先有低资源、单 SQLite authority 的实现;同时不能为这一 能力新增只有一个文件的 workspace package,也不能把安装器接入默认启动路径。
决策
1. Repository 留在既有 @qinglong/local-sqlite
新增显式 @qinglong/local-sqlite/plugin-package-install subpath,不从 package 根入口
或 runtime 入口导出,不新增 workspace package和第三方依赖。Repository 复用既有
LocalSqliteOperationAuthority 的单连接有界队列和 Node SQLite transaction authority。
禁用插件安装时,edge/standalone 默认 composition 不加载该 subpath,不增加 timer、 watcher、socket、后台进程或第二个数据库连接。
2. 三张表分别拥有历史、当前指针和重放事实
0037-plugin-package-installs 新增:
QingLong3PluginPackageInstalls:原子保存每个 installation 的不可变lock_json与当前 durable record,同时保留所有 generation 历史;QingLong3PluginPackageInstallHeads:每个 Project/Package 只有一个当前 installation head;QingLong3PluginPackageInstallMutations:保存 mutation ID、命令摘要、结果 version 与 record digest,作为 exact replay ledger。
表级 CHECK、FK 和索引将 lock/record JSON 的 installation、Project、Package、
operation、state、generation、version 与 digest 同索引列绑定。Repository 的
findLock 每次读取重新规范化并重算 lock digest。typed Drizzle schema、真实 SQLite
catalog、migration manifest 和 readiness contract 必须保持 lockstep。
0038-capability-v19 只允许从 exact local-control-core v18 前驱推进到 v19,并发布
"plugin_package_install":1。runtime readiness 要求 34 张表、全部受审索引、无
owned-table trigger、完整 migration history 和 exact capability JSON。
3. 首建也是需要 head fence 的提交
领域层新增 PluginPackageInstallCreate,固定:
- normalized initial queued record;
- create mutation ID;
- expected head,首装必须为 null,替换必须精确绑定 installation ID、version 和 record digest;
- domain-separated create command digest。
create command digest 同时绑定 candidate record digest 和 expected head,不能复用 record 自己的 initial mutation digest。这样同一 queued record 不能在审批后从 “首装”静默改成“替换另一 head”,也不能改变被替换 generation 后继续命中 replay。
替换只接受同一 Project/Package 的不同 installation;上一 head 必须为
active | failed 终态,且候选 record 的 previous-active pointer 必须与其一致。
首装只接受 generation 1、null previous active 和空 head。
4. 所有写入使用短 BEGIN IMMEDIATE 与 exact CAS
create 在同一 transaction 内:
- 复验 Project 存在且未 archived;
- 读取并比较 expected head;
- 插入 immutable installation、mutation ledger;
- 首建或替换 head;
- COMMIT 后返回重新规范化的 durable record。
后续 commit 同时比较 installation ID、expected version、expected record digest、 mutation ID/digest 和完整 next record。任一漂移返回稳定 conflict;SQLite、约束或 corrupt record 统一映射为 unavailable,不泄漏 driver/schema 细节。
同 mutation exact replay 返回当前已前进的 record,不把历史结果倒写成旧状态。 同 ID 不同 command digest 永远冲突。record JSON 每次读取都重新走 runtime-core normalizer,不能因为索引列看似正确而接受被手工篡改的内容。
5. 恢复只扫描当前 head
恢复查询只返回当前 head 中 queued | staged | activating 的 installation,按
Package name、installation ID 稳定排序,cursor exact continuation,每页最多 64 条。
旧 generation 即使历史上仍是非终态,也不允许绕过 head authority 被重新执行。
动作仍由 ADR-0134 决定:
queued→resume_stage;staged→resume_activation;activating→inspect_activation。
Repository 不读取 bundle、调用 staging、发布资源、注册 Tool/Task/Trigger 或创建 recovery timer。
6. 可注入组合不等于生产入口开放
本 ADR 不实现:
- PostgreSQL parity 已由 ADR-0137 完成,但 production composition 仍不可达;
- 具体 Approved Action 产品 consumer、Policy/Audit 入口;
- Task、Workflow、Prompt、Tool 或 Trigger 的原子 generation publisher;
- production startup recovery lifecycle、operator repair 和旧代 GC;
- OCI client、publisher revoke/index、管理 API/CLI/UI 或自动更新;
- Runtime Extension 或动态代码加载。
因此 migration 和 Repository 的存在不能被解释为 Plugin Package 已可在生产安装或 执行。任何产品入口必须在上述闭环及独立审计完成前保持不可达。
影响
- 本地 Profile 获得 crash-safe 安装历史、单 head、exact replay 和恢复候选 authority。
- SQLite 只在显式安装/恢复操作中付费,不增加常驻资源;低配路由器继续使用单连接、 bounded page 和短 transaction。
packages/importer 数不增加;能力留在 runtime-core/local-sqlite 现有包的显式 subpath。- 集群 adapter 已由 ADR-0137 实现,并与 SQLite 共用同一可执行语义合同;后续修改 不能把 JSON/schema 或锁策略提升为第二套领域语义。
- bundle staging 成功仍不能直接激活;它只能形成下一次 CAS 所需的 evidence。
验证
单元和架构门禁覆盖:
- initial queued record 与 head-bound create command;
- 首装空 head、replacement exact head 和 previous-active fence;
- durable install/head/mutation 原子 create;
- exact create replay 与 command/head drift conflict;
- version + record digest + mutation digest CAS;
- record 前进后的旧 mutation replay不回退状态;
- terminal-only head replacement 和历史保留;
- current-head-only 稳定 recovery pagination;
- archived Project 与 corrupt record fail-closed;
0037/0038manifest checksum、v19 capability 和 typed schema/catalog lockstep;- explicit subpath-only export;
- runtime-core、local-sqlite、Profile artifact、edge import 与 dependency audit。
ADR-0138 加入共享 lock 合同与本地端到端组合后,当前结果为 runtime-core 215/215、 local-sqlite 68/68、local-admin 50/50,均 0 fail。生产产品入口、真实断电恢复与 operator 级安装恢复仍是后续 Gate。