diff --git a/.dev/STATE.md b/.dev/STATE.md index 1901ea88..7a30db6d 100644 --- a/.dev/STATE.md +++ b/.dev/STATE.md @@ -13,6 +13,14 @@ Roadmap detail lives in [PLAN.md](PLAN.md), not here. ## Recently landed +- **v0.20.0** (2026-07-21) — #128 closed #119 + #65: structural parity guard on every write + path (directive shapes byte-equal or presence-matched by class; anchors exact). Calibrated + empirically: byte-equal draft over 211 real pairs → 362 false positives in 3 classes + (contents/index titles, prf:* titles, code-cell kernel tags) → calibrated guard passes + 200/211, remainder pending-drift artifacts. Found live damage during calibration: sympy.fr + dropped (sympy)= anchor (restored, .fr#16). First wave after this may loudly fail files + with pre-existing anchor damage — that is the guard working the backlog. Next P0 item: + round-trip invariant (#94 Phase 2); nested-directive coverage belongs there. - **v0.19.0** (2026-07-21) — #124 closed #123: `rebase-stale-siblings` (default off) refreshes non-overlapping sibling PRs during waves via update-branch — no re-translation, message-gated 422s. **Harness-validated pre-release** under the harness-first policy: pinned workflow to the diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3ffef49a..797f7b46 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -9,7 +9,7 @@ - **Review Mode**: Runs in TARGET repo, posts quality review comments on translation PRs - **Rebase Mode**: Runs in TARGET repo, rebases conflicted translation PRs when a sibling PR is merged -**Current Version**: v0.19.0 | **Tests**: 1,153+ (49 suites; exact count in CI) | **Glossary**: 357 terms (zh-cn, fa, fr) +**Current Version**: v0.20.0 | **Tests**: 1,173+ (50 suites; exact count in CI) | **Glossary**: 357 terms (zh-cn, fa, fr) --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c23b0a5..9717025b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.0] - 2026-07-21 + +### Added +- **Structural parity guard on every write path** (#119, #65 — first item of the #120 P0 plan): the silent-corruption class (#118, #119, #65) is structural MyST mutated by a path that should have passed it through verbatim, with the run reporting success every time — eight instances to date, every one discovered weeks later on a downstream strict build. Sync (markdown and renamed-file handlers) and forward resync (post-finalization, checking exactly the bytes that would be written) now extract the structural tokens — top-level fenced-directive openings and `(label)=` target anchors — from source and output, and **fail the file loudly on divergence**. Directive name sequences must match (#118's fence-wrap collapses the sequence and fails on count); structural arguments (`{raw}` format, `{include}`/`{figure}` paths, `{solution}`'s exercise label) must match byte-for-byte (#119's defect); flexible arguments — `{admonition}`/`{prf:*}` titles, `{contents}`/`{index}` display text, `{code-cell}`'s edition-pinned kernel tag — need only presence to match, though a dropped argument still fails; anchor sequences must match exactly, with multiset-diff diagnostics that name dropped or surplus labels (#65). The rules were **calibrated against the real corpus**: a byte-equal-everything draft dry-run over all 211 source/target lecture pairs across five editions produced 362 false positives in exactly the three flexible-arg classes, and the calibrated guard passes 200/211 (the rest are pending-drift artifacts of current-vs-current comparison, which the guard never performs in situ). The dry-run also found live production damage — `sympy.fr` had dropped its `(sympy)=` anchor, restored in lecture-python-programming.fr#16. **Operational note**: files whose *historical* translations already dropped anchors will fail their next sync until repaired — loud by design; resync is the repair tool. Scanning is a top-level stateful fence walk (nested directives are #94 Phase 2's round-trip territory); the guard lives in `src/structural-parity.ts` per the rule that guards go in modules Jest can load. + +### Fixed +- Docs and template comments name the CLI invocation correctly as `translate forward --github` (#127) — the bare `forward --github` is not a runnable command. + ## [0.19.0] - 2026-07-21 ### Added diff --git a/package-lock.json b/package-lock.json index 35342897..aab2efd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "action-translation", - "version": "0.19.0", + "version": "0.20.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "action-translation", - "version": "0.19.0", + "version": "0.20.0", "license": "MIT", "dependencies": { "@actions/core": "^2.0.3", diff --git a/package.json b/package.json index fc0bd167..2c9f5d3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "action-translation", - "version": "0.19.0", + "version": "0.20.0", "private": true, "type": "module", "description": "GitHub Action to sync and review translations across repositories",