Record the Chinese-only pipeline finding and the delivery script gate

The "Spanish" production on oldbox is a second Chinese draft, and the
drafter's hard-coded Simplified Chinese instructions are why. Also recorded:
the target-language audio render belongs to UmbraTrans per spec 18, the
Qwen3-TTS cross-language clone probe is still outstanding, and the live UI
was re-measured after the gate.
This commit is contained in:
Umbra Tools
2026-11-06 20:09:23 -08:00
parent 3db6ac3b8b
commit 6bf0baa2f8

View File

@@ -290,11 +290,55 @@ Each pass has quality gates. Quality is the primary concern; generation speed is
- **UmbraAudio suite: 890 passed / 8 failed / 6 skipped** - the same 8 pre-existing failures
(2 `test_pronunciation`, 6 `voice_profile_manager` accent); +9 new tests, no regressions.
Web server restarted; all pages return 200 (auth gate intact, `/` -> 303 `/login`).
- **The pipeline is CHINESE-ONLY, and one production says otherwise.** Measured on oldbox
(2026-11-06): `productions/deal-hunter-es` declares `target_language: es-ES`, its glossary
says `"script": "Spanish"` with Spanish style rules and the title `Cazador de Ofertas`, and
it has a complete 32-chapter run (39,083 s) - **but all 3,038 translated paragraphs are
Chinese** (3,034 with Han characters, same proportion as the zh run; 2,792 of 3,038 differ
in wording from zh, so it is a *second Chinese draft*, not a copy). **Cause: the drafter's
instructions are hard-coded to Simplified Chinese in three places** -
`prompts.py` `SYSTEM_DRAFTER` ("把英文太空冒险小说译成简体中文"), `draft_prompt`
("请把下面英文译为简体中文", JSON key `"zh"`), `SYSTEM_CRITIC` ("对照英文原文和中文译文").
The glossary carries the target but the *instruction* says Chinese, so the model followed
the instruction; the critic is hard-coded the same way, so it checked the Chinese text and
found nothing wrong - there was nothing wrong with it for the language it was asked about.
`pipeline.py` has **zero** references to `target_language`. **Consequence: no non-Chinese
translation has ever been produced by that pipeline**; the other 9 languages exist only as
the single-pass sample files.
- **That defect is now guarded at the contract boundary** (commit `3db6ac3`).
`umbraaudio/languages.py` gained script expectations per language (`Language.scripts`,
`required_script`, `forbidden_scripts`) plus `script_counts()` and `script_mismatch()`;
`import_translations` now checks the delivered text's script against the declared language
**before writing a single row** and refuses the delivery. Japanese needs kana, Korean needs
Hangul, and Chinese carrying kana is a Japanese translation in the wrong lane. An unknown
language is not checked (no expectation to check against). Proven on the real data: the es
delivery is refused with *"the text is mostly Han characters, so it is not Spanish"* and
**0 rows written**; the 4,300-row Chinese delivery still imports unchanged.
The check is about the *script*, not the language - it cannot tell Spanish from Portuguese,
but it can tell Chinese from Spanish, which is the mistake that actually happens.
- **Target-language audio is not built here, and that is deliberate.** Measured:
`umbraaudio/generation/chapter_builder.py` synthesizes `u["text"]` (the English source) and
**never reads `utterance_translations`** - the translation does not reach the TTS path at
all. Spec 18 §4.1 assigns "target-language voice casting" and "target-language TTS
rendering" to **UmbraTrans**, and §4.2 forbids UmbraTrans from reusing UmbraAudio's audio
markers as target-language markers. So the renderer belongs to UmbraTrans, not here. The
alignment pass is what makes it possible: it hands UmbraTrans one row per utterance, keyed
to the verified speaker, with `voiced=false` for the collapses.
- **Open prerequisite for any target-language render:** does Qwen3-TTS speak Chinese with an
**English** voice reference? The engine and the server both accept a per-request
`language` (`QwenTTSEngine.generate(..., language=...)` -> `scripts/qwen_tts_server.py`
`Form("english")`), so it is possible in principle - but it has not been probed, because
LM Studio and the TTS model must not be co-resident on this 16 GB Mac and the alignment run
is using LM Studio. Probe after the alignment finishes.
- **IN FLIGHT:** full-book model alignment running (`--chapters all`, output
`zhprod/delivery-full-llm.json`); progress at ch-9 of 32, **0 needing review** so far, 184
`zhprod/delivery-full-llm.json`); progress at ch-11 of 32, **0 needing review** so far, 289
align cache entries. Auto-ingest watcher armed (`ingest-after-align.sh` waits for the
process to exit, then runs `scripts/import_translations.py --write --replace` so the
model-aligned delivery replaces the mechanical rows automatically).
- **Live UI re-measured after the gate commit and a server restart:** ch1 zh = 12 x "✓
Chinese" / 354 CJK / 0 untranslated; ch6 zh = 415 x "✓ Chinese" / 8,231 CJK / 1
untranslated (the chapter announcement); ch6 es = 0 (the Spanish lane is correctly empty -
the gate stopped the mislabelled delivery); ch6 ru = 0.
- **Two production translation defects found, NOT fixed** (translation-quality, not code):
`ch-15-p0100` source is a passage about dust on a console but the Chinese is "是控制台上的";
`ch-25-p0089` source describes cuboid shapes but the Chinese is "怎么才能接通电源?". Both