Files
qinglong/docs/adr/ADR-0160-sqlite-postgresql-immutable-tool-invocation-artifact-repository.md
T

4.8 KiB
Raw Blame History

ADR-0160SQLite/PostgreSQL 不可变 Tool Invocation Artifact 仓库

  • 状态:Accepted
  • 日期:2026-07-26
  • 关联:ADR-0043、ADR-0063、ADR-0125、ADR-0158、ADR-0159 RFC D-29/D-30/D-123/D-149/D-150

背景

ADR-0159 已把 Tool input 与审批 preview 从 plan 移到两个不可变 Artifact,但只有领域 envelope 和 Repository port 仍不足以支持重启、集群派发或主库晋升。双方言必须用同一 身份和重放语义持久化,同时保持低配设备不引入新进程或依赖,并避免 Cluster 的 admin、Package 与 Worker authority 取得敏感调用材料。

决策

1. 使用现有 Profile adapter package

  • SQLite adapter 位于 @qinglong/local-sqlite/tool-invocation-artifact
  • PostgreSQL adapter 位于 @qinglong/cluster-postgres/tool-invocation-artifact
  • 不增加 workspace package、第三方依赖、常驻进程、timer、watcher 或缓存。

每个方言各有 input/preview 两张表。表保存受审 mirror column 和完整有界 Artifact JSONinput JSON 只有 AES-256-GCM envelope,数据库永不保存 key material 或明文。

2. 双 Artifact 必须原子提交并精确重放

Repository 一次只接受具有相同 Project、action 和 sealing time 的 input/preview pair。SQLite 使用共享 BEGIN IMMEDIATE operation authorityPostgreSQL 使用短 SERIALIZABLE transaction。任一行失败必须回滚两行。

相同 Artifact ID 只有完整规范化内容一致才能返回 existing。部分 pair、mirror column 漂移、非法 JSON、digest 漂移或相同身份不同内容均失败关闭。PostgreSQL 并发 唯一键竞争在事务回滚后重读:赢家内容完全一致时收敛为 exact replay,否则为 conflict。

3. PostgreSQL 不以行锁换取扩大权限

Artifact 行不可变,因此 replay 读取不得使用 FOR SHARE。PostgreSQL 对行锁读取会 要求更新类权限,这与 append-only authority 冲突。runtime 只取得两张表的 SELECT, INSERTUPDATE, DELETE 必须由数据库拒绝。admin、Package manager、 Package executor 和 Worker ingress 对两表均无 SELECT/INSERT/UPDATE/DELETE。 迁移 authority 继续独占 DDL。

4. Capability 与 schema 必须锁步

  • SQLite0057-tool-invocation-artifacts 建表, 0058-capability-v29 发布 tool_invocation_artifact:1;当前 58 条 migration、 capability v29、51 张 typed owned table
  • PostgreSQLpg-0031-tool-invocation-artifacts 原子建表、安装 ACL 并发布 capability v30;当前 31 条 migration、capability v30、50 张表。

双方言 readiness、Drizzle schema、migration manifest/checksum、table/column/index/ CHECK/FK contract 必须一起前进,不能只修改 migration SQL。

低配与集群影响

  • Edge 仍是单 SQLite 文件和现有共享写 authority;每次调用只增加两个有界行和一次 短事务,不增加后台工作;
  • input 明文上限 64 KiB,完整 envelope 上限 96 KiBpreview 内容上限 8 KiB,完整 envelope 上限 16 KiB
  • Cluster 的两个 Artifact 随 PostgreSQL WAL 复制,不在应用层再造复制协议;
  • retention/rekey 不进入 runtime 热路径,后续必须由短生命周期 maintenance authority 实现。

被否决方案

  1. 给 runtime 授予 UPDATE 以继续使用 FOR SHARE:为不可变表扩大权限,且不能 提供额外正确性。
  2. 分别 autocommit 两个 Artifact:崩溃会留下无法判定的半 pair。
  3. 把 Artifact 写入 start barrier JSON:扩大热表和恢复读取面,并复制 ciphertext。
  4. 让 admin/Package executor 代写:混淆执行 authority 与管理/发布 authority。
  5. 新增 Artifact workspace package:没有独立部署、依赖或权限生命周期。

验证

  • SQLite database/schema/Artifact repository 定向测试 16/16
  • PostgreSQL migration/schema/readiness/Artifact repository 定向测试 33/33
  • Artifact repository 额外覆盖 concurrent unique winner exact replay、半 pair rollback、损坏 mirror fail-closed 与禁止 FOR SHARE
  • PostgreSQL 18.4 arm64 双节点物理 HA
    • runtime 原子写入并 exact replay
    • input JSON 不含测试明文;
    • admin、Package manager、Package executor、Worker ingress 均被数据库拒绝;
    • remote_apply standby 已观察两行;
    • timeline 1→2 promotion 后 runtime 可读取并重放;
    • 旧主经 pg_rewind 只读同步重入,总 gate passed=true

后续门禁

  1. retention、key rotation/rekey、遗失 key 和损坏 row 的 inspect/manual recovery
  2. 首个 trusted built-in adapter 在 durable start 后解封并再次验证 Registry
  3. adapter response-loss/进程崩溃的无重复副作用恢复证据。

Artifact reference 与最终 start barrier 的双方言原子绑定已由 ADR-0161 完成,不再属于开放项。