The earlier fix corrected two of the repair rules and left the rest with the same
flaw, and the punctuation boundary it introduced let the table case back in. A
review found the remainder.
Every repair rule inserted newlines with no idea whether it was inside a markdown
table row. A dose row such as "| Dexamethasone | 0.6 mg/kg PO (max 16 mg) - single
dose | 1 |" was split mid-row, which drops that row's citation and every row
below it out of the table. A "#" column destroyed the table outright. The rules
now skip table rows, dividers and headers entirely.
Ranges were still split in three other places: in a heading, so "### Dexamethasone
0.15 - 0.6 mg/kg" rendered as a heading reading "Dexamethasone 0.15"; after a
citation; and beside a hash, where "Room # 4" became a heading. A hyphen now
starts a list item only when a number does not follow, and a run-together heading
is recognised by the capital letter after the hashes rather than by position.
Adjacent citation merging could also cross a paragraph break, turning "[1]\n\n[2]
Vancomycin only if MRSA" into "[1, 2]Vancomycin only if MRSA" -- joining two
paragraphs and moving a citation onto a claim it never supported. Merging is now
limited to citations on the same line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three rendering defects in the clinical assistant, all confirmed by running the
code rather than reading it.
A spaced hyphen anywhere in a sentence was rewritten as a list item, so every
numeric range written that way was split in two:
"Give dexamethasone 0.15 - 0.6 mg/kg orally."
-> "Give dexamethasone 0.15\n- 0.6 mg/kg orally."
which renders as a truncated sentence followed by a bullet, and a dose range
therefore reads as a different dose. The same applied to SpO2 targets, pH ranges
and age ranges. A hyphen now starts a list item only at the beginning of a line
or after sentence punctuation, which still catches the case the rule was written
for.
An answer ending in a bold phrase lost its closing marker, because trailing
emphasis was stripped unconditionally and the strip ran twice. Only an unpaired
marker is removed now.
Inline maths swallowed dollar amounts: "Costs $5 to $10 per dose" rendered the
text between the signs as an equation and dropped both signs. A $...$ span is
now treated as maths only when it contains something mathematical, so subscripts
and fractions still render.
Citation handling is unchanged and covered by the same tests: clusters still
merge and sort, unknown source numbers stay literal, links still resolve by index,
and bare numbers in a table's Source column still become bracketed tokens.
The state before these fixes is tagged pre-citation-fixes-20260828.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Voice lists were a single flat set from LITELLM_TTS_VOICES, so picking a
model could leave an incompatible voice selected and the request would
fail at the gateway. Voices are now resolved per model family (Kokoro,
Kitten, Supertonic, Groq Orpheus EN/AR), with a compatibility check that
falls back through user → admin → env → first valid voice. Groq Orpheus
requests also pin response_format to wav.
Also refreshes the cardiac/respiratory auscultation samples, extends the
well-visit component, and fixes the Android launch theme background
(@null → colorPrimary) so the splash does not flash through.
NOTE: this is in-progress work that was already sitting uncommitted in
the working tree; it is committed here as-is so the tree was clean for
the release bump.