mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
docs(ql3): freeze sealed reconciliation planning
This commit is contained in:
@@ -11,6 +11,17 @@
|
||||
|
||||
最新增量证据(2026-08-21):
|
||||
|
||||
- D-390/ADR-0483(契约已冻结,待实现):`reconciliation_captured` 只证明原始字节可恢复,不授予自动回灌。下一切片先把 capture
|
||||
manifest/receipt 升为 v2:逻辑名保持内容无关,payload 改用 SQLite 可识别的固定 `target.sqlite* / legacy.sqlite* /
|
||||
recovery.sqlite` 名称,terminal asset/directory 收敛为 `0400/0500`,并把 activation source/target baseline SHA-256 纳入 lineage。
|
||||
main-only 使用 immutable readonly;只有 WAL+SHM 完整配对且无 journal 时使用普通 readonly,打开前后必须重验全部 asset;hot
|
||||
journal、不完整 sidecar 或漂移不执行 SQLite recovery,直接 `manual_required`。既有 Local Owner 增加独立
|
||||
`reconciliation.plan.prepare|commit|verify`,以 `reconciliation_captured → reconciliation_plan_prepared → reconciliation_planned`
|
||||
CAS fence 发布固定八领域的 bounded count/digest/disposition plan。plan 不保存 row value、command、Secret、credential、日志或业务
|
||||
标识,不产生 `import_ready/rollback_ready/legacy_ready`;unknown schema、不可逆 Run/history、Secret custody、target-only Package/AI 与
|
||||
未映射 Legacy facts 必须保守。实现继续进入 `deployment/reconciliation/sealed-bundle|planning/`,单 handle、64 KiB builder、Edge/
|
||||
Standalone 2/8 MiB cache,不新增 package/dependency/binary/daemon 或 Local SQLite mutation authority import。本条在 Linux/Docker
|
||||
readonly hash-stability、crash replay、完整 package/backend、架构/release 与十四档 artifact 门完成前保持 Proposed。
|
||||
- D-389/ADR-0482(已接受):target stopped 后的 `reconciliation_required` 不能直接逆迁移或覆盖 2.x source;既有 Local Owner
|
||||
已实现独立 `reconciliation.capture.prepare|commit|verify`,只允许 exact stopped reconciliation head,以 instance CAS 建立唯一
|
||||
capture fence,并把 target main/sidecars、Legacy source main/sidecars、activation recovery 与内容无关 lineage 以固定 64 KiB
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# ADR-0483:密封 Reconciliation Bundle 的有界数据域计划
|
||||
|
||||
- 状态:Proposed(D-390 契约冻结)
|
||||
- 日期:2026-08-21
|
||||
- 关联 RFC:QL-RFC-0001 D-05、D-06、D-17、D-64、D-87、D-184、D-259、D-383、D-389、D-390
|
||||
- 关联 ADR:ADR-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 baseline;Task/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 MiB;schema object 上限 4,096,table 上限 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 fence;restart/rollback/第二计划不能越过。
|
||||
4. commit 产生固定八领域、固定 disposition、≤64 KiB 的内容无关 plan/receipt;unknown、Secret/credential、Run 与未映射事实保守分类。
|
||||
5. plan/receipt/head 各崩溃窗口和 response loss exact replay;verify 完全只读且不打开 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 不增长。
|
||||
|
||||
## 未包含
|
||||
|
||||
- 逐对象明细分页、人工冲突选择和审批;
|
||||
- 任何 Legacy/target 写入、Secret 解密、credential 导出或服务 restart;
|
||||
- 自动 checkpoint、hot journal recovery 或损坏数据库修复;
|
||||
- Cluster/PostgreSQL/Kubernetes reconciliation;
|
||||
- 固定物理 Edge/NAS 的断电、FTL 写放大与介质销毁证明。
|
||||
@@ -486,6 +486,7 @@
|
||||
| [ADR-0480](./ADR-0480-authenticated-atomic-legacy-data-application.md) | 受认证、原子且可恢复的 Legacy Data Application | Accepted |
|
||||
| [ADR-0481](./ADR-0481-committed-legacy-data-deployment-lineage.md) | Committed Legacy Data Receipt 的本机部署 Lineage | Accepted |
|
||||
| [ADR-0482](./ADR-0482-stopped-local-sqlite-reconciliation-recovery-bundle.md) | 停止态 Local SQLite Reconciliation Recovery Bundle | Accepted |
|
||||
| [ADR-0483](./ADR-0483-sealed-reconciliation-bundle-domain-plan.md) | 密封 Reconciliation Bundle 的有界数据域计划 | Proposed(D-390 契约冻结) |
|
||||
|
||||
## 规则
|
||||
|
||||
|
||||
Reference in New Issue
Block a user