Repository navigation
Preserve localised _toc.yml part captions on sync - #291
Conversation
…only target fetch (QuantEcon#254) Review follow-up on the _toc.yml caption preservation: - Matching by identical file set lost the caption of exactly the part a lecture-adding sync touches (measured on the real python/zh-cn pair: append one lecture to the first part and 基础工具 reverted to English). Parts are now paired by largest shared membership, each target part used once, ties to the nearest position. A part with no shared files keeps the source caption and is named in a warning; a matched part whose caption is already byte-identical to the source is logged as not localised. No positional guessing. - The merge no longer round-trips through yaml.dump (201 of 204 lines changed on lecture-python's TOC, comments dropped, re-dumped even when nothing was preserved). Caption values are substituted into the source text and the result is verified by parse against the intended document; noArrayIndent re-serialisation is the fallback only. - The rebase replay's `type: 'toc'` site in index.ts now fetches targetContent too, so a rebase no longer re-copies English captions. - Both target-TOC fetches swallow only a 404; any other status fails the fetch instead of silently dropping the captions on a transient error. - Code moved to src/toc-captions.ts (new module, new test file) so this PR and QuantEcon#289 — which inserts at the same point in sync-orchestrator.ts and adds the same js-yaml import — merge cleanly in either order. - Tests: byte-identity on a QuantEcon-style fixture with a comment, quoted caption and trailing whitespace; gain/lose/reorder a lecture; parts moved; new part; no-overlap part not guessed; split part; target-only parts; never-localised no-op by reference; no parts; malformed target; quoting; commented-out caption line; the block-scalar fallback; matcher unit tests. Orchestrator test asserts bytes. CHANGELOG, FAQ and architecture map updated. Merged current main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks @kp992 — this is the right interim step for #254: fetch the target's TOC alongside the source and carry its captions forward, with the source staying the structural authority. The fetch is done the same way as the other target reads and the tests are genuine assertions. As with #289, What the review found
What changed in
|
There was a problem hiding this comment.
🟡 Changes recommended
The CHANGELOG entry is recorded under the already-released [0.28.0] section and the FAQ cites v0.28.0, both misstating which release contains this still-unreleased (atop 0.29.3) fix.
2 open findings
What changed in this PR
This PR fixes issue #254, where a lecture-adding sync regenerated the target's lectures/_toc.yml as a verbatim mirror of the source and silently overwrote hand-translated part captions with English. It introduces a new src/toc-captions.ts module that carries the target's localised captions forward by matching parts on shared file membership (not identity or position), substituting only the caption values into the source text and falling back to a noArrayIndent re-serialisation only when an in-place substitution fails parse verification. The sync and rebase fetch paths in index.ts now also read the existing target TOC and fail loudly on any non-404 fetch error. This is an interim step toward the structured TOC merge tracked in #259.
Changes:
- New
toc-captions.tswithmergeTargetCaptions/matchPartsByOverlap, wired intoSyncOrchestrator.processTocFile. index.tsnow fetchestargetContenton both the sync and rebase TOC paths, treating only a 404 as a first delivery and throwing otherwise.- New unit + orchestrator tests, plus FAQ, architecture, and CHANGELOG documentation.
| File | Description |
|---|---|
src/toc-captions.ts |
New caption-merge module (membership matching, in-place substitution, parse-verified fallback). |
src/sync-orchestrator.ts |
Calls mergeTargetCaptions in processTocFile when target content is present. |
src/index.ts |
Fetches target TOC on sync + rebase paths; fails on non-404 fetch errors. |
src/__tests__/toc-captions.test.ts |
Thorough unit tests for merge/matching behaviour. |
src/__tests__/sync-orchestrator.test.ts |
Orchestrator-level TOC caption-preservation tests. |
docs/user/faq.md |
New FAQ entry — contains an incorrect version reference (v0.28.0). |
docs/developer/architecture.md |
Adds toc-captions.ts to the module map. |
dist-action/index.js |
Rebuilt bundle, in sync with source. |
CHANGELOG.md |
New Fixed entry — placed under the released [0.28.0] section instead of [Unreleased]. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
|
||
| ### Why did the translated section captions in `_toc.yml` revert to English after a sync? | ||
|
|
||
| They should no longer. A sync mirrors the source `_toc.yml` so that lecture additions, removals and reorderings propagate, and until v0.28.0 it mirrored the part captions too, overwriting whatever the edition had translated — silently, because English captions build fine and review mode never looks at the TOC. The sync now reads the target's current `_toc.yml` and carries each part's translated caption forward, matching parts by the lectures they contain rather than by name or position, so a part that gains or loses a lecture keeps its caption. Only the caption lines change; the rest of the TOC is byte-identical to the source. |
There was a problem hiding this comment.
Fixed in 13e16a3 — the FAQ now says the sync mirrored the part captions "in v0.29.3 and earlier", which is accurate today and stays accurate whatever number the release carrying this fix gets.
The CHANGELOG entry sat under the released [0.28.0] section, and the FAQ said captions were mirrored "until v0.28.0". The fix is not in any release yet: the entry moves to [Unreleased], and the FAQ now says "in v0.29.3 and earlier", which stays true whatever the next version is. From Copilot's review of QuantEcon#291. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…om main; rebuilt in the next commit)
…QuantEcon#291 interaction - The CHANGELOG entry sat under [0.29.1] [YANKED], and git merged it there without a conflict. It now sits in the [Unreleased] section next to the QuantEcon#254 entry: one added line against main, none removed. - FAQ: the two _toc.yml answers each said the rest of the TOC is byte-identical, measured from different baselines. Each now names the other change. - New orchestrator test: a lecture-adding sync whose new lecture fails, against a localised target TOC. The caption merge runs first and the entry filter second, and together they hand back the target TOC unchanged. - dist-action/index.js.map rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* filter failed-new-file entries from _toc.yml before opening PR * fix tests * Byte-preserving TOC filter, fetch-failure coverage, accurate PR-body notice (#156) Review follow-up on the failed-new-file TOC filter: - removeTocFileEntries no longer round-trips the TOC through yaml.dump. QuantEcon TOCs use the zero-indent block style js-yaml re-indents, so a one-entry removal became a whole-file rewrite (401 changed lines on the 204-line lecture-python TOC). Entries are now spliced out of the original text — the `- file:` line plus any deeper-indented `title:`/`sections:` children — and the result is verified by parse against the filtered document before a noArrayIndent re-serialisation fallback is considered. Real-TOC check: exactly the removed lines differ, nothing else. - Recursion covers `sections:` as well as `chapters:` at any depth. - A new markdown file whose *source* fetch fails never reached the filter (it is in fetchErrors, not filesToSync). fetchAllFileContents now probes the target on that path — 404 means nothing will be delivered under the name, any other answer keeps the entry — and passes the list into processFiles so those entries leave the TOC too. - The PR-body notice says what actually happened: names the TOC(s) the entries were removed from, or states that no TOC was in the changeset and a pre-existing target entry may still dangle. The unconditional "failure issue has been filed" is reworded; result.filteredTocPaths carries the distinction. - js-yaml import moved beside the other local imports so the bundle diff is the change itself (+207/-9) rather than an esbuild module reshuffle. - Tests: byte-identity on a QuantEcon-style fixture with comments, quoting and trailing whitespace; title/sections removal; nested sections; url/glob siblings; the fallback path; unparseable input; fetch-failed inputs; no-TOC-in-changeset; both PR-body wordings. Duplicate empty-set test removed. CHANGELOG and FAQ entries added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * #156: CHANGELOG entry to [Unreleased]; FAQ and a test for the #291 interaction - The CHANGELOG entry sat under [0.29.1] [YANKED], and git merged it there without a conflict. It now sits in the [Unreleased] section next to the #254 entry: one added line against main, none removed. - FAQ: the two _toc.yml answers each said the rest of the TOC is byte-identical, measured from different baselines. Each now names the other change. - New orchestrator test: a lecture-adding sync whose new lecture fails, against a localised target TOC. The caption merge runs first and the entry filter second, and together they hand back the target TOC unchanged. - dist-action/index.js.map rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Matt McKay <mmcky@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* chore: release v0.29.4 Patch: #291 carries the target's translated _toc.yml part captions forward on sync and on the rebase replay (the #254 interim), #289 removes a failed new lecture's entry from the sync PR's _toc.yml (#156), and #335 is the ml rule-19 completive. No source change in this commit; dist-action/ rebuilds byte-identical. STATE.md brought up to date with the two merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * dev: STATE.md — date the new resume item instead of the heading The resume checklist's heading stays "2026-09-23, after v0.29.3", since only item 1 is new; the item now carries its own date (2026-10-09), as later additions on the page do. The verified: stamp is unchanged: this PR did not re-verify every bullet. Item 1 re-wrapped to the file's width. From Copilot's review of #354. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

When a sync run regenerated
_toc.yml, it mirrored the source directly and silently overwrote any translated part captions in the target.Two-part fix: fetch the existing target TOC in
index.tsso it is available astargetContent, then inprocessTocFilecallmergeTargetCaptionswhich matches source and target parts by file-set membership and carries the target's caption forward for unchanged parts. New parts with no target equivalent keep the source caption.Part of #254 — the interim caption-preservation step; the structured TOC merge in #259 and the shared deterministic localisation check supersede it, so this PR does not close #254.