# Delta 协议兼容性与互操作范围

> **状态：第 1–4 节是 0.1.x 历史基线，已被 0.3.1 的 M1–M4 与加固实现取代。** 当时 nested 拒绝、checkpoint 不可恢复等描述仅用于保留审计过程，不代表当前能力。当前口径以 README 的“能力边界”与 [CHANGELOG](../CHANGELOG.md) 为准；第 7 节的证据要求仍适用。
>
> 本文把“协议日志兼容”“Parquet 物理类型兼容”和“完整表格式互通”分开描述，作为申报、README、测试和验收的共同依据。

## 当前（0.3.x）口径

- 读取：reader v1，SNAPPY/UNCOMPRESSED，本地文件系统；flat primitive 与 struct / 标准 3-level list / map。
- 恢复：完整 JSON 回放，或从最新单文件 checkpoint 恢复后叠加 JSON；多部分 checkpoint 拒绝。
- 写入：writer v2，非分区 append，flat 与嵌套叶均覆盖 11 种基础类型；date/timestamp 带 Parquet logical annotation；非空 `writerFeatures` 拒绝。
- 安全拒绝：reader ≥ 2、writer > 2、JSON/checkpoint 删除向量、真实未知 action、checkpoint 缺失 protocol/metaData、非字符串 stats、`timestamp_ntz`、decimal/uuid、未支持压缩。
- 已知边界：flat 与 nested 叶均校验 Delta 逻辑类型与 Parquet 物理类型；分区回填覆盖 11 种类型；对拍结论仅适用于仓库指定 fixture。

历史小节保留原文，仅用于追溯 0.1.x 审计过程。

## 1. 目标协议

目标是 **Delta Lake Transaction Log Protocol v1/v2 的受限实现**：

- 读取 `minReaderVersion = 1` 的表，并识别日志中的 `protocol`、`metaData`、`add`、`remove`、`commitInfo` action；
- 写入本项目生成的 `minReaderVersion = 1, minWriterVersion = 2` 表；
- 事务日志采用 Delta 的按版本编号的 JSON commit 文件（`_delta_log/00000000000000000000.json`），每行一个 action；
- 当前交付要求从 version 0 开始完整回放 JSON 日志。存在 `_last_checkpoint` 不代表本项目已经支持 checkpoint 快照恢复；日志被截断或只能依靠 checkpoint 的表必须明确返回不支持错误。

这里的“v1/v2”指 Delta protocol 的 reader/writer version 字段，不等于声称实现了 Delta 规范的全部 feature。遇到高于支持范围的 protocol version、未知必需 feature、删除向量、column mapping 或其他需要额外协议语义的表，读取端必须拒绝并说明原因，不能静默忽略。

## 2. 当前交付的完整支持范围（0.1.x）

下表是当前版本可对外承诺的闭集。只有同时满足所有条件，才称为“兼容表”。

| 层次 | 支持 | 必须拒绝或暂不承诺 |
|---|---|---|
| 日志 | version 0 起的连续 JSON commit；`protocol`/`metaData`/`add`/`remove`/`commitInfo`；版本回放、快照、时间旅行、文件清单 | 仅有 checkpoint、日志从非 0 开始、日志缺版本、需要 checkpoint 恢复 |
| protocol | reader v1；本项目写出 writer v2；已知 feature 集合 | 高于 reader v1 的 reader protocol；未实现的 writer protocol 或 feature |
| schema | 顶层 flat fields；primitive：boolean、integer/int、long、float、double、string、binary、date、timestamp 的识别；当前读取实测以 int/string 等 flat 列为主 | `struct`、`array/list`、`map` 的完整数据读取/写入；decimal、uuid 等需要保留额外逻辑语义的类型 |
| Parquet | UNCOMPRESSED、SNAPPY；本地文件系统；按 `add.path` 读取 | ZSTD、GZIP、BROTLI、LZ4；对象存储；缺失数据文件 |
| 表操作 | `create_table`、非分区 `append_rows`；分区值读取、分区等值过滤、文件 min/max 剪枝 | 分区 append、MERGE/UPDATE/DELETE、CDF、删除向量、column mapping、并发冲突仲裁 |
| checkpoint | 可识别 `_last_checkpoint` 并提示其存在 | 解析 checkpoint Parquet 并用它重建快照；checkpoint-only 表 |

“完整支持”在本项目中指：对于上述闭集，日志解析、快照状态转换、文件定位、schema 校验、Parquet 解码、错误处理和写后回读都必须有测试；闭集之外的输入必须得到结构化错误，而不是返回空结果或部分正确的数据。

## 3. 嵌套 schema 的边界与终点定义

嵌套 schema 不是 Delta 协议的可选装饰，而是 Delta `metaData.schemaString` 与 Parquet physical/logical schema 的一部分。当前版本**不支持嵌套表的数据读取或写入**；它只解析出嵌套字段并拒绝执行。这意味着当前版本不能宣称与“任意标准 Delta 表”互通，只能宣称与上表闭集内的 flat Delta 表互通。

要把承诺提升为“同一协议下 flat + nested 互通”，必须完成以下独立门槛，缺一不可：

1. 支持 Parquet definition/repetition level，正确处理 `struct`、`list`（标准 3-level LIST）和 `map` 的 null、empty、重复元素与嵌套组合；
2. Delta schema JSON 与 Parquet schema 双向映射，保留 nullable、字段顺序、字段名和嵌套路径；
3. `add.stats` 对嵌套字段的缺失、null 和 min/max 不能被当作 flat 值误用；无法安全剪枝时必须放弃剪枝而继续正确读取；
4. checkpoint 中的嵌套 action schema 可解析，能够从 checkpoint 加载快照，并与从 JSON 回放得到的快照逐字段一致；
5. 对外部引擎完成写入和读取对拍：Spark/Databricks、delta-rs 或 DuckDB 至少选两种，分别覆盖 flat 与 nested 表；
6. 对 nested `struct`、`array/list`、`map` 各提供至少一个正向 fixture、一个 null/empty 边界用例、一个外部引擎结果对照；
7. 写入端生成的 nested Parquet 必须能被外部引擎读回，外部引擎生成的 nested Delta 必须能被本项目读回；只做“识别后报错”不算完成。

因此，路线图中的“checkpoint 所需的最小嵌套 shape”只能作为中间实现，不得写成完整 nested schema 兼容。完成上述门槛前，申报材料应使用“flat 子集兼容、nested 明确拒绝”的表述。

## 4. 同一协议下的互操作对照案例

以下案例使用相同的 Delta protocol 配置（reader v1；写入侧 writer v2；JSON log + Parquet data files），仅改变 schema，目的是把“协议日志可读”与“基础格式可读”分开验收。

### 案例 A：扁平表（当前版本应通过）

**表结构**：`id: long NOT NULL, name: string, active: boolean`；一个或多个 SNAPPY Parquet 数据文件；日志包含 version 0 的 `protocol`/`metaData`/`add`。

**对照流程**：

1. Spark 或 delta-rs 写出 3 行 flat Delta 表；记录 schema、版本号、文件数和每列值。
2. `moondelta-core snapshot_at(0)` 回放日志，读取数据文件并输出同样的列名、nullability、行数和值。
3. `moondelta-core create_table + append_rows` 写出同结构表。
4. Spark、delta-rs 或 DuckDB 读取本项目输出，断言 schema、行数和值一致；再比较 Delta log 中 add 文件、`numRecords` 和 min/max 统计。

**通过标准**：三方结果在列顺序、类型、null、行数和值上逐项一致；统计缺失时只能少做剪枝，不能少读或多读数据。

### 案例 B：嵌套表（当前版本必须明确失败）

**表结构**：`id: long, profile: struct<name:string, age:int>, tags: array<string>, attrs: map<string,string>`；同样使用 reader v1/writer v2 和标准 Delta JSON log。

**外部引擎基线**：Spark、delta-rs 或 DuckDB 能打开该表并返回 3 行，其中至少包含 `profile = NULL`、`tags = []`、`attrs = {}` 等边界值。把外部引擎输出保存为 JSON/CSV 作为 oracle；CSV 只用于展示，不能替代嵌套 schema 断言。

**当前版本预期**：

- 能读取并报告 Delta schema 中的 nested 类型；
- 在进入 Parquet 数据读取前返回 `Unsupported`，错误包含字段路径和 nested kind；
- 不得返回 0 行、把数组当字符串、丢弃 struct 子字段或静默忽略 map；
- 测试报告必须把“外部引擎可读、moondelta-core 有意拒绝”标为能力缺口，而不是互操作通过。

**未来通过标准**：完成第 3 节七项门槛后，同一 fixture 才能改为正向断言，并要求本项目与至少两种外部引擎逐字段、逐行一致。

## 5. 申报与 README 的统一措辞

建议将“Spark / Databricks / delta-rs / DuckDB 可直接互通，已有表可以平移使用”改为：

> 在 Delta protocol reader v1 / writer v2、version 0 起连续 JSON 日志、flat schema、支持的 Parquet 压缩与本地文件系统范围内，moondelta-core 与 Spark、Databricks、delta-rs、DuckDB 进行目录级互操作验证；嵌套 schema、checkpoint-only 表及未实现 feature 当前明确拒绝，完成 nested 读写和 checkpoint 恢复验收后再扩大承诺。

该措辞同时说明了协议目标、必需行为和可验证边界，不把“遵循 Delta 日志格式”误写成“完整实现 Delta 表格式”。

## 6. DeepSeek harness 对话任务分配（内联）

以下任务可直接复制给 DeepSeek harness。每个对话只负责一个可验收产物；主对话负责合并结果，不接受没有 fixture、命令或 diff 的口头结论。

### 任务 D1：协议审计

**指派提示词**：

> 读取 `申报书.md`、`README.md`、`docs/协议兼容性与互操作范围.md` 及 `src/`。建立 Delta protocol reader/writer version、action、feature 与代码路径的矩阵。逐项标记“已实现且有测试 / 只识别 / 明确拒绝 / 未覆盖”，禁止把 README 的声明当作证据。输出 `artifacts/protocol-audit.md`，每个结论附文件路径、测试名和建议补测命令。

**验收**：矩阵覆盖 protocol、JSON log、checkpoint、schema、Parquet codec、读写操作和错误策略；没有“支持所有 Delta”之类无证据结论。

### 任务 D2：flat 互操作对拍

**指派提示词**：

> 在同一 Delta reader v1 / writer v2 配置下，准备一个包含 long/string/boolean、null 和至少两个文件的 flat 表。分别用 Spark 或 delta-rs 写出并读取，再用 `moondelta-core` 读取和写出。保存 schema、版本、文件清单、行数、逐列值、add.stats 与命令输出到 `artifacts/interop-flat/`。若环境缺少外部引擎，写出可复现的阻塞说明，不得伪造结果。

**验收**：提供可重放命令和逐项 diff；外部引擎至少一个，最好两个；本项目的错误或缺失文件不能被忽略。

### 任务 D3：nested 能力缺口对照

**指派提示词**：

> 生成同一 protocol 配置的 nested Delta fixture，覆盖 `struct`、标准 3-level `list`、`map`，并包含 null/empty 边界。用 Spark、delta-rs 或 DuckDB 读取作为 oracle，再执行 `moondelta-core`。确认当前实现是否结构化拒绝且不返回假成功。输出 fixture 来源、schema JSON、外部引擎输出、moondelta 错误和缺口清单到 `artifacts/interop-nested/`。

**验收**：每种 nested kind 至少一个 fixture；明确区分“外部引擎成功”和“本项目当前拒绝”；不得把负向测试写成互操作通过。

### 任务 D4：申报文本一致性修订

**指派提示词**：

> 对照 `docs/协议兼容性与互操作范围.md`，检查 `申报书.md` 与 `README.md` 是否仍声称任意标准 Delta 表、完整 nested schema 或 checkpoint 恢复。只提出最小文字 diff：把协议版本、flat 支持范围、nested 明确拒绝、外部引擎对照和未来门槛写清楚；不要扩大实现范围。输出 unified diff 到 `artifacts/claim-alignment.patch`。

**验收**：每一处互操作承诺都能映射到本文闭集、测试或明确的未来门槛。

## 7. 证据与发布门槛

发布或申报前至少保留以下证据：

- `moon check --target native`、`moon test --target native`；
- flat 对拍的外部引擎版本、命令、输入 fixture 哈希和结果 diff；
- nested 对照的外部引擎成功结果与本项目结构化拒绝日志；
- protocol 审计矩阵与申报/README 文本 diff；
- 所有新增 fixture 的来源、许可证和 SHA-256。

在 nested 读写、checkpoint 恢复和至少两种外部引擎对拍完成前，项目版本标签和申报材料只能使用“受限 flat Delta 互操作”或等价表述。
