Files
qinglong/docs/adr/ADR-0150-bounded-plugin-package-semantic-materialization.md
T

178 lines
8.9 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-0150:有界 Plugin Package 语义物化
- 状态:Accepted(纯领域物化、POSIX staging reader 与 Cluster OCI reader 已实现;
ADR-0151 已实现 SQLite/PostgreSQL immutable revision repositoryTask 批量发布与
全局 Tool snapshot 尚未实现)
- 日期:2026-07-25
- 关联:ADR-0091、ADR-0132 至 ADR-0135、ADR-0149、QL-RFC-0001
D-90/D-130/D-131/D-133/D-144
## 背景
ADR-0149 已经让本地 pointer rename 与 Kubernetes ConfigMap CAS 原子发布一份
`PluginPackageResourceGeneration`。它能回答“哪个 Package 的哪一代、哪些路径当前
生效”,但不能回答:
- 每个路径采用什么格式;
- Task、Workflow、Prompt、Tool 如何规范化;
- Package 内引用和已审批权限如何复验;
- staging/OCI 的实际字节怎样与 generation content tree 对账;
- 路由设备如何避免扫描、watcher 和一次读入 64 MiB bundle
- Cluster 多副本怎样得到相同的不可变语义,而不是各自动态注册。
若 consumer 直接把 JSON/YAML 交给 Task repository 或 Tool registry,它会在资源之间
产生部分发布;若由 pointer publisher 顺便解析,又会把字节发布、业务语义和执行
authority 合并。
## 决策
### 1. 语义契约继续留在 runtime-core subpath
新增
`@qinglong/runtime-core/plugin-package-resource-materialization`,不新增 workspace
package、第三方依赖、数据库、timer、watcher、socket 或动态 import。它只接收:
- ADR-0149 的 active generation
- 与 generation 精确匹配的完整 `PluginPackageLock@v2`
- lock 摘要绑定的 canonical `package.json`
- 一次 caller-owned byte session 顺序读取的 exact resource bytes
- 受信 composition root 提供的不可变 TaskSpec semantic registry。
物化前后各观察一次 active generation;中途发生 upgrade、disable 或 pointer 漂移时
失败关闭。读取 session 必须显式 `open → read → close`,实现不得在 close 后保留
authoritative cache、watcher 或 timer。
### 2. v1 资源只接受严格 UTF-8 JSON
四类文件必须位于 Manifest/generation 已锁定的路径,文件名以 `.json` 结尾,每个最大
1 MiB,全部业务资源合计最大 8 MiB:
- `qinglong/plugin-package-task-resource@v1`
- `qinglong/plugin-package-workflow-resource@v1`
- `qinglong/plugin-package-prompt-resource@v1`
- `qinglong/plugin-package-tool-resource@v1`
JSON 只负责作者输入;物化结果按核心规范化,不要求作者手工排序 object key。输入必须
是严格 UTF-8、exact shape、无未知字段。每项保存实际 source bytes 和 SHA-256,全部
entry 再按 path 重算 ADR-0135 content tree,必须与 generation/lock 一致。
### 3. Task v1 只开放既有 command 语义
Task 资源提供 package-local id、name、description、labels、enabled、kind 与 spec。
核心生成稳定 `pkg:{packageName}:{id}` Task identity,并通过 ADR-0091 的同一
`TaskSpecSemanticRegistry` 规范化。
首版只接受 `kind=command``qinglong/command@v1`,且 Manifest 必须已审批
`system.command`。Package Secret requirement 目前只有声明,没有 Project SecretRef
绑定 ceremony;因此含 secret environment 的 Package Task 必须失败关闭,不能把
bundle 中写死的 SecretRef 当作已审批绑定。
物化层只输出 `PluginPackageTaskDefinitionDraft`。它不逐项调用
`appendTaskDefinitionRevision`,因为当前单项 Repository 无法证明一代 Package 的
多 Task 原子可见。
### 4. Workflow 与 Prompt 是可审计定义,不冒充执行引擎
Workflow v1 最多 128 个 step。step id 唯一,`needs` 必须引用同 Workflow step
图必须无环;`task` 只能引用同一 Package generation 中存在的 Task id。首版不允许
跨 Package、current/latest、任意 Tool/Prompt 动态引用。
Prompt v1 只提供 text template 和最多 64 个参数;template 最大 512 KiB,声明参数
`{{name}}` placeholder 必须 exact 一致。它不选择模型、不读取 Secret、不调用
Tool,也不声称已经存在 Prompt executor。
### 5. Tool 只形成 Definition,不注入 handler
Tool 文件包裹 ADR-0133 的 exact `ToolDefinition`
- name 必须以 `{packageName}.` 命名空间开头;
- Package 内 identity 不得重复;
- required Project permission 只允许映射到 Manifest 已审批的
artifact/run/secret/task 权限;
- 一代 Package 最多 128 个 Tool,继续服从全局 immutable registry 上限。
物化层输出 `ToolDefinition` 供后续受信 composition 使用,不接受 handler、execute、
module path 或 runtime register。Definition 存在不等于 Tool 可执行。
### 6. revision 自包含且不成为第二个 active pointer
`qinglong/plugin-package-materialized-revision@v1` 完整保存:
- active generation
- immutable lock
- canonical Manifest 与 Manifest digest
- 与 generation 一一对应的 source bytes/digest 和规范化资源;
- domain-separated `revisionDigest`
normalizer 重验 generation↔lock、Manifest↔lock、Manifest↔resource references、
source descriptors↔content tree、Package 引用、权限和 revision digest。revision 以
`generationDigest` 为 immutable key;未来 repository 只允许 create/exact replay。
consumer 仍先读 active generation,再找同 generation 的 revision,不引入第二个
“current”指针。
本 ADR 定义 repository portADR-0151 已在后续实现 SQLite/PostgreSQL durable
store。跨 Package 全局 Tool snapshot 与 Task/Workflow/Prompt 发布事务仍未完成。
### 7. 两种 Profile 使用不同 byte adapter、共享同一语义
本地 `@qinglong/local-admin/package-resource-materialization`
- 一次打开 owner-only 0700 staging generation
- 一次解析最大 64 KiB receipt
- 验证目录 device/inode、0600 no-follow blob、exact inventory、bytes/digest
- 每个 path 最多读取一次,close 后丢弃 session 元数据。
Cluster 复用
`@qinglong/cluster-admin/plugin-package-oci-stage` 的 allowlisted HTTPS、exact registry
credential、Manifest/config/referrer signature 与 canonical bundle inspector。同一
OCI layer 只流式取得一次,sink 最多保留 8 MiB 目标资源,并在 reader close 时清理
未消费 Buffer。它不把 resource reader 接入常驻 `cluster-control`
## Profile 影响
- edge/standalone:只有显式物化请求才打开 staging;没有目录扫描、后台线程或新
数据库连接。8 MiB 是单次业务资源输入硬上限,不是常驻保留目标。
- cluster:每次短生命周期 admin materialization 重新验证 digest-pinned OCI source
多副本可以竞争未来 immutable revision create,但不能覆盖 active pointer 或注入
进程内动态 registry。
- worker:不导入管理 byte adapter;未来只消费已发布、与 execution revision 绑定的
结果。
## 被否决方案
1. **为 materializer 新增 workspace package**:没有独立部署、权限或依赖生命周期,
会继续把 `packages/` 拆碎。
2. **按路径逐项注册 Task/Tool**:中途失败会让同一 generation 部分生效。
3. **在 active pointer 中嵌入解析结果**:会突破 ConfigMap/路由器 pointer 预算,并让
pointer publisher 获得业务语义 authority。
4. **运行时扫描 staging 或 watch ConfigMap**:扫描不是审批事实,watcher 又制造常驻
资源和 stale cache。
5. **Package 自带 JS handler 或 dynamic import**Definition 会变成控制面代码注入。
6. **现在发明 SecretRef 模板替换**:没有安装绑定审批、rotation 与审计 ceremony
会把字符串替换误写成安全 Secret authority。
## 验证
- runtime-core:四种 schema、Task command registry、Prompt parameter、Workflow DAG/
引用、Tool namespace/permission、source/content/revision digest、strict UTF-8、
active generation 双观察与 root/subpath 隔离;
- local-admin:真实 0700/0600 stage、单 session exact read、重复/越界、blob tamper、
unknown inventory 和 close
- cluster-admin:同一受信 OCI inspector 的资源 capture、lock source、exact read、
digest/allowlist/credential 既有负向回归;
- ADR-0151SQLite/PostgreSQL create/exact replay、损坏数据 fail-closed、双方言
schema/readiness/ACL、真实 PostgreSQL 18.4 与 physical HA
- architectureworkspace importer 仍为 21,不新增第三方依赖或常驻 Profile root。
## 后续
ADR-0151 已实现以 `generationDigest` 为键的 SQLite/PostgreSQL immutable revision
repository 与 create/exact-replay contract。下一阶段分别设计:
1. Package TaskDefinition 多资源原子 reconciliation
2. 全部 active generation 的 immutable Tool registry snapshot
3. Workflow/Prompt 独立版本仓库和执行器;
4. Package Secret requirement 到 Project SecretRef 的强认证、可审计绑定 ceremony。
在这些 Gate 完成前,物化结果可验证但仍不进入生产执行路径。