Skip to content

forward resync wrapped an entire document in a code fence and mis-derived its title (ak_aiyagari, v0.17.0) #118

Description

@mmcky

Silent whole-document corruption on one file in the lecture-python.zh-cn resync wave. Filing separately from #107/#116/#117 — this is a different failure mode from any of those, and the most severe defect the wave surfaced.

What happened

On lectures/ak_aiyagari.md, the resync emitted its new translation: frontmatter, then an opening code fence, then the entire original document verbatim, then a closing fence at EOF. It wrapped the document body in a code block.

The head of the file after the resync:

line content
36 --- — the new translation frontmatter ends
37 (blank)
38 a bare triple-backtick — stray fence opener
39–50 a duplicated copy of the original jupytext/kernelspec frontmatter
53 # 长寿、异质性个体、世代交叠模型 — the H1
55–56 the {include} _admonition/gpu.md block

CommonMark closes a fence at the first bare triple-backtick, so the block opened at line 38 ran to line 56 — swallowing both the H1 title and the gpu.md include. A second stray triple-backtick sat at EOF.

How it presented

Not as anything resembling a fenced document. It surfaced as 13 × Document headings start at H2, not H1 [myst.header] under a strict build, which reads like a trivial "add a title" problem. It was filed downstream that way (QuantEcon/lecture-python.zh-cn#182) and the recommended fix was to restore an H1 — which would have papered over the real bug and left the include broken.

Nothing caught it at merge time. The edition's CI lacked -n -W on the notebook build (since fixed in QuantEcon/lecture-python.zh-cn#185), so it took a cold strict build weeks later to surface it.

Provenance

Introduced by the resync in QuantEcon/lecture-python.zh-cn#103, commit 17b5b98, 2026-07-18, tool v0.17.0.

fence count balanced
before resync (a664481) 120 yes
after resync (17b5b98) 124 no
English source 122 yes

The correct output was 122 — the pre-resync file was at 120 because it predated upstream's gpu.md include, which legitimately adds two. The engine added two more on top.

Same root cause, second symptom: the title

The same commit wrote translation.title as V, σ, μ.

That is not a title. It is taken from line 686 of the English source — a Python tuple-unpacking statement inside a code cell (V, σ, μ, K, L, r, w, τ, D, G, δ, r_old, w_old = state).

So the title-derivation path did not find the H1 either. That points at a single underlying cause rather than two coincidences: the engine failed to parse this file's document structure and fell through to a degraded path that both mis-derived the title and treated the body as opaque text to be fenced.

Worth investigating what is distinctive about ak_aiyagari.md that triggered the fallback — it is one of the largest lectures in the edition (1465 lines, 59 code cells) and is a JAX-migration rewrite, so size or a parse timeout are plausible starting points.

Scope

A sweep across all 8 edition clones in the program found no other file with this corruptionlecture-intro.zh-cn, the three lecture-python-programming editions, and the source repos are all clean, and lecture-python.zh-cn is clean apart from this one file. So it is rare, not systematic. That makes it more concerning rather than less: a rare silent corruption is one that will not be caught by pattern-matching on known-bad output.

Fixed downstream

Repaired in QuantEcon/lecture-python.zh-cn#183. The file now matches the English source at 122 balanced fences, with the H1 and include restored and the title corrected.

Suggested guard

Whatever the root cause turns out to be, a cheap post-write assertion would have caught this at generation time rather than weeks later: fence parity (openers must balance closers), and a sanity check that the derived title corresponds to an actual H1 rather than arbitrary body text. Both are O(lines) and would have failed this write immediately.

Related: #90 (silent data loss, but in the merge path rather than the resync writer); #65 (translator mangling MyST structural syntax). See also the companion issue on {raw} argument stripping filed alongside this one.

🤖 Generated with Claude Code

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 working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions