fix(report): Urdu rendered as tofu whenever the STT mislabelled the language (bd-osmk0, bd-gipr1) - #307
Merged
Merged
Conversation
…age — 44 reports rendered as tofu
Rifat reported a coach debrief that was "only empty boxes with no content"
(R66). The screenshot was in the feedback sheet as a floating image the Sheets
API will not return; pulling it out of an authenticated PDF export showed the
teacher hero report with every Urdu glyph painted as a box while the English
chrome rendered perfectly.
The chain, end to end:
1. hero-report.service resolved
lang = language || analysis.language || session.transcript_language || 'en'
transcript_language is an STT label, and since 2026-08-11 Soniox has been
returning 'en' / 'hindi' / 'javanese' / 'sindhi' for Urdu classroom audio
(bd-bfy69, still open). So an Urdu-bodied report went down the Latin arm.
2. hero-report.template's Latin arm named ONLY 'Fraunces'/'Lexend'. The
NastaliqUrdu @font-face was embedded but never referenced from any rule in
that branch.
3. Railway's Chromium has no system fonts, so those glyphs had nothing to
fall back to. macOS silently substitutes a system Nastaliq, which is
exactly why every local render looked correct.
Measured on prod: 44 already-generated reports carry non-Latin script while
sitting in a non-ur/ar branch.
Verified with CSS.getPlatformFontsForNode, before and after, same instrument:
BEFORE the Urdu run resolved to the SYSTEM 'Noto Nastaliq Urdu'
(isCustomFont=false — absent on Railway); AFTER it resolves to the EMBEDDED
face in all three stacks. Latin still resolves to Lexend/Fraunces first; the
fallbacks only catch what those cannot draw.
Also fixed .pframe .pcap, which pinned LLM-generated photo captions to
Latin-only Lexend in EVERY branch — so those tofu'd even on a correctly
labelled Urdu report. That is the bd-2362 class: the small-print element
nobody re-checks after the main stacks are fixed.
Tests: 20 + 12 new, red-first against this tree (9 font assertions failed
before the template change; the language suite could not resolve its module).
Full suite shows zero new failures — the four suites that differed under the
parallel run all pass in isolation both with and without the change.
Closes: bd-osmk0
Closes: bd-gipr1
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
hyasin270
added a commit
that referenced
this pull request
Aug 19, 2026
…age — 44 reports rendered as tofu (#307) (#308) Rifat reported a coach debrief that was "only empty boxes with no content" (R66). The screenshot was in the feedback sheet as a floating image the Sheets API will not return; pulling it out of an authenticated PDF export showed the teacher hero report with every Urdu glyph painted as a box while the English chrome rendered perfectly. The chain, end to end: 1. hero-report.service resolved lang = language || analysis.language || session.transcript_language || 'en' transcript_language is an STT label, and since 2026-08-11 Soniox has been returning 'en' / 'hindi' / 'javanese' / 'sindhi' for Urdu classroom audio (bd-bfy69, still open). So an Urdu-bodied report went down the Latin arm. 2. hero-report.template's Latin arm named ONLY 'Fraunces'/'Lexend'. The NastaliqUrdu @font-face was embedded but never referenced from any rule in that branch. 3. Railway's Chromium has no system fonts, so those glyphs had nothing to fall back to. macOS silently substitutes a system Nastaliq, which is exactly why every local render looked correct. Measured on prod: 44 already-generated reports carry non-Latin script while sitting in a non-ur/ar branch. Verified with CSS.getPlatformFontsForNode, before and after, same instrument: BEFORE the Urdu run resolved to the SYSTEM 'Noto Nastaliq Urdu' (isCustomFont=false — absent on Railway); AFTER it resolves to the EMBEDDED face in all three stacks. Latin still resolves to Lexend/Fraunces first; the fallbacks only catch what those cannot draw. Also fixed .pframe .pcap, which pinned LLM-generated photo captions to Latin-only Lexend in EVERY branch — so those tofu'd even on a correctly labelled Urdu report. That is the bd-2362 class: the small-print element nobody re-checks after the main stacks are fixed. Tests: 20 + 12 new, red-first against this tree (9 font assertions failed before the template change; the language suite could not resolve its module). Full suite shows zero new failures — the four suites that differed under the parallel run all pass in isolation both with and without the change. Closes: bd-osmk0 Closes: bd-gipr1 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rifat reported a coach debrief that was "only empty boxes with no content" (HITL sheet R66). The screenshot was a floating image in the sheet that the Sheets API will not return; pulling it out of an authenticated PDF export showed the teacher hero report with every Urdu glyph painted as a box while the English chrome rendered perfectly.
The chain
hero-report.serviceresolvedlang = language || analysis.language || session.transcript_language || 'en'.transcript_languageis an STT label, and since 2026-08-11 Soniox has returneden/hindi/javanese/sindhifor Urdu classroom audio (bd-bfy69, still open — the upstream cause). So an Urdu-bodied report went down the Latin arm.hero-report.template's Latin arm named only'Fraunces'/'Lexend'. TheNastaliqUrdu@font-facewas embedded but never referenced from any rule in that branch.Measured on prod: 44 already-generated reports carry non-Latin script while sitting in a non-
ur/arbranch.What changed
hero-report.template.js— the Urdu/Arabic faces are a permanent fallback in every branch, never gated onlang. Latin still resolves to Lexend/Fraunces first. Same fix bd-2644 applied to the coach card..pframe .pcap— LLM-generated photo captions were pinned to Latin-only Lexend in every branch, so they tofu'd even on a correctly-labelled Urdu report. The bd-2362 class.report-language.js(new) —resolveReportLanguage()constrains the answer toLANGUAGE_OFFER, so an unofferable STT label can no longer choose the script branch or the language the narrative LLM is told to write in. Unofferable lands onofferDefaultLanguage()(ur), not the emergency English floor. Split out so it is testable without booting the service's dependency graph.Verification
CSS.getPlatformFontsForNode, before and after, same instrument:Noto Nastaliq Urdu,isCustomFont=false— absent on RailwayNot fixed here
bd-bfy69 — Soniox transcribing Urdu into Devanagari and mislabelling the language. 67 sessions since 11 Aug. That script reaches coaches directly in the FICO evidence field (R62/R63/R64) and there is no Devanagari font in the repo, so it will still not render. This PR stops the Urdu damage; it does not fix the transcription.
Closes: bd-osmk0
Closes: bd-gipr1
🤖 Generated with Claude Code