Skip to content

Sync overwrites localised _toc.yml part captions with English, and nothing detects it #254

Description

@mmcky

A lecture-adding sync regenerates the target's lectures/_toc.yml as a full mirror of the source, and part captions are carried over untranslated. Any localised caption in the target is silently overwritten with English, and nothing in the pipeline notices.

The field instance

QuantEcon/lecture-python.zh-cn#202 merged 2026-07-24 and replaced 14 hand-set Chinese captions with the English source's. The published Chinese site served 19 English section captions and zero Chinese ones for 17 days before anyone noticed. Repaired by hand in QuantEcon/lecture-python.zh-cn#245, which will itself be reverted by the next lecture-adding sync.

A second instance is open right now: QuantEcon/lecture-intro.zh-cn#278 and #279 both revert 14 Chinese captions (导言→Introduction, 经济数据→Economic Data, and twelve more) and are sitting in the merge queue.

Why nothing catches it

Three independent gaps, each verified:

The engine never translates a caption. _toc.yml is parsed only for lecture discovery — src/cli/commands/init.ts:68 declares caption?: string; and parseTocLectures ignores it. No mode emits a translated caption, so there is no localisation to preserve in the first place.

Review mode cannot see it. reviewer.ts:688-691 filters the diff to docsFolder + .md, so _toc.yml is not reviewed at all. The verdict on #202 reads PASS, scores 9/9/9/9, all four diffChecks true, recommendationReasons: [] — a clean bill on a PR that de-localised the entire published navigation. The gating terminology category never sees a caption.

The build cannot see it. English captions are valid MyST; the strict -n -W build passes. There is no reader-facing failure signal at all — the page simply renders in the wrong language.

That combination is the reason dwell time was 17 days rather than one CI cycle, and it is why this class deserves a deterministic check rather than reviewer judgement.

What would fix it

Roughly in increasing order of cost:

  1. A deterministic assertion — for a target whose language is non-Latin-script, fail when a _toc.yml caption loses target-script characters relative to the target's current file. This is a script, not a model, and it catches the whole class. It is also the cheapest of the three and worth doing regardless of the others.
  2. Preserve target captions on regeneration — when mirroring _toc.yml, carry the target's existing caption for any part whose membership is unchanged, rather than overwriting from source.
  3. Translate new captions — a genuinely new part (as Reinforcement Learning was in E2E harness: a full run should test a single release, not sync-at-ref plus review-at-@v0 #202) has no target caption to preserve, so it needs one produced. dev: record the v0.25.0 release and shadow-gate validation #245 shows what good looks like here: every new caption was already attested in the edition's own prose, so the material to do this well exists in-repo.

Option 2 alone would have prevented both field instances. Option 3 is what makes the result complete rather than half-English.

Relationship to existing issues

Adjacent to #178 (sync applies no localisation rules), but distinct: #178 concerns figure labels and font config inside .md lectures, whereas this is a non-.md file that no mode reads for content. The mechanism, the detection gap and the fix are all different, which is why it is filed separately rather than folded in.

Also adjacent to #156 (sync opens a PR whose _toc.yml references a lecture the run failed to translate) — same file, same full-mirror regeneration, different failure.

Programme-side context and the defect-class framing: QuantEcon/project-translation#28, which proposes certifying the reviewer by injected-defect catch rate. De-localisation is one of its M0 classes, and this issue is why it is expected to score near zero there and to need a shape gate rather than a tuned floor.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghigh-priorityAddress soon

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions