实现本地清洗实验与产物保存
This commit is contained in:
@@ -0,0 +1,545 @@
|
||||
# 0005:本地清洗实验运行与产物保存契约
|
||||
|
||||
## 状态
|
||||
|
||||
已批准并冻结(2026-08-22)。
|
||||
|
||||
本设计扩展 `0002` 中已划定的输入输出适配层,并落实 `0003` 中“由未来适配层决定是否保存成功输出”的边界。
|
||||
它不修改 `DocumentSnapshot`、`ProposedChange`、`Change`、`TransformResult` 或组件契约,也不改变
|
||||
`0004` 当时只读验证已经完成的历史事实。
|
||||
|
||||
`0004` 没有授权在那一轮验证中保存真实清洗产物,不等于永久禁止未来的本地实验输出。本设计批准后,
|
||||
新的实验可以在本设计的位置、保留周期和隐私边界内保存产物。
|
||||
|
||||
## 1. 问题与可观察现象
|
||||
|
||||
当前内存核心已经能返回清洗后 Markdown 和逐项 `Change`,但仓库还没有文件读取、安全输出、JSON 审计或
|
||||
人可读 diff。当前只能通过临时 Python 代码在内存中查看结果,会带来四个实际问题:
|
||||
|
||||
1. 评审者无法直接打开清洗后 Markdown;
|
||||
2. 没有统一 diff,难以确认“只改了批准内容”;
|
||||
3. `Change` 只在 Python 对象中,进程结束后无法复核组件、理由、原文和改后文本;
|
||||
4. 批量处理时无法追溯实际输入、工具版本、组件顺序和每份文档的结束状态。
|
||||
|
||||
真实清洗流程必须能保存结果,但不能因此让组件读写文件,也不能把 `failed` 或 `unstable` 的部分文本冒充
|
||||
成功输出。保存的 diff、`before` / `after` 和清洗后 Markdown 都可能还原真实原文,因此也不能当成普通日志
|
||||
提交到 Git。
|
||||
|
||||
## 2. 目标与非目标
|
||||
|
||||
### 2.1 目标
|
||||
|
||||
- 建立只服务本地评审的最小实验运行层;
|
||||
- 显式读取调用方列出的 Markdown 文件,不递归猜测数据集;
|
||||
- 继续使用同一个 `Pipeline.transform()` 完成清洗,不在适配层实现第二套规则;
|
||||
- 对每份成功文档保存独立的清洗后 Markdown、机器可读审计和人可读 diff;
|
||||
- 保存整次运行清单,使输入范围、工具环境、组件顺序、哈希、状态和产物位置可追溯;
|
||||
- 从文件级别区分 `success`、`failed` 和 `unstable`,不为后两者生成正式清洗文档;
|
||||
- 不覆盖、改名或移动任何输入;
|
||||
- 将含真实文本的产物限定为 Git 忽略的本地敏感数据。
|
||||
|
||||
### 2.2 非目标
|
||||
|
||||
- 不实现 Inspector、Finding、人工建议或审核状态;
|
||||
- 不实现 Profile 对象、TOML/YAML/JSON 配置文件或组件自动发现;
|
||||
- 不提供安装后稳定的公共 CLI、服务接口、CI 集成或 Web 界面;
|
||||
- 不原地覆盖、备份或恢复输入文件;
|
||||
- 不把 `partial_markdown` 写成清洗产物;
|
||||
- 不建设数据库、远程对象存储、任务调度或长期审计系统;
|
||||
- 不引入 Markdown parser、AST、新清洗规则或多轮执行;
|
||||
- 不定义真实数据适合公开、共享或长期归档的条件;
|
||||
- 不处理 PDF、DOCX、图片、转换器 JSON 或文件间关联。
|
||||
|
||||
## 3. 当前基础和不改变的边界
|
||||
|
||||
当前 `TransformResult` 已经包含持久化所需的主要运行事实:
|
||||
|
||||
| 对象 | 已有信息 | 本设计的处理 |
|
||||
| --- | --- | --- |
|
||||
| `ComponentInfo` | 组件标识、版本、参数、适用边界 | 写入运行清单 |
|
||||
| `Change` | 候选引用、理由、范围、`before`、`after`、批次前后哈希 | 写入文档审计 |
|
||||
| `RunError` | 组件、阶段、错误类型和安全说明 | 写入文档审计 |
|
||||
| `ResidualProposal` | 最终复查的有效残留修改 | 只写入 `failed` / `unstable` 审计 |
|
||||
| `TransformResult` | 状态、输入/当前哈希、修改、错误、成功或部分文本 | 决定可以生成哪些产物 |
|
||||
|
||||
本设计只在核心外补充文件身份、运行身份、环境和序列化信息。以下边界保持不变:
|
||||
|
||||
- 组件仍只接收 `DocumentSnapshot` 并返回 `ProposedChange`;
|
||||
- 组件不知道文件路径、运行 ID、输出目录或 JSON 格式;
|
||||
- `Pipeline` 仍只处理内存 Markdown;
|
||||
- Python 字符下标仍是修改位置的唯一权威;
|
||||
- reporter 不重新判断业务规则,只表示现有运行结果;
|
||||
- 文件适配层不把读写成功冒充成清洗成功。
|
||||
|
||||
## 4. 方案比较
|
||||
|
||||
### 4.1 继续只返回内存结果
|
||||
|
||||
不增加新契约,但每次真实实验都需要临时代码,结果无法留存,人也无法方便查看完整 Markdown 和 diff。
|
||||
不采用。
|
||||
|
||||
### 4.2 只保存清洗后 Markdown
|
||||
|
||||
人可以打开结果,但无法证明是哪个组件改了什么,也无法区分输入不同、组件不同还是工具不同。
|
||||
输出文件与内存审计会脱节。不采用。
|
||||
|
||||
### 4.3 建设完整 CLI、Profile 和批处理平台
|
||||
|
||||
能够一次解决对外运行,但当前只有一个无参数组件,配置、退出码、安装命令和公共兼容都没有足够消费者与失败样本。
|
||||
这会过早固定外部接口。不采用。
|
||||
|
||||
### 4.4 本地实验运行层加结构化产物
|
||||
|
||||
调用方显式提供文档和 `Pipeline`,外层只负责严格读取、调用核心、表示结果和写入新目录。它不提供稳定公共 CLI,
|
||||
也不引入配置语言。每次运行同时保存成功文本、JSON 审计和 diff。
|
||||
|
||||
采用此方案。它能满足当前 5 份论文的人工复核,同时不迫使核心、组件和未来对外工具提前承担未经验证的接口。
|
||||
|
||||
## 5. 总体边界与依赖方向
|
||||
|
||||
```text
|
||||
调用方显式列出文档和顺序
|
||||
│
|
||||
▼
|
||||
本地实验运行层
|
||||
│ │
|
||||
│ ├── 读取 UTF-8 Markdown
|
||||
│ └── 记录文档身份与环境
|
||||
▼
|
||||
Pipeline.transform(markdown)
|
||||
│
|
||||
▼
|
||||
TransformResult
|
||||
│
|
||||
▼
|
||||
reporter / 产物写入
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
JSON cleaned.md unified diff
|
||||
```
|
||||
|
||||
依赖必须保持从外向内:
|
||||
|
||||
```text
|
||||
本地实验脚本
|
||||
│
|
||||
▼
|
||||
experiment.py
|
||||
├────────► pipeline.py ──────► 当前内存核心
|
||||
├────────► reporting.py ─────► models.py
|
||||
└────────► artifact_store.py
|
||||
```
|
||||
|
||||
- `reporting.py` 可以依赖数据模型,不读文件,不调用组件;
|
||||
- `artifact_store.py` 只负责产物路径、权限、写入校验和原子发布,不调用流水线,不解释修改语义;
|
||||
- `experiment.py` 可以依赖 `Pipeline`、reporter 和 artifact store,负责输入预检与文档级批量编排;
|
||||
- 业务组件和内存核心不反向导入这三个模块;
|
||||
- 日期目录、文件名或未来存储位置的变化应限制在 artifact store,不改变 reporter 或流水线;
|
||||
- 实验脚本可以显式组装 ClinDB 流水线,但通用模块不得硬编码 `data/md/`、DOI 或论文缩写。
|
||||
|
||||
## 6. 输入契约
|
||||
|
||||
### 6.1 显式文档清单
|
||||
|
||||
调用方必须按顺序提供非空文档清单。每项至少包含:
|
||||
|
||||
- `document_id`:当前运行内唯一的稳定标识;
|
||||
- `source_path`:实际读取路径;
|
||||
- `source_label`:写入清单的可展示来源,用于避免无条件泄露绝对路径。
|
||||
|
||||
`document_id` 只允许小写 ASCII 字母、数字、`.`、`_` 和 `-`,必须以字母或数字开头,不得包含路径分隔符或
|
||||
`..` 路径段。文档 ID 重复、源路径重复、源文件不存在或不是普通文件时,整次运行在产生最终产物目录前失败。
|
||||
|
||||
第一个实际实验的文档 ID 固定为 `dmp`、`ejhf`、`jama`、`sim` 和 `springer`,输入只是 `data/md/` 中对应的
|
||||
5 份本地副本。本设计不授权对仓库外 GovDoc 数据保存清洗产物。
|
||||
|
||||
### 6.2 编码和文本保真
|
||||
|
||||
- 输入以二进制读取,再使用严格 UTF-8 解码;
|
||||
- 无效 UTF-8 使整次运行在预检阶段失败,不使用替换字符或自动猜测编码;
|
||||
- 不剔除 UTF-8 BOM,不规范化 Unicode,不转换换行,不补文件末换行;
|
||||
- 清洗后 Markdown 使用 UTF-8 严格编码,保留内存输出的所有字符。
|
||||
|
||||
核心快照哈希仍以 Markdown 字符串重新编码后的 UTF-8 字节为权威。适配层另外记录原始文件字节的 SHA-256;
|
||||
对有效 UTF-8 输入,两者应当一致。如果不一致,视为适配层错误并停止运行。
|
||||
|
||||
### 6.3 流水线来源
|
||||
|
||||
实验运行层接收调用方已显式建立的 `Pipeline`。它不根据文件名、内容或安装环境选择组件,也不在不同文档之间改变
|
||||
组件顺序。当前没有 Profile 身份;运行清单直接记录每次结果中已验证的有序 `ComponentInfo`。
|
||||
|
||||
## 7. 运行状态与批量语义
|
||||
|
||||
### 7.1 文档级别
|
||||
|
||||
每份文档独立调用同一个 `Pipeline`。文档结果直接沿用核心状态:
|
||||
|
||||
- `success`:可以保存正式清洗 Markdown、文档审计和 diff;
|
||||
- `failed`:只保存文档审计和错误,不持久化 `partial_markdown`;
|
||||
- `unstable`:只保存文档审计和残留候选,不持久化 `partial_markdown`。
|
||||
|
||||
对 `failed` 和 `unstable`,之前已经发生的 `Change` 仍必须写入审计,但不得生成名为 `cleaned.md`、`output.md`
|
||||
或类似正式输出的文件。
|
||||
|
||||
### 7.2 批量级别
|
||||
|
||||
单份文档的核心失败或不稳定不中断其他已预检文档。运行层继续按调用方给定的顺序处理,并在清单中保留每份文档的
|
||||
状态。整体状态使用以下优先级:
|
||||
|
||||
```text
|
||||
任一文档 failed → 整体 failed
|
||||
否则任一文档 unstable → 整体 unstable
|
||||
否则 → 整体 success
|
||||
```
|
||||
|
||||
这是实验汇总状态,不改变核心 `RunStatus`。整体失败时,其他文档的成功产物可以保留,但 `manifest.json`
|
||||
必须明确该批次并非全部成功。
|
||||
|
||||
### 7.3 预检与致命错误
|
||||
|
||||
在调用任何组件前,运行层必须完成:
|
||||
|
||||
1. 验证运行日期、UTC 偏移、运行 ID 和所有文档 ID;
|
||||
2. 验证输出根目录不会落入任何输入文件路径;
|
||||
3. 确认最终运行目录不存在;
|
||||
4. 读取所有输入字节、严格解码并计算哈希;
|
||||
5. 验证所有文档清单字段。
|
||||
|
||||
任一预检失败都使整次运行立即失败,不调用 `Pipeline`,不生成最终运行目录。产物写入、JSON 序列化或最终目录
|
||||
发布失败同样是整次运行的致命错误,不得把不完整临时目录报告为已完成实验。
|
||||
|
||||
## 8. 产物目录与发布
|
||||
|
||||
### 8.1 固定位置
|
||||
|
||||
仓库内本地实验产物固定放在:
|
||||
|
||||
```text
|
||||
artifacts/<run_date>/runs/<run_id>/
|
||||
```
|
||||
|
||||
`artifacts/` 已由 `.gitignore` 忽略。本设计不授权从 Git 忽略中移除该目录,也不允许通过 `git add -f`
|
||||
强制提交任何产物。
|
||||
|
||||
`run_date` 使用 `YYYY-MM-DD`,表示实验开始时本机时区中的日历日期。本地实验脚本在启动时只计算一次日期和
|
||||
UTC 偏移,再把它们显式传给运行层;运行层不在处理不同文档时重新读取日期。`run_date` 必须能够按 ISO 8601
|
||||
日历日期严格解析,日期目录不接受其他格式。
|
||||
|
||||
`run_id` 由调用方显式提供,适用与 `document_id` 相同的安全字符规则。不使用当前时间、随机数或文件名暗中生成
|
||||
运行 ID。同一日期下的目标运行目录已存在时必须拒绝运行,不覆盖、合并或自动加后缀。同一个 `run_id` 可以在
|
||||
不同日期下再次使用,两次运行仍由完整日期路径区分。
|
||||
|
||||
### 8.2 目录结构
|
||||
|
||||
```text
|
||||
artifacts/
|
||||
└── <run_date>/
|
||||
└── runs/
|
||||
└── <run_id>/
|
||||
├── manifest.json
|
||||
└── documents/
|
||||
└── <document_id>/
|
||||
├── result.json
|
||||
├── cleaned.md # 仅 success
|
||||
└── changes.diff # 仅 success
|
||||
```
|
||||
|
||||
所有 `success` 文档都生成 `cleaned.md` 和 `changes.diff`。即使零修改,`cleaned.md` 仍完整保存成功输出,
|
||||
`changes.diff` 是长度为零的文件。这使每份成功文档的产物结构一致。
|
||||
|
||||
`failed` 和 `unstable` 文档目录只包含 `result.json`。不创建隐含部分结果的 Markdown 或 diff。
|
||||
|
||||
### 8.3 原子发布
|
||||
|
||||
artifact store 先在 `artifacts/<run_date>/runs/` 下创建当次运行专用的临时目录,完成全部文件写入、
|
||||
哈希校验和清单校验后,
|
||||
再在同一文件系统内将它重命名为最终 `<run_id>` 目录。
|
||||
|
||||
- 发布前的临时目录不是成功产物;
|
||||
- 任一写入或校验失败时尝试清理本次专用临时目录;
|
||||
- 不对已存在的最终目录使用替换语义;
|
||||
- 本设计只保证“不发布已知不完整的运行目录”,不承诺跨平台断电耐久性。
|
||||
|
||||
## 9. 持久化事实与 JSON 契约
|
||||
|
||||
### 9.1 事实权威
|
||||
|
||||
- `manifest.json` 是整次运行身份、环境、流水线、文档索引和汇总的权威;
|
||||
- 每份文档的 `result.json` 是该文档状态、哈希、实际修改、错误和残留候选的权威;
|
||||
- `cleaned.md` 是成功文本内容的权威;
|
||||
- `changes.diff` 是从输入和成功输出派生的人工评审视图,不是第二份修改记录。
|
||||
|
||||
清单中的计数和路径是从文档结果派生的索引,发布前必须校验与对应 `result.json` 和文件存在性一致。
|
||||
|
||||
### 9.2 JSON 通用表示
|
||||
|
||||
两类 JSON 都使用:
|
||||
|
||||
- UTF-8,不写 BOM;
|
||||
- 根对象的 `schema_version` 固定为整数 `1`;
|
||||
- 两空格缩进;
|
||||
- 不把非 ASCII 字符转成反斜杠加 `u` 的 Unicode 转义形式;
|
||||
- 文件末有一个 `\n`;
|
||||
- 数组顺序保留调用方文档顺序、组件顺序和核心修改顺序。
|
||||
|
||||
JSON 中的字段名和枚举值使用英文标识符。组件的中文修改理由和 `before` / `after` 保留原文。
|
||||
|
||||
### 9.3 `manifest.json`
|
||||
|
||||
运行清单至少包含:
|
||||
|
||||
```text
|
||||
schema_version
|
||||
run
|
||||
run_id
|
||||
run_date
|
||||
utc_offset
|
||||
status
|
||||
started_at_utc
|
||||
completed_at_utc
|
||||
retention_until
|
||||
tool
|
||||
name
|
||||
package_version
|
||||
python_version
|
||||
platform
|
||||
git_commit # 无法取得时为 null
|
||||
git_dirty # 无法取得时为 null
|
||||
pipeline
|
||||
components[] # 实际有序 ComponentInfo
|
||||
component_id
|
||||
version
|
||||
parameters
|
||||
applicability
|
||||
documents[]
|
||||
document_id
|
||||
source_label
|
||||
status
|
||||
input_sha256
|
||||
current_sha256
|
||||
change_count
|
||||
result_path
|
||||
cleaned_path # 非 success 为 null
|
||||
diff_path # 非 success 为 null
|
||||
summary
|
||||
document_count
|
||||
success_count
|
||||
failed_count
|
||||
unstable_count
|
||||
change_count
|
||||
```
|
||||
|
||||
`run_date` 必须等于产物路径中的日期。`utc_offset` 使用 `+HH:MM` 或 `-HH:MM`,说明计算该日期时的本机
|
||||
UTC 偏移。`started_at_utc`、`completed_at_utc` 和 `retention_until` 使用带 `Z` 的 UTC ISO 8601 字符串。
|
||||
时间、平台和 Git 信息属于外层运行证据,不进入组件参数,也不影响核心结果的确定性。清单不读取或保存环境变量、
|
||||
用户名、主机名或其他可能包含秘密的全局环境信息。
|
||||
|
||||
`pipeline.components` 必须来自本次文档结果中的已验证元数据。同一次运行中各文档的组件元数据不一致时,
|
||||
视为运行层错误,不发布最终目录。
|
||||
|
||||
### 9.4 `result.json`
|
||||
|
||||
文档审计至少包含:
|
||||
|
||||
```text
|
||||
schema_version
|
||||
document
|
||||
document_id
|
||||
source_label
|
||||
status
|
||||
input_sha256
|
||||
current_sha256
|
||||
changes[]
|
||||
component_id
|
||||
component_version
|
||||
component_position
|
||||
proposal_ref
|
||||
component_position
|
||||
snapshot_sha256
|
||||
proposal_index
|
||||
edit_index
|
||||
reason
|
||||
span
|
||||
start
|
||||
end
|
||||
location # 派生定位,不用于应用修改
|
||||
line # 1-based,相对 before_sha256 快照
|
||||
column # 1-based,相对 before_sha256 快照
|
||||
before
|
||||
after
|
||||
before_sha256
|
||||
after_sha256
|
||||
errors[]
|
||||
component_id
|
||||
component_version
|
||||
component_position
|
||||
stage
|
||||
error_type
|
||||
message
|
||||
residual_proposals[]
|
||||
component_id
|
||||
component_version
|
||||
component_position
|
||||
proposal_ref
|
||||
proposal
|
||||
snapshot_sha256
|
||||
reason
|
||||
edits[]
|
||||
snapshot_sha256
|
||||
span
|
||||
start
|
||||
end
|
||||
expected_text
|
||||
replacement
|
||||
output
|
||||
cleaned_path # 非 success 为 null
|
||||
diff_path # 非 success 为 null
|
||||
```
|
||||
|
||||
`location` 由 reporter 基于对应 `before_sha256` 快照派生,只便于人查看。`span.start` / `span.end`
|
||||
仍是唯一修改权威。当多个组件产生中间快照时,reporter 必须按组件批次从输入重放已记录修改,每次都校验前后哈希;
|
||||
无法重放时不得发布产物。
|
||||
|
||||
`result.json` 不内嵌完整 `output_markdown` 或 `partial_markdown`。成功全文只在 `cleaned.md` 中保存;
|
||||
失败和不稳定的部分全文不落盘。`changes`、`residual_proposals` 中的局部原文仍属于完整本地审计的一部分。
|
||||
|
||||
## 10. diff 契约
|
||||
|
||||
`changes.diff` 使用原始输入和最终成功输出生成 unified diff:
|
||||
|
||||
- 旧文件标签固定为 `a/<document_id>.md`;
|
||||
- 新文件标签固定为 `b/<document_id>.md`;
|
||||
- 不把绝对路径和时间戳写入 diff 头;
|
||||
- 上下文固定为 3 行;
|
||||
- diff 自身使用 `\n` 作为报告换行,不表示清洗后 Markdown 被转换为 `\n`;
|
||||
- 零修改的成功文档产生空 diff;
|
||||
- diff 只用于人工评审,不用于重放或应用修改。
|
||||
|
||||
统一 diff 只展示输入到最终输出的总变化。如果需要查看某个组件的原因和中间快照,以 `result.json`
|
||||
中的 `Change` 审计为准。
|
||||
|
||||
## 11. 隐私、权限和保留周期
|
||||
|
||||
### 11.1 数据级别
|
||||
|
||||
本设计生成的所有产物都按“本地敏感实验数据”处理,包括:
|
||||
|
||||
- `cleaned.md` 中的完整文档;
|
||||
- `changes.diff` 中的原文上下文;
|
||||
- `result.json` 中的 `before`、`after` 和残留候选;
|
||||
- `manifest.json` 中可能暴露数据集结构的文档名和来源标签。
|
||||
|
||||
这些产物:
|
||||
|
||||
- 只能保存在本机 `artifacts/` 下;
|
||||
- 不得提交、推送、发布、上传或复制到 Wiki;
|
||||
- 终端默认只输出运行 ID、状态、计数和产物目录,不输出 `before`、`after` 或 diff 片段;
|
||||
- 实验结束后不得自动拷贝到其他仓库或用户目录。
|
||||
|
||||
第一版创建运行目录时将目录权限设为只有当前用户可读、写和进入,普通产物文件只有当前用户可读写。
|
||||
如果平台不支持这些权限语义,实验必须明确报错,不静默降级为宽松权限。
|
||||
|
||||
### 11.2 保留周期
|
||||
|
||||
本地产物默认保留 30 个日历日。`manifest.json` 记录根据 `completed_at_utc` 计算的 `retention_until`。
|
||||
|
||||
第一版不实现自动删除,避免在没有人工确认时执行破坏性操作。到期产物只标记为待清理;删除前必须由用户确认
|
||||
具体运行目录。需要超过 30 天保留、跨机共享或长期归档时,必须先单独确认保存位置和数据边界。
|
||||
|
||||
## 12. 实现结构
|
||||
|
||||
批准后允许新增:
|
||||
|
||||
```text
|
||||
src/mdpolish/
|
||||
├── artifact_store.py # 日期路径、权限、写入校验和原子产物发布
|
||||
├── experiment.py # 显式文件输入、预检和批量运行
|
||||
└── reporting.py # JSON 表示、位置派生和 unified diff
|
||||
scripts/
|
||||
└── run_clindb_arxiv_experiment.py # 仓库内已知 5 份论文的本地运行脚本
|
||||
tests/
|
||||
├── test_artifact_store.py
|
||||
├── test_experiment.py
|
||||
└── test_reporting.py
|
||||
```
|
||||
|
||||
- 三个新模块不从顶层 `mdpolish.__init__` 导出,暂不承诺稳定公共 API;
|
||||
- 本地脚本显式创建只包含 `ArxivSubmissionStampComponent` 的 `Pipeline`;
|
||||
- 脚本启动时捕获一次本机日期、UTC 偏移和 UTC 开始时间,不在文档循环中重新计算日期;
|
||||
- 脚本输入是 `data/md/` 中的 5 份缩写文件,输出为 `artifacts/<run_date>/runs/<run_id>/`;
|
||||
- 脚本可以接收运行 ID,但不是通用文件清洗 CLI,不注册 `project.scripts`;
|
||||
- 精确命令和实际运行步骤在实现并验证后进入 `guides/`,README 只链接当前权威,不在多处复制。
|
||||
|
||||
第一版继续只使用 Python 标准库。不修改包安装命令,不增加运行依赖。
|
||||
|
||||
## 13. 测试和验收
|
||||
|
||||
### 13.1 合成测试
|
||||
|
||||
自动测试只使用 `tmp_path` 和小型虚构 Markdown,不读取或复制真实论文。至少覆盖:
|
||||
|
||||
- 空文档、中文、组合 Unicode、UTF-8 BOM、`\n`、`\r\n` 和无末尾换行;
|
||||
- 无效 UTF-8、缺失文件、非普通文件、重复文档 ID、重复源路径和非法运行 ID 在预检失败;
|
||||
- 非法日期、日期路径与 manifest 不一致、同日重复运行 ID 明确失败;
|
||||
- 目标运行目录已存在时拒绝,不覆盖其中任何文件;
|
||||
- `success` 生成三类文档产物,零修改时仍生成完整 `cleaned.md` 和空 diff;
|
||||
- `failed` / `unstable` 只生成 `result.json`,不保存部分全文;
|
||||
- 一份文档核心失败后继续处理其他文档,整体状态按第 7.2 节汇总;
|
||||
- `result.json` 完整表示候选引用、修改理由、范围、位置、`before`、`after` 和哈希;
|
||||
- 多组件修改可以重放中间快照并正确派生行列,哈希不一致时拒绝发布;
|
||||
- unified diff 使用逻辑文档名,不包含绝对路径或时间;
|
||||
- 输出 Markdown 重新读取后的哈希等于核心 `current_sha256`;
|
||||
- 运行前后输入文件的字节和哈希不变;
|
||||
- JSON 编码、schema 版本、顺序、文件末换行和清单计数符合契约;
|
||||
- 产物目录及文件权限不宽于第 11 节的边界;
|
||||
- 产物写入中途失败时不发布最终运行目录。
|
||||
|
||||
### 13.2 本地 5 份论文验收
|
||||
|
||||
实现并通过合成测试后,允许对 `data/md/` 中 5 份本地副本运行实验脚本,并在本地
|
||||
`artifacts/<run_date>/runs/<run_id>/` 保存完整产物。验收必须确认:
|
||||
|
||||
1. 清单列出 `dmp`、`ejhf`、`jama`、`sim` 和 `springer` 共 5 份输入;
|
||||
2. 流水线只有 `paper.arxiv_submission_stamp` `1.0.0`,参数为空;
|
||||
3. 5 份文档都为 `success`;
|
||||
4. `sim` 和 `springer` 各有 1 条删除,其余文档零修改,合计 2 条 `Change`;
|
||||
5. 两条修改的组件、理由、原文、空替换、位置和前后哈希都可从 `result.json` 查看;
|
||||
6. `sim` 和 `springer` 的 diff 只包含已批准的提交戳删除,Springer 合法参考文献保留;
|
||||
7. 所有 `cleaned.md` 与对应成功哈希一致;
|
||||
8. 5 份输入文件在运行前后逐字节不变;
|
||||
9. 产物受 Git 忽略,提交前 `git status` 不列出任何实验产物。
|
||||
|
||||
真实实验产物只用于本地人工评审,不进入合成测试期望值、Wiki、Git 提交或终端输出。
|
||||
|
||||
## 14. 风险与代价
|
||||
|
||||
- **JSON 过早成为契约:** 第一版只服务本地实验,不承诺向后兼容;字段语义变化时必须增加
|
||||
`schema_version`,不静默改变旧文件语义。
|
||||
- **完整审计会复制真实片段:** `before`、`after` 和 diff 有意保留原文,换取可人工复核;代价是所有产物都必须
|
||||
按敏感数据处理。
|
||||
- **批量中部分文档成功:** 这便于查看每份输入,但调用方必须检查整体和文档状态,不能因为目录里有部分
|
||||
`cleaned.md` 就宣称整批成功。
|
||||
- **行列派生需要重放修改:** 它能在不改核心模型的情况下便于人定位,但增加 reporter 的复杂度。重放过程必须
|
||||
完全验证哈希,失败就拒绝发布。
|
||||
- **时间和环境使 manifest 不再字节级确定:** 这些是复现实验必需的外层证据,不影响相同文本和流水线的核心结果
|
||||
确定性。
|
||||
- **严格权限会降低跨平台便携性:** 第一版是本机 Linux 实验能力,不借本设计声称 Windows 或共享盘已支持。
|
||||
- **固定 30 天只是本地实验保留约定:** 第一版不自动删除,所以仍需要用户定期确认和清理过期目录。
|
||||
|
||||
## 15. 批准后的实施边界
|
||||
|
||||
批准本设计后,只授权:
|
||||
|
||||
1. 新增第 12 节列出的模块、本地脚本和测试;
|
||||
2. 按第 6 至 11 节实现显式输入、核心调用、JSON、diff、原子发布和保留信息;
|
||||
3. 对 `data/md/` 的 5 份本地论文副本运行只含 arXiv 组件的实验;
|
||||
4. 将完整本地产物保存到 Git 忽略的 `artifacts/<run_date>/runs/<run_id>/`;
|
||||
5. 实现后根据真实行为更新 README、对应 `explanation/` 和经验证的 `guides/`。
|
||||
|
||||
批准不授权:
|
||||
|
||||
- 修改或覆盖任何输入文件;
|
||||
- 对 `/home/lihaoze/gov_test_data` 或其他仓库外数据保存产物;
|
||||
- 把实验产物加入 Git、Wiki、其他仓库、云存储或外部系统;
|
||||
- 实现原地覆盖、公共 CLI、Profile、Inspector、parser、多轮执行或其他清洗组件;
|
||||
- 提交、推送、创建 PR 或发布。
|
||||
Reference in New Issue
Block a user