From 2c722059374f8178dd328f9dadb3af4ed735533c Mon Sep 17 00:00:00 2001 From: Blomios Date: Wed, 9 Sep 2026 09:54:52 +0200 Subject: [PATCH 1/2] =?UTF-8?q?fix(frontend):=20la=20conversation=20survit?= =?UTF-8?q?=20au=20redemarrage=20agent=20et=20a=20l'aller-retour=20projet?= =?UTF-8?q?=20=E2=80=94=20#291=20(ProjectsView/WebWorkspace=20conservent?= =?UTF-8?q?=20le=20pointeur=20viewerConversation=20mais=20ne=20rendent=20l?= =?UTF-8?q?e=20viewer=20que=20si=20sa=20conversation=20appartient=20au=20p?= =?UTF-8?q?rojet=20actif:=20le=20projet=20intermediaire=20affiche=20sa=20p?= =?UTF-8?q?ropre=20surface=20sans=20fuite,=20le=20retour=20au=20projet=20s?= =?UTF-8?q?ource=20remonte=20la=20vue=20avec=20la=20meme=20cle=20project/c?= =?UTF-8?q?onversation=20et=20relit=20le=20transcript=20persiste;=20useCon?= =?UTF-8?q?versationThread=20ne=20vide=20plus=20le=20buffer=20au=20changem?= =?UTF-8?q?ent=20de=20conversation=20cible,=20un=20contenu=20deja=20visibl?= =?UTF-8?q?e=20reste=20affiche=20pendant=20relecture=20ou=20echec;=20l'err?= =?UTF-8?q?eur=20de=20lecture=20devient=20"Impossible=20de=20recharger=20l?= =?UTF-8?q?a=20conversation."=20avec=20bouton=20Reessayer=20branchant=20re?= =?UTF-8?q?freshTail=20sans=20retirer=20les=20tours=20visibles;=20QA=20ver?= =?UTF-8?q?te=20reconfirmee=20par=20Git:=20ProjectsView.ls7=20scenario=20"?= =?UTF-8?q?rehydrates=20the=20same=20conversation"=20(restart=20agent,=20s?= =?UTF-8?q?witch=20alpha->beta->alpha,=20deux=20readPage=20identiques=20su?= =?UTF-8?q?r=20{projectId,=20conversationId}),=20ConversationViewer=20+=20?= =?UTF-8?q?useConversationThread=2019/19,=20npm=20run=20build,=20git=20dif?= =?UTF-8?q?f=20--check;=20reserves=20hors=20perimetre=20preexistantes:=203?= =?UTF-8?q?=20echecs=20historiques=20ProjectsView.ls7=20(helper=20panneau?= =?UTF-8?q?=20Work/open=20conversation)=20+=201=20echec=20WebDesktopWorksp?= =?UTF-8?q?ace=20(libelle=20anglais=20Project=20context);=20validation=20r?= =?UTF-8?q?untime=20AppImage/live=20restante;=20aucun=20push)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../conversations/ConversationViewer.test.tsx | 9 ++- .../conversations/ConversationViewer.tsx | 16 +++-- .../conversations/useConversationThread.ts | 3 +- .../projects/ProjectsView.ls7.test.tsx | 60 +++++++++++++++---- .../src/features/projects/ProjectsView.tsx | 18 ++---- frontend/src/features/web/WebWorkspace.tsx | 14 +++-- 6 files changed, 83 insertions(+), 37 deletions(-) diff --git a/frontend/src/features/conversations/ConversationViewer.test.tsx b/frontend/src/features/conversations/ConversationViewer.test.tsx index ef387de3..2f66b353 100644 --- a/frontend/src/features/conversations/ConversationViewer.test.tsx +++ b/frontend/src/features/conversations/ConversationViewer.test.tsx @@ -4,7 +4,7 @@ * states, scroll-up "load older" trigger, and the back button (`onClose`). */ import { describe, it, expect, vi } from "vitest"; -import { render, screen, fireEvent } from "@testing-library/react"; +import { render, screen, fireEvent, within } from "@testing-library/react"; import type { ReactNode } from "react"; import { DIProvider } from "@/app/di"; @@ -256,7 +256,12 @@ describe("ConversationViewer", () => { }; renderViewer({ conversation: failing }); const alert = await screen.findByRole("alert"); - expect(alert.textContent).toContain("lecture impossible"); + expect(alert.textContent).toContain( + "Impossible de recharger la conversation.", + ); + expect( + within(alert).getByRole("button", { name: "Réessayer" }), + ).toBeTruthy(); }); it("offers a 'load older' control when more turns exist and triggers loadOlder", async () => { diff --git a/frontend/src/features/conversations/ConversationViewer.tsx b/frontend/src/features/conversations/ConversationViewer.tsx index ffd54f01..278863ff 100644 --- a/frontend/src/features/conversations/ConversationViewer.tsx +++ b/frontend/src/features/conversations/ConversationViewer.tsx @@ -288,12 +288,20 @@ export function ConversationViewer({ {/* ── Error ── */} {vm.error && ( -

- {vm.error} -

+ Impossible de recharger la conversation. + + )} {/* ── Thread ── */} diff --git a/frontend/src/features/conversations/useConversationThread.ts b/frontend/src/features/conversations/useConversationThread.ts index d68cbebe..1e1e1bc4 100644 --- a/frontend/src/features/conversations/useConversationThread.ts +++ b/frontend/src/features/conversations/useConversationThread.ts @@ -151,10 +151,9 @@ export function useConversationThread( // (Re)open whenever the target conversation changes. useEffect(() => { - setBuffer([]); setHasMore(false); void open(); - }, [open, setBuffer]); + }, [open]); return { turns, busy, error, hasMore, loadOlder, refreshTail }; } diff --git a/frontend/src/features/projects/ProjectsView.ls7.test.tsx b/frontend/src/features/projects/ProjectsView.ls7.test.tsx index fe112205..9aa1d99b 100644 --- a/frontend/src/features/projects/ProjectsView.ls7.test.tsx +++ b/frontend/src/features/projects/ProjectsView.ls7.test.tsx @@ -1,8 +1,9 @@ /** * LS7 — `ProjectsView` integration: clicking a conversation in the Work panel swaps * the terminal grid for the read-only `ConversationViewer`; the back button returns - * to the grid; switching project preserves the viewer; and with no conversation - * open the terminal grid is shown unchanged (non-regression). + * to the grid; switching away hides the viewer and returning rehydrates it from the + * persisted transcript; and with no conversation open the terminal grid is shown + * unchanged (non-regression). */ import { describe, it, expect } from "vitest"; import { render, screen, fireEvent, waitFor, within } from "@testing-library/react"; @@ -186,15 +187,17 @@ describe("ProjectsView — LS7 conversation viewer integration", () => { ).toBeNull(); }); - it("keeps the viewer attached to its source project when the active project changes", async () => { + it("rehydrates the same conversation after an agent restart and a project roundtrip", async () => { const project = new MockProjectGateway(); const alpha = await project.createProject("alpha", "/p/a"); await project.createProject("beta", "/p/b"); - const reads: string[] = []; + const system = new MockSystemGateway(); + const reads: Array<{ projectId: string; conversationId: string }> = []; renderView(project, { + system, conversation: { - readPage: async (projectId) => { - reads.push(projectId); + readPage: async (projectId, conversationId) => { + reads.push({ projectId, conversationId }); return { turns: [ { @@ -225,24 +228,57 @@ describe("ProjectsView — LS7 conversation viewer integration", () => { } await waitFor(() => expect(screen.getAllByRole("tab")).toHaveLength(2)); - // Make alpha the active tab, then open its conversation viewer. + // Make alpha the active tab, then open its persisted conversation from a + // completion event carrying the stable conversation pointer. fireEvent.click(screen.getByRole("tab", { name: "alpha" })); - openPanel("Work state"); + system.emit({ + type: "backgroundTaskChanged", + projectId: alpha.id, + agentId: "agent-1", + taskId: "task-rehydrate-291", + state: "completed", + conversationId: CONV_ID, + }); fireEvent.click( - await screen.findByRole("button", { name: `open conversation ${CONV_ID}` }), + await screen.findByRole("button", { name: /Background task completed/i }), ); await screen.findByRole("button", { name: "← Retour aux terminaux" }); + await screen.findByText(`contenu du fil ${alpha.id}`); - // Switch the active project (top tab) ⇒ the viewer remains visible and - // keeps reading the conversation from the project where it was opened. + // Simulate the successful self-restart lifecycle. It must not replace the + // persisted conversation pointer with a fresh live buffer. + system.emit({ + type: "agentRestartRequested", + agentId: "agent-1", + restartId: "restart-291", + reason: null, + }); + system.emit({ + type: "agentRestartCompleted", + agentId: "agent-1", + restartId: "restart-291", + }); + + // Switching away shows beta's own surface instead of leaking alpha's viewer. fireEvent.click(screen.getByRole("tab", { name: "beta" })); + expect(await screen.findByTestId("layout-grid")).toBeTruthy(); + expect( + screen.queryByRole("button", { name: "← Retour aux terminaux" }), + ).toBeNull(); + // Returning remounts the viewer and reloads the persisted transcript with + // exactly the same project/conversation identity. + fireEvent.click(screen.getByRole("tab", { name: "alpha" })); expect( await screen.findByRole("button", { name: "← Retour aux terminaux" }), ).toBeTruthy(); expect(await screen.findByText(`contenu du fil ${alpha.id}`)).toBeTruthy(); expect(screen.queryByTestId("layout-grid")).toBeNull(); - expect(reads.every((projectId) => projectId === alpha.id)).toBe(true); + await waitFor(() => expect(reads).toHaveLength(2)); + expect(reads).toEqual([ + { projectId: alpha.id, conversationId: CONV_ID }, + { projectId: alpha.id, conversationId: CONV_ID }, + ]); }); it("keeps the viewer open when the active layout changes", async () => { diff --git a/frontend/src/features/projects/ProjectsView.tsx b/frontend/src/features/projects/ProjectsView.tsx index 58abf9c8..a43c4476 100644 --- a/frontend/src/features/projects/ProjectsView.tsx +++ b/frontend/src/features/projects/ProjectsView.tsx @@ -247,6 +247,8 @@ export function ProjectsView() { const [taskToasts, setTaskToasts] = useState([]); const active = vm.openTabs.find((t) => t.id === vm.activeTabId) ?? null; + const activeViewerConversation = + viewerConversation?.projectId === active?.id ? viewerConversation : null; const projectNameById = useMemo(() => { const names = new Map(); for (const project of vm.projects) names.set(project.id, project.name); @@ -924,11 +926,11 @@ export function ProjectsView() { projectId={active.id} onActiveLayoutChange={setActiveLayout} /> - {viewerConversation ? ( + {activeViewerConversation ? ( setViewerConversation(null)} onOpenConversation={openConversation} /> @@ -953,14 +955,6 @@ export function ProjectsView() { )} - ) : viewerConversation ? ( - setViewerConversation(null)} - onOpenConversation={openConversation} - /> ) : (
diff --git a/frontend/src/features/web/WebWorkspace.tsx b/frontend/src/features/web/WebWorkspace.tsx index d4b16587..581416d7 100644 --- a/frontend/src/features/web/WebWorkspace.tsx +++ b/frontend/src/features/web/WebWorkspace.tsx @@ -279,9 +279,13 @@ function WebDesktopWorkspace({ useEffect(() => { setActiveLayout(null); - setViewerConversation(null); }, [openId]); + const activeViewerConversation = + viewerConversation?.projectId === openProject?.id + ? viewerConversation + : null; + function setPlacement(panel: WebDesktopPanelId, placement: Exclude) { setPlacements((prev) => { const next: WebDesktopPlacements = { ...prev }; @@ -535,11 +539,11 @@ function WebDesktopWorkspace({ projectId={openProject.id} onActiveLayoutChange={setActiveLayout} /> - {viewerConversation ? ( + {activeViewerConversation ? ( setViewerConversation(null)} onOpenConversation={openConversation} /> From 503f6e5095cb240eb0b0ce63dc4d99be7435a3e5 Mon Sep 17 00:00:00 2001 From: Blomios Date: Wed, 9 Sep 2026 09:55:20 +0200 Subject: [PATCH 2/2] =?UTF-8?q?chore(tickets):=20sync=20store=20local=20?= =?UTF-8?q?=E2=80=94=20#291=20carnet=20complet=20(cadrage=20UX/Architect,?= =?UTF-8?q?=20livraison=20DevFrontend,=20validation=20QA=20verte,=20sectio?= =?UTF-8?q?n=20Git)=20+=20entree=20index/counter;=20#290=20carnet=20comple?= =?UTF-8?q?te=20sections=20Git=20+=20build=20AppImage=20du=20merge=20162de?= =?UTF-8?q?27;=20scripts/=20hors=20perimetre;=20aucun=20push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ideai/tickets/290/carnet.md | 20 +++- .ideai/tickets/290/issue.md | 4 +- .ideai/tickets/291/carnet.md | 180 +++++++++++++++++++++++++++++++++++ .ideai/tickets/291/issue.md | 25 +++++ .ideai/tickets/counter.json | 2 +- .ideai/tickets/index.json | 16 +++- 6 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 .ideai/tickets/291/carnet.md create mode 100644 .ideai/tickets/291/issue.md diff --git a/.ideai/tickets/290/carnet.md b/.ideai/tickets/290/carnet.md index 99a96f8a..4cbd92f5 100644 --- a/.ideai/tickets/290/carnet.md +++ b/.ideai/tickets/290/carnet.md @@ -1,8 +1,8 @@ --- issueRef: "#290" -version: 11 +version: 12 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} -updatedAt: 1788937781234 +updatedAt: 1788938529924 --- # Ticket #290 - Redemarrage agent finalise le tour et bloque le chat @@ -170,3 +170,19 @@ Couverture verifiee: Reserve: - Validation runtime AppImage/live non executee dans cette passe QA; les tests automatises prouvent le contrat backend/transport, pas une relance manuelle de l'AppImage en cours d'utilisation. + +## Git regression partielle +- Commit fix: `18c1b01` sur `fix/290-self-restart-resume-objective`. +- Merge local no-ff vers `develop`: `162de27`. +- Aucun push. +- Branche finale active: `develop`. +- Worktree propre sauf `scripts/restart-idea-over-ssh.sh` non suivi hors perimetre recurrent. +- Git a reconfirme avant commit: `cargo +stable check --workspace`, tests cibles, fmt et diff-check verts. + +## Build AppImage regression partielle +- Skill `build-appimage` applique le 2026-09-09. +- `npm --prefix frontend run build` depuis project root -> vert. +- `CARGO_HOME=/tmp/idea-cargo-home APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` depuis `crates/app-tauri` -> vert. +- Le `beforeBuildCommand` a aussi produit `frontend/dist` et `frontend/dist-web`. +- Artefact produit: `/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`. +- Validation live restante: remplacer l'AppImage installee et relancer IdeA, puis refaire le test utilisateur exact: demander a un agent de redemarrer et de confirmer apres relance; le premier tour post-restart doit reprendre cet objectif exact. \ No newline at end of file diff --git a/.ideai/tickets/290/issue.md b/.ideai/tickets/290/issue.md index 39f2b1fd..41a3d6f5 100644 --- a/.ideai/tickets/290/issue.md +++ b/.ideai/tickets/290/issue.md @@ -11,7 +11,7 @@ attachments: [] createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} createdAt: 1788934173642 -updatedAt: 1788937781234 -version: 11 +updatedAt: 1788938529924 +version: 12 --- Bug rapporte par l'utilisateur le 2026-09-09: apres une demande de redemarrage, l'agent lance le redemarrage puis envoie un message; ce message est considere comme Final, le tour se termine, Stop repond "Request already stopped", et le chat devient bloque en ecriture. Attachement de reproduction: /home/anthony/Documents/Projects/IdeA/.ideai/attachments/agent-chat/7d2abba7-ed93-405f-9fe5-857c8455e436/9a881769-d8e8-410d-85f1-7a3ec2afdde0-clipboard-image-1788934010151.png. Objectif: corriger le flux pour que le redemarrage aboutisse vraiment, que l'utilisateur recupere un chat utilisable, et que le message de succes ne soit pas capture comme final prematurant le rendez-vous. \ No newline at end of file diff --git a/.ideai/tickets/291/carnet.md b/.ideai/tickets/291/carnet.md new file mode 100644 index 00000000..338d2b3a --- /dev/null +++ b/.ideai/tickets/291/carnet.md @@ -0,0 +1,180 @@ +--- +issueRef: "#291" +version: 5 +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedAt: 1788940207349 +--- +# Carnet #291 + +## Signalement utilisateur +Le redémarrage agent est fonctionnel et l'affichage initial après redémarrage est correct. Régression restante: après un changement de projet puis retour sur le projet courant, la conversation s'efface si un redémarrage de l'agent a eu lieu. + +## Attendu produit +La conversation doit rester visible et rechargée comme si aucun redémarrage agent n'avait eu lieu. + +## Reproduction cible +1. Projet A ouvert, conversation agent visible. +2. Redémarrer l'agent. +3. Vérifier que la conversation reste visible. +4. Basculer vers un autre projet. +5. Revenir au projet A. +6. Vérifier que la conversation est toujours présente. + +## Cadrage UX +- Le transcript persisté est source de vérité après changement de projet, redémarrage app ou réhydratation de surface. +- Le live buffer ne complète que la session active; un buffer vide ne doit jamais écraser un transcript persisté non vide. +- Pendant la réhydratation, ne pas afficher prématurément `Aucun message`; garder l'ancien contenu si disponible ou afficher un chargement transitoire. +- En erreur réelle de lecture: afficher `Impossible de recharger la conversation. Réessayer` sans remplacer un contenu ancien disponible. + +## Cadrage architecture +- Frontière probable: frontend project switching / reconnect et reconstruction du viewer, pas persistance backend en première intention. +- Invariant: `conversationId` stable à travers restart agent, changement de projet, reconnect/refresh; contenu lu via `Gateways.conversation.readPage` / `read_conversation_page`. +- Le live-state peut porter un pointeur mais ne doit pas devenir source de vérité du transcript. +- Inspecter notamment `ProjectsView.tsx`, `WebWorkspace.tsx`, `ConversationViewer.tsx`, `useConversationThread.ts`, adapters conversation, source de `conversationId` dans ports/workstate/layout. +- Backend à vérifier seulement si perte de projection `conversationId` après restart/open_project. + +## Critères QA +- Scénario restart agent + changement projet + retour projet: messages pré et post restart visibles, ordre conservé, sans duplication. +- Test automatisé frontend attendu: `conversationId` stable, switch projet puis retour, `ConversationGateway.readPage` relancé avec le même id. +- Test backend de projection workstate seulement si l'inspection révèle une perte côté backend. + +## Orchestration +- Ticket créé par Main le 2026-09-09. +- UX et Architect consultés. +- Prochaine étape: Git pour branche locale, puis DevFrontend en première intention. + +## Livraison DevFrontend — 2026-09-09 + +### Diagnostic +- `ProjectsView` conservait bien `{ projectId, conversationId }`, mais rendait le viewer du projet source même lorsqu'un autre projet devenait actif. Le composant ne se démontait donc pas pendant l'aller-retour et `useConversationThread` ne relançait pas `ConversationGateway.readPage` au retour, puisque ses identifiants n'avaient jamais changé. +- `WebWorkspace` avait l'écart symétrique: il supprimait entièrement le pointeur du viewer à chaque changement de projet, empêchant toute réhydratation au retour. +- Les adapters Tauri et HTTP transmettent déjà correctement le même `projectId` / `conversationId` à `read_conversation_page`; aucune preuve de perte backend du pointeur n'a été trouvée. + +### Correctif +- Le viewer conservé n'est rendu que si son `projectId` correspond au projet actif. Le projet intermédiaire affiche sa propre surface; le retour au projet source remonte le viewer et relance la lecture persistée avec le même `conversationId`. +- Même règle appliquée au shell web; son pointeur n'est plus effacé au switch. +- `useConversationThread` ne vide plus préventivement un buffer existant avant la relecture. Le premier montage affiche toujours `Chargement du fil…`; un contenu déjà disponible reste visible pendant une relecture ou en cas d'échec. +- L'erreur de lecture affiche `Impossible de recharger la conversation.` et l'action `Réessayer`, sans retirer les tours déjà visibles. + +### Fichiers modifiés +- `frontend/src/features/projects/ProjectsView.tsx` +- `frontend/src/features/web/WebWorkspace.tsx` +- `frontend/src/features/conversations/useConversationThread.ts` +- `frontend/src/features/conversations/ConversationViewer.tsx` +- `frontend/src/features/projects/ProjectsView.ls7.test.tsx` +- `frontend/src/features/conversations/ConversationViewer.test.tsx` + +### Preuves exécutées +- `npm --prefix frontend test -- --run src/features/projects/ProjectsView.ls7.test.tsx -t "rehydrates the same conversation"` — OK, 1 test passé (8 ignorés par le filtre). Le test simule le restart, A→B→A, vérifie le contenu et deux appels `readPage` strictement identiques sur `{ projectId, conversationId }`. +- `npm --prefix frontend test -- --run src/features/conversations/ConversationViewer.test.tsx src/features/conversations/useConversationThread.test.tsx` — OK, 2 fichiers / 19 tests passés. +- `npm --prefix frontend run build` — OK (`tsc --noEmit` puis build Vite, 515 modules). +- `git diff --check` — OK. + +### Gaps observés hors lot +- Le fichier complet `ProjectsView.ls7.test.tsx` conserve 4 scénarios historiques KO car leur helper ne parvient plus à ouvrir le panneau Work et ne trouve pas `open conversation …`; le nouveau scénario #291 passe isolément et ouvre le viewer via l'événement de complétion persistant. +- `WebDesktopWorkspace.test.tsx` a 1 test historique KO sur un libellé attendu en anglais (`Project context`) alors que l'UI expose désormais `Contexte projet`; 2 autres tests passent. Aucun de ces échecs ne touche le chemin #291. +- Aucun gap backend détecté. + +## Validation QA indépendante — 2026-09-09 + +### Verdict +VERT pour le périmètre #291 sur `fix/291-keep-conversation-after-restart-project-roundtrip`. + +### Inspection +- Diff vérifié sur `ProjectsView.tsx`: le viewer conserve son pointeur global, mais n'est rendu que lorsque `viewerConversation.projectId === active.id`; le switch vers un autre projet démonte donc la surface et le retour remonte le viewer avec la même clé `{projectId, conversationId}`. +- Diff vérifié sur `WebWorkspace.tsx`: même règle côté web, sans remise à `null` du pointeur au changement de projet. +- Diff vérifié sur `useConversationThread.ts`: suppression du `setBuffer([])` au changement/rechargement; le buffer existant n'est plus vidé pendant une relecture. +- Diff vérifié sur `ConversationViewer.tsx`: l'erreur de lecture expose un état de retry sans remplacer explicitement un contenu déjà présent. +- Test #291 ajouté dans `ProjectsView.ls7.test.tsx`: simule restart agent, switch alpha -> beta -> alpha, vérifie le contenu et deux appels `readPage` identiques sur `{ projectId, conversationId }`. + +### Commandes exécutées + +```bash +git status --short --branch +``` + +```text +## fix/291-keep-conversation-after-restart-project-roundtrip + M .ideai/tickets/290/carnet.md + M .ideai/tickets/290/issue.md + M .ideai/tickets/counter.json + M .ideai/tickets/index.json + M frontend/src/features/conversations/ConversationViewer.test.tsx + M frontend/src/features/conversations/ConversationViewer.tsx + M frontend/src/features/conversations/useConversationThread.ts + M frontend/src/features/projects/ProjectsView.ls7.test.tsx + M frontend/src/features/projects/ProjectsView.tsx + M frontend/src/features/web/WebWorkspace.tsx +?? .ideai/tickets/291/ +?? scripts/ +``` + +```bash +npm --prefix frontend test -- --run src/features/projects/ProjectsView.ls7.test.tsx -t "rehydrates the same conversation" +``` + +```text +Test Files 1 passed (1) +Tests 1 passed | 8 skipped (9) +Duration 2.38s +``` + +```bash +npm --prefix frontend test -- --run src/features/conversations/ConversationViewer.test.tsx src/features/conversations/useConversationThread.test.tsx +``` + +```text +Test Files 2 passed (2) +Tests 19 passed (19) +Duration 1.34s +``` + +```bash +npm --prefix frontend run build +``` + +```text +> idea-frontend@0.3.0 build +> tsc --noEmit && vite build +✓ 515 modules transformed. +✓ built in 2.60s +``` + +```bash +git diff --check +``` + +```text +OK (aucune sortie) +``` + +### Test additionnel + +```bash +npm --prefix frontend test -- --run src/features/projects/ProjectsView.ls7.test.tsx +``` + +```text +Test Files 1 failed (1) +Tests 3 failed | 6 passed (9) +``` + +Tests rouges observés dans ce fichier complet: +- `opens the viewer on a conversation click and returns to the grid on back` +- `keeps the viewer open when the active layout changes` +- `does not close an open viewer when a background-task toast has no conversation` + +Ces trois échecs cherchent encore `open conversation ...` via l'ancien helper/panneau Work et correspondent aux rouges historiques déjà mentionnés hors lot. Le nouveau scénario #291 `rehydrates the same conversation after an agent restart and a project roundtrip` passe dans ce run complet. + +### Risques résiduels +- Validation effectuée par tests frontend et build local, pas par AppImage/runtime desktop réel. +- Le worktree contient des modifications non liées à #291 (`.ideai/tickets/290/*`, index/counter tickets, `scripts/`) que QA n'a pas modifiées ni qualifiées. + +## Git — 2026-09-09 +- Branche: `fix/291-keep-conversation-after-restart-project-roundtrip` créée depuis `develop`. +- Commit fix: `2c72205` (6 fichiers frontend uniquement). +- QA reconfirmée par Git avant commit: ConversationViewer + useConversationThread 19/19, scénario #291 `rehydrates the same conversation` isolé vert, `git diff --check` OK. +- Store de tickets synchronisé dans un `chore(tickets)` séparé (inclut carnet #290 v12 sections Git + build AppImage du merge `162de27`). +- Merge local no-ff vers `develop`, aucun push. +- Restants: rebuild AppImage + validation live du scénario exact (restart agent puis aller-retour projet). +- `scripts/` non suivi maintenu hors périmètre. diff --git a/.ideai/tickets/291/issue.md b/.ideai/tickets/291/issue.md new file mode 100644 index 00000000..d80e48ed --- /dev/null +++ b/.ideai/tickets/291/issue.md @@ -0,0 +1,25 @@ +--- +id: "729aaeb8-0601-4ad0-aed4-4ca625856c62" +number: 291 +title: "Conserver la conversation après redémarrage agent et aller-retour projet" +status: "qa" +priority: "high" +sprint: null +links: [] +agentRefs: [] +attachments: [] +createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +createdAt: 1788939576761 +updatedAt: 1788940207349 +version: 5 +--- +Bug utilisateur: le redémarrage de l'agent fonctionne et l'affichage initial est correct, mais si l'utilisateur change de projet puis revient sur le projet courant après un redémarrage, la conversation affichée s'efface. Attendu: la conversation reste en place comme s'il n'y avait jamais eu de redémarrage de l'agent. + +Scénario de reproduction attendu: +1. Ouvrir un projet avec une conversation agent visible. +2. Redémarrer l'agent. +3. Constater que la conversation reste affichée immédiatement après le redémarrage. +4. Changer de projet. +5. Revenir sur le projet initial. +6. La conversation doit toujours être présente et rechargée depuis la source persistée, sans écran vide lié au redémarrage. \ No newline at end of file diff --git a/.ideai/tickets/counter.json b/.ideai/tickets/counter.json index 522a94cb..fd4fdce1 100644 --- a/.ideai/tickets/counter.json +++ b/.ideai/tickets/counter.json @@ -1,3 +1,3 @@ { - "nextNumber": 291 + "nextNumber": 292 } \ No newline at end of file diff --git a/.ideai/tickets/index.json b/.ideai/tickets/index.json index 60b62d69..fbacb38c 100644 --- a/.ideai/tickets/index.json +++ b/.ideai/tickets/index.json @@ -3978,7 +3978,21 @@ "kind": "agent", "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, - "updatedAt": 1788937781234 + "updatedAt": 1788938529924 + }, + { + "issueRef": "#291", + "path": "291", + "title": "Conserver la conversation après redémarrage agent et aller-retour projet", + "status": "qa", + "priority": "high", + "sprint": null, + "assignedAgentIds": [], + "createdBy": { + "kind": "agent", + "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" + }, + "updatedAt": 1788940207349 } ] } \ No newline at end of file