206953fb66
session.resume built two projections of the same lineage with two separate get_messages_as_conversation calls — the model-fed copy (tip rows, alternation- repaired) and the display copy (full lineage, verbatim). The display fetch already reads a superset of the model fetch (the tip rows are part of the lineage), so the second call re-scanned the same messages. Add SessionDB.get_resume_conversations(): one lineage SELECT, split into the tip (model) and full (display) projections in memory via the extracted _rows_to_conversation helper. Byte-identical to the two separate reads (test_get_resume_conversations_matches_separate_reads covers a lineage with a dangling tool-call tail so repair diverges the two lengths, a single session, and replayed-user dedup). Both session.resume build paths (deferred + eager) now use it; the subagent single-read path is unchanged. From the Desktop performance audit (P1: "Remove repeated resume transcript work") — the gateway half. The renderer's concurrent REST prefetch is left as-is (the audit flags dropping it as measure-first).