Files
qinglong/docs/adr/ADR-0483-sealed-reconciliation-bundle-domain-plan.md
T

176 lines
12 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-0483:密封 Reconciliation Bundle 的有界数据域计划
- 状态:AcceptedD-390 已实现)
- 日期:2026-08-21
- 关联 RFCQL-RFC-0001 D-05、D-06、D-17、D-64、D-87、D-184、D-259、D-383、D-389、D-390
- 关联 ADRADR-0064、ADR-0094、ADR-0095、ADR-0194、ADR-0201、ADR-0314、ADR-0315、ADR-0482
- 细化:ADR-0482 第 3、5、7 节的 terminal asset layout 与只读消费者契约
## 背景
ADR-0482 已把 stopped `reconciliation_required` 的 target、Legacy source、recovery 和 lineage 冻结为不可变 bundle,但
`reconciliation_captured` 只证明原始字节可恢复,不说明两侧哪些数据域发生变化、哪些事实可映射、哪些只能保留或人工裁决。直接从
capture 跳到 import 会把“证据保全”错误提升成写 authority,并且无法回答 3.0 Run、Secret、Plugin Package、AI、identity 与 audit
如何安全降级到 2.x。
当前 bundle 还使用 `target-main``target-wal` 等逻辑名作为实际文件名。SQLite 只识别 `<main>-wal/-shm/-journal`,因此下游若要
检查一致 snapshot,只能再复制一整份数据库或修改 bundle。前者会在路由器、NAS 和小闪存设备上制造不可接受的第二次空间放大;后者
破坏 ADR-0482 的不可变承诺。D-390 必须先修正物理读取契约,再建立严格只读、固定内存和固定输出的领域计划。
## 决策
### 1. Capture schema v2 使用 SQLite 可识别的固定物理名并密封
manifest 继续只保存固定逻辑名,不保存输入绝对路径;实际 payload 使用固定映射:
| 逻辑名 | 物理名 |
| --- | --- |
| `target-main` | `target.sqlite` |
| `target-wal/-shm/-journal` | `target.sqlite-wal/-shm/-journal` |
| `legacy-main` | `legacy.sqlite` |
| `legacy-wal/-shm/-journal` | `legacy.sqlite-wal/-shm/-journal` |
| `recovery-main` | `recovery.sqlite` |
capture manifest/receipt 升为 schema v2,并把 activation 中的 `sourceSha256``targetSha256` 纳入内容无关 lineage projection 和
manifest。receipt 发布后、instance head 推进前,所有 asset 从 `0600` 收敛为 `0400`assets directory 从 `0700` 收敛为 `0500`
并 fsync 文件、目录及父目录。部分 chmod、receipt-response-loss 和 head-response-loss 均由同一 commit 收敛;terminal verify 只接受
完整密封状态。3.0 尚未发布,本 ADR 不为内部 alpha schema v1 增加双格式兼容或自动重写。
### 2. 只允许两种零复制 SQLite 读取方式
- main 无 `-wal/-shm/-journal`:使用 `file:...?...immutable=1``readOnly`
- `-wal``-shm` 同时存在、无 `-journal`:使用普通 `readOnly`,依赖密封 mode 阻止 SQLite 改写 shm。
存在 hot journal、WAL/SHM 不配对、额外 sidecar、mode/owner/link/hash 漂移时,不打开数据库、不尝试 checkpoint/recovery,也不把 main
当作完整 snapshot;该数据库 inventory 固定为 `manual_required`。每次打开前后重验 manifest 中全部 bytes/hash 和 stat,任何变化失败
关闭。handle 固定 `defensive``trusted_schema=OFF``query_only=ON``temp_store=MEMORY``mmap_size=0`,禁止 extension、attach、
vacuum、DDL、DML 和网络。
### 3. 增加独立 plan prepare、commit、verify
既有 `ql3-local-deploy` 增加:
```text
local.deployment.reconciliation.plan.prepare
local.deployment.reconciliation.plan.commit
local.deployment.reconciliation.plan.verify
```
调用方显式提供独立私有 `planRoot`、UUID `planId``captureRoot/captureId`、exact `bundleDigest`、当前 instance head digest、
`legacyTimezone|null` 和时间。prepare 验证密封 bundle 与 `reconciliation_captured` head,以 CAS 推进为
`reconciliation_plan_prepared`,并在 `<planRoot>/<planId>` no-replace 发布固定 intent。planRoot 不得位于 deployment root、capture
directory 或任一 SQLite asset 内。
commit 只读取密封 bundle,发布固定 schema plan、terminal receipt,再以 plan digest 把 head 推进为 `reconciliation_planned`
verify 只重验 terminal plan、receipt、bundle binding 与 head,不打开 SQLite、不创建/清理文件。prepare/commit response loss、plan/receipt/
head 崩溃窗口均 exact replay;不同 command 不能复用 planId。
### 4. Plan 只提供固定领域 summary 和 disposition
plan 固定包含以下有序领域,不保存 row value、command、Secret、credential、日志、业务标识或单条对象名称:
1. `schema_lineage`:两侧 catalog、migration/capability 与未知对象摘要;
2. `automation`Legacy Crontabs 与 3.0 Task/Trigger/adoption ledger 的计数和基线漂移;
3. `secret_and_config`Legacy Envs/Auths 与 3.0 Secret/data-directory adoption 的计数和 custody 缺口;
4. `run_history`Run/Attempt/Event/Step 等不可逆历史;
5. `plugin_package`install/materialization/publication/lifecycle/quarantine
6. `ai_and_tool`provider、prompt、Tool execution/output/key binding
7. `identity_policy_audit`Project、RoleBinding、credential、pepper、approval 与 audit
8. `unknown`:不在固定分类中的 table/schema facts。
每个领域只返回 bounded counts、inventory digest 和以下 disposition 之一:
```text
aligned | legacy_changed | target_changed | diverged |
target_only | manual_required | unsupported
```
`aligned` 也不授予写入;`target_only` 表示必须保留 3.0 bundle 或由后续显式 exporter 处理;`manual_required/unsupported` 禁止自动计划。
只要未知 schema、hot journal、不完整 sidecar、未映射 Legacy facts、active/inconclusive Run 或 credential/Secret custody 缺口存在,整体
outcome 必须为 `manual_required`。本 ADR 不产生 `import_ready``rollback_ready``legacy_ready`
### 5. Diff 使用已提交 baseline,不猜测语义等价
capture manifest 的 source/target baseline SHA-256 与 captured asset set 判定数据库级 `unchanged|changed`。target 内既有
`QingLong3LegacyAdoptions``QingLong3LegacyDataDirectoryAdoptions` 只提供已提交 adoption baselineTask/Trigger current revision、
Secret version 和各领域 table count/digest 只用于保守分类。名称相同、数量相同或 JSON 看起来相似都不能自动视为等价。
Legacy timezone 是显式 reviewed input;若与可证明的 adopted trigger timezone 不一致则 `manual_required`。D-390 不解密 Secret、不读取
credential/token、不执行 Legacy command,也不构造 2.x SQL。逐对象 private diagnostics、人工选择与目标 adapter 由后续 ADR 定义,必须
消费 exact plan digest。
### 6. 低资源与 package 边界
实现继续内聚在 `@qinglong/local-owner-cli/src/deployment/reconciliation/``sealed-bundle/``planning/` 子目录,不新增 workspace
package、production dependency、binary、daemon、timer、watcher、listener、socket 或后台 retry。不得把新文件平铺到 package `src/`
根,也不得从 deployment planning import Local SQLite mutation/adoption authority。
内存上限为单个 SQLite handle、64 KiB hash buffer、固定八领域数组和不超过 64 KiB 的 plan builder。Edge/Standalone SQLite cache
分别不超过 2/8 MiBschema object 上限 4,096table 上限 512,领域 row count 只保留安全整数和总量,不把对象列表放入内存。plan
文件不超过 64 KiB。Cluster/PostgreSQL 不消费 Local bundle 或 plan。
## 被拒绝的替代方案
### 为分析再复制 target 与 Legacy 数据库
拒绝。最坏会在 capture 已占两份数据库后再增加两份,直接破坏路由器和小闪存设备的空间模型。
### 直接以 immutable 打开带 WAL 的 main
拒绝。SQLite immutable 会忽略 WAL,可能静默丢失已经提交但未 checkpoint 的事实。
### 让 readonly SQLite 使用可写 shm
拒绝。真实验证表明 readonly handle 仍可能改写 `-shm`;只有密封资产才能保持 bundle hash 不变。
### 从表名相似自动生成回灌 SQL
拒绝。相同名称不证明 Project、revision、Policy、Secret custody、append-only history 或幂等身份等价。
### 把完整 row diff 写入 plan
拒绝。会泄漏敏感内容、产生无界内存/文件,并把 inventory 偷换成未审批的迁移 payload。
## 验收条件
1. Capture v2 固定物理名、0400/0500 密封、baseline digest 与全部 crash replay 可验证;v1 不被静默接受。
2. main-only immutable 与 WAL+SHM readonly 在 Linux/Docker 中可读取且前后 bytes/hash/stat 完全不变;hot journal/unpaired sidecar 零
SQLite open 并稳定 `manual_required`
3. plan prepare 只接受 exact `reconciliation_captured` bundle/head 并建立唯一 CAS fencerestart/rollback/第二计划不能越过。
4. commit 产生固定八领域、固定 disposition、≤64 KiB 的内容无关 plan/receiptunknown、Secret/credential、Run 与未映射事实保守分类。
5. plan/receipt/head 各崩溃窗口和 response loss exact replayverify 完全只读且不打开 SQLite。
6. 无 import、DML、checkpoint、service、Docker/init/network 副作用,无新增 package/dependency/常驻对象或跨包写 authority import。
7. Edge 固定 2 MiB cache、Standalone 8 MiB cache;完整 Local Owner/backend/package、架构、release、十四档 artifact 与真实 Docker
readonly rehearsal 通过,基础 Edge closure 不增长。
## 实现与验证证据
D-390 已在既有 `@qinglong/local-owner-cli/src/deployment/reconciliation/` 内实现。`sealed-bundle/reader.ts` 只接受经 terminal
validator 验证的密封 capturemain-only 走 immutable readonlyWAL+SHM 完整配对走普通 readonlyhot journal、sidecar 不配对或
任一 stat/hash/mode 漂移均在 SQLite open 前失败关闭。`planning/` 以固定 contract、inventory、prepare/commit/verify 将 instance head
`reconciliation_captured` CAS 推进到 `reconciliation_plan_prepared`、再推进到 `reconciliation_planned`。plan 只包含固定八领域的
有界计数、digest、disposition 和保守 outcome,不保存表名、路径、row value、Secret、credential、命令或日志,也不产生 import authority。
实现没有新增 workspace package、production dependency、binary、daemon、listener、timer、watcher 或网络访问。workspace 仍为 18
packages`singleSourcePackages=[]``shallowSourcePackages=[]`Local Owner 为 `146 source / 145 nested / 1 root binary entry`,新增的
5 个源文件全部进入 `deployment/reconciliation/sealed-bundle|planning/`,没有回到 `src/` 根平铺。Edge/Standalone cache 固定为
2/8 MiBhash buffer 与 plan 上限均为 64 KiBschema/table 上限为 4,096/512。
验收结果:reconciliation 聚焦套件 `24 total / 22 pass / 2 conditional Docker skip / 0 fail`;真实 Linux/Docker main-only 与
WAL+SHM readonly/hash-stability rehearsal `2/2`;完整 Local Owner `246 total / 239 pass / 7 conditional skip / 0 fail`tracked
backend `1540 total / 1538 pass / 2 conditional skip / 0 fail`18-package clean build/逐包测试通过。Edge import、Cluster dependency、
package boundary、service-manager bridge、Local image、image release、release version 与 deployment-lock surface 审计均 compatible。
十四档 artifact audit 均 compatible。基础 Edge/Standalone 仍为 `2,611,978 / 2,612,056` bytes、319 files、58 modulesAdopted
仍为 `2,831,713 / 2,831,836` bytes、339 files、59 modulesApplication+AI 为 `4,529,710 / 4,529,842` bytes、516 files、
144 modulesMCP 为 `7,337,910 / 7,338,018` bytes、805 files、228 modules。一次性 plan authority 未进入基础常驻闭包,也没有被
Cluster/PostgreSQL 消费。
## 未包含
- 逐对象明细分页、人工冲突选择和审批;
- 任何 Legacy/target 写入、Secret 解密、credential 导出或服务 restart
- 自动 checkpoint、hot journal recovery 或损坏数据库修复;
- Cluster/PostgreSQL/Kubernetes reconciliation
- 固定物理 Edge/NAS 的断电、FTL 写放大与介质销毁证明。