mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:17:47 +08:00
178 lines
8.9 KiB
Markdown
178 lines
8.9 KiB
Markdown
# ADR-0150:有界 Plugin Package 语义物化
|
||
|
||
- 状态:Accepted(纯领域物化、POSIX staging reader 与 Cluster OCI reader 已实现;
|
||
ADR-0151 已实现 SQLite/PostgreSQL immutable revision repository;Task 批量发布与
|
||
全局 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 port;ADR-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-0151:SQLite/PostgreSQL create/exact replay、损坏数据 fail-closed、双方言
|
||
schema/readiness/ACL、真实 PostgreSQL 18.4 与 physical HA;
|
||
- architecture:workspace 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 完成前,物化结果可验证但仍不进入生产执行路径。
|