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

12 KiB
Raw Blame History

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-maintarget-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 中的 sourceSha256targetSha256 纳入内容无关 lineage projection 和 manifest。receipt 发布后、instance head 推进前,所有 asset 从 0600 收敛为 0400assets 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=1readOnly
  • -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 固定 defensivetrusted_schema=OFFquery_only=ONtemp_store=MEMORYmmap_size=0,禁止 extension、attach、 vacuum、DDL、DML 和网络。

3. 增加独立 plan prepare、commit、verify

既有 ql3-local-deploy 增加:

local.deployment.reconciliation.plan.prepare
local.deployment.reconciliation.plan.commit
local.deployment.reconciliation.plan.verify

调用方显式提供独立私有 planRoot、UUID planIdcaptureRoot/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. automationLegacy Crontabs 与 3.0 Task/Trigger/adoption ledger 的计数和基线漂移;
  3. secret_and_configLegacy Envs/Auths 与 3.0 Secret/data-directory adoption 的计数和 custody 缺口;
  4. run_historyRun/Attempt/Event/Step 等不可逆历史;
  5. plugin_packageinstall/materialization/publication/lifecycle/quarantine
  6. ai_and_toolprovider、prompt、Tool execution/output/key binding
  7. identity_policy_auditProject、RoleBinding、credential、pepper、approval 与 audit
  8. unknown:不在固定分类中的 table/schema facts。

每个领域只返回 bounded counts、inventory digest 和以下 disposition 之一:

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_readyrollback_readylegacy_ready

5. Diff 使用已提交 baseline,不猜测语义等价

capture manifest 的 source/target baseline SHA-256 与 captured asset set 判定数据库级 unchanged|changed。target 内既有 QingLong3LegacyAdoptionsQingLong3LegacyDataDirectoryAdoptions 只提供已提交 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 packagessingleSourcePackages=[]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 failtracked backend 1540 total / 1538 pass / 2 conditional skip / 0 fail18-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 写放大与介质销毁证明。