translate forward resolves its glossary only relative to process.cwd(), has no --glossary option to override, and logs nothing either way. Run it from any directory that is not a repo containing glossary/<lang>.json — which includes the target repo, a workspace/bench root, or anywhere a user would naturally invoke a globally-installed CLI — and it silently translates with no glossary at all.
The code
src/cli/commands/forward.ts (~L778):
function loadGlossary(language: string): Glossary | undefined {
const candidates = [
path.join(process.cwd(), 'glossary', `${language}.json`),
path.join(process.cwd(), `glossary-${language}.json`),
];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
try { return JSON.parse(fs.readFileSync(candidate, 'utf-8')) as Glossary; }
catch { /* Ignore parse errors */ }
}
}
return undefined;
}
Compare loadGlossary in src/sync-orchestrator.ts, used by the Action, which does this properly:
|
sync-orchestrator.ts (Action) |
forward.ts (CLI resync) |
| Built-in glossary |
resolved from an explicit builtInGlossaryDir — always found |
not attempted — CWD-relative only |
| Custom path |
customGlossaryPath fallback |
no --glossary option exists on forward |
| On success |
logs ✓ Loaded built-in glossary for <lang> with N terms |
silent |
| On failure |
logs a warning |
silent — bare return undefined, and catch {} swallows parse errors too |
init has the same CWD-relative candidates but at least accepts --glossary as an escape hatch. forward has neither the escape hatch nor the diagnostics.
Production evidence
Found while fixing a terminology defect the reviewer correctly caught on QuantEcon/lecture-python.zh-cn#198. The zh-cn glossary is explicit and context-aware here:
| en |
zh-cn |
context |
| Marginal distribution |
边缘分布 |
probability |
| Marginal product of capital |
资本边际产量 |
economics |
| Marginal revenue |
边际收入 |
microeconomics |
So 边缘分布 for the probability sense, 边际 reserved for the economics senses. The estate does not match that.
The init path honoured the glossary. All four lectures containing the term among the 37 seeded by QuantEcon/lecture-python.zh-cn#196 (translate init, 2026-07-22) use 边缘分布. Zero wrong.
The forward path did not. In the 2026-07-19 resync wave, prob_matrix.md went from 12 wrong / 28 correct before the resync to 25 wrong / 35 correct after — the resync introduced 13 new occurrences of the non-glossary rendering into a document where the glossary term was already dominant. That is not stale text being preserved; it is newly generated text ignoring a glossary entry that exists.
Fleet-wide in lecture-python.zh-cn: 9 lectures carry 边际分布 (54 occurrences) while 6 others use 边缘分布 correctly, and prob_matrix.md contains both renderings in the same file (25 vs 35). lecture-intro.zh-cn has one affected file.
To be precise about what this does and does not prove: I cannot recover the working directory the wave was run from, so I cannot state with certainty that it ran glossary-less. What is certain is that the defect exists by construction, that the production signature matches it exactly, and that nobody could ever tell either way, because the code logs nothing. That last part is arguably the worst of it — a silent, unobservable difference in translation quality depending on where you happened to cd.
Suggested fix
- Resolve the built-in glossary package-relative, as
sync-orchestrator.ts does, so forward finds it regardless of CWD.
- Log the outcome either way —
✓ Loaded glossary for zh-cn (N terms) or a loud warning. A translation run that silently drops terminology enforcement should not look identical to one that applies it.
- Add
--glossary <path> to forward, matching init.
- Consider whether a glossary-less run should be an error rather than a warning for a language that has a built-in glossary — for this estate, translating zh-cn without the 357-term glossary is never intentional.
Worth noting this compounds #146 (review mode ignores glossary-path): between them, the resync path may translate without a glossary and the review path may then judge the result against the wrong one, in a category — terminology — that verdict v2 makes gating. The one genuine production gate observed so far (#198) was a terminology finding.
Related
translate forwardresolves its glossary only relative toprocess.cwd(), has no--glossaryoption to override, and logs nothing either way. Run it from any directory that is not a repo containingglossary/<lang>.json— which includes the target repo, a workspace/bench root, or anywhere a user would naturally invoke a globally-installed CLI — and it silently translates with no glossary at all.The code
src/cli/commands/forward.ts(~L778):Compare
loadGlossaryinsrc/sync-orchestrator.ts, used by the Action, which does this properly:sync-orchestrator.ts(Action)forward.ts(CLI resync)builtInGlossaryDir— always foundcustomGlossaryPathfallback--glossaryoption exists onforward✓ Loaded built-in glossary for <lang> with N termsreturn undefined, andcatch {}swallows parse errors tooinithas the same CWD-relative candidates but at least accepts--glossaryas an escape hatch.forwardhas neither the escape hatch nor the diagnostics.Production evidence
Found while fixing a terminology defect the reviewer correctly caught on QuantEcon/lecture-python.zh-cn#198. The zh-cn glossary is explicit and context-aware here:
So 边缘分布 for the probability sense, 边际 reserved for the economics senses. The estate does not match that.
The
initpath honoured the glossary. All four lectures containing the term among the 37 seeded by QuantEcon/lecture-python.zh-cn#196 (translate init, 2026-07-22) use 边缘分布. Zero wrong.The
forwardpath did not. In the 2026-07-19 resync wave,prob_matrix.mdwent from 12 wrong / 28 correct before the resync to 25 wrong / 35 correct after — the resync introduced 13 new occurrences of the non-glossary rendering into a document where the glossary term was already dominant. That is not stale text being preserved; it is newly generated text ignoring a glossary entry that exists.Fleet-wide in
lecture-python.zh-cn: 9 lectures carry 边际分布 (54 occurrences) while 6 others use 边缘分布 correctly, andprob_matrix.mdcontains both renderings in the same file (25 vs 35).lecture-intro.zh-cnhas one affected file.To be precise about what this does and does not prove: I cannot recover the working directory the wave was run from, so I cannot state with certainty that it ran glossary-less. What is certain is that the defect exists by construction, that the production signature matches it exactly, and that nobody could ever tell either way, because the code logs nothing. That last part is arguably the worst of it — a silent, unobservable difference in translation quality depending on where you happened to
cd.Suggested fix
sync-orchestrator.tsdoes, soforwardfinds it regardless of CWD.✓ Loaded glossary for zh-cn (N terms)or a loud warning. A translation run that silently drops terminology enforcement should not look identical to one that applies it.--glossary <path>toforward, matchinginit.Worth noting this compounds #146 (review mode ignores
glossary-path): between them, the resync path may translate without a glossary and the review path may then judge the result against the wrong one, in a category —terminology— that verdict v2 makes gating. The one genuine production gate observed so far (#198) was a terminology finding.Related
glossary-pathprob_matrix.md25-vs-35 split is a case where both problems are visible in one file