Un dossier de fichiers par discussion

Les fichiers du chat vivaient dans un pot commun : on changeait de discussion
et on revoyait les mêmes pièces jointes, et supprimer une discussion laissait
derrière elle tout ce qu'on y avait déposé ou fait écrire à l'agent (seules ses
captures, déjà rangées par identifiant, partaient avec).

Chaque discussion a désormais son dossier, <workspace>/discussions/<id>/ :

- les dépôts (uploads/), les captures (captures/) et ce que l'agent écrit y
  atterrissent ; le shell et les chemins relatifs du modèle y sont résolus ;
- le panneau Fichiers s'ouvre sur ce dossier et n'en sort pas, et se redessine
  quand la discussion change (bascule ou vidage, signalés par le flux SSE) ;
- supprimer une discussion — ou la vider — emporte ses fichiers. Les deux gestes
  le disent maintenant avant de demander confirmation ; « clear chat » en
  demandait aucune.

La racine du dossier de travail reste la borne de sécurité : les liens des
anciens messages (uploads/x.pdf, captures/<id>/y.jpg) continuent d'ouvrir leur
fichier par un chemin de repli. Au démarrage, une migration range les captures
dans le dossier de leur discussion et rend chaque dépôt à la discussion qui le
mentionne dans son journal ; ce que personne ne réclame reste à la racine,
atteignable par le bouton « hors discussion » du panneau, qui disparaît une fois
le ménage fait.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
This commit is contained in:
Claude committed 2026-08-16 15:33:55 +00:00
1 parent d42c334b9a
commit 001d633750
21 files changed
+914 -168

No files matched your search

+10 -4
View File
@@ -10,11 +10,17 @@ import (
"testing"
)
// withWorkspace pointe le dossier de travail sur un dossier temporaire. Le
// chemin est mémoïsé par un sync.Once (chat_workspace.go), donc on force la
// variable directement — c'est le seul moyen d'isoler ces tests.
// withWorkspace pointe le dossier de travail sur un dossier temporaire et rend
// le dossier de la discussion ACTIVE — celui où atterrissent les fichiers et
// que le panneau ouvre. Le chemin de la racine est mémoïsé par un sync.Once
// (chat_workspace.go), donc on force la variable directement : c'est le seul
// moyen d'isoler ces tests.
//
// testHome donne au passage une base à part : la discussion active s'y lit, et
// aucun test ne doit toucher celle de la machine qui les fait tourner.
func withWorkspace(t *testing.T) string {
t.Helper()
testHome(t)
dir := t.TempDir()
// EvalSymlinks : sur macOS /var est un lien vers /private/var, et
// workspaceRel compare des chemins résolus. Sans ça tout serait « hors du
@@ -29,7 +35,7 @@ func withWorkspace(t *testing.T) string {
prevPath := agentWorkspace()
workspacePath = dir
t.Cleanup(func() { workspacePath = prevPath })
return dir
return convWorkspace()
}
func writeFile(t *testing.T, path, body string) {