Report stale translations and broken anchors - #30
Conversation
Each Finnish page records the git blob hash of the English page it was translated from, in its own frontmatter. The English pages carry nothing, so editing one needs no ceremony: the edit changes its content, which changes its hash, which makes its translations report as stale on their own. All 21 were translated from the English content as it stands, so they are stamped current. An unknown frontmatter key is inert in Material — verified that it does not render, does not reach the search index, and builds clean under --strict. Part of #28. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes the gap that made translating 21 pages risky: nothing told us when a translation fell behind its source. scripts/translation_status.py compares each translation's stamp against the current English blob and sorts pages into missing, unstamped, stale and current. For stale pages it resolves the stamped blob back through git and prints the English diff since the translation was written, which turns "this page is stale" into the specific change to apply. It also prints the stamp value to record, so nobody computes a hash by hand. scripts/check_anchors.py walks the built HTML and verifies every internal anchor resolves. Anchors derive from heading text, so translating a heading breaks links to it — including links on pages nobody touched — and mkdocs --strict does not check them at all. This broke 16 links during the Finnish translation. The workflow reports and does not block, per #25: fallback_to_default means a lagging translation serves English, so nothing is broken while it lags. It comments on PRs that touch docs/en/**, scoped to the pages that PR actually changed, and writes a full report to the job summary on every run so pre-existing drift stays visible after the PR that caused it has merged. Part of #28. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Computing a blob hash and editing frontmatter by hand is exactly the mechanical step that goes wrong quietly. The helper derives the English source from the translation's own path, replaces an existing stamp rather than duplicating it, and leaves other frontmatter keys alone. Part of #28. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Makes translating a page a repeatable operation rather than an ad-hoc prompt. Encodes the glossary-first rule, the structure and unit requirements, the two markdown traps that neither --strict nor GitHub's preview catches, and the anchor handling — including the delayed fault where a link in an already-translated page breaks only once its target page gets translated. The stamping discipline lives here: the skill re-stamps only when it has actually re-translated. A stamp updated without translation work reports green, which is the one failure the status check cannot detect. Closes #29. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Yeah, broken anchors should fail the run. |
@mairas on #30: broken anchors should fail. Unlike a stale translation, which is not broken because the page serves English, a broken anchor is a link that goes nowhere and has already shipped. The check moves last so the status report and the PR comment are still published when it fails. Also fixes a false green in the checker itself: pointing it at a missing or empty site directory reported "all anchors resolve" and exited 0, which is exactly the case where CI should be loudest. It now exits 2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two holes found by probing the tools rather than reading them. A translation whose English source was deleted was invisible: the report walks the sources, so it simply stopped counting the page while the translation stayed in the tree and kept being served. Now reported as orphaned. stamp_translation.py would happily stamp a page under docs/en/, writing a self-referential hash into the source. Source pages carry no stamp by design — that is what makes an English edit ceremony-free — so it now refuses, reading the default language from mkdocs.yml rather than assuming "en". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Re-reviewYour call is implementedBroken anchors now fail the run. One non-obvious consequence worth knowing: the anchor check had to move to the last step. It was second, and a failing step aborts the ones after it — so a broken anchor would have suppressed the staleness report and the PR comment, hiding one problem behind another. The report is published first, then the run fails. Two defects found in this passBoth came from probing the tools rather than re-reading them, and both are fixed in Orphaned translations were invisible. The report walks the English sources, so deleting
There was also a false green in the anchor checker: pointed at a missing or empty site directory it reported "All anchors resolve" and exited 0 — the one case where CI should be loudest. Fixed in Deviation from the spec, flagged deliberately#28 lists three buckets: missing, stale, current. I report five. Verified in CI, not just locallyThree green runs on this branch. The report and the anchor output now appear in the job logs as well as the job summary — the first run put everything in the summary only, which meant nothing was visible where you look when it misbehaves. Still not verifiedThe PR comment path. It only fires on Fork PRs. 🤖 Generated with Claude Code |
|
LGTM! |
Closes #28. Closes #29.
Closes the gap that made translating 21 pages risky: nothing told us when a translation fell behind its source.
What is here
scripts/translation_status.py— sorts every page intomissing,unstamped,staleorcurrent. For stale pages it resolves the stamped blob back through git and prints the English diff since the translation was written, which turns "this page is stale" into the specific change to apply. It prints the stamp value to record, so nobody computes a hash by hand.scripts/check_anchors.py— walks the built HTML and verifies every internal anchor resolves. This broke 16 links during the Finnish translation andmkdocs build --strictdid not notice one of them.scripts/stamp_translation.py— writes the stamp. Deriving the English source from the translation's own path and editing frontmatter by hand is the mechanical step that goes wrong quietly..github/workflows/translation-status.yml— comments on PRs touchingdocs/en/**, scoped to the pages that PR actually changed, and writes the full report to the job summary on every run so pre-existing drift stays visible after the PR that caused it has merged..claude/skills/translate-page/SKILL.md— translation as a repeatable operation. Encodes the glossary-first rule, structure and unit requirements, the two markdown traps neither--strictnor GitHub's preview catches, and anchor handling — including the delayed fault where a link in an already-translated page breaks only once its target page is translated.All 21 Finnish pages stamped, currently all
current.Design, as settled on #25
Report, do not block:
fallback_to_defaultmeans a lagging translation serves English, so nothing is broken while it lags. State, not commits: the English page carries no token to bump, so an English edit needs no ceremony — the edit changes the content, which changes the hash, which makes its translations report stale on their own.One decision, since resolved
I implemented anchor validation as reporting, because #25 says "report, do not block" twice — while thinking anchors are a different class: a stale translation is not broken (the page serves English), a broken anchor is a link that goes nowhere and has already shipped. @mairas decided on this PR that it should fail, and it now does. The check runs last so the status report is still published when it fails.
Verified
--strict. Checked rather than assumed.1.1A→1.3Ain the English errata page and confirmed the report flags exactly that page and shows exactly that line.Not verified
The PR comment path is not exercised by this PR, because it only fires on
docs/en/**changes and this PR touches none. The job summary is exercised; the comment logic stays unproven until a PR edits an English page. Flagging so a green run is not read as broader than it is.🤖 Generated with Claude Code