docs: harden pairing gate per review — structural signature, not counts

Review findings addressed:

- The gate compared only heading and code-block COUNTS, understating the
  contract it claims to enforce. It now compares ordered structural
  signatures: heading depths, fenced code blocks verbatim (info string +
  content), table column counts, list kinds, and every link target except
  the language switcher. Proven red on a heading demotion, a reworded
  code-block comment, and a retargeted link; green on all existing pairs.
- Stated the gate's limit explicitly (header comment + docs/i18n/README.md
  both languages): green means fresh and structurally sound, NOT verified —
  translation quality is the reviewer's half of the contract.
- first-line extraction no longer silently drops the last character of a
  newline-less file (split with limit instead of indexOf slice).
- isExcluded documents the trailing-slash-is-the-boundary invariant.
- Rollout guidance: grow the required frontier at the pace translation
  review is resourced.
- dsh-code-review's doc-sync sublist is now the exhaustive chain.

docs/i18n/README.zh.md updated via the minimal-diff workflow and
re-fingerprinted.
This commit is contained in:
Ziya
2026-07-03 02:06:57 -07:00
parent cd9d559805
commit a899226397
4 changed files with 103 additions and 30 deletions

View File

@@ -16,20 +16,22 @@ This repo's documentation is read by people and agents both inside and outside t
A blob hash, not a commit hash, so the fingerprint is computable for an English file edited in the same PR (`git hash-object docs/foo.md`), and so staleness is a pure content comparison. The fingerprint is also the update tool: `git cat-file -p <hash>` recovers the exact source text a stale translation was based on, and `git diff <hash> <current-blob>` isolates what changed so the translation can be updated minimally instead of re-translated.
- **Language switcher.** Both files link to each other immediately after their H1 heading: the English file carries `English | [中文](foo.zh.md)` and the Chinese file carries `[English](foo.md) | 中文`.
- **Structure mirrors the source.** Heading hierarchy, list shape, table columns, and code blocks match the English file one to one — see [translation-rules.md](translation-rules.md) for the full preservation rules. Existing Markdown gates apply to `.zh.md` files unchanged (`verify-md-wrap`, `verify-md-links`).
- **Structure mirrors the source.** Heading depths and order, list kinds, table columns, link targets, and verbatim code blocks match the English file one to one — see [translation-rules.md](translation-rules.md) for the full preservation rules. Existing Markdown gates apply to `.zh.md` files unchanged (`verify-md-wrap`, `verify-md-links`).
## The gate: verify-translation-pairing
`pnpm run verify-translation-pairing` (part of `doc-sync`, so CI and the pre-push hook run it) enforces the contract mechanically:
1. Every English file listed as `required` in [scripts/translation-pairing.manifest.json](../../scripts/translation-pairing.manifest.json) has a `.zh.md` sibling.
2. Every existing `.zh.md` file — required or not — passes all of: its English source exists (no orphans), its fingerprint matches the source's current blob hash (no stale translations), both sides carry the language switcher, and its fenced-code-block and heading counts equal the source's.
2. Every existing `.zh.md` file — required or not — passes all of: its English source exists (no orphans), its fingerprint matches the source's current blob hash (no stale translations), both sides carry the language switcher, and its structural signature matches the source in order — heading depths, verbatim code blocks (info string and content), table column counts, list kinds, and every link target apart from the switcher.
3. Files listed as `excluded` have no `.zh.md` sibling at all.
`pnpm run verify-translation-pairing --list` prints the current translation state of every document in scope — missing, stale, or ok — and is the work list for translation batches. It never fails; it reports.
The practical rule this gate creates: **when a PR edits an English document that has a `.zh.md` sibling, the same PR updates the translation** (run the [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) skill), exactly like the repo's existing doc-sync rule for code and READMEs. A PR that leaves a translation stale goes red in CI.
The gate's limit, stated plainly: **a green gate means fresh and structurally sound, not verified.** It checks the fingerprint and the shape; it cannot judge whether the Chinese is accurate, well-termed, or natural — that is the reviewer's half of the contract, per [translation-rules.md](translation-rules.md). A re-fingerprinted `.zh.md` with a sloppy translation passes the gate; it must not pass review.
## Scope, exclusions, and rollout
**Scope**: the root `README.md` and everything under `docs/**`. Package READMEs (`packages/**`) join the scope in a later batch.
@@ -40,7 +42,7 @@ The practical rule this gate creates: **when a PR edits an English document that
- `docs/AGENTS.md` — agent instructions, maintained in English only like the root `AGENTS.md`.
- `docs/i18n/terminology.md` — the terminology table is itself bilingual by construction.
**Rollout**: the `required` list in the manifest is the enforcement frontier, not the goal. The goal is full bilingual coverage of the scope. Translation lands in reviewable batches (core entry docs, cookbook, RFCs, postmortems, …); each merged batch adds its files to `required`, so the gate ratchets forward and never regresses. Documents not yet in `required` are backlog — visible in `--list` — but any translation that already exists is held to the full contract regardless of the list.
**Rollout**: the `required` list in the manifest is the enforcement frontier, not the goal. The goal is full bilingual coverage of the scope. Translation lands in reviewable batches (core entry docs, cookbook, RFCs, postmortems, …); each merged batch adds its files to `required`, so the gate ratchets forward and never regresses. Documents not yet in `required` are backlog — visible in `--list` — but any translation that already exists is held to the full contract regardless of the list. Pairing a document is a commitment: every later English edit to it must carry the translation along, so grow the frontier at the pace translation review is actually resourced, not ahead of it.
## Division of labor