Skip to content

translate forward loads its glossary CWD-relative and fails silently — resyncs can run with no glossary and nothing says so #149

Description

@mmcky

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

  1. Resolve the built-in glossary package-relative, as sync-orchestrator.ts does, so forward finds it regardless of CWD.
  2. 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.
  3. Add --glossary <path> to forward, matching init.
  4. 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

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions