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

8.9 KiB
Raw Permalink Blame History

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=commandqinglong/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 importDefinition 会变成控制面代码注入。
  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 完成前,物化结果可验证但仍不进入生产执行路径。