Files
qinglong/docs/adr/ADR-0136-sqlite-plugin-package-install-repository-and-head-cas.md
T

148 lines
7.3 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-0136SQLite Plugin Package 安装仓库与 Head CAS
- 状态:Accepted(首建/替换 head、完整 lock、mutation ledger、版本与 record digest
CAS、bounded recovery、migration/schema/readiness lockstep 已实现;ADR-0137 已补
PostgreSQL parityADR-0138 已补本地 activation publisher/组合;具体产品 consumer、
startup lifecycle 与生产管理入口仍未开放)
- 日期:2026-07-24
- 关联 RFCQL-RFC-0001 D-08、D-09、D-130、D-132、D-133、D-134
- 关联 ADRADR-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 内:
1. 复验 Project 存在且未 archived
2. 读取并比较 expected head
3. 插入 immutable installation、mutation ledger
4. 首建或替换 head
5. COMMIT 后返回重新规范化的 durable record。
后续 commit 同时比较 installation ID、expected version、expected record digest、
mutation ID/digest 和完整 next record。任一漂移返回稳定 conflictSQLite、约束或
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。
## 验证
单元和架构门禁覆盖:
1. initial queued record 与 head-bound create command
2. 首装空 head、replacement exact head 和 previous-active fence
3. durable install/head/mutation 原子 create
4. exact create replay 与 command/head drift conflict
5. version + record digest + mutation digest CAS
6. record 前进后的旧 mutation replay不回退状态;
7. terminal-only head replacement 和历史保留;
8. current-head-only 稳定 recovery pagination
9. archived Project 与 corrupt record fail-closed
10. `0037`/`0038` manifest checksum、v19 capability 和 typed schema/catalog lockstep
11. explicit subpath-only export
12. 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。