Fix Soniox transcript assembly and mobile processing-card placement
Soniox's streaming adapter treated each WebSocket response as a word boundary and read duration_ms instead of the provider's end_ms. This could turn res + umed into res umed, leave zero-length segments, and extend timestamps into silence. Preserve final word pieces across frames, honor provider timestamps, and send the documented finalize control when VAD requests it. Draining now waits for the buffered final-word callback, including on timeout, and synchronous failover flushes its committed tail exactly once.
On mobile, Process Now inserted its pending card before Goals, and the first pending array entry could hide a newer capture. Render the newest capture below the Conversations heading and filters, above completed rows. Pending-only views retain their heading without a contradictory empty state; Daily Recaps hides pending conversation cards.
Validation:
- 147 Soniox tests and 166 VAD/transcript downstream tests passed, including cross-frame downstream merging, finalization order, timeout cleanup, and synchronous failover tails.
- 146 Flutter spine and focused widget tests passed; analyzer ratchet passed.
- Mobile verification: 23 selected hermetic journeys passed, zero skipped.
- Synthetic Flutter runtime preview inspected with agent-flutter: Goals → Conversations → filters → processing card → completed row. This is not physical-iPhone acceptance.
- Independent Luna backend and UI reviews; findings addressed.
- Real-audio replays checked the adapter against the provider. These changes correct protocol defects; they do not establish a general hallucination-rate improvement. Private account evidence is excluded.
Protocol references: token fields, manual finalization.
Product invariants: no locked invariant citations required by scripts/pr-preflight --suggest; existing INV-UI-1 presentation retained.
Failure-Class: FC-external-protocol-subset-read-as-absent