"""One-off: fold the corrected SpeechAce numbers and the Azure-vs-SpeechAce recommendation into the record."""
p = 'harness/15_evaluation_doc.py'; s = open(p).read()
old7 = s[s.index("O += ['## 7. English pronunciation services'"):s.index("# ---- what it adds up to")]
new7 = '''O += ['## 7. English pronunciation services: SpeechAce vs Azure', '',
      'SpeechAce was run on the first 29 s of every English window (its scripted endpoint caps audio at 30 s on our plan; the reference was cut to the words the child reached). Compared on words both sides reached, quality score < 40 = wrong:', '',
      '| Subtask | n | count r | MAE (words, 29-s window) | per-word precision | per-word recall | F1 | Gemini 3.8 Flash on the full 60 s, same children |', '|---|---:|---:|---:|---:|---:|---:|---|',
      '| English word list | 137 | 0.89 | 6.8 | 0.40 | 0.46 | 0.43 | count MAE 5.9, per-word F1 0.34 |',
      '| English story | 109 | 0.93 | 6.7 | 0.45 | 0.38 | 0.41 | count MAE 5.8, per-word F1 0.34 |', '',
      'So on English, SpeechAce\\'s per-word marks are the best we measured (the audio LLMs are stronger on Urdu, where SpeechAce cannot run), its counts are on par, and it is the only scorer that also names the mispronounced sound. Costs: $0.008 per 15 s of audio (about 3 cents per 60-s window in two calls) plus a plan tier; en-US/en-GB dialect models only; every number here is on 8 kHz audio and should be re-measured on 16 kHz voice notes.', '',
      '**Azure Pronunciation Assessment** was not evaluated: both documented keys return 401 and the same key is on the production service, so English reading has been on the silent fallback path (P1 bug filed). Given the SpeechAce result there is no reason to regenerate it: SpeechAce covers the same job with child-speech evidence, nonword phone-list scoring and a working key.', '']

'''
s = s.replace(old7, new7)
s = s.replace("'| English phoneme detail | Azure Pronunciation Assessment once the key is regenerated; SpeechAce for nonwords | Only vendors with phoneme output; SpeechAce is weak on 8 kHz audio and should be re-measured on 16 kHz voice notes | — |',",
              "'| English per-word marks and phoneme detail | **SpeechAce** scripted scoring, two 30-s calls per window, plus its phone-list endpoint for nonwords | Best per-word F1 on English of anything tried (0.41–0.43), counts on par with Gemini, names the mispronounced sound; retire Azure rather than regenerate its dead key | Gemini 3.8 Flash counts with coach-decided chips |',")
open(p, 'w').write(s)

p = 'PLAN.html'; s = open(p).read()
s = s.replace("<tr><td>English sound-level detail</td><td>Microsoft Azure pronunciation service once the key is regenerated; SpeechAce for made-up words</td><td>The only vendors with sound-level output; SpeechAce was weak on this 8 kHz audio and must be re-measured on 16 kHz voice notes</td><td>—</td><td class=\"num\">2 to 4 cents</td></tr>",
              "<tr><td>English per-word marks and sound-level detail</td><td><strong>SpeechAce</strong>, two 30-second calls per window, plus its made-up-word endpoint</td><td>On English its per-word flags were the best we measured (right about 4 times in 10, twice Gemini's rate), its counts are on par, and it is the only scorer that names the mispronounced sound. Azure is dead and not worth regenerating.</td><td>Gemini 3.8 Flash counts with the coach marking chips</td><td class=\"num\">about 3 cents per window</td></tr>")
s = s.replace("<tr><td>English story (60 s)</td><td class=\"num\">62%</td><td><div class=\"bar\"><i style=\"width:62%\"></i></div></td><td>Every scorer is weaker here: best is GPT audio or Gemini 3.5 Flash-Lite at 4 to 5 words off, Gemini 3.8 Flash 5.8; per-word flags right only about a third of the time. Accented child English on 8 kHz audio is the hardest input we have.</td><td><span class=\"pill ok\">counts ready</span>, chips coach-decided</td></tr>",
              "<tr><td>English story (60 s)</td><td class=\"num\">62%</td><td><div class=\"bar\"><i style=\"width:62%\"></i></div></td><td>The hardest input we have (accented child English on 8 kHz audio). Counts: Gemini 3.8 Flash and GPT audio 4 to 6 words off. Per-word flags: <strong>SpeechAce</strong> is the best at right about 4 times in 10 (twice the Gemini models), and it names the mispronounced sound.</td><td><span class=\"pill ok\">counts ready</span>, chips from SpeechAce as suggestions</td></tr>")
s = s.replace("<div class=\"decision\"><div class=\"tag\">D4</div><div><strong>Azure.</strong> Regenerate the keys in the Azure portal, or retire Azure for English and use SpeechAce plus our own alignment. Live English reading tests are on the weaker fallback until this is decided.</div></div>",
              "<div class=\"decision\"><div class=\"tag\">D4</div><div><strong>Retire Azure, use SpeechAce for English.</strong> The evaluation says SpeechAce's English per-word marks are the best we measured and it is the only scorer that names the mispronounced sound, so there is no reason to regenerate the dead Azure key. What needs deciding is the SpeechAce plan tier (the 30-second cap means two calls per window; the Pro tier raises it to 45 s). Live English reading tests stay on the weaker fallback until the swap ships.</div></div>")
s = s.replace("<li>Our Microsoft Azure pronunciation service is dead: both stored keys are rejected, and the same key is on the live server, so English reading tests have quietly been falling back to a weaker method. Filed as an urgent bug; the key must be regenerated in the Azure portal.</li>",
              "<li>Our Microsoft Azure pronunciation service is dead: both stored keys are rejected, and the same key is on the live server, so English reading tests have quietly been falling back to a weaker method. Filed as an urgent bug; the fix is to swap that path to SpeechAce rather than regenerate the key.</li>")
s = s.replace("<li>Left: regenerate the Azure key and rerun the English test; run the forced-alignment method for Urdu word marks; hand-transcribe 30 Urdu reads as a gold standard.</li>",
              "<li>Left: swap the English pronunciation path from Azure to SpeechAce; run the forced-alignment method for Urdu word marks; hand-transcribe 30 Urdu reads as a gold standard.</li>")
open(p, 'w').write(s)
print('patched 15 + PLAN')
