Files
mdpolish/research-wiki/reference/review-projection-schema-v1.md
T

10 KiB
Raw Blame History

ReviewDocument 机器投影 schema 1.0

本文记录 mdpolish.review.review_document_to_dict()render_json_report()parse_json_report() 当前稳定的跨进程查询 口径。公共 Python 类型和运行校验以代码与测试为准;生产契约的设计理由见 0012-review-document-machine-projection.md,只读解析与本地页面边界见 0014-generic-local-reviewer.md

1. Schema 身份与入口

每个投影都包含:

{
  "schema_name": "mdpolish.review",
  "schema_version": "1.0"
}

schema 版本独立于 mdpolish 包版本和 modifier 版本。公共入口是:

from mdpolish.review import parse_json_report, render_json_report, review_document_to_dict

payload = review_document_to_dict(review, detail="summary")
json_text = render_json_report(review, detail="full")
parsed = parse_json_report(json_text, expected_detail="full")

前两个生产入口只接受内存中的 ReviewDocument。JSON reporter 编码同 detail 的正式 dict,不定义另一套字段,也不读写文件。 reader 只接受内存字符串,返回新的普通 JSON 基本值容器;它不接收路径,也不恢复 ReviewDocument

2. 顶层字段

字段 JSON 类型 口径
schema_name string 固定为 mdpolish.review
schema_version string 当前固定为 1.0
detail string summarychangesfull
status string successfailedunstable
current_kind string success_outputpartial_output
stages_complete boolean 是否能证明全部 modifier transform 阶段完成
hash_contract object 所有 Markdown 哈希的算法、编码和规范化口径
coordinate_contract object span、行列和物理换行口径
input object 输入文本摘要;full 增加正文
current object 当前文本摘要;full 增加正文
counts object modifier、完成阶段、实际修改、错误和残留候选数量
modifiers array 全部 modifier 的权威顺序
stages array 已完成 transform 阶段的权威顺序
errors array Pipeline 记录的错误顺序
residual_proposals array 只在 changes / full 出现;候选未应用

JSON object 成员顺序不构成语义。所有 array 顺序构成语义,消费者不得重新按 ID、哈希或文本排序后再解释位置。

3. 内容暴露等级

内容 summary changes full
状态、哈希、位置口径和计数
modifier id、版本和位置
modifier 参数和 applicability
阶段前后哈希、长度和修改数
实际修改的 reason、位置、before / after
稳定错误代码和错误位置
Python 诊断类型和错误消息
残留候选的 reason、expected_text / replacement
输入、当前和阶段完整 Markdown

默认是 summary。被隐藏的内容键不存在;null 和空字符串都不是“已隐藏”的替代值。summary 不含直接正文,但仍含 modifier identity、哈希和计数,不自动等于匿名、脱敏或适合公开传播。

4. 固定口径

4.1 哈希

"hash_contract": {
  "algorithm": "sha256",
  "encoding": "utf-8",
  "normalization": "none"
}

哈希是精确 Markdown 的 sha256(markdown.encode("utf-8")).hexdigest()。不规范化 Unicode、BOM、空白、末尾换行或 CR/LF/CRLF。Change.before_sha256 / after_sha256 是完整阶段快照哈希,不是修改片段哈希。

4.2 坐标

"coordinate_contract": {
  "offset_unit": "unicode_code_point",
  "span_index_base": 0,
  "span_end": "exclusive",
  "location_index_base": 1,
  "physical_line_endings": ["lf", "crlf", "cr"]
}
  • span 相对于所属 ReviewStage.before_markdown,使用 0-based Unicode code point 半开范围;
  • location.line / column 指向 span.start,使用同一阶段修改前文本中的 1-based Unicode code point 位置;
  • CRLF 形成一个物理换行,但在 offset 中占两个 code point
  • 不提供最终全文坐标、UTF-16、byte offset 或终端显示宽度。

所有整数都在 0..2**53-1;参数中的有符号整数在 -(2**53-1)..2**53-1。所有 float 必须有限。

5. 嵌套对象

5.1 文本摘要

inputcurrent 以及 stage 的 before / after 都至少包含:

{
  "sha256": "64 位小写十六进制",
  "code_point_length": 123
}

full 增加 markdowncurrent.markdown 是否为正式输出必须看根字段 current_kindpartial_output 不能当作成功结果。

5.2 Counts

"counts": {
  "modifier_count": 3,
  "completed_stage_count": 2,
  "change_count": 5,
  "error_count": 1,
  "residual_proposal_count": 0
}

change_count 只统计已完成 stage 中的实际 ReviewChange,不含 residual proposal edit。零修改的完整 stage 仍计入 completed_stage_count

5.3 Modifier

所有 detail

{
  "position": 0,
  "modifier_id": "example.normalize",
  "version": "1.0.0"
}

changes / full 增加 parametersapplicabilityparameters 是有序 array-of-pairs;所有内部 tuple 继续投影成 array,不根据二元组外形猜成 JSON object

"parameters": [
  ["pattern", " {2,}"],
  ["replacement", " "]
]

5.4 Stage

所有 detail

{
  "modifier_position": 0,
  "before": {"sha256": "...", "code_point_length": 20},
  "after": {"sha256": "...", "code_point_length": 18},
  "change_count": 1
}

changes / full 增加 changes arrayfull 还给两个文本摘要增加 markdownmodifier_position 引用根 modifiers[position],不复制 modifier 元数据。

5.5 实际 Change

只在 changes / full 出现:

{
  "proposal_index": 0,
  "edit_index": 0,
  "reason": "修改原因",
  "location": {"line": 3, "column": 7},
  "span": {"start": 24, "end": 31},
  "before": "原片段",
  "after": "新片段",
  "before_sha256": "修改前完整阶段哈希",
  "after_sha256": "修改后完整阶段哈希"
}

5.6 Error

所有 detail 都含 codestage、modifier 位置与身份。changes / full 再增加诊断用的 diagnostic_typemessage

stage 稳定 code
preflight run.preflight_failed
transform run.transform_failed
final_review run.final_review_failed

代码只稳定表达失败阶段。diagnostic_type 是 Python 异常类名,message 是人类消息,不能作为稳定程序分支条件。当前 RunError 没有保存更细的稳定原因;不能从类名或消息猜造更细代码。

5.7 Residual proposal

只在 changes / full 出现:

{
  "modifier_position": 0,
  "proposal_index": 0,
  "snapshot_sha256": "当前完整快照哈希",
  "reason": "候选原因",
  "edits": [
    {
      "edit_index": 0,
      "span": {"start": 10, "end": 15},
      "expected_text": "原片段",
      "replacement": "候选片段"
    }
  ]
}

它是 final review 证据,没有应用,不进入 stage 或实际 change count。

6. JSON 编码

render_json_report() 固定使用标准库 JSON 的以下语义:

  • ensure_ascii=False
  • allow_nan=False
  • indent=2
  • 无 UTF-8 BOM
  • 返回字符串末尾不额外添加换行。

返回值是 Python str。调用方保存或发送时负责 UTF-8 编码、媒体类型、权限和保留周期。库不接收路径或文件对象。

7. 只读解析保证

from mdpolish.review import ReviewParseError, parse_json_report

payload = parse_json_report(json_text)
full_payload = parse_json_report(json_text, expected_detail="full")

expected_detail 可以省略,也可以显式指定 summarychangesfull。报告自己的 detail 不匹配时失败。返回结果是本次 解析新建的 dict/list;修改它不会恢复或改变 producer 侧的 ReviewDocument

所有 detail 都检查:

  • JSON object key 唯一,字符串可以严格编码为 UTF-8,整数和浮点数满足第 4 节范围;
  • schema、detail、enum、稳定错误代码、数组顺序、引用位置和计数;
  • summary / changes 没有越过各自的正文暴露边界;
  • 状态、current_kind、错误阶段、完成阶段和残留候选相容。

summarychanges 没有完整阶段正文,reader 不会声称能验证不存在的文本。full 另外检查:

  • input、current、所有 stage before/after 的码点长度和 SHA-256
  • 第一个阶段从 input 开始,相邻阶段首尾相接,最后一个完成阶段等于 current;
  • Change span 的原文、位置、哈希、proposal/edit index、冲突和正式报告顺序;
  • 用核心共用的精确编辑原语重放每个 Change 批次,结果与 stage after 完全相同;
  • 零修改阶段的 before 和 after 完全相同;
  • residual edit 的原文、范围、顺序和冲突。

任一步失败都抛出 ReviewParseError。顶层错误消息只包含字段路径和契约类别,不包含正文、modifier 参数、reason 或诊断消息。 reader 不调用 Modifier,不修复、截断或猜测损坏结果。

这是只读解析,不是反序列化。返回 dict 不能重新运行 Pipeline、恢复 Python 模型或获得原始 TransformResult 的权威身份。

8. 兼容策略

schema_version 使用 MAJOR.MINOR

  • 删除、改名、改类型、改变位置/哈希/顺序语义、改变已有 enum 或错误代码含义,需要提升 major;
  • 在相同 detail 中新增更高敏感度的正文承载字段,需要提升 major,或增加必须显式请求的新 detail;
  • 不改变旧字段解释的非正文可选字段,可以提升 minor;
  • 实现修正为重新符合已有契约,不改变 schema 版本;
  • schema 版本不跟随 Python 包版本自动变化。

同一 major 的消费者必须忽略未知 object 字段,但必须保持 array 顺序;不得把未知状态当成 success。消费者应拒绝自己不 支持的 schema major。

schema 1.0 仍是单向生产契约。官方 reader 接受同一 major 的已知字段语义,并忽略未知 object 字段的语义;未知状态、 detail、enum 或坐标契约仍会失败。当前没有 ReviewDocument 反序列化器、JSON Schema 文件、历史迁移器或数据库 schema。