7.9 KiB
ADR-0154:Project-scoped 不可变 Tool Definition Snapshot
- 状态:Accepted(架构边界、纯 planner、canonical digest、轻量 contribution、 8 MiB snapshot 上限、双方言 append-only repository、current-vector proof、 分页双观察 production coordinator、本机 application gate、Cluster package-executor Job、executor-only PostgreSQL ACL 与 physical HA snapshot durability 已实现;Tool consumer admission 与 trusted handler binding 尚未实现)
- 日期:2026-07-26
- 关联:ADR-0133、ADR-0149 至 ADR-0153、QL-RFC-0001 D-131/D-144/D-148
背景
ADR-0150 允许 active Plugin Package 提供经 Package name 和已审批 permission 围栏的
ToolDefinition,ADR-0133 提供 immutable ToolDefinitionRegistry 与 Policy-fenced
invocation plan。但目前仍不能把多个 active Package 的 Definition 组成可供产品读取的
一致 registry。
这里必须避免两个看似直接但错误的推论:
- Package 提供 Definition 不等于 Package 可以注入 handler;
- 一个进程级全局 registry 不能代表所有 Project,因为不同 Project 可以安装不同 Package generation。
如果 registry 没有绑定完整 active Package 集合,移除 Package、升级 generation 或 无 Tool 的 Package 发生变化时,旧 registry 可能继续被误认为当前。
决策
1. Snapshot 必须以 Project 为边界
qinglong/project-tool-definition-snapshot@v1 至少绑定:
projectId;- 完整、唯一排序的 active Package vector;
- 每个 Package 的 name、installation、generation、generation digest、lock digest 和 materialized revision digest;
- 完整、唯一排序的 Tool Definition 与来源 Package/generation;
- active vector digest、definition digest 和 snapshot digest;
- 数据库提交时间。
active vector 必须包含没有 Tool 的 active Package。否则“Package 从无 Tool 升级为有 Tool”或“有 Tool 降级为无 Tool”不能可靠改变 snapshot identity。
一个 Project 最多 128 个 active Package、128 个 Tool Definition;任何重复
name + version、跨 Package namespace、来源 revision 漂移或超限都失败关闭。
完整 canonical snapshot JSON 另受 8 MiB 硬上限。
2. Builder 使用双观察,不建立第二个 Package pointer
caller-driven builder 固定执行:
- 从耐久 install heads 分页读取一个 Project 的完整 active vector;
- 对每个 Package 读取 active generation,必须与 install vector 精确一致;
- 按 generation digest 逐项读取 immutable materialized revision,立即提取只含 generation、revision digest 和 Tool Definition 的轻量 contribution,然后释放完整 revision;
- 从 contributions 构造确定性 snapshot;
- 再次读取完整 install vector 和每个 active generation;
- 全部未变才允许提交 snapshot。
planner 不得要求同时持有全部 24 MiB materialized revision;否则 128 个 active Package 会在低配设备上制造不可接受的理论峰值。snapshot 不是第二个 active pointer。 source of truth 仍是 durable active install 与 ADR-0149 generation;snapshot 只是这一完整集合的不可变、可审计投影。
3. 双方言使用 append-only snapshot + source item
SQLite/PostgreSQL 使用既有 storage package 的显式 subpath,不新增 workspace package:
- snapshot 主表按
Project + activeVectorDigest唯一; - source item 表保存该 snapshot 的完整 Package vector;
- Definition 保存在规范化 snapshot JSON 中,并由索引列/CHECK 绑定 identity;
- create 只允许首次插入或完整 exact replay;
- update/delete 禁止。
findCurrent(Project) 必须在一次数据库观察中证明 snapshot source item 与当前 active
install heads 双向完全相等。安装刚提交而 snapshot 尚未发布、Package 被移除或任一
generation 改变时返回 unavailable/absent,禁止回退到旧 snapshot。这样不需要 timer、
watcher、LISTEN 或可变内存 head。
PostgreSQL 只允许 ql3_package_executor 创建 snapshot;未来 Tool invocation consumer
只取得经过 current-vector 复验的 SELECT,不取得 Package install、materialization 或
snapshot 写权。
4. Definition snapshot 不包含 handler
snapshot 和现有 ToolDefinitionRegistry 均不得包含函数、module path、dynamic import、
command、URL、MCP credential、数据库 service 或网络 client。Package 安装不能创建或
替换 handler binding。
后续受信 handler binding 必须由产品 composition 独立提供,并精确绑定:
- Project Tool snapshot digest;
- Tool name、version 和 definition digest;
- reviewed handler adapter identity/version;
- execution class、timeout/redaction/audit contract;
- 可用 Profile 与所需外部 authority。
没有 exact binding 的 Definition 必须保持不可调用。built-in、MCP、HTTP 和未来隔离 Package runtime 可以使用不同 binding ceremony,但均不能由 Package resource JSON 自行声明为已信任代码。
5. Profile 保持请求驱动
- edge/standalone:Task publication gate 后按 Project 构造 snapshot;空 Package 集合 产生合法空 snapshot。读取与提交使用单 SQLite authority,无常驻 cache/timer。
- cluster:既有 package-executor Job 在 Task publication 后构造并提交 snapshot; 多 Job 通过 active vector exact replay 收敛。
- worker:不构造 snapshot;只接受未来 execution plan 中已绑定的 handler identity。
被否决方案
- 一个进程级全局 registry:会混淆不同 Project 的安装集合和 Policy authority。
- 只收集有 Tool 的 Package:无 Tool→有 Tool 的升级不能被完整 source vector 围栏。
- 把 handler/module/URL 写进 Package Tool JSON:把 Definition 安装升级为控制面 代码或网络 authority 注入。
- 以 registry cache 作为 current head:进程重启、多副本和 Package 更新会产生 stale truth;current 必须由数据库 active vector 复验。
- 每次 Tool call 重读 staging/OCI:绕过 immutable revision,并把调用可用性绑定 到 Registry。
- 为 snapshot 新建 package:没有独立部署或版本生命周期,显式 subpath 足够。
实现门
- runtime-core pure planner、canonical digest、轻量 contribution、8 MiB、 128/128 bounds,以及 active-source/pending-Project keyset、双观察 publication、 有界 recovery/final probe 和 drift tests(已完成,runtime-core 275/275);
- 双方言 append-only snapshot/source-item schema、exact replay、active-vector
current proof 与显式 storage subpath(已完成:SQLite capability v25;
PostgreSQL
pg-0027/ capability v26); - install vector 在 queued/staged/activating 窗口保持旧 active generation、active commit 后旧 snapshot 立即失效,以及 stale publish conflict 的双方言共享契约 (已完成;source page 精确绑定 active install 与 immutable revision,publication 在提交前完成第二次完整观察);
- local application 在未来 Tool consumer admission 前按 Project 请求驱动发布 current snapshot(已完成;位于 Task publication 后、Secret/Run/lifecycle/admission 前;只分页处理缺 current snapshot 的 active Project,空 Project 生成合法空 snapshot,无 timer/watcher/全历史扫描);
- PostgreSQL
ql3_package_executor的 snapshotSELECT/INSERT、其余常驻角色 default deny、真实六角色 PostgreSQL 18 integration 与 physical HA readiness (已完成;package-executor recovery Job 在 Task publication 后运行同一有界 coordinator。PG18.4 arm64 六角色真库 38 pass/1 条件 skip;25 个 physical HA 具体 gate 与总passed全绿,snapshot 在旧主由 executor 发布、晋升前完成remote_apply复制并在 timeline 2 以相同三层 digest 读取); - 独立 ADR 冻结 trusted handler binding、preview/approval、StepRun/Trace/Audit 后, 才能开放 Tool execution(待实现)。