Files
deepseek-harness/docs/rfc/implemented/process/2026-07-02-bilingual-docs-and-pairing-gate.zh.md
T
Tianyi Cui e6fad266a6 docs(rfc): define and enforce a uniform RFC format; adopt it across the corpus
Define the in-file RFC contract in docs/rfc/README.md § The file format:
the header block (`# RFC: <title>` plus a dateless Status enum
cross-checked against the lifecycle folder), the per-lifecycle body
skeleton (a Problem opener everywhere; Proposal/Alternatives considered/
Acceptance criteria/Risks in proposed/; present-tense Decision/
Consequences with proposal-era headings banned in implemented/; the
frozen proposal shape in rejected/), and a mandatory Alternatives
considered section with a date-fenced grandfather comment for pre-format
RFCs whose alternatives are not reconstructible from the record.

Enforce it with a new doc-sync gate, scripts/verify-rfc-format.ts, and
normalize all 112 RFCs to it: ~15 Status-line spellings collapse to the
enum, 29 Context openers become Problem, the 39 legacy-format XXX debt
markers are resolved and banned from reappearing, proposal-era sections
in implemented RFCs are rewritten to shipped reality (including the
web/fs/subagent seam RFCs' migration plans and test checklists, closing
the doc-tiers deferred-work item on the web seam), every RFC gains an
Alternatives considered section or the grandfather comment, and the
bilingual pair is re-mirrored and re-recorded.

Move the generated index tables out of README.md into a fully generated
docs/rfc/INDEX.md — gen-rfc-index now writes the whole file, and
verify-rfc-classification checks its freshness and rejects index-shaped
rows in the curated README — which makes room for the format contract to
live in the README front door instead of a separate FORMAT.md.

The decision record, and the first RFC written in the new format, is
docs/rfc/implemented/process/2026-07-05-uniform-rfc-format.md.
2026-07-05 22:58:25 +08:00

6.3 KiB
Raw Blame History

RFC: 通过配对兄弟文件与配对门禁实现双语文档

Status: implemented

English | 中文

问题

本仓库的 README 与 docs 目录树会被公司内外的人和 agent(智能体)以中英两种语言阅读。没有机制、纯靠手工维护第二语言,正是译文腐烂的方式:一侧继续演进,另一侧默默地说谎,而没有门禁会注意到。对这类不变式,本仓库一贯的答案是把它编码成机械检查(见质量门禁doc-sync 强制),因此双语政策随附一道门禁一起交付。

决策

  • **配对兄弟文件,两种语言同权。**一对文档是三个兄弟文件:英文 foo.md、中文 foo.zh.md,加一份一致性记录 foo.i18n.yaml。没有哪种语言是正典——一篇文档可以先用中文撰写和评审、之后再译成英文,反之亦可;约束这对文件的是两侧必须说同样的话,且配对整体合入(两种语言加记录,绝不单独落一侧)。政策见 docs/i18n/README.md;翻译规则见 docs/i18n/translation-rules.md;术语真源见 docs/i18n/terminology.md
  • 旁挂记录两侧 blob hash,使一致性可检查。foo.i18n.yaml 保存两侧文件在上一次确认一致状态下各自的完整 git blob hash。此后改了任一侧而没重新确认配对,都能被机械检测出来——纯内容比较、无需查询历史——而且同一个 PR 里改动的文件也能算出 hash,commit hash 式的记录做不到这一点。重新记录(verify-translation-pairing --write)产生一份可评审的 yaml diff:确认一致在 PR 里是一个显式、可见的动作。
  • **verify-translation-pairing 加入 doc-sync。**门禁(scripts/verify-translation-pairing.ts)强制执行:required 的配对存在;任何已存在的配对完整(三个文件齐全)且一致(两个 hash 都匹配、切换行双向互链、结构签名一致);被排除的文件(生成物或本身即双语的)保持不配对。scripts/translation-pairing.manifest.json 中的 required 清单是一个棘轮:每个合入的翻译批次把自己的文件加进去,覆盖面只增不减。
  • **翻译是 agent 的工作,由人评审。**进仓的工作流是 .agents/skills/dsh-translate-docs,与 dsh-code-review 同一模式:skill 承载工作流,并把真源让给文档。

曾考虑的替代方案

  • 英文为正典源、指纹放在译文内——本 RFC 最初提出的设计:.zh.md 文件携带一条 HTML 注释记录英文源的 blob hash,翻译只沿 EN → ZH 单向流动。评审中修订:团队需要中文先行的撰写方式(先写、先审中文 RFC,再译英文),两种语言同权,而单向正典模型无法表达这一点。覆盖两侧的旁挂记录取代了文件内的单向指纹;blob hash 的机制原样保留。
  • 语言目录(docs/en/ + docs/zh/Kubernetes/ECharts 模式)——否决:本仓库没有把 locale 映射到路由的文档站框架,挪动每个英文文件会搅动所有既有交叉引用,且 verify-md-links/verify-doc-refs 将需要路径映射逻辑而不是原样工作。
  • 独立翻译仓库(PingCAP docs/docs-cn 模式)——否决:适合有独立发布节奏的文档产品,对 monorepo 自己的文档而言过重;还会把译文置于本仓库门禁够不到的地方。
  • 中英混排单文件(一个文件、两种语言)——否决:每个 diff 都翻倍,破坏一段一行约定的 diff 工效,且局部不一致不可见。
  • Commit hash 式记录(MDN l10n.sourceCommit 模式)——否决,改用 blob hash:同一个 PR 内的改动还没有 commit hash,MDN 模式无法表达「与本 PR 引入的状态一致」,且校验它需要 git 历史而非文件内容。
  • 比较配对两侧的 git 时间戳(无记录)——否决:纯格式化的改动会误报,一次无关改动之后提交的另一侧会漏报;只有内容同一性这个信号与门禁的承诺名实相符。

业界先例

带语言后缀的配对兄弟文件是中国大厂的主流约定(ant-design 的 index.zh-CN.md/index.en-US.mdarco-design 的 README.zh-CN.md 加顶部切换行;Apache ShardingSphere 的 387 对 .cn.md/.en.md)——但这些仓库都没有在 CI 里强制配对或一致性;约定纯靠评审维系。一致性自动化存在于中国之外:MDN 的 l10n.sourceCommit front-matter 指纹、Vue 的 Ryu-Cho action(监视上游 commit、为陈旧译文自动开 issue/PR)、Kubernetes 的本地化漂移脚本、微软 Azure co-op-translatorCI 中由源 hash 驱动的 LLM 重译)。本设计把两者结合:中文生态的文件布局,加 hash 对门禁,再加一个进仓 agent skill(技能)替代 bot 服务。

后果

  • 修改已配对文档的任一侧,同一个 PR 就有义务更新另一侧并重新记录配对——门禁把 doc-sync 规则双语化,不变式由 CI(而非评审者的记忆)承载。
  • 每个配对给目录树多添一个文件。记录由机器写入(--write),代价是目录噪音而非维护负担;换来的是「谁在何时确认过这对一致」可以从 yaml 的 git blame 直接回答。
  • 两侧说法冲突时,没有机械规则裁决谁赢——由 PR 评审裁决。这是同权的代价,是有意接受的:另一个选项(正典语言)禁止中文先行撰写。
  • 生成文档(cordis-catalog/tool-catalog/module-graph.md)暂被排除;计划中的后续工作是让它们的生成器在输出英文的同时输出中文,届时移出排除清单。
  • 推进天然是渐进的:required 之外的文档是可见的 backlog--list),不是红的 CI,因此配对按可评审的批次落地,无需一个巨型 PR。
  • 记录的 hash 兼作更新工具(git cat-file -p <hash> 能还原任一侧上次确认的文本,用于基于 diff 的最小更新),所以这套机制从不强迫整篇重译。