Relecture de l'écrivain différé (chat_persist.go) : l'ordre, la suppression
et les bascules tiennent ; trois écarts de comportement corrigés.
- saveToolResult et la télémétrie de generate lisaient la clé brute de la
discussion active ; passés à convEnsureActive, ils pouvaient en CRÉER une
(tâche de fond sur base neuve → discussion vide dans la liste). Nouveau
convActiveID : cache chaud, sinon la clé en base, jamais de création.
- Un résultat d'outil confié après la suppression de sa discussion restait en
mémoire pour toujours (l'écrivain l'écarte, la copie ne partait plus) : il
n'est plus retenu.
- Vidage de l'écrivain avant « verrouiller la mémoire » (sinon l'envoi tout
juste fait ne touchait le disque qu'au déverrouillage) et avant le restart
systemd après MAJ (SIGTERM ne laisse rien finir).
Tests : résultat d'une discussion supprimée ni écrit ni retenu ; base neuve,
saveToolResult reste sous « nosession » sans créer de discussion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StartTurn persistait AVANT de lancer la génération : un marshal, puis deux
commits bbolt (contenu, puis index), soit deux fsync et plusieurs ouvertures
de base — 26-30 ms mesurés sur un NVMe Windows, bien plus sur un /mnt/user
d'Unraid ou un disque de parité, payés à chaque message avant que la requête
ne parte vers llama-server. Même facture au milieu d'un tour (vérification du
mode Code) et pour chaque résultat d'outil coupé (« voir plus »).
- chat_persist.go : un écrivain unique et ordonné. L'instantané reste pris
sous c.mu (images → références, marshal, identifiant de la discussion) ;
seule l'E/S part en différé. Le dernier instantané de chaque discussion
l'emporte (numéro pris sous c.mu), les lots sont fusionnés en UNE
transaction : contenu + index ensemble, résultats d'outils compris.
- Seuls StartTurn, la persistance en cours de tour et les résultats d'outils
sont asynchrones. Fin de tour, reset, bascules, compactage manuel restent
synchrones et attendent tout ce qui précède ; la suppression écarte puis
attend les écritures qui la visent (plus de discussion ressuscitée).
- Discussion active gardée en RAM, créée sous verrou (plus de double
identifiant sur base neuve) et changée sous c.mu avec le contenu : un
instantané ne peut plus écrire l'ancien fil sous le nouvel identifiant.
- « Voir plus » servi depuis la mémoire tant que le résultat n'est pas écrit ;
verrouillage vérifié avant de rendre un id.
- En plein tour, la base reçoit le journal sous sa forme de fin de tour
(compactLog, version pure de compactLogLocked) ; la mémoire n'est pas touchée.
- Vidage de l'écrivain avant redémarrage, « Quitter », exec et (dé)chiffrement.
Le contexte vu par le modèle ne change pas : il lit c.Messages en mémoire, les
résultats complets d'outils et la compaction du journal ne servent qu'à l'UI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>