--- name: conversation-log-ls6-rotation-and-paginated-read description: Contrat figé de la rétention/rotation du transcript et de la lecture paginée riche (surface humaine). metadata: type: reference --- # LS6 — Rotation transcript + lecture paginée Hygiène de persistance du `log.jsonl` + surface de lecture humaine paginée (viewer LS7). ## Invariant pivot INV-LS6 La rotation n'élague/déplace JAMAIS un tour d'id ≥ `up_to` du handoff courant : le tour up_to + tous les postérieurs restent dans le segment ACTIF. Sans handoff (ou up_to nil) ⇒ aucune rotation. Garantit que `ConversationLog::read(since=up_to)` (fold incrémental) et la reprise restent corrects après rotation. Motif : `read(since)` cherche le curseur par position ; retirer up_to du fichier casserait le fold. ## Rotation - Stratégie = **archive segmentée** (`log.jsonl` actif + `log.1.jsonl`… anciens), PAS troncature/suppression (transcript = surface humaine riche à préserver). - Déclencheur **hors chemin chaud** : use case `RotateConversationLog` à la reprise/ouverture, best-effort, idempotent. L'`append` ne déclenche JAMAIS la rotation (zéro latence ajoutée). - Seuils sur segment actif : `ROTATE_AFTER_TURNS=500` OU `ROTATE_AFTER_BYTES=1MiB`. Backstop disque `MAX_ARCHIVE_SEGMENTS=20` (drop du plus ancien seulement). Politique pure domaine `rotation_plan(...) -> Skip|Archive{keep_from=up_to}`. - Mécanique : verrou conversation bref ; réécriture actif **atomique en dernier** (tmp+rename) ; aucun tour perdu (∪ actif+archives) ; pagination dédoublonne par TurnId (crash-safe). ## Lecture paginée (port ISP `ConversationArchive`, impl FsConversationLog) - `page(conversation, PageCursor{anchor,direction Forward|Backward}, limit)` → `TurnSlice{turns, has_more}`. limit clampé [1,200] défaut 50. Ordre chrono croissant. Archive-aware. - Use case `ReadConversationPage` → DTO humain `TurnPage{turns:[TurnView{id,at_ms,role,source,text COMPLET,text_len}], has_more, next_anchor}`. **Texte complet, non borné, non distillé** = surface HUMAINE. - `ConversationLog` (append/read/last) inchangé = vue segment actif (chemin chaud + dérivation). ## Frontière Lecture riche consommée UNIQUEMENT par la commande Tauri du viewer humain, jamais par compose_convention_file/handoff/agent. Rotation ne touche que le transcript brut, jamais le handoff/injection. ## Frontend LS6 = backend + commande Tauri `read_conversation_page`. Tout le React (viewer) = LS7.