Files
qinglong/docs/adr/ADR-0154-project-scoped-immutable-tool-definition-snapshot.md
T

148 lines
7.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-0154Project-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 固定执行:
1. 从耐久 install heads 分页读取一个 Project 的完整 active vector
2. 对每个 Package 读取 active generation,必须与 install vector 精确一致;
3. 按 generation digest 逐项读取 immutable materialized revision,立即提取只含
generation、revision digest 和 Tool Definition 的轻量 contribution,然后释放完整
revision
4. 从 contributions 构造确定性 snapshot
5. 再次读取完整 install vector 和每个 active generation
6. 全部未变才允许提交 snapshot。
planner 不得要求同时持有全部 24 MiB materialized revision;否则 128 个 active
Package 会在低配设备上制造不可接受的理论峰值。snapshot 不是第二个 active pointer。
source of truth 仍是 durable active install 与
ADR-0149 generationsnapshot 只是这一完整集合的不可变、可审计投影。
### 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/standaloneTask 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。
## 被否决方案
1. **一个进程级全局 registry**:会混淆不同 Project 的安装集合和 Policy authority。
2. **只收集有 Tool 的 Package**:无 Tool→有 Tool 的升级不能被完整 source vector
围栏。
3. **把 handler/module/URL 写进 Package Tool JSON**:把 Definition 安装升级为控制面
代码或网络 authority 注入。
4. **以 registry cache 作为 current head**:进程重启、多副本和 Package 更新会产生
stale truthcurrent 必须由数据库 active vector 复验。
5. **每次 Tool call 重读 staging/OCI**:绕过 immutable revision,并把调用可用性绑定
到 Registry。
6. **为 snapshot 新建 package**:没有独立部署或版本生命周期,显式 subpath 足够。
## 实现门
1. 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);
2. 双方言 append-only snapshot/source-item schema、exact replay、active-vector
current proof 与显式 storage subpath(已完成:SQLite capability v25
PostgreSQL `pg-0027` / capability v26);
3. install vector 在 queued/staged/activating 窗口保持旧 active generation、active
commit 后旧 snapshot 立即失效,以及 stale publish conflict 的双方言共享契约
(已完成;source page 精确绑定 active install 与 immutable revisionpublication
在提交前完成第二次完整观察);
4. local application 在未来 Tool consumer admission 前按 Project 请求驱动发布
current snapshot(已完成;位于 Task publication 后、Secret/Run/lifecycle/admission
前;只分页处理缺 current snapshot 的 active Project,空 Project 生成合法空
snapshot,无 timer/watcher/全历史扫描);
5. PostgreSQL `ql3_package_executor` 的 snapshot `SELECT/INSERT`、其余常驻角色
default deny、真实六角色 PostgreSQL 18 integration 与 physical HA readiness
(已完成;package-executor recovery Job 在 Task publication 后运行同一有界
coordinator。PG18.4 arm64 六角色真库 38 pass/1 条件 skip25 个 physical HA
具体 gate 与总 `passed` 全绿,snapshot 在旧主由 executor 发布、晋升前完成
`remote_apply` 复制并在 timeline 2 以相同三层 digest 读取);
6. 独立 ADR 冻结 trusted handler binding、preview/approval、StepRun/Trace/Audit 后,
才能开放 Tool execution(待实现)。