feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,188 @@
# ADR-0220Plugin Package 当前安装清单查询
- 状态:Accepted
- 日期:2026-07-29
- 关联 RFCQL-RFC-0001 D-140、D-141、D-142、D-174、D-176、D-175、D-207、D-210
- 关联 ADRADR-0142、ADR-0143、ADR-0144、ADR-0184、ADR-0186、ADR-0217
## 背景
QingLong 3.0 的 Plugin Package 已有以下写入和恢复链路:
| 能力 | 当前状态 |
| --- | --- |
| install / reinstall / upgrade / rollback | 已实现 |
| proposal / approval / consume / dispatch | 已实现 |
| staged / activating / failed recovery | 已实现 |
| publisher revoke / quarantine / capability withdrawal | 已实现 |
| 精确查询当前安装 | D-210 前缺失 |
| 分页查询 Project 当前安装清单 | D-210 前缺失 |
| disable / uninstall | 未定义,后续独立状态机 |
管理 CLI 和 Cluster transport 虽然能检查 proposal/approval,却不能回答“某个 Project
当前装了什么、哪个版本可用、是否已经因 publisher 撤销而隔离”。部署者只能直接查
SQLite/PostgreSQL;这既不是稳定产品 API,也会暴露内部 schema 和过宽数据库权限。
此外,安全隔离采用 append-only overlay,而不是改写 install history。一个 install
record 可以仍是 `active`,但对应 lock 已有 quarantine event 和 withdrawal receipt。
仅回显 `install.state` 会把已撤回能力误报为可用。
## 决策
### 1. 独立只读 port,不扩大 mutation authority
`@qinglong/runtime-core/plugin-package-install` 增加
`PluginPackageInstallInventoryRepository`
- `findCurrent(projectId, packageName)`
- `listCurrentPage({ projectId, limit, after? })`
该接口与既有 `PluginPackageInstallRepository` 分离。查询 composition 只依赖 inventory
port,不因复用同一个具体 adapter 而取得 admit/stage/activate/fail 等写能力。
清单只读取 current head,不返回历史 revision;顺序固定为 Package name 升序,游标仅含
最后一项 Package name。core 与 Cluster 页大小为 `1..64`,使用 `limit + 1` 判定
`truncated`,禁止 offset、任意排序、模糊查询和无界数组。
### 2. 查询结果必须感知 quarantine
SQLite 和 PostgreSQL 使用相同语义:
1.`(project_id, package_name)` current head 取得精确 install record
2. 只连接同时匹配 Project、Package、installation、lock 和 install record digest 的
quarantine event
3. quarantine event 必须存在对应 withdrawal receipt
4. event、receipt 与 record 的 canonical JSON、digest 和 target binding 必须重新规范化
并完全一致。
无隔离事实时返回 `quarantine: null`。event/receipt 只有一侧存在、JSON 损坏、target
漂移或出现多个 current 结果时失败关闭,不降级成“未隔离”。
产品层派生:
- 存在完整 quarantine fact`availability=quarantined`
- 无 quarantine 且 install state 为 `active``availability=active`
- 其他 install state`availability=not_active`
因此安全 availability 不由可变缓存或 install enum 推断。
### 3. Local 产品入口与低配预算
既有 `ql3-package` 增加:
- `plugin-package.installation.inspect`
- `plugin-package.installation.list`
命令继续使用 Owner credential、pepper、私有命令文件和 Project Policy 认证。认证成功后
才动态加载 SQLite inventory adapter;只读命令不构造 proposal、approval、dispatcher 或
Package executor service。
Profile 预算固定为:
| Profile | 默认页 | 最大页 |
| --- | ---: | ---: |
| Edge / 路由设备 | 8 | 16 |
| Standalone | 32 | 64 |
每次仍是一次命令、一个短进程、一个 SQLite authority;不增加后台 timer、watcher、
listener、缓存或常驻连接。
### 4. Cluster 管理入口
Cluster 复用现有独立 `cluster-admin` management process,不接入常驻
`cluster-control`
- 只接受受信 transport 注入的 active `multi_factor|hardware` User principal
- 使用目标 Project 的 `package.manage`
- 复用 durable `plugin-package.inspect` quota
- 由 package-manager PostgreSQL authority 执行只读 exact/keyset 查询;
- 页大小硬限 64。
Cluster client 不只校验 exact JSON shape,还校验响应 Project、Package identity、
Package name 严格递增、请求游标之后的顺序、`next` 必须等于末项,以及
availability/quarantine/failure 事实完整性。远端不能用格式正确但语义漂移的响应欺骗
operator。
### 5. 低敏产品摘要
Local/Cluster 只返回:
- installation / Project / Package / version / operation
- state、generation、active/previous lock digest、recovery action
- failure 摘要;
- derived availability
- quarantine reason/mode/event/time 与 withdrawal status/receipt/time
- record version/time/digest。
不返回 source locator、完整 manifest、approval/stage reference、authentication ID、
credential、数据库 DSN 或内部命令内容。
### 6. 不在本决策实现 disable / uninstall
正常停用和卸载不是查询,也不能等同于安全 quarantine
- 必须定义 Task、Tool、Workflow、Prompt 与 Secret binding 的撤出顺序;
- 必须处理 durable running、completion/recovery 与新 start barrier
- 必须保留安装、审批、执行和审计历史;
- 必须检查跨 Package/Workflow 引用;
- uninstall 必须区分逻辑退役、物理制品回收和失败恢复。
这些语义需要独立、可重放、双方言原子的状态机 RFC。D-210 不删除任何安装或历史,
也不把 quarantine 当作用户可选的 disable。
## 不采用方案
### 直接暴露数据库表
拒绝。会把 schema 变成公共 API,绕过认证、Project fence、配额、边界校验和脱敏。
### 把查询方法加入管理 mutation service
拒绝。会迫使只读入口装配 proposal、approval、dispatcher 和 executor authority。
### 全量或 offset 分页
拒绝。并发安装时会重复/遗漏,且无法给路由设备提供确定内存上限。
### 新建 inventory package 或常驻同步 daemon
拒绝。没有独立部署或依赖隔离价值,违反 D-175/D-207,并增加低配设备安装、SBOM、
空闲 RSS 和维护成本。
### 顺便删除、disable 或复用 quarantine
拒绝。删除破坏历史;quarantine 表达 publisher/lock 安全撤回,不表达用户正常退役。
## 影响
- workspace 保持 20 个 package
- 不新增 migration、第三方依赖、进程、端口或后台资源;
- mutation repository contract 和既有写路径不变;
- Local 与 Cluster 获得一致的 current inventory 语义;
- Cluster transport public operation 从 9 个增加为 11 个;
- disable/uninstall 明确列为后续独立状态机,而不是缺省删除行为。
## 验证
1. GitNexus impactSQLite repository 为 HIGH24 upstream、4 direct),PostgreSQL
repository 为 HIGH4 upstream、2 direct);transport normalizer 为 HIGH6
upstream、2 direct);新增服务/transport/client/HA 场景入口为 LOW。实现只增加只读
方法并保持既有 mutation 语义不变;
2. runtime-core page/cursor、SQLite exact/list、双方言共享 repository contract、
Local 完整 Package 生命周期、Cluster service/transport/client 定向回归通过;
3. Local 生命周期在 publisher revoke 后证明 install record 仍为 active,但 inspect/list
均输出 `availability=quarantined`、精确 reason 和 withdrawal status,且不泄露敏感
material
4. Cluster 三包 TypeScript 闭包通过;`cluster-admin` 全包 132 pass、1 条真实
Kubernetes 条件 skipTLS client 6/6,覆盖 11 个公开 operation、跨 Project、
重复/乱序 keyset、错误 next cursor 与不完整 quarantine 响应拒绝;
5. PostgreSQL 18.4 arm64 physical HA 使用公开
`PostgresPluginPackageInstallRepository` 在隔离后和 timeline 1→2 提升后复验
current exact/listevent/receipt 完全一致;
6. HA 输出
`pluginPackageQuarantineInventorySurvivesPromotion=true`36 个具体 gate 与总
`passed=true`;临时依赖 link 由 trap 清理,Docker 资源由 HA fixture 清理。
当前未完成证据是六个 Local Profile 的正式 bytes/files/RSS artifact matrix:它仍由
exact local-sqlite Drizzle RC 未物化阻断。该限制不影响 D-210 的 Cluster HA 结论,也
不能用从受审本机镜像临时提取的依赖替代正式 lockfile 安装与远端 CI。