From 33e65dec743af8fd88473ffe129bd23e1ab2fe71 Mon Sep 17 00:00:00 2001 From: Blomios Date: Sat, 20 Jun 2026 19:54:45 +0200 Subject: [PATCH] =?UTF-8?q?chore(wip):=20=C3=A9tat=20runtime=20.ideai=20+?= =?UTF-8?q?=20m=C3=A9moire=20(checkpoint=20Lot=20D=20actions=20contr=C3=B4?= =?UTF-8?q?l=C3=A9es)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Conversations live, MEMORY.md et note checkpoint workstate-controlled-actions-lot-d. Co-Authored-By: Claude Opus 4.8 --- .../handoff.md | 8 +-- .../log.jsonl | 2 + .../handoff.md | 8 +-- .../log.jsonl | 2 + .ideai/memory/MEMORY.md | 1 + ...oint-workstate-controlled-actions-lot-d.md | 64 +++++++++++++++++++ 6 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 .ideai/memory/checkpoint-workstate-controlled-actions-lot-d.md diff --git a/.ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/handoff.md b/.ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/handoff.md index 9acb15b..5b8684e 100644 --- a/.ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/handoff.md +++ b/.ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/handoff.md @@ -1,11 +1,9 @@ --- -upTo: e43c2b68-a855-4433-8067-64f6fc4fa59c +upTo: 68b5b6c0-9d14-44f9-bcc6-e745f018662d objective: Nouvelle feature cadrée par Architect, prête à démarrer le développement : « Gestion des limites de session des agents » (détection hiérarchique des limites de session + reprise auto annulable). C'est --- **Objectif :** Nouvelle feature cadrée par Architect, prête à démarrer le développement : « Gestion des limites de session des agents » (détection hiérarchique des limites de session + reprise auto annulable). C'est -- **Response:** Décision Git : **MERGE effectué** (local, sans push). Feature terminée et QA verte (réserve env. acceptable : seuls les tests loopback socket Unix non exécutables en sandbox, alternatives skips vertes) → rien ne justifiait de différer. ## Ce qui a été fait 1. Pendant l'opération, des écritures runtime live (`.ideai/conversations/6bc594e8…`) ont resurgi et bloquaient le checkout. Je les ai committées proprement sur la feature : - `8074aec` — chore(wip): état runtime .ideai (flux conversation live) 2. `git checkout develop` puis `git merge --no-ff feature/agent-skill-awareness-v2`. - Pas de rebase nécessaire : `develop` était ancêtre direct, historique non divergent. - Merge commit : **`63eb49a`** — merge(skills): intègre agent-skill-awareness-v2 dans develop. ## Historique résultant (develop) ``` * 63eb49a merge(skills): intègre agent-skill-awareness-v2 dans develop |\ | * 8074aec chore(wip): état runtime .ideai (flux conversation live) | * cc575ef chore(wip): état runtime .ideai (…checkpoint skill-awareness) | * 018eb1a fix(input): fiabilise la livraison de délégation et journalise le submit | * befff76 feat(skills): injecte un paragraphe d'awareness skills dans le fichier de convention |/ * e832af5 (base commune) ``` Merge sans conflit (stratégie ort), 22 fichiers / +575 -95. ## État final - Branche courante : **`develop`**, tip = `63eb49a`. - `develop` : **devant `origin/develop` de 15 commits** (local uniquement, aucun push — conforme). - Working tree propre au moment du merge (le flux runtime live peut le re-salir à tout instant, c'est normal). - Branche `feature/agent-skill-awareness-v2` **conservée** (entièrement fusionnée). Je ne la supprime pas : tu as parlé de « reprise de chantier », donc je la garde disponible. Dis-moi si tu veux que je la supprime maintenant qu'elle est intégrée. Aucune action sortante effectuée. Pour publier (`push origin develop`), il me faut ta validation explicite. -- **Prompt:** Inventaire des chantiers en cours après merge local `feature/agent-skill-awareness-v2` dans `develop` (`63eb49a`). Branches visibles: locales `feature/agent-skill-awareness`, `feature/agent-skills`, `fix/cold-start-delivery-race`, `feature/agent-skill-awareness-v2`; distante `origin/feature/agent-session-limits`; develop ahead origin/develop de 15; main ahead origin/main de 4. Merci de déterminer pour chaque branche si elle est déjà intégrée, superseded, encore active ou à conserver, sans suppression pour l'instant. Identifie les chantiers applicatifs réellement en cours/restants et la stratégie repo avant de reprendre le prochain. Ne committe/merge/supprime rien dans cette étape. - **Response:** Inventaire repo après merge `agent-skill-awareness-v2` → `develop` (`63eb49a`). **Aucune suppression / commit / merge fait** (lecture seule). Méthode : `git cherry` (patch-id) **+** vérification du contenu réel dans `develop`, car les branches sont anciennes et leurs patches ont été re-landés différemment (les patch-ids diffèrent même quand le contenu est présent). ## Statut par branche **`feature/agent-skill-awareness-v2`** — ✅ INTÉGRÉE (mergée à l'instant, `63eb49a`). Conserver tant que reprise possible, sinon supprimable. **`feature/agent-skill-awareness` (v1)** — 🟠 SUPERSEDED par v2. Ses commits uniques portent une **autre approche** : outil MCP `idea_skill_read` + manifeste de skills + brief « capacités IdeA » inconditionnel (`ab34363`, `566bff4`, `1a10d67`). Vérif : `idea_skill_read` **absent de develop** → v2 a délibérément retenu une autre voie (injection des corps de skills dans le fichier de convention + paragraphe d'awareness via `lifecycle.rs`). Son fix cold-start (`e93a2c1`) est déjà couvert par develop. ⇒ rien à récupérer sauf si tu veux ressusciter l'approche `idea_skill_read` (non recommandé, v2 a tranché). **À archiver/supprimer.** **`feature/agent-skills` (L12, `ef101db`)** — 🟢 INTÉGRÉE PAR ÉQUIVALENT. Le domaine skills est présent dans develop : `crates/domain/src/skill.rs`, `crates/application/src/skill/{mod,usecases}.rs`, `crates/infrastructure/src/store/skill.rs` (+ tests). Re-landé via un autre chemin. **Superseded en tant que branche.** **`fix/cold-start-delivery-race` (`9590eac`)** — 🟢 INTÉGRÉE PAR ÉQUIVALENT. La logique cold-start (`release_cold_start`, gate armé) est dans develop : `crates/infrastructure/src/input/mod.rs:341,895`. La branche traîne en plus de vieux `release(0.1.0)/(0.2.0)` → elle a divergé d'un vieux `main`. **Stale, supprimable.** **`origin/feature/agent-session-limits` (`3f3504e`)** — 🟢 MERGÉE (ancêtre de develop). Branche distante stale ; le feature session-limits est dans develop. **`origin/wip/p8c-checkpoint-before-codex`** — branche distante WIP fondations enforcement OS (permissions), non rattachée localement ; à clarifier si encore vivante (probablement absorbée par le chantier permissions/sandbox déjà livré en code). ## État publication (local-only, conforme « pas de push ») - `develop` : **ahead origin/develop de 15** (contient tout ce qui précède). - `main` : **ahead origin/main de 4** = `release(0.3.0)` mergée localement, jamais poussée. origin/main = encore 0.2.0. ## Chantiers applicatifs réellement restants (vs branches mortes) Les 4 branches non-mergées ne portent **aucun chantier vivant** : tout est soit intégré, soit superseded. Le travail restant est ailleurs (cf. mémoire projet, pas dans des branches) : 1. **Validation e2e réelle sur AppImage** — nombreux items « ✓ RÉSOLU EN CODE … valid. e2e restante » (socket MCP, MCP inter-agents au restart, MCP natif en structuré, modèle terminal natif). Bloqueur connu : `checkpoint-blocked-until-appimage-030` / blocage build AppImage 0.3.0. 2. **Enforcement permissions/sandbox** : résiduel `$HOME`/résume du chemin structuré (memory `permissions-sandbox-system-state`). 3. Optionnels persistance : P9 FileGuard, P10 résumé LLM. ## Stratégie repo recommandée avant le prochain chantier - **Nettoyage différé** (sur ton GO, étape suivante) : supprimer en local `feature/agent-skill-awareness`, `feature/agent-skills`, `fix/cold-start-delivery-race` (toutes superseded/intégrées) ; idem branches distantes stale `origin/feature/agent-session-limits` (suppression distante = action sortante → validation explicite requise). - **Prochaine branche** : repartir **de `develop`** (`63eb49a`) avec un `feature/` neuf pour le prochain chantier (probablement la **validation e2e AppImage 0.3.0**, qui est le vrai bloqueur courant). - **Push** : `develop` (+15) et `main` (+4 / release 0.3.0) attendent une validation explicite avant publication — je ne pousse rien sans ton accord. Dis-moi quel chantier on reprend : je crée la branche adéquate depuis `develop`. - **Prompt:** Démarrage du prochain chantier applicatif recommandé par Architect: `UX conversations/délégations + read-model live-state minimal`. Branche courante attendue `develop` à `63eb49a`, mais des fichiers runtime `.ideai/conversations/6bc594e8...` peuvent être dirty à cause du live. Merci de décider et exécuter la gestion repo locale avant implémentation: créer/basculer sur une branche de feature depuis develop, gérer proprement le dirty runtime sans perdre de données, et retourner branche courante + état. Ne supprime pas les branches obsolètes pour l'instant, ne push pas. - **Response:** Gestion repo locale faite pour démarrer le chantier « UX conversations/délégations + read-model live-state minimal ». ## Décisions & exécution 1. **Dirty runtime géré sans perte** : 4 fichiers `.ideai/conversations/*` (handoff + log.jsonl, flux live append-only) étaient modifiés sur `develop`. Je les ai **committés sur develop** (leur place naturelle, develop accumule déjà l'état runtime) plutôt que de les stash/jeter → zéro perte. - `338051e` — chore(wip): état runtime .ideai (flux conversation live) 2. **Nouvelle branche de feature créée depuis un `develop` propre** : - `git checkout -b feature/conversation-ux-live-state` (depuis `develop` @ `338051e`). - Nom dérivé du sujet Architect (kebab-case). ## État final - **Branche courante : `feature/conversation-ux-live-state`** (tip `338051e`). - Base : `develop` (`338051e` = `63eb49a` + le chore runtime), historique linéaire, prête pour l'implémentation. - Working tree : propre au moment de la bascule (le flux live peut le re-salir, c'est attendu ; je re-figerai au moment de committer le code de la feature en séparant code / runtime). - `develop` désormais ahead origin/develop de **16** (local only). Aucune branche obsolète supprimée. **Aucun push.** DevBackend/DevFrontend peuvent implémenter sur `feature/conversation-ux-live-state`. Reviens vers moi à l'implémentation terminée (tests verts) pour les commits atomiques et la décision de merge. @@ -23,4 +21,6 @@ objective: Nouvelle feature cadrée par Architect, prête à démarrer le dével - **Response:** Lot C `workstate conversation summaries` intégré. QA re-vérifié vert de mon côté avant tout commit (fmt OK ; application workstate 21, app-tauri dto_agents 21, aucun warning Rust ; vitest 2 fichiers/19 ; tsc clean). **3 commits atomiques** sur `feature/workstate-conversation-summaries` (code feature / runtime séparés) : - `e9edadc` feat(workstate): résumés de conversation dans le read-model (Lot C backend) — assemblage des résumés dans application + DTO camelCase et exposition app-tauri (dto.rs, state.rs). - `c50622e` feat(workstate): UI des résumés de conversation (Lot C frontend) — domaine/mock alignés, `ProjectWorkStatePanel` enrichi. - `78500d8` chore(wip): état runtime .ideai (flux conversation live) — runtime isolé, jamais mêlé au code feature. **Merge local** : feature TERMINÉE et VERTE → `git merge --no-ff feature/workstate-conversation-summaries` dans **develop** → commit de merge `6e1ba7e`. `--no-ff` pour garder la trace d'intégration du lot, cohérent avec Lot A (`3bfb932`) et Lot B (`64c2c14`). **Pas de push, aucune branche supprimée.** `feature/workstate-conversation-summaries` conservée (sur `e9edadc→78500d8`). **État final** : - Branche courante : `develop` (HEAD = `6e1ba7e`). - Working tree : propre (`git status` vide). - `feature/workstate-conversation-summaries` préservée et entièrement contenue dans `develop`. - `develop` en avance sur origin (rien poussé). - Trilogie workstate Lot A→B→C désormais intégrée dans `develop` ; prêt pour une future release `develop → main` sur ta validation explicite. - **Prompt:** Prépare le dépôt pour le Lot D `workstate controlled actions` après Lot C mergé dans `develop` @ `6e1ba7e`. État probable: dirty runtime/memory lié au checkpoint Lot C (`.ideai/*`, nouvelle note mémoire). Merci de gérer ce dirty sans perte et sans le mélanger au futur code applicatif, puis créer/basculer sur une branche feature propre depuis `develop`. Nom suggéré: `feature/workstate-controlled-actions`. Contraintes: pas de push, pas de suppression de branche. Retourne commits éventuels, branche courante, status final. - **Response:** Dépôt prêt pour le Lot D `workstate controlled actions`. **Dirty traité** : tout le dirty était runtime/mémoire (aucun code applicatif), commité tel quel sur `develop` : - `7c71544` chore(wip): état runtime .ideai + mémoire (checkpoint Lot C résumés conversation) — conversations live, layouts, `MEMORY.md` et nouvelle note `checkpoint-workstate-conversation-summaries-lot-c.md` (auparavant non suivie, désormais versionnée). Cohérent avec l'historique runtime. **Branche feature** : créée et basculée depuis `develop` (@ `7c71544`) : - `feature/workstate-controlled-actions` **Pas de push, aucune branche supprimée.** **État final** : - Branche courante : `feature/workstate-controlled-actions` (HEAD = `7c71544`, partagé avec `develop`). - Working tree : propre (`git status` vide). - `develop` en avance sur origin (checkpoint local, non poussé). Le dev Lot D peut démarrer. Reviens vers moi pour committer/merger une fois QA vert. -- **Prompt:** Lot D `workstate controlled actions` est validé QA vert sur `feature/workstate-controlled-actions`. Merci de gérer les commits atomiques et la décision de merge local, sans push. QA vert: - `cargo fmt --all -- --check` OK - `cargo test -p application --test workstate_actions` OK, 7 passed - `cargo test -p application --test workstate` OK, 21 passed - `cargo test -p app-tauri --test dto_agents` OK, 25 passed - `cargo test -p app-tauri --test list_live_agents_r0b` OK, 5 passed - `cargo check -p app-tauri` OK - `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx` OK, 25 tests - `cd frontend && npx vitest run src/adapters/agent.test.ts` OK, 8 tests - `cd frontend && npx vitest run src/features/layout/singletonAgent.test.tsx src/features/layout/agentAlreadyRunning.test.tsx` OK, 9 tests - `cd frontend && npx tsc --noEmit` OK Fichiers attendus: - Backend/Tauri: `crates/application/src/workstate/actions.rs`, `crates/application/tests/workstate_actions.rs`, `crates/application/src/workstate/mod.rs`, `crates/application/src/lib.rs`, `crates/app-tauri/src/{dto.rs,commands.rs,state.rs,lib.rs}`, `crates/app-tauri/tests/dto_agents.rs`. - Frontend: `frontend/src/ports/index.ts`, `frontend/src/adapters/{agent.ts,agent.test.ts}`, `frontend/src/adapters/mock/index.ts`, `frontend/src/features/workstate/{ProjectWorkStatePanel.tsx,workstate.test.tsx}`, `frontend/src/features/layout/{singletonAgent.test.tsx,agentAlreadyRunning.test.tsx}`. - Dirty runtime `.ideai/*` possible; isole-le si présent. Décide commits atomiques, merge local éventuel vers `develop`, état final. Ne push pas, ne supprime pas de branche. \ No newline at end of file +- **Prompt:** Lot D `workstate controlled actions` est validé QA vert sur `feature/workstate-controlled-actions`. Merci de gérer les commits atomiques et la décision de merge local, sans push. QA vert: - `cargo fmt --all -- --check` OK - `cargo test -p application --test workstate_actions` OK, 7 passed - `cargo test -p application --test workstate` OK, 21 passed - `cargo test -p app-tauri --test dto_agents` OK, 25 passed - `cargo test -p app-tauri --test list_live_agents_r0b` OK, 5 passed - `cargo check -p app-tauri` OK - `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx` OK, 25 tests - `cd frontend && npx vitest run src/adapters/agent.test.ts` OK, 8 tests - `cd frontend && npx vitest run src/features/layout/singletonAgent.test.tsx src/features/layout/agentAlreadyRunning.test.tsx` OK, 9 tests - `cd frontend && npx tsc --noEmit` OK Fichiers attendus: - Backend/Tauri: `crates/application/src/workstate/actions.rs`, `crates/application/tests/workstate_actions.rs`, `crates/application/src/workstate/mod.rs`, `crates/application/src/lib.rs`, `crates/app-tauri/src/{dto.rs,commands.rs,state.rs,lib.rs}`, `crates/app-tauri/tests/dto_agents.rs`. - Frontend: `frontend/src/ports/index.ts`, `frontend/src/adapters/{agent.ts,agent.test.ts}`, `frontend/src/adapters/mock/index.ts`, `frontend/src/features/workstate/{ProjectWorkStatePanel.tsx,workstate.test.tsx}`, `frontend/src/features/layout/{singletonAgent.test.tsx,agentAlreadyRunning.test.tsx}`. - Dirty runtime `.ideai/*` possible; isole-le si présent. Décide commits atomiques, merge local éventuel vers `develop`, état final. Ne push pas, ne supprime pas de branche. +- **Response:** Lot D `workstate controlled actions` intégré. QA re-vérifié vert de mon côté avant tout commit (fmt OK ; application workstate_actions 7, workstate 21, app-tauri dto_agents 25, list_live_agents_r0b 5 ; frontend 5 fichiers/42 — workstate+projects 25, agent 8, singletonAgent+agentAlreadyRunning 9 ; tsc clean). **3 commits atomiques** sur `feature/workstate-controlled-actions` (code feature / runtime séparés) : - `3408c96` feat(workstate): actions contrôlées sur le work-state (Lot D backend) — nouveau module `workstate/actions.rs` + test `workstate_actions.rs`, intégration au read-model, commandes Tauri (commands/dto/state/lib). - `1c6441b` feat(workstate): UI des actions contrôlées (Lot D frontend) — port + adaptateur agent étendus, mock aligné, `ProjectWorkStatePanel` câblé. - `0976648` chore(wip): état runtime .ideai (flux conversation live) — runtime isolé. **Merge local** : feature TERMINÉE et VERTE → `git merge --no-ff feature/workstate-controlled-actions` dans **develop** → commit de merge `c1e99d1`. `--no-ff` cohérent avec Lots A (`3bfb932`), B (`64c2c14`), C (`6e1ba7e`). **Pas de push, aucune branche supprimée.** `feature/workstate-controlled-actions` conservée (sur `3408c96→0976648`). **État final** : - Branche courante : `develop` (HEAD = `c1e99d1`). - Working tree : propre (`git status` vide). - `feature/workstate-controlled-actions` préservée et entièrement contenue dans `develop`. - `develop` en avance sur origin (rien poussé). - Quadrilogie workstate Lot A→B→C→D intégrée dans `develop` ; prêt pour une future release `develop → main` sur ta validation explicite. +- **Prompt:** Prépare le dépôt pour le Lot E1 `controlled auto-memory harvest` après Lot D mergé dans `develop` @ `c1e99d1`. État probable: dirty runtime/memory lié au checkpoint Lot D (`.ideai/*`, nouvelle note mémoire). Merci de gérer ce dirty sans perte et sans le mélanger au futur code applicatif, puis créer/basculer sur une branche feature propre depuis `develop`. Nom suggéré: `feature/controlled-auto-memory-harvest`. Contraintes: pas de push, pas de suppression de branche. Retourne commits éventuels, branche courante, status final. \ No newline at end of file diff --git a/.ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/log.jsonl b/.ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/log.jsonl index a31620e..321d324 100644 --- a/.ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/log.jsonl +++ b/.ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/log.jsonl @@ -57,3 +57,5 @@ {"id":"c10a7683-5468-472c-adbb-37fb6fe8c470","conversation":"6bc594e8-a37c-0dbd-1de6-6e3b73002cb4","atMs":1781976728705,"source":{"kind":"agent","agentId":"a6ced819-b893-4213-b003-9e9dc79b9641"},"role":"prompt","text":"Prépare le dépôt pour le Lot D `workstate controlled actions` après Lot C mergé dans `develop` @ `6e1ba7e`.\n\nÉtat probable: dirty runtime/memory lié au checkpoint Lot C (`.ideai/*`, nouvelle note mémoire). Merci de gérer ce dirty sans perte et sans le mélanger au futur code applicatif, puis créer/basculer sur une branche feature propre depuis `develop`.\n\nNom suggéré: `feature/workstate-controlled-actions`.\n\nContraintes: pas de push, pas de suppression de branche. Retourne commits éventuels, branche courante, status final."} {"id":"c5df55db-1dc4-465e-bb3a-512806489e73","conversation":"6bc594e8-a37c-0dbd-1de6-6e3b73002cb4","atMs":1781976745549,"source":{"kind":"agent","agentId":"cd0b4cf1-1bef-4fae-ade5-f0a6b49bbaf5"},"role":"response","text":"Dépôt prêt pour le Lot D `workstate controlled actions`.\n\n**Dirty traité** : tout le dirty était runtime/mémoire (aucun code applicatif), commité tel quel sur `develop` :\n- `7c71544` chore(wip): état runtime .ideai + mémoire (checkpoint Lot C résumés conversation) — conversations live, layouts, `MEMORY.md` et nouvelle note `checkpoint-workstate-conversation-summaries-lot-c.md` (auparavant non suivie, désormais versionnée). Cohérent avec l'historique runtime.\n\n**Branche feature** : créée et basculée depuis `develop` (@ `7c71544`) :\n- `feature/workstate-controlled-actions`\n\n**Pas de push, aucune branche supprimée.**\n\n**État final** :\n- Branche courante : `feature/workstate-controlled-actions` (HEAD = `7c71544`, partagé avec `develop`).\n- Working tree : propre (`git status` vide).\n- `develop` en avance sur origin (checkpoint local, non poussé).\n\nLe dev Lot D peut démarrer. Reviens vers moi pour committer/merger une fois QA vert."} {"id":"e43c2b68-a855-4433-8067-64f6fc4fa59c","conversation":"6bc594e8-a37c-0dbd-1de6-6e3b73002cb4","atMs":1781977881625,"source":{"kind":"agent","agentId":"a6ced819-b893-4213-b003-9e9dc79b9641"},"role":"prompt","text":"Lot D `workstate controlled actions` est validé QA vert sur `feature/workstate-controlled-actions`. Merci de gérer les commits atomiques et la décision de merge local, sans push.\n\nQA vert:\n- `cargo fmt --all -- --check` OK\n- `cargo test -p application --test workstate_actions` OK, 7 passed\n- `cargo test -p application --test workstate` OK, 21 passed\n- `cargo test -p app-tauri --test dto_agents` OK, 25 passed\n- `cargo test -p app-tauri --test list_live_agents_r0b` OK, 5 passed\n- `cargo check -p app-tauri` OK\n- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx` OK, 25 tests\n- `cd frontend && npx vitest run src/adapters/agent.test.ts` OK, 8 tests\n- `cd frontend && npx vitest run src/features/layout/singletonAgent.test.tsx src/features/layout/agentAlreadyRunning.test.tsx` OK, 9 tests\n- `cd frontend && npx tsc --noEmit` OK\n\nFichiers attendus:\n- Backend/Tauri: `crates/application/src/workstate/actions.rs`, `crates/application/tests/workstate_actions.rs`, `crates/application/src/workstate/mod.rs`, `crates/application/src/lib.rs`, `crates/app-tauri/src/{dto.rs,commands.rs,state.rs,lib.rs}`, `crates/app-tauri/tests/dto_agents.rs`.\n- Frontend: `frontend/src/ports/index.ts`, `frontend/src/adapters/{agent.ts,agent.test.ts}`, `frontend/src/adapters/mock/index.ts`, `frontend/src/features/workstate/{ProjectWorkStatePanel.tsx,workstate.test.tsx}`, `frontend/src/features/layout/{singletonAgent.test.tsx,agentAlreadyRunning.test.tsx}`.\n- Dirty runtime `.ideai/*` possible; isole-le si présent.\n\nDécide commits atomiques, merge local éventuel vers `develop`, état final. Ne push pas, ne supprime pas de branche."} +{"id":"07175815-62fd-4f58-9d20-1db29f66da1e","conversation":"6bc594e8-a37c-0dbd-1de6-6e3b73002cb4","atMs":1781977932121,"source":{"kind":"agent","agentId":"cd0b4cf1-1bef-4fae-ade5-f0a6b49bbaf5"},"role":"response","text":"Lot D `workstate controlled actions` intégré. QA re-vérifié vert de mon côté avant tout commit (fmt OK ; application workstate_actions 7, workstate 21, app-tauri dto_agents 25, list_live_agents_r0b 5 ; frontend 5 fichiers/42 — workstate+projects 25, agent 8, singletonAgent+agentAlreadyRunning 9 ; tsc clean).\n\n**3 commits atomiques** sur `feature/workstate-controlled-actions` (code feature / runtime séparés) :\n- `3408c96` feat(workstate): actions contrôlées sur le work-state (Lot D backend) — nouveau module `workstate/actions.rs` + test `workstate_actions.rs`, intégration au read-model, commandes Tauri (commands/dto/state/lib).\n- `1c6441b` feat(workstate): UI des actions contrôlées (Lot D frontend) — port + adaptateur agent étendus, mock aligné, `ProjectWorkStatePanel` câblé.\n- `0976648` chore(wip): état runtime .ideai (flux conversation live) — runtime isolé.\n\n**Merge local** : feature TERMINÉE et VERTE → `git merge --no-ff feature/workstate-controlled-actions` dans **develop** → commit de merge `c1e99d1`. `--no-ff` cohérent avec Lots A (`3bfb932`), B (`64c2c14`), C (`6e1ba7e`).\n\n**Pas de push, aucune branche supprimée.** `feature/workstate-controlled-actions` conservée (sur `3408c96→0976648`).\n\n**État final** :\n- Branche courante : `develop` (HEAD = `c1e99d1`).\n- Working tree : propre (`git status` vide).\n- `feature/workstate-controlled-actions` préservée et entièrement contenue dans `develop`.\n- `develop` en avance sur origin (rien poussé).\n- Quadrilogie workstate Lot A→B→C→D intégrée dans `develop` ; prêt pour une future release `develop → main` sur ta validation explicite."} +{"id":"68b5b6c0-9d14-44f9-bcc6-e745f018662d","conversation":"6bc594e8-a37c-0dbd-1de6-6e3b73002cb4","atMs":1781978078220,"source":{"kind":"agent","agentId":"a6ced819-b893-4213-b003-9e9dc79b9641"},"role":"prompt","text":"Prépare le dépôt pour le Lot E1 `controlled auto-memory harvest` après Lot D mergé dans `develop` @ `c1e99d1`.\n\nÉtat probable: dirty runtime/memory lié au checkpoint Lot D (`.ideai/*`, nouvelle note mémoire). Merci de gérer ce dirty sans perte et sans le mélanger au futur code applicatif, puis créer/basculer sur une branche feature propre depuis `develop`.\n\nNom suggéré: `feature/controlled-auto-memory-harvest`.\n\nContraintes: pas de push, pas de suppression de branche. Retourne commits éventuels, branche courante, status final."} diff --git a/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/handoff.md b/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/handoff.md index c9262b0..20de29c 100644 --- a/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/handoff.md +++ b/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/handoff.md @@ -1,11 +1,9 @@ --- -upTo: e1535768-8ce1-41b7-bc7e-e549fe28ea00 +upTo: 8f958013-ea37-49e1-967f-aa3cdab42835 objective: CADRAGE ARCHITECTURE (pas de code) — Support de la délégation inter-agents (idea_ask_agent/idea_reply) pour les profils CODEX. ## Contexte / gap déjà localisé Le round-trip inter-agent fonctionne pour --- **Objectif :** CADRAGE ARCHITECTURE (pas de code) — Support de la délégation inter-agents (idea_ask_agent/idea_reply) pour les profils CODEX. ## Contexte / gap déjà localisé Le round-trip inter-agent fonctionne pour -- **Prompt:** Diagnostic architecture demandé par Main. Contexte: IdeA intègre Codex CLI. Le profil Codex actuel utilise McpConfigStrategy::TomlConfigHome { target: ".codex/config.toml", home_env: "CODEX_HOME" }, donc au lancement IdeA écrit {runDir}/.codex/config.toml et pousse CODEX_HOME vers ce dossier pour isoler MCP/permissions par agent. Problème produit: chaque agent Codex redemande une connexion ChatGPT/OpenAI, et si l’utilisateur crée beaucoup d’agents il atteint une limite de requests/login. De plus un agent jamais lancé peut échouer lors d’une délégation car il n’est pas authentifié. Questions: 1) quelle solution respecte l’architecture hexagonale/SOLID pour partager l’auth Codex au niveau ordinateur/utilisateur tout en gardant la config MCP/permissions isolée par agent ? 2) faut-il introduire un port Auth/RuntimeHealth/Readiness pour préflight au moment de création/configuration d’agent ? 3) quels changements concrets recommandes-tu dans le domaine/application/infrastructure ? Merci de répondre en français, concis mais actionnable. -- **Prompt:** Cadrage architecture obligatoire avant implémentation. Contexte produit : nous voulons ajouter à IdeA une gestion automatique de la croissance des conversations d'agents afin de réduire les coûts tokens. Le système existant a déjà : log canonique `.ideai/conversations/.../log.jsonl`, handoff incrémental `handoff.md`, injection du handoff au lancement/relaunch, conversation_id logique de paire IdeA, provider session store, reprise session provider quand possible. Besoin : mettre en place une compression/rotation automatique des conversations d'agents, mais avec stabilité maximale. Contraintes fortes validées avec l'utilisateur : - Ne jamais couper un agent au milieu du développement d'une feature. - Ne jamais interrompre un tour en cours, outil en cours, terminal occupé, délégation pendante, ou attente de `idea_reply`. - Ne pas casser les contacts inter-agents : les messages doivent cibler l'identité logique AgentId/ConversationId, pas une session physique instable. - Si un autre agent essaie de contacter un agent en rotation, le routage doit être stable : queue, attente ou ancienne session jusqu'à bascule sûre. - La rotation doit être opportuniste, non interruptive : au seuil normal, préparer/planifier au prochain point sûr ; seuil critique = signaler/attendre point sûr, pas tuer au milieu. - Attention au detach/attach de l'agent sur la cellule : la cellule est une vue, pas la session. - Ne jamais faire de rotation automatique si l'utilisateur est en train d'écrire dans la cellule : focus, buffer d'entrée non envoyé, frappe récente, IME/composition, prompt interactif si détectable. - Nouvelle session idéalement démarrée détachée/background, vérifiée prête, puis bascule atomique du routage + attachement cellule. - Checkpoint durable obligatoire avant bascule : log canonique append OK + handoff à jour. Demande : 1. Lire l'architecture et le code existant autour agent lifecycle, terminal sessions, orchestrator routing, conversation log/handoff, provider sessions, layout/cell attach. 2. Proposer un design hexagonal/SOLID précis : domaine, ports éventuels, use cases, adapters, events, UI state. 3. Découper en lots implémentables DevBackend/DevFrontend/QA, avec tests attendus. 4. Identifier les pièces déjà existantes à réutiliser et les risques. 5. Ne pas coder. Rends un rapport utilisable directement par DevBackend/DevFrontend/QA. - **Prompt:** Contexte bug IdeA: une délégation inter-agent longue envoyée à un agent Codex TUI arrive visiblement tronquée/partielle dans le terminal cible. Code actuel: frontend `frontend/src/features/terminals/useWritePortal.ts` écrit `head.text` en un seul `handle.write(encoder.encode(head.text))`, attend `submitDelayMs` puis écrit `\r`; chemin headless `crates/infrastructure/src/input/mod.rs::make_headless_sink` écrit aussi `text.as_bytes()` d'un coup puis submit. `makeTerminalHandle` sérialise déjà les invokes et `PortablePtyAdapter::write` fait `write_all+flush`. Hypothèse: le TUI/paste handling ou IPC/PTY avec gros bloc peut perdre/ignorer une fin avant Enter. Proposition de correction locale: introduire un envoi chunké pour les injections de délégation (front et headless), avec chunks UTF-8 safe, pause courte entre chunks, puis délai anti-paste existant avant submit; ack seulement après tous les chunks + submit. Ajouter tests `useWritePortal` qui vérifient texte long chunké en ordre et ack après submit; tests infra input headless si simple. Est-ce compatible avec l'architecture actuelle (single writer, submit config, hexagonal), et vois-tu un meilleur point de correction ? Réponds court avec risques/contraintes. - **Prompt:** Validation rapide architecture pour correctif IdeA: le runtime structuré Codex lance `codex exec` depuis le run dir isolé et doit permettre l’écriture dans le project root quand les permissions résolues donnent workspace-write. Proposition: faire transiter `PreparedContext.project_root`/équivalent depuis `StructuredSessionFactory::start` vers `CodexExecSession`, puis ajouter `--add-dir ` à la ligne `codex exec` en workspace-write, sans ajouter `--ask-for-approval` car `codex exec` le refuse. Est-ce conforme Ports & Adapters/hexagonal ? Signale toute frontière à ne pas franchir. - **Response:** Oui, c’est conforme à l’architecture Ports & Adapters, avec une réserve de frontière importante. Validation : - Faire porter `project_root` dans un DTO/VO de contexte préparé (`PreparedContext.project_root` ou équivalent) est acceptable si cette donnée représente le périmètre métier d’exécution du projet, pas un détail Tauri/Codex. - `StructuredSessionFactory::start` peut transmettre cette donnée vers l’adapter runtime concret : c’est précisément le rôle de la couche application/composition d’orchestrer les ports avec le contexte résolu. - Ajouter `--add-dir ` dans `CodexExecSession` est au bon endroit si `CodexExecSession` est l’adapter infrastructure/presentation-backend spécifique à Codex CLI. Le flag `--add-dir` est un détail technique de l’adapter Codex, donc il ne doit pas remonter dans le domaine. - Conditionner cet ajout à `workspace-write` est cohérent : l’intention métier est “la session peut écrire dans le workspace résolu”, l’implémentation Codex est “ajouter le root autorisé à la sandbox CLI”. - Ne pas ajouter `--ask-for-approval` est correct si `codex exec` le refuse. Il ne faut pas forcer une option non supportée au niveau adapter. Frontières à ne pas franchir : - Ne pas faire dépendre le domaine/application de `codex exec`, `--add-dir`, `--sandbox`, ni des noms de flags Codex. Ces détails restent dans l’adapter Codex. - Ne pas laisser `CodexExecSession` recalculer seul le project root à partir du run dir isolé si le project root a déjà été résolu par les couches supérieures. L’adapter doit consommer un contexte préparé, pas réinterpréter l’état global. - Ne pas élargir systématiquement les permissions : `--add-dir` doit être ajouté uniquement pour le `projectRoot` validé/résolu du projet et uniquement quand le mode effectif est `workspace-write`. - Ne pas confondre `cwd` et périmètre d’écriture : le cwd peut rester le run dir isolé pour l’exécution/session, tandis que `project_root` devient une racine autorisée explicite. - Ne pas exposer ce comportement côté React/Tauri commands comme une option utilisateur brute. L’UI demande un lancement d’agent/session ; la politique de sandbox reste dans les services/adapters backend. Formulation recommandée côté modèle : plutôt qu’un champ trop Codex-spécifique, utiliser un nom neutre comme `workspace_roots`, `writable_roots`, `project_root`, ou `ExecutionScope { cwd, project_root, sandbox_mode }`. Puis l’adapter Codex traduit `ExecutionScope + workspace-write` en `codex exec --add-dir `. @@ -23,4 +21,6 @@ objective: CADRAGE ARCHITECTURE (pas de code) — Support de la délégation int - **Prompt:** Après Lot A (ProjectWorkState live/busy) et Lot B (tickets FIFO par agent) mergés dans `develop` @ `64c2c14`, l'utilisateur demande d'enchaîner sur tout ce qu'il reste. Cadre le prochain lot prioritaire du chantier UX conversations/délégations. Objectif pressenti: Lot C — conversation summaries depuis log/handoff en lecture best-effort, pour que le panneau Work donne une vue lisible des threads sans charger tout le journal. Merci de définir précisément: - objectif produit et inclus/exclus, - source de vérité backend (ConversationLog/HandoffStore/ConversationRegistry ou autre), - frontières hexagonales: domaine/application/infra/Tauri/frontend, - DTOs exacts et évolution de `ProjectWorkState` ou nouvelle commande si préférable, - stratégie preview/handoff (bornes, erreurs best-effort, pas de mutation), - impacts frontend et UI minimale, - tests attendus et commandes. Contraintes: - Pas de nouvelle persistance durable si les logs/handoff existent déjà. - Ne pas exposer tout `log.jsonl`; previews/summaries seulement. - Ne pas bloquer l’état live si un log est corrompu/absent; best-effort. - Respecter la suite du chantier UX et éviter de mélanger avec actions Lot D ou mémoire automatique Lot E. - Ne code pas. - **Response:** ## Lot C — Conversation summaries dans le panneau Work ### Objectif produit Objectif : enrichir le panneau `Work` avec une vue lisible des fils de conversation associés aux tickets/live work, sans exposer ni charger tout `log.jsonl`. Inclus : - afficher, pour chaque `conversationId` visible dans le work-state, un résumé compact issu du `handoff.md` quand il existe ; - compléter par un aperçu borné des derniers tours du `ConversationLog` quand le handoff est absent ou vide ; - signaler sobrement l’état de preview : `ready`, `missing`, `partial`, `unavailable` ; - rester strictement read-only et best-effort : aucune écriture, aucun recalcul de handoff, aucun blocage de `live/busy/tickets` si un fichier est absent/corrompu. Exclus : - pas d’action utilisateur sur les conversations ; - pas de lecture complète affichée du journal ; - pas de mutation/réparation des logs/handoff ; - pas de génération LLM de résumé ; - pas de mémoire automatique Lot E ; - pas de changement de FIFO, reprise, cancel, requeue ou actions Lot D. ### Source de vérité backend Source durable primaire : `HandoffStore` (`.ideai/conversations//handoff.md`). Source durable secondaire : `ConversationLog` (`.ideai/conversations//log.jsonl`) uniquement via une lecture bornée (`last(n)`), pour construire une preview courte quand le handoff est absent ou inutilisable. `ConversationRegistry` ne doit pas être la source de vérité du Lot C : il est live/in-memory, utile pour résoudre ou suivre des paires pendant l’orchestration, mais le panneau Work doit refléter les conversations déjà matérialisées par les tickets et leur persistance durable. Source des conversations à inspecter : les `conversation_id` déjà présents dans les `QueuedTicketSnapshot` projetés par `GetProjectWorkState`. En option additive, si le snapshot live expose déjà un `conversation_id` stable ailleurs, on peut l’ajouter plus tard, mais Lot C doit se limiter aux conversations visibles par les tickets pour éviter d’ouvrir un chantier layout/session. ### Frontières hexagonales Domaine : - réutiliser les ports existants `ConversationLog` et `HandoffStore` ; - ajouter seulement des value objects purs de read-model si nécessaire : `ConversationWorkSummary`, `ConversationPreviewStatus`, éventuellement `ConversationTurnPreview` ; - aucun accès FS, aucun Tauri, aucun type UI. Application : - étendre `GetProjectWorkState` avec un collaborateur de lecture de previews, ou extraire un petit service applicatif `ConversationPreviewReader` utilisé par `GetProjectWorkState` ; - dépendances injectées : `Arc` et `Arc`, ou `Option>` si on veut rendre l’enrichissement désactivable dans les tests legacy ; - le use case continue à renvoyer le work-state même si toutes les previews échouent. Infrastructure : - aucune nouvelle persistance ; - réutiliser `FsHandoffStore` et `FsConversationLog` déjà câblés sous `/.ideai/conversations/` ; - si `FsHandoffStore::load` renvoie une erreur de parsing, l’application la convertit en preview `unavailable/partial`, pas en `AppError` global ; - `FsConversationLog::last` est déjà tolérant aux lignes corrompues, donc c’est le bon fallback. Tauri : - garder la commande existante `get_project_work_state` ; - faire évoluer `ProjectWorkStateDto` additivement ; - ne pas créer `get_conversation_log` ni endpoint brut. Frontend : - types TS miroir dans le domaine UI ; - adapter Tauri inchangé côté méthode (`getProjectWorkState`) ; - mock gateway normalise `conversations: []` pour compat tests ; - composant `ProjectWorkStatePanel` mappe `conversationId -> summary` et affiche une ligne compacte par ticket/thread. ### DTO recommandé Préférence : étendre `ProjectWorkState` avec une liste top-level de résumés, référencée par `conversationId`. Ça évite de dupliquer le résumé sur plusieurs tickets du même fil. Rust application : ```rust pub struct ProjectWorkState { pub agents: Vec, pub conversations: Vec, } pub struct ConversationWorkSummary { pub conversation_id: ConversationId, pub status: ConversationPreviewStatus, pub objective_preview: Option, pub summary_preview: Option, pub summary_len: usize, pub up_to: Option, pub recent_turns: Vec, } pub enum ConversationPreviewStatus { Ready, Missing, Partial, Unavailable, } pub struct ConversationTurnWorkPreview { pub role: TurnRole, pub source: TicketWorkSource, pub at_ms: u64, pub text_preview: String, pub text_len: usize, } ``` Wire DTO camelCase : ```ts export interface ProjectWorkState { agents: AgentWorkState[]; conversations: ConversationWorkSummary[]; } export type ConversationPreviewStatus = | "ready" | "missing" | "partial" | "unavailable"; export interface ConversationWorkSummary { conversationId: string; status: ConversationPreviewStatus; objectivePreview?: string; summaryPreview?: string; summaryLen: number; upTo?: string; recentTurns: ConversationTurnWorkPreview[]; } export interface ConversationTurnWorkPreview { role: "prompt" | "response" | "toolActivity"; source: TicketWorkSource; atMs: number; textPreview: string; textLen: number; } ``` `AgentTicketState` ne change pas : il porte déjà `conversationId`, `taskPreview`, `taskLen`, `status`, `requesterLabel`. Le frontend joint via `conversationId`. Compatibilité : les mocks et tests peuvent normaliser `conversations` à `[]` quand absent, comme ils le font déjà pour `tickets`. ### Stratégie preview/handoff Bornes proposées : - `HANDOFF_PREVIEW_MAX_CHARS = 480` ; - `OBJECTIVE_PREVIEW_MAX_CHARS = 160` ; - `RECENT_TURNS_MAX = 3` ; - `TURN_PREVIEW_MAX_CHARS = 220`. Algorithme par `conversationId` unique : 1. tenter `handoffs.load(conversation)` ; 2. si `Ok(Some(handoff))` : - `status = ready` ; - `summaryPreview = normalize_truncate(handoff.summary_md, 480)` ; - `summaryLen = handoff.summary_md.chars().count()` ; - `objectivePreview = handoff.objective.map(truncate 160)` ; - `upTo = Some(handoff.up_to)` ; 3. si `Ok(None)` : `status = missing`, puis tenter `log.last(conversation, 3)` ; 4. si handoff illisible mais `log.last` marche : `status = partial`, `summaryPreview = None`, `recentTurns = ...` ; 5. si handoff et log échouent : `status = unavailable`, champs preview vides ; 6. dans tous les cas, ne jamais propager l’erreur au `ProjectWorkState` global. Normalisation : même règle que `task_preview` Lot B : trim + collapse whitespace + troncature à la borne, jamais mid-codepoint. On peut factoriser un helper applicatif privé dans `workstate`. Important : ne pas appeler `ConversationLog::read(None)` dans Lot C. Le fallback doit être `last(RECENT_TURNS_MAX)` uniquement. ### UI minimale Dans `ProjectWorkStatePanel` : - garder la structure actuelle agent > tickets ; - sous chaque ticket, afficher une ligne thread compacte si un résumé existe : - objectif si présent : `Goal: ` ; - sinon résumé : `` ; - sinon dernier tour : `Last: ` ; - badge discret selon statut : `Summary`, `No summary`, `Partial`, `Unavailable` ; - ne pas ajouter de modal ni bouton “ouvrir log” ; - ne pas afficher les fichiers, chemins, ni JSON brut ; - si `conversationId` n’a pas de summary dans la map, ne pas casser la ligne ticket : afficher seulement l’état Lot B existant. Le panneau doit rester scannable : pas de cartes imbriquées, pas de gros bloc Markdown rendu. Texte simple, clamp/line wrap raisonnable. ### Tests attendus Application (`crates/application/tests/workstate.rs`) : - conserve agents/live/busy/tickets même si le preview reader échoue ; - déduplique les `conversationId` de plusieurs tickets ; - handoff présent produit `ready`, `objectivePreview`, `summaryPreview`, `summaryLen`, `upTo` ; - handoff absent + log présent produit `missing` avec `recentTurns` borné ; - handoff corrompu + log lisible produit `partial` ; - handoff/log indisponibles produit `unavailable` sans `AppError` global ; - previews tronquées et whitespace normalisé. Infrastructure (`crates/infrastructure/tests/conversation_log.rs`) : - normalement peu ou pas de nouveau test requis si `FsConversationLog::last` et `FsHandoffStore::load` sont déjà couverts ; - ajouter seulement un test si un helper d’adapter dédié apparaît, mais éviter d’en créer un. Tauri DTO (`crates/app-tauri/tests/dto_agents.rs`) : - sérialisation camelCase de `conversations` ; - conversion des statuts `ready/missing/partial/unavailable` ; - compat additive avec agents/tickets existants. Frontend (`frontend/src/features/workstate/workstate.test.tsx`) : - mock sans `conversations` normalisé à `[]` ; - ticket avec `conversationId` affiche objectif/résumé ; - status `partial`/`unavailable` ne masque pas le ticket ; - refresh existant sur `agentBusyChanged` / `delegationReady` continue. Commandes : ```bash cargo test -p application workstate cargo test -p app-tauri dto_agents cargo test -p infrastructure conversation_log npm --prefix frontend test -- workstate ``` Selon l’organisation actuelle du workspace, finir par : ```bash cargo test npm --prefix frontend test ``` ### Décision de lot Lot C doit être un enrichissement read-only de `ProjectWorkState`, pas une nouvelle surface d’action. Le backend reste source d’autorité pour les bornes et la robustesse ; le frontend ne fait que joindre `tickets[].conversationId` vers `state.conversations[]` et rendre une preview compacte. Ça respecte les contraintes : aucune nouvelle persistance, aucun log brut exposé, best-effort par conversation, et pas de mélange avec les futures actions de gestion ou la mémoire automatique. - **Prompt:** Après Lots A/B/C `workstate` mergés dans `develop` @ `6e1ba7e`, cadre le prochain lot prioritaire. L'utilisateur demande d'enchaîner sur tout ce qu'il reste. Contexte livré: - Lot A: panneau/read-model live/busy par agent. - Lot B: tickets FIFO par agent, source human/agent, statuts inProgress/queued. - Lot C: summaries de conversations best-effort top-level, jointes via conversationId. Objectif pressenti: Lot D — actions UX contrôlées depuis Work: ouvrir/rattacher l'agent/la cellule, voir la conversation, arrêter un agent, copier résumé. Merci de choisir le périmètre minimal prioritaire et de cadrer précisément. Merci de définir: - objectif produit et inclus/exclus, - actions à livrer dans ce lot, avec priorité, - ports/use cases backend existants à réutiliser ou nouveaux contrats nécessaires, - frontières Tauri/frontend, - contraintes de sécurité/permissions et cohérence avec single-agent/live-session, - impacts UI dans le panneau Work, - tests attendus et commandes. Contraintes: - Ne pas mélanger avec mémoire automatique Lot E. - Ne pas inventer une nouvelle session si une action doit seulement focaliser/rattacher l'existant. - Ne pas casser l'historique conversationnel ni exposer log brut. - Ne code pas. -- **Response:** ## Lot D — actions contrôlées depuis Work ### Objectif produit Rendre le panneau `Work` opérable sans changer sa nature : il reste un tableau de bord live/conversations, mais l’utilisateur peut agir sur l’état déjà visible. Périmètre minimal prioritaire : - retrouver ou afficher la cellule d’un agent live ; - rattacher une session déjà live à une cellule existante et déterminée ; - arrêter explicitement un agent live ; - voir davantage de la preview conversationnelle Lot C ; - copier le résumé affiché. Exclus : - pas de lancement d’un agent neuf depuis Work ; - pas de création automatique de cellule/layout ; - pas de cancel/requeue/priorisation FIFO ; - pas de modification de handoff, mémoire automatique ou Lot E ; - pas de lecture/exposition brute de `log.jsonl` ; - pas de transcript complet. ### Actions à livrer Priorité 1 — `Open` / `Go to cell` - Si `agent.live.nodeId` correspond à une cellule visible dans le layout actif : focus/scroll/highlight de cette cellule. - Aucun appel backend. Aucun spawn. - Libellé UI : `Open` ou `Go to cell` selon la place. Priorité 2 — `Attach` - Si l’agent est live mais sa cellule hôte n’est plus visible, rattacher la session à une cellule existante et non ambiguë. - Cible autorisée pour ce lot : une cellule visible déjà pinnée sur le même agent et sans session, ou une cellule explicitement sélectionnée par l’utilisateur dans le layout. - Si aucune cible déterministe : action désactivée avec message court du type `Pin this agent to a cell first`. - Ne jamais créer une nouvelle session. Ne jamais appeler `launch_agent`. Priorité 3 — `Stop` - Arrête la session live de l’agent après confirmation. - Fonctionne pour PTY et structured. - Ne supprime pas l’agent, ne supprime pas le handoff, ne vide pas `conversationId`. - Après succès : refresh Work + layout/live state via events existants ou refresh explicite. Priorité 4 — `View conversation` - Expansion inline dans Work sur la base de `ProjectWorkState.conversations[]`. - Montre objectif, summary preview, et les `recentTurns` déjà bornés par Lot C. - Pas de nouveau endpoint de transcript. Priorité 5 — `Copy summary` - Frontend-only via clipboard. - Copie uniquement le texte déjà exposé par Lot C : objectif + summary preview + recent turns bornés si summary absent. - Désactivé si aucune preview exploitable. ### Backend Réutiliser : - `GetProjectWorkState` : source unique du panneau, inchangé fonctionnellement ; - `LiveSessions` / `TerminalSessions` / `StructuredSessions` : source de vérité live ; - `LayoutGateway.mutateLayout` avec `setSession` / `setCellAgent` ; - `close_terminal` et `close_agent_session` comme primitives existantes ; - `attach_live_agent` existant, mais à durcir. Contrats backend recommandés : 1. Faire évoluer `attach_live_agent` en vrai use case applicatif `AttachLiveAgent`. Entrée : ```rust pub struct AttachLiveAgentInput { pub project: Project, pub agent_id: AgentId, pub node_id: NodeId, } ``` Sortie : ```rust pub struct AttachLiveAgentOutput { pub agent_id: AgentId, pub node_id: NodeId, pub session_id: SessionId, pub kind: LiveSessionKind, } ``` Comportement : - chercher la session live dans `TerminalSessions`, sinon `StructuredSessions` ; - rebind `node_id` sans spawn ; - retourner `NOT_FOUND` si l’agent n’est plus live ; - idempotent si déjà attaché à cette cellule ; - ne touche pas `ConversationLog`, `HandoffStore`, `ProviderSessionStore`. 2. Ajouter `StopLiveAgent` plutôt que faire deviner le registre au frontend. Entrée : ```rust pub struct StopLiveAgentInput { pub project: Project, pub agent_id: AgentId, } ``` Sortie : ```rust pub struct StopLiveAgentOutput { pub agent_id: AgentId, pub session_id: SessionId, pub kind: LiveSessionKind, } ``` Comportement : - résoudre la session live par agent ; - si PTY : déléguer à `CloseTerminal` / `PtyPort.kill` ; - si structured : `StructuredSessions::remove` puis `AgentSession::shutdown()` ; - publier `DomainEvent::AgentExited { agent_id, code }` ou l’équivalent déjà relayé ; - no-op contrôlé ou `NOT_FOUND` si déjà arrêté. Préférence : `NOT_FOUND` côté API, frontend l’interprète comme refresh. Pourquoi pas seulement `close_terminal(sessionId)` : `WorkState.live.kind` existe, et le modèle a deux registries. Une commande par agent garde la cohérence single-agent côté backend. ### Tauri / DTO Nouvelles/évolutions : ```ts attach_live_agent({ request: { projectId, agentId, nodeId } }) -> { agentId, nodeId, sessionId, kind } stop_live_agent({ request: { projectId, agentId } }) -> { agentId, sessionId, kind } ``` `LiveAgentDto` peut être réutilisé si on lui ajoute/garantit `kind`; sinon créer un DTO dédié. Préférence : enrichir `LiveAgent` additivement avec `kind: "pty" | "structured"`, cohérent avec `LiveWorkSession.kind`. Pas de commande `get_conversation_log`, `open_conversation`, `copy_summary`. ### Frontend Ports UI : - `AgentGateway.attachLiveAgent(projectId, agentId, nodeId): Promise` doit retourner `kind` ; - ajouter `AgentGateway.stopLiveAgent(projectId, agentId): Promise` ; - ne pas faire appeler `close_terminal` directement par `ProjectWorkStatePanel` pour stopper un agent ; - `LayoutGateway.loadLayout/mutateLayout` réutilisés pour trouver/attacher la cellule cible. Coordination UI recommandée : - extraire le helper de focus cellule (`goToCell`) de `LayoutGrid` vers `features/layout/focusCell.ts` ; - ajouter un petit hook `useWorkActions(projectId, activeLayoutId?)` consommé par `ProjectWorkStatePanel` ; - le hook sait charger le layout actif, trouver les feuilles, vérifier si `live.nodeId` est visible, et appeler `attachLiveAgent + mutateLayout(setCellAgent/setSession)` si une cible déterministe existe. ### Sécurité / cohérence Règles obligatoires : - `Open` et `Attach` ne spawnent jamais. Interdit d’appeler `launchAgent` depuis Work dans Lot D. - `Attach` ne doit pas écraser une cellule qui héberge déjà une session différente, sauf remplacement explicite futur hors lot. - `Stop` demande confirmation ; c’est une action destructive sur le process, mais pas sur les fichiers. - `Stop` ne supprime ni agent, ni tickets, ni conversation summary, ni handoff. - `Stop` doit libérer la live registry pour préserver l’invariant “un agent = une session live max”. - Si un ticket est `inProgress`, afficher dans la confirmation que le tour en cours sera interrompu ; ne pas tenter de résoudre le ticket dans ce lot. - Pas de permission filesystem nouvelle. Les seules mutations durables sont les mutations layout déjà existantes (`setSession`/`setCellAgent`) lors d’un attach. ### UI minimale Dans chaque ligne agent : - bouton `Open` si live ; - bouton `Attach` si live mais cellule non visible et une cible existe ; - bouton `Stop` si live ; - état désactivé explicite si action impossible. Dans chaque ticket/summary : - bouton disclosure `View` pour ouvrir/fermer les détails Lot C ; - bouton `Copy` à côté du summary, pas sur toute la ligne ; - garder l’UI dense : pas de drawer global, pas de modal conversation, pas de rendu Markdown lourd. Feedback : - erreurs action en inline dans le panneau ; - après `Stop`/`Attach`, refresh `getProjectWorkState` et layout actif ; - `Copy` affiche un état bref `Copied`. ### Tests attendus Application Rust : - `AttachLiveAgent` rebind PTY sans changer `session_id` ; - `AttachLiveAgent` rebind structured sans changer `session_id` ; - attach agent absent => `NOT_FOUND` ; - attach même node => idempotent ; - `StopLiveAgent` arrête PTY et retire la session live ; - `StopLiveAgent` arrête structured via `shutdown()` et retire la session live ; - stop absent => `NOT_FOUND` ; - stop ne touche pas handoff/log/provider sessions. Tauri DTO/tests : - request camelCase pour `attach_live_agent` et `stop_live_agent` ; - response sérialise `kind`; - erreurs `INVALID` ids et `NOT_FOUND` live session. Frontend tests : - `Open` focus une cellule visible et n’appelle aucun launch ; - `Attach` appelle `attachLiveAgent` puis `mutateLayout(setSession)` sur une cible déterministe ; - `Attach` désactivé sans cible ; - `Stop` demande confirmation puis appelle `stopLiveAgent`, refresh Work ; - `Stop` sur agent busy affiche l’avertissement ; - `View conversation` n’affiche que les champs Lot C ; - `Copy summary` copie uniquement la preview exposée, pas de log brut ; - pas de régression des refresh events Lot A/B/C. Commandes : ```bash cargo test -p application terminal_usecases structured_registry workstate cargo test -p app-tauri dto_agents list_live_agents_r0b npm --prefix frontend test -- workstate npm --prefix frontend test -- LayoutGrid singletonAgent agentAlreadyRunning ``` Puis en validation large : ```bash cargo test npm --prefix frontend test ``` ### Décision de cadrage Lot D doit être “actions sur état existant”, pas “nouveau centre de contrôle”. Le seul ajout backend vraiment justifié est de faire porter `attach/stop` par des contrats agent-level qui respectent PTY + structured et l’invariant single-agent. La conversation reste une preview bornée issue de Lot C ; le log brut et la mémoire automatique restent hors périmètre. \ No newline at end of file +- **Response:** ## Lot D — actions contrôlées depuis Work ### Objectif produit Rendre le panneau `Work` opérable sans changer sa nature : il reste un tableau de bord live/conversations, mais l’utilisateur peut agir sur l’état déjà visible. Périmètre minimal prioritaire : - retrouver ou afficher la cellule d’un agent live ; - rattacher une session déjà live à une cellule existante et déterminée ; - arrêter explicitement un agent live ; - voir davantage de la preview conversationnelle Lot C ; - copier le résumé affiché. Exclus : - pas de lancement d’un agent neuf depuis Work ; - pas de création automatique de cellule/layout ; - pas de cancel/requeue/priorisation FIFO ; - pas de modification de handoff, mémoire automatique ou Lot E ; - pas de lecture/exposition brute de `log.jsonl` ; - pas de transcript complet. ### Actions à livrer Priorité 1 — `Open` / `Go to cell` - Si `agent.live.nodeId` correspond à une cellule visible dans le layout actif : focus/scroll/highlight de cette cellule. - Aucun appel backend. Aucun spawn. - Libellé UI : `Open` ou `Go to cell` selon la place. Priorité 2 — `Attach` - Si l’agent est live mais sa cellule hôte n’est plus visible, rattacher la session à une cellule existante et non ambiguë. - Cible autorisée pour ce lot : une cellule visible déjà pinnée sur le même agent et sans session, ou une cellule explicitement sélectionnée par l’utilisateur dans le layout. - Si aucune cible déterministe : action désactivée avec message court du type `Pin this agent to a cell first`. - Ne jamais créer une nouvelle session. Ne jamais appeler `launch_agent`. Priorité 3 — `Stop` - Arrête la session live de l’agent après confirmation. - Fonctionne pour PTY et structured. - Ne supprime pas l’agent, ne supprime pas le handoff, ne vide pas `conversationId`. - Après succès : refresh Work + layout/live state via events existants ou refresh explicite. Priorité 4 — `View conversation` - Expansion inline dans Work sur la base de `ProjectWorkState.conversations[]`. - Montre objectif, summary preview, et les `recentTurns` déjà bornés par Lot C. - Pas de nouveau endpoint de transcript. Priorité 5 — `Copy summary` - Frontend-only via clipboard. - Copie uniquement le texte déjà exposé par Lot C : objectif + summary preview + recent turns bornés si summary absent. - Désactivé si aucune preview exploitable. ### Backend Réutiliser : - `GetProjectWorkState` : source unique du panneau, inchangé fonctionnellement ; - `LiveSessions` / `TerminalSessions` / `StructuredSessions` : source de vérité live ; - `LayoutGateway.mutateLayout` avec `setSession` / `setCellAgent` ; - `close_terminal` et `close_agent_session` comme primitives existantes ; - `attach_live_agent` existant, mais à durcir. Contrats backend recommandés : 1. Faire évoluer `attach_live_agent` en vrai use case applicatif `AttachLiveAgent`. Entrée : ```rust pub struct AttachLiveAgentInput { pub project: Project, pub agent_id: AgentId, pub node_id: NodeId, } ``` Sortie : ```rust pub struct AttachLiveAgentOutput { pub agent_id: AgentId, pub node_id: NodeId, pub session_id: SessionId, pub kind: LiveSessionKind, } ``` Comportement : - chercher la session live dans `TerminalSessions`, sinon `StructuredSessions` ; - rebind `node_id` sans spawn ; - retourner `NOT_FOUND` si l’agent n’est plus live ; - idempotent si déjà attaché à cette cellule ; - ne touche pas `ConversationLog`, `HandoffStore`, `ProviderSessionStore`. 2. Ajouter `StopLiveAgent` plutôt que faire deviner le registre au frontend. Entrée : ```rust pub struct StopLiveAgentInput { pub project: Project, pub agent_id: AgentId, } ``` Sortie : ```rust pub struct StopLiveAgentOutput { pub agent_id: AgentId, pub session_id: SessionId, pub kind: LiveSessionKind, } ``` Comportement : - résoudre la session live par agent ; - si PTY : déléguer à `CloseTerminal` / `PtyPort.kill` ; - si structured : `StructuredSessions::remove` puis `AgentSession::shutdown()` ; - publier `DomainEvent::AgentExited { agent_id, code }` ou l’équivalent déjà relayé ; - no-op contrôlé ou `NOT_FOUND` si déjà arrêté. Préférence : `NOT_FOUND` côté API, frontend l’interprète comme refresh. Pourquoi pas seulement `close_terminal(sessionId)` : `WorkState.live.kind` existe, et le modèle a deux registries. Une commande par agent garde la cohérence single-agent côté backend. ### Tauri / DTO Nouvelles/évolutions : ```ts attach_live_agent({ request: { projectId, agentId, nodeId } }) -> { agentId, nodeId, sessionId, kind } stop_live_agent({ request: { projectId, agentId } }) -> { agentId, sessionId, kind } ``` `LiveAgentDto` peut être réutilisé si on lui ajoute/garantit `kind`; sinon créer un DTO dédié. Préférence : enrichir `LiveAgent` additivement avec `kind: "pty" | "structured"`, cohérent avec `LiveWorkSession.kind`. Pas de commande `get_conversation_log`, `open_conversation`, `copy_summary`. ### Frontend Ports UI : - `AgentGateway.attachLiveAgent(projectId, agentId, nodeId): Promise` doit retourner `kind` ; - ajouter `AgentGateway.stopLiveAgent(projectId, agentId): Promise` ; - ne pas faire appeler `close_terminal` directement par `ProjectWorkStatePanel` pour stopper un agent ; - `LayoutGateway.loadLayout/mutateLayout` réutilisés pour trouver/attacher la cellule cible. Coordination UI recommandée : - extraire le helper de focus cellule (`goToCell`) de `LayoutGrid` vers `features/layout/focusCell.ts` ; - ajouter un petit hook `useWorkActions(projectId, activeLayoutId?)` consommé par `ProjectWorkStatePanel` ; - le hook sait charger le layout actif, trouver les feuilles, vérifier si `live.nodeId` est visible, et appeler `attachLiveAgent + mutateLayout(setCellAgent/setSession)` si une cible déterministe existe. ### Sécurité / cohérence Règles obligatoires : - `Open` et `Attach` ne spawnent jamais. Interdit d’appeler `launchAgent` depuis Work dans Lot D. - `Attach` ne doit pas écraser une cellule qui héberge déjà une session différente, sauf remplacement explicite futur hors lot. - `Stop` demande confirmation ; c’est une action destructive sur le process, mais pas sur les fichiers. - `Stop` ne supprime ni agent, ni tickets, ni conversation summary, ni handoff. - `Stop` doit libérer la live registry pour préserver l’invariant “un agent = une session live max”. - Si un ticket est `inProgress`, afficher dans la confirmation que le tour en cours sera interrompu ; ne pas tenter de résoudre le ticket dans ce lot. - Pas de permission filesystem nouvelle. Les seules mutations durables sont les mutations layout déjà existantes (`setSession`/`setCellAgent`) lors d’un attach. ### UI minimale Dans chaque ligne agent : - bouton `Open` si live ; - bouton `Attach` si live mais cellule non visible et une cible existe ; - bouton `Stop` si live ; - état désactivé explicite si action impossible. Dans chaque ticket/summary : - bouton disclosure `View` pour ouvrir/fermer les détails Lot C ; - bouton `Copy` à côté du summary, pas sur toute la ligne ; - garder l’UI dense : pas de drawer global, pas de modal conversation, pas de rendu Markdown lourd. Feedback : - erreurs action en inline dans le panneau ; - après `Stop`/`Attach`, refresh `getProjectWorkState` et layout actif ; - `Copy` affiche un état bref `Copied`. ### Tests attendus Application Rust : - `AttachLiveAgent` rebind PTY sans changer `session_id` ; - `AttachLiveAgent` rebind structured sans changer `session_id` ; - attach agent absent => `NOT_FOUND` ; - attach même node => idempotent ; - `StopLiveAgent` arrête PTY et retire la session live ; - `StopLiveAgent` arrête structured via `shutdown()` et retire la session live ; - stop absent => `NOT_FOUND` ; - stop ne touche pas handoff/log/provider sessions. Tauri DTO/tests : - request camelCase pour `attach_live_agent` et `stop_live_agent` ; - response sérialise `kind`; - erreurs `INVALID` ids et `NOT_FOUND` live session. Frontend tests : - `Open` focus une cellule visible et n’appelle aucun launch ; - `Attach` appelle `attachLiveAgent` puis `mutateLayout(setSession)` sur une cible déterministe ; - `Attach` désactivé sans cible ; - `Stop` demande confirmation puis appelle `stopLiveAgent`, refresh Work ; - `Stop` sur agent busy affiche l’avertissement ; - `View conversation` n’affiche que les champs Lot C ; - `Copy summary` copie uniquement la preview exposée, pas de log brut ; - pas de régression des refresh events Lot A/B/C. Commandes : ```bash cargo test -p application terminal_usecases structured_registry workstate cargo test -p app-tauri dto_agents list_live_agents_r0b npm --prefix frontend test -- workstate npm --prefix frontend test -- LayoutGrid singletonAgent agentAlreadyRunning ``` Puis en validation large : ```bash cargo test npm --prefix frontend test ``` ### Décision de cadrage Lot D doit être “actions sur état existant”, pas “nouveau centre de contrôle”. Le seul ajout backend vraiment justifié est de faire porter `attach/stop` par des contrats agent-level qui respectent PTY + structured et l’invariant single-agent. La conversation reste une preview bornée issue de Lot C ; le log brut et la mémoire automatique restent hors périmètre. +- **Prompt:** Après Lots Work A/B/C/D mergés dans `develop` @ `c1e99d1`, l'utilisateur demande d'enchaîner sur tout ce qu'il reste. Détermine le prochain chantier prioritaire hors Work et cadre un lot implémentable. Contexte livré: - A: read-model/panneau live/busy. - B: tickets FIFO par agent. - C: summaries conversationnels best-effort. - D: actions contrôlées Open/Attach/Stop/View/Copy. Restes connus mémoire projet: - persistance conversationnelle produit/cross-profile plus profonde, - handoff/canonical log incrémental, - mise à jour automatique mémoire/contexte pendant la vie d'un agent, - docs architecture potentiellement obsolètes, - intégration MCP réelle/AppImage/e2e. Merci de choisir le prochain lot prioritaire le plus utile et cadrer précisément: - objectif produit, - inclus/exclus, - frontières domaine/application/infra/Tauri/frontend si applicable, - tests attendus, - ordre de délégation recommandé. Ne code pas. +- **Response:** ## Prochain chantier prioritaire : Lot E1 — Auto-memory contrôlée aux checkpoints ### Choix Le prochain lot hors Work le plus utile est **la capitalisation automatique en mémoire projet**, mais en première marche contrôlée : écrire/mettre à jour la mémoire uniquement quand un agent émet une directive mémoire explicite dans sa réponse de fin de tour. Pourquoi ce lot maintenant : - Work A-D donne la visibilité et les actions sur les conversations. - Le log/handoff incrémental et le cross-profile sont déjà très avancés dans le code/tests (`RecordTurn`, `FsConversationLog`, `FsHandoffStore`, provider sessions, swap préservant le pair id). - La mémoire existe déjà : CRUD, `FsMemoryStore`, recall, injection au lancement, MCP `idea_memory_read/write`. - Le vrai trou produit restant est que l’utilisateur ne doit pas avoir à transformer lui-même les apprentissages d’un agent en mémoire durable. Ce lot est volontairement **déterministe et safe** : pas d’extraction LLM libre depuis tout le log, pas d’écriture depuis un résumé ambigu, pas de pollution massive. ### Objectif Produit Quand un agent termine un tour et indique explicitement une information à mémoriser, IdeA la persiste automatiquement dans `.ideai/memory/` sans action utilisateur. L’agent peut donc écrire, dans sa réponse finale, une directive structurée du genre : ```markdown ```idea-memory slug: architecture-workstate-actions title: Work panel actions type: project description: Work actions are Open/Attach/Stop/View/Copy, without spawning new sessions. --- The Work panel may focus or attach existing sessions, but must not launch a new agent from Work. ``` ``` ``` IdeA détecte ce bloc au checkpoint, le valide, puis crée ou remplace la note mémoire correspondante via les ports existants. ### Inclus - Parser déterministe de blocs `idea-memory` dans les réponses agent. - Un use case applicatif `HarvestMemoryFromTurn` branché après un `Response` turn réussi. - Écriture via le store mémoire existant, sous garde fichier quand applicable. - Publication `MemorySaved` existante pour que l’UI puisse se rafraîchir. - Injection d’une courte consigne dans le contexte agent : quand une décision durable est prise, émettre un bloc `idea-memory`. - Best-effort strict : une directive invalide ou une erreur mémoire ne fait jamais échouer la réponse, le ticket, ni le handoff. ### Exclus - Pas d’extraction automatique libre depuis tout `log.jsonl`. - Pas de summarizer LLM. - Pas de création de “suggestions” à valider dans une nouvelle persistance. - Pas de refonte du MemoryPanel. - Pas d’auto-modification du contexte global `CLAUDE.md`. - Pas de mélange avec l’e2e MCP/AppImage. - Pas de changement des règles Work A-D. ### Frontières Hexagonales **Domaine** Ajouter des value objects purs, sans I/O : ```rust pub struct MemoryHarvestDirective { pub slug: MemorySlug, pub title: String, pub description: String, pub r#type: MemoryType, pub body: MarkdownDoc, } pub struct MemoryHarvestReport { pub found: usize, pub saved: usize, pub skipped: usize, } ``` Le domaine porte les invariants : slug valide, body non vide, type autorisé. Pas de `tokio`, pas de FS. **Application** Nouveau use case : ```rust pub struct HarvestMemoryFromTurn { memories: Arc, events: Arc, // optionnel si on veut passer par le garde existant : // guard: Arc, } pub struct HarvestMemoryFromTurnInput { pub project: Project, pub conversation: ConversationId, pub agent_id: AgentId, pub turn: ConversationTurn, } ``` Règles : - ne traiter que `TurnRole::Response` ; - parser uniquement le texte du tour courant ; - pour chaque directive valide, construire un `Memory` et appeler `MemoryStore::save(project.root, &memory)` ; - publier `DomainEvent::MemorySaved { slug }` ; - retourner un rapport, mais ne jamais propager l’erreur vers l’orchestration live si appelé en mode checkpoint. Branchement : - dans le chemin où `RecordTurn` est déjà appelé pour la réponse d’un `ask` réussi ; - après append/handoff, car le log canonique reste prioritaire ; - si harvest échoue, log diagnostic seulement. **Infrastructure** Aucun nouveau stockage. Réutiliser : - `FsMemoryStore` ; - `FsConversationLog` / `FsHandoffStore` restent inchangés ; - `FileGuard` si le wiring courant permet de passer par `WriteMemory`, sinon utiliser directement `MemoryStore` mais garder le lot borné aux écritures IdeA internes. **Tauri** Composition root : - construire `HarvestMemoryFromTurn` avec le `memory_store_port` et `event_bus` existants ; - l’injecter dans l’orchestrateur à côté de `RecordTurnProvider`. Pas de nouvelle commande Tauri obligatoire. **Frontend** Minimal : - `useMemory` doit écouter `MemorySaved` / `MemoryDeleted` / `MemoryIndexRebuilt` et rafraîchir l’index si le panneau mémoire est monté. - Aucun nouvel écran. - Optionnel : afficher une ligne discrète dans MemoryPanel quand l’index change, mais pas nécessaire pour E1. ### Format Directive Recommandé Bloc fenced unique ou multiple : ```markdown ```idea-memory slug: kebab-case-slug title: Human title type: project|reference|feedback|user description: One-line recall hook --- Markdown body to persist. ``` ``` Contraintes : - `slug` obligatoire ; - `title` obligatoire ; - `description` obligatoire et bornée ; - `body` obligatoire ; - taille max par bloc, par exemple `8 KiB` ; - max blocs par réponse, par exemple `3` ; - bloc invalide ignoré individuellement, sans bloquer les autres. ### Sécurité et Qualité Mémoire - Une réponse ne peut pas écrire hors `.ideai/memory` car elle passe par `MemorySlug` + `MemoryStore`. - Pas de chemins arbitraires. - Pas de suppression automatique. - Remplacement idempotent par slug : une même décision peut être raffinée. - Les agents doivent recevoir la consigne : n’émettre un bloc mémoire que pour des décisions durables, conventions, préférences utilisateur ou faits projet stables. - Ne pas mémoriser les tâches temporaires, logs d’exécution, tickets, erreurs transitoires. ### Tests Attendues **Domaine / parser** - parse un bloc valide ; - parse plusieurs blocs ; - refuse slug invalide ; - refuse body vide ; - borne taille et nombre de blocs ; - ignore les fences non `idea-memory` ; - conserve le Markdown du body sans corruption. **Application** - `HarvestMemoryFromTurn` ignore `Prompt` et `ToolActivity` ; - une `Response` valide crée/remplace une note mémoire ; - publie `MemorySaved` par note sauvegardée ; - directives partiellement invalides : sauvegarde les valides, skip les invalides ; - erreur `MemoryStore` convertie en rapport/diagnostic sans casser l’appelant checkpoint ; - pas de lecture du `log.jsonl` complet. **Orchestrateur** - un `idea_ask_agent` réussi avec réponse contenant un bloc `idea-memory` écrit la mémoire après reply ; - la réponse rendue au demandeur ne contient pas d’erreur même si la mémoire échoue ; - `RecordTurn` continue de produire Prompt + Response comme avant ; - pas de harvest sur timeout/cancel/réponse absente. **Tauri / state** - composition root câble le harvester ; - événement `MemorySaved` relayé comme avant. **Frontend** - `MemoryPanel` se rafraîchit sur `MemorySaved` ; - pas de refresh sur événements sans rapport ; - l’éditeur manuel continue de fonctionner. Commandes ciblées : ```bash cargo test -p domain memory_harvest cargo test -p application memory_harvest orchestrator_service cargo test -p app-tauri state npm --prefix frontend test -- memory ``` Validation large : ```bash cargo test npm --prefix frontend test ``` ### Ordre de Délégation Recommandé 1. **Architecture** : valider le format `idea-memory`, les invariants et le point de branchement exact après `RecordTurn`. 2. **DevBackend** : domaine parser + use case `HarvestMemoryFromTurn` + wiring orchestrateur/state. 3. **DevFrontend** : refresh MemoryPanel sur événements mémoire, sans nouvelle UI lourde. 4. **QA** : tests ciblés parser/application/orchestrateur/frontend, puis régression Work A-D. 5. **Git** : revue diff, vérification que le lot ne touche pas MCP/AppImage ni les docs hors corrections nécessaires. ### Suite Après E1 E1 prépare deux lots futurs, sans les mélanger : - **E2** : extraction assistée depuis handoff/log avec stratégie substituable, probablement LLM optionnel ou heuristique plus riche. - **MCP/AppImage e2e** : validation réelle des outils IdeA dans un agent lancé depuis l’application packagée. Décision : faire E1 maintenant. C’est le plus petit lot qui transforme les conversations observables en apprentissage durable, tout en respectant les frontières existantes et sans risquer une mémoire automatique bruitée. \ No newline at end of file diff --git a/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/log.jsonl b/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/log.jsonl index 1816d15..6015ead 100644 --- a/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/log.jsonl +++ b/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/log.jsonl @@ -41,3 +41,5 @@ {"id":"998a4f67-a352-4e89-a5ee-71260d89f112","conversation":"7a2f446c-2efa-0c56-08dd-07cdc5cac19b","atMs":1781975262249,"source":{"kind":"agent","agentId":"dce19c75-9669-4e45-b8de-9950025157da"},"role":"response","text":"## Lot C — Conversation summaries dans le panneau Work\n\n### Objectif produit\n\nObjectif : enrichir le panneau `Work` avec une vue lisible des fils de conversation associés aux tickets/live work, sans exposer ni charger tout `log.jsonl`.\n\nInclus :\n- afficher, pour chaque `conversationId` visible dans le work-state, un résumé compact issu du `handoff.md` quand il existe ;\n- compléter par un aperçu borné des derniers tours du `ConversationLog` quand le handoff est absent ou vide ;\n- signaler sobrement l’état de preview : `ready`, `missing`, `partial`, `unavailable` ;\n- rester strictement read-only et best-effort : aucune écriture, aucun recalcul de handoff, aucun blocage de `live/busy/tickets` si un fichier est absent/corrompu.\n\nExclus :\n- pas d’action utilisateur sur les conversations ;\n- pas de lecture complète affichée du journal ;\n- pas de mutation/réparation des logs/handoff ;\n- pas de génération LLM de résumé ;\n- pas de mémoire automatique Lot E ;\n- pas de changement de FIFO, reprise, cancel, requeue ou actions Lot D.\n\n### Source de vérité backend\n\nSource durable primaire : `HandoffStore` (`.ideai/conversations//handoff.md`).\n\nSource durable secondaire : `ConversationLog` (`.ideai/conversations//log.jsonl`) uniquement via une lecture bornée (`last(n)`), pour construire une preview courte quand le handoff est absent ou inutilisable.\n\n`ConversationRegistry` ne doit pas être la source de vérité du Lot C : il est live/in-memory, utile pour résoudre ou suivre des paires pendant l’orchestration, mais le panneau Work doit refléter les conversations déjà matérialisées par les tickets et leur persistance durable.\n\nSource des conversations à inspecter : les `conversation_id` déjà présents dans les `QueuedTicketSnapshot` projetés par `GetProjectWorkState`. En option additive, si le snapshot live expose déjà un `conversation_id` stable ailleurs, on peut l’ajouter plus tard, mais Lot C doit se limiter aux conversations visibles par les tickets pour éviter d’ouvrir un chantier layout/session.\n\n### Frontières hexagonales\n\nDomaine :\n- réutiliser les ports existants `ConversationLog` et `HandoffStore` ;\n- ajouter seulement des value objects purs de read-model si nécessaire : `ConversationWorkSummary`, `ConversationPreviewStatus`, éventuellement `ConversationTurnPreview` ;\n- aucun accès FS, aucun Tauri, aucun type UI.\n\nApplication :\n- étendre `GetProjectWorkState` avec un collaborateur de lecture de previews, ou extraire un petit service applicatif `ConversationPreviewReader` utilisé par `GetProjectWorkState` ;\n- dépendances injectées : `Arc` et `Arc`, ou `Option>` si on veut rendre l’enrichissement désactivable dans les tests legacy ;\n- le use case continue à renvoyer le work-state même si toutes les previews échouent.\n\nInfrastructure :\n- aucune nouvelle persistance ;\n- réutiliser `FsHandoffStore` et `FsConversationLog` déjà câblés sous `/.ideai/conversations/` ;\n- si `FsHandoffStore::load` renvoie une erreur de parsing, l’application la convertit en preview `unavailable/partial`, pas en `AppError` global ;\n- `FsConversationLog::last` est déjà tolérant aux lignes corrompues, donc c’est le bon fallback.\n\nTauri :\n- garder la commande existante `get_project_work_state` ;\n- faire évoluer `ProjectWorkStateDto` additivement ;\n- ne pas créer `get_conversation_log` ni endpoint brut.\n\nFrontend :\n- types TS miroir dans le domaine UI ;\n- adapter Tauri inchangé côté méthode (`getProjectWorkState`) ;\n- mock gateway normalise `conversations: []` pour compat tests ;\n- composant `ProjectWorkStatePanel` mappe `conversationId -> summary` et affiche une ligne compacte par ticket/thread.\n\n### DTO recommandé\n\nPréférence : étendre `ProjectWorkState` avec une liste top-level de résumés, référencée par `conversationId`. Ça évite de dupliquer le résumé sur plusieurs tickets du même fil.\n\nRust application :\n\n```rust\npub struct ProjectWorkState {\n pub agents: Vec,\n pub conversations: Vec,\n}\n\npub struct ConversationWorkSummary {\n pub conversation_id: ConversationId,\n pub status: ConversationPreviewStatus,\n pub objective_preview: Option,\n pub summary_preview: Option,\n pub summary_len: usize,\n pub up_to: Option,\n pub recent_turns: Vec,\n}\n\npub enum ConversationPreviewStatus {\n Ready,\n Missing,\n Partial,\n Unavailable,\n}\n\npub struct ConversationTurnWorkPreview {\n pub role: TurnRole,\n pub source: TicketWorkSource,\n pub at_ms: u64,\n pub text_preview: String,\n pub text_len: usize,\n}\n```\n\nWire DTO camelCase :\n\n```ts\nexport interface ProjectWorkState {\n agents: AgentWorkState[];\n conversations: ConversationWorkSummary[];\n}\n\nexport type ConversationPreviewStatus =\n | \"ready\"\n | \"missing\"\n | \"partial\"\n | \"unavailable\";\n\nexport interface ConversationWorkSummary {\n conversationId: string;\n status: ConversationPreviewStatus;\n objectivePreview?: string;\n summaryPreview?: string;\n summaryLen: number;\n upTo?: string;\n recentTurns: ConversationTurnWorkPreview[];\n}\n\nexport interface ConversationTurnWorkPreview {\n role: \"prompt\" | \"response\" | \"toolActivity\";\n source: TicketWorkSource;\n atMs: number;\n textPreview: string;\n textLen: number;\n}\n```\n\n`AgentTicketState` ne change pas : il porte déjà `conversationId`, `taskPreview`, `taskLen`, `status`, `requesterLabel`. Le frontend joint via `conversationId`.\n\nCompatibilité : les mocks et tests peuvent normaliser `conversations` à `[]` quand absent, comme ils le font déjà pour `tickets`.\n\n### Stratégie preview/handoff\n\nBornes proposées :\n- `HANDOFF_PREVIEW_MAX_CHARS = 480` ;\n- `OBJECTIVE_PREVIEW_MAX_CHARS = 160` ;\n- `RECENT_TURNS_MAX = 3` ;\n- `TURN_PREVIEW_MAX_CHARS = 220`.\n\nAlgorithme par `conversationId` unique :\n1. tenter `handoffs.load(conversation)` ;\n2. si `Ok(Some(handoff))` :\n - `status = ready` ;\n - `summaryPreview = normalize_truncate(handoff.summary_md, 480)` ;\n - `summaryLen = handoff.summary_md.chars().count()` ;\n - `objectivePreview = handoff.objective.map(truncate 160)` ;\n - `upTo = Some(handoff.up_to)` ;\n3. si `Ok(None)` : `status = missing`, puis tenter `log.last(conversation, 3)` ;\n4. si handoff illisible mais `log.last` marche : `status = partial`, `summaryPreview = None`, `recentTurns = ...` ;\n5. si handoff et log échouent : `status = unavailable`, champs preview vides ;\n6. dans tous les cas, ne jamais propager l’erreur au `ProjectWorkState` global.\n\nNormalisation : même règle que `task_preview` Lot B : trim + collapse whitespace + troncature à la borne, jamais mid-codepoint. On peut factoriser un helper applicatif privé dans `workstate`.\n\nImportant : ne pas appeler `ConversationLog::read(None)` dans Lot C. Le fallback doit être `last(RECENT_TURNS_MAX)` uniquement.\n\n### UI minimale\n\nDans `ProjectWorkStatePanel` :\n- garder la structure actuelle agent > tickets ;\n- sous chaque ticket, afficher une ligne thread compacte si un résumé existe :\n - objectif si présent : `Goal: ` ;\n - sinon résumé : `` ;\n - sinon dernier tour : `Last: ` ;\n - badge discret selon statut : `Summary`, `No summary`, `Partial`, `Unavailable` ;\n- ne pas ajouter de modal ni bouton “ouvrir log” ;\n- ne pas afficher les fichiers, chemins, ni JSON brut ;\n- si `conversationId` n’a pas de summary dans la map, ne pas casser la ligne ticket : afficher seulement l’état Lot B existant.\n\nLe panneau doit rester scannable : pas de cartes imbriquées, pas de gros bloc Markdown rendu. Texte simple, clamp/line wrap raisonnable.\n\n### Tests attendus\n\nApplication (`crates/application/tests/workstate.rs`) :\n- conserve agents/live/busy/tickets même si le preview reader échoue ;\n- déduplique les `conversationId` de plusieurs tickets ;\n- handoff présent produit `ready`, `objectivePreview`, `summaryPreview`, `summaryLen`, `upTo` ;\n- handoff absent + log présent produit `missing` avec `recentTurns` borné ;\n- handoff corrompu + log lisible produit `partial` ;\n- handoff/log indisponibles produit `unavailable` sans `AppError` global ;\n- previews tronquées et whitespace normalisé.\n\nInfrastructure (`crates/infrastructure/tests/conversation_log.rs`) :\n- normalement peu ou pas de nouveau test requis si `FsConversationLog::last` et `FsHandoffStore::load` sont déjà couverts ;\n- ajouter seulement un test si un helper d’adapter dédié apparaît, mais éviter d’en créer un.\n\nTauri DTO (`crates/app-tauri/tests/dto_agents.rs`) :\n- sérialisation camelCase de `conversations` ;\n- conversion des statuts `ready/missing/partial/unavailable` ;\n- compat additive avec agents/tickets existants.\n\nFrontend (`frontend/src/features/workstate/workstate.test.tsx`) :\n- mock sans `conversations` normalisé à `[]` ;\n- ticket avec `conversationId` affiche objectif/résumé ;\n- status `partial`/`unavailable` ne masque pas le ticket ;\n- refresh existant sur `agentBusyChanged` / `delegationReady` continue.\n\nCommandes :\n\n```bash\ncargo test -p application workstate\ncargo test -p app-tauri dto_agents\ncargo test -p infrastructure conversation_log\nnpm --prefix frontend test -- workstate\n```\n\nSelon l’organisation actuelle du workspace, finir par :\n\n```bash\ncargo test\nnpm --prefix frontend test\n```\n\n### Décision de lot\n\nLot C doit être un enrichissement read-only de `ProjectWorkState`, pas une nouvelle surface d’action. Le backend reste source d’autorité pour les bornes et la robustesse ; le frontend ne fait que joindre `tickets[].conversationId` vers `state.conversations[]` et rendre une preview compacte. Ça respecte les contraintes : aucune nouvelle persistance, aucun log brut exposé, best-effort par conversation, et pas de mélange avec les futures actions de gestion ou la mémoire automatique."} {"id":"d3798b0f-d037-44be-b407-976e8e250da6","conversation":"7a2f446c-2efa-0c56-08dd-07cdc5cac19b","atMs":1781976583977,"source":{"kind":"agent","agentId":"a6ced819-b893-4213-b003-9e9dc79b9641"},"role":"prompt","text":"Après Lots A/B/C `workstate` mergés dans `develop` @ `6e1ba7e`, cadre le prochain lot prioritaire. L'utilisateur demande d'enchaîner sur tout ce qu'il reste.\n\nContexte livré:\n- Lot A: panneau/read-model live/busy par agent.\n- Lot B: tickets FIFO par agent, source human/agent, statuts inProgress/queued.\n- Lot C: summaries de conversations best-effort top-level, jointes via conversationId.\n\nObjectif pressenti: Lot D — actions UX contrôlées depuis Work: ouvrir/rattacher l'agent/la cellule, voir la conversation, arrêter un agent, copier résumé. Merci de choisir le périmètre minimal prioritaire et de cadrer précisément.\n\nMerci de définir:\n- objectif produit et inclus/exclus,\n- actions à livrer dans ce lot, avec priorité,\n- ports/use cases backend existants à réutiliser ou nouveaux contrats nécessaires,\n- frontières Tauri/frontend,\n- contraintes de sécurité/permissions et cohérence avec single-agent/live-session,\n- impacts UI dans le panneau Work,\n- tests attendus et commandes.\n\nContraintes:\n- Ne pas mélanger avec mémoire automatique Lot E.\n- Ne pas inventer une nouvelle session si une action doit seulement focaliser/rattacher l'existant.\n- Ne pas casser l'historique conversationnel ni exposer log brut.\n- Ne code pas."} {"id":"e1535768-8ce1-41b7-bc7e-e549fe28ea00","conversation":"7a2f446c-2efa-0c56-08dd-07cdc5cac19b","atMs":1781976715720,"source":{"kind":"agent","agentId":"dce19c75-9669-4e45-b8de-9950025157da"},"role":"response","text":"## Lot D — actions contrôlées depuis Work\n\n### Objectif produit\n\nRendre le panneau `Work` opérable sans changer sa nature : il reste un tableau de bord live/conversations, mais l’utilisateur peut agir sur l’état déjà visible.\n\nPérimètre minimal prioritaire :\n- retrouver ou afficher la cellule d’un agent live ;\n- rattacher une session déjà live à une cellule existante et déterminée ;\n- arrêter explicitement un agent live ;\n- voir davantage de la preview conversationnelle Lot C ;\n- copier le résumé affiché.\n\nExclus :\n- pas de lancement d’un agent neuf depuis Work ;\n- pas de création automatique de cellule/layout ;\n- pas de cancel/requeue/priorisation FIFO ;\n- pas de modification de handoff, mémoire automatique ou Lot E ;\n- pas de lecture/exposition brute de `log.jsonl` ;\n- pas de transcript complet.\n\n### Actions à livrer\n\nPriorité 1 — `Open` / `Go to cell`\n- Si `agent.live.nodeId` correspond à une cellule visible dans le layout actif : focus/scroll/highlight de cette cellule.\n- Aucun appel backend. Aucun spawn.\n- Libellé UI : `Open` ou `Go to cell` selon la place.\n\nPriorité 2 — `Attach`\n- Si l’agent est live mais sa cellule hôte n’est plus visible, rattacher la session à une cellule existante et non ambiguë.\n- Cible autorisée pour ce lot : une cellule visible déjà pinnée sur le même agent et sans session, ou une cellule explicitement sélectionnée par l’utilisateur dans le layout.\n- Si aucune cible déterministe : action désactivée avec message court du type `Pin this agent to a cell first`.\n- Ne jamais créer une nouvelle session. Ne jamais appeler `launch_agent`.\n\nPriorité 3 — `Stop`\n- Arrête la session live de l’agent après confirmation.\n- Fonctionne pour PTY et structured.\n- Ne supprime pas l’agent, ne supprime pas le handoff, ne vide pas `conversationId`.\n- Après succès : refresh Work + layout/live state via events existants ou refresh explicite.\n\nPriorité 4 — `View conversation`\n- Expansion inline dans Work sur la base de `ProjectWorkState.conversations[]`.\n- Montre objectif, summary preview, et les `recentTurns` déjà bornés par Lot C.\n- Pas de nouveau endpoint de transcript.\n\nPriorité 5 — `Copy summary`\n- Frontend-only via clipboard.\n- Copie uniquement le texte déjà exposé par Lot C : objectif + summary preview + recent turns bornés si summary absent.\n- Désactivé si aucune preview exploitable.\n\n### Backend\n\nRéutiliser :\n- `GetProjectWorkState` : source unique du panneau, inchangé fonctionnellement ;\n- `LiveSessions` / `TerminalSessions` / `StructuredSessions` : source de vérité live ;\n- `LayoutGateway.mutateLayout` avec `setSession` / `setCellAgent` ;\n- `close_terminal` et `close_agent_session` comme primitives existantes ;\n- `attach_live_agent` existant, mais à durcir.\n\nContrats backend recommandés :\n\n1. Faire évoluer `attach_live_agent` en vrai use case applicatif `AttachLiveAgent`.\n\nEntrée :\n```rust\npub struct AttachLiveAgentInput {\n pub project: Project,\n pub agent_id: AgentId,\n pub node_id: NodeId,\n}\n```\n\nSortie :\n```rust\npub struct AttachLiveAgentOutput {\n pub agent_id: AgentId,\n pub node_id: NodeId,\n pub session_id: SessionId,\n pub kind: LiveSessionKind,\n}\n```\n\nComportement :\n- chercher la session live dans `TerminalSessions`, sinon `StructuredSessions` ;\n- rebind `node_id` sans spawn ;\n- retourner `NOT_FOUND` si l’agent n’est plus live ;\n- idempotent si déjà attaché à cette cellule ;\n- ne touche pas `ConversationLog`, `HandoffStore`, `ProviderSessionStore`.\n\n2. Ajouter `StopLiveAgent` plutôt que faire deviner le registre au frontend.\n\nEntrée :\n```rust\npub struct StopLiveAgentInput {\n pub project: Project,\n pub agent_id: AgentId,\n}\n```\n\nSortie :\n```rust\npub struct StopLiveAgentOutput {\n pub agent_id: AgentId,\n pub session_id: SessionId,\n pub kind: LiveSessionKind,\n}\n```\n\nComportement :\n- résoudre la session live par agent ;\n- si PTY : déléguer à `CloseTerminal` / `PtyPort.kill` ;\n- si structured : `StructuredSessions::remove` puis `AgentSession::shutdown()` ;\n- publier `DomainEvent::AgentExited { agent_id, code }` ou l’équivalent déjà relayé ;\n- no-op contrôlé ou `NOT_FOUND` si déjà arrêté. Préférence : `NOT_FOUND` côté API, frontend l’interprète comme refresh.\n\nPourquoi pas seulement `close_terminal(sessionId)` : `WorkState.live.kind` existe, et le modèle a deux registries. Une commande par agent garde la cohérence single-agent côté backend.\n\n### Tauri / DTO\n\nNouvelles/évolutions :\n\n```ts\nattach_live_agent({ request: { projectId, agentId, nodeId } })\n -> { agentId, nodeId, sessionId, kind }\n\nstop_live_agent({ request: { projectId, agentId } })\n -> { agentId, sessionId, kind }\n```\n\n`LiveAgentDto` peut être réutilisé si on lui ajoute/garantit `kind`; sinon créer un DTO dédié. Préférence : enrichir `LiveAgent` additivement avec `kind: \"pty\" | \"structured\"`, cohérent avec `LiveWorkSession.kind`.\n\nPas de commande `get_conversation_log`, `open_conversation`, `copy_summary`.\n\n### Frontend\n\nPorts UI :\n- `AgentGateway.attachLiveAgent(projectId, agentId, nodeId): Promise` doit retourner `kind` ;\n- ajouter `AgentGateway.stopLiveAgent(projectId, agentId): Promise` ;\n- ne pas faire appeler `close_terminal` directement par `ProjectWorkStatePanel` pour stopper un agent ;\n- `LayoutGateway.loadLayout/mutateLayout` réutilisés pour trouver/attacher la cellule cible.\n\nCoordination UI recommandée :\n- extraire le helper de focus cellule (`goToCell`) de `LayoutGrid` vers `features/layout/focusCell.ts` ;\n- ajouter un petit hook `useWorkActions(projectId, activeLayoutId?)` consommé par `ProjectWorkStatePanel` ;\n- le hook sait charger le layout actif, trouver les feuilles, vérifier si `live.nodeId` est visible, et appeler `attachLiveAgent + mutateLayout(setCellAgent/setSession)` si une cible déterministe existe.\n\n### Sécurité / cohérence\n\nRègles obligatoires :\n- `Open` et `Attach` ne spawnent jamais. Interdit d’appeler `launchAgent` depuis Work dans Lot D.\n- `Attach` ne doit pas écraser une cellule qui héberge déjà une session différente, sauf remplacement explicite futur hors lot.\n- `Stop` demande confirmation ; c’est une action destructive sur le process, mais pas sur les fichiers.\n- `Stop` ne supprime ni agent, ni tickets, ni conversation summary, ni handoff.\n- `Stop` doit libérer la live registry pour préserver l’invariant “un agent = une session live max”.\n- Si un ticket est `inProgress`, afficher dans la confirmation que le tour en cours sera interrompu ; ne pas tenter de résoudre le ticket dans ce lot.\n- Pas de permission filesystem nouvelle. Les seules mutations durables sont les mutations layout déjà existantes (`setSession`/`setCellAgent`) lors d’un attach.\n\n### UI minimale\n\nDans chaque ligne agent :\n- bouton `Open` si live ;\n- bouton `Attach` si live mais cellule non visible et une cible existe ;\n- bouton `Stop` si live ;\n- état désactivé explicite si action impossible.\n\nDans chaque ticket/summary :\n- bouton disclosure `View` pour ouvrir/fermer les détails Lot C ;\n- bouton `Copy` à côté du summary, pas sur toute la ligne ;\n- garder l’UI dense : pas de drawer global, pas de modal conversation, pas de rendu Markdown lourd.\n\nFeedback :\n- erreurs action en inline dans le panneau ;\n- après `Stop`/`Attach`, refresh `getProjectWorkState` et layout actif ;\n- `Copy` affiche un état bref `Copied`.\n\n### Tests attendus\n\nApplication Rust :\n- `AttachLiveAgent` rebind PTY sans changer `session_id` ;\n- `AttachLiveAgent` rebind structured sans changer `session_id` ;\n- attach agent absent => `NOT_FOUND` ;\n- attach même node => idempotent ;\n- `StopLiveAgent` arrête PTY et retire la session live ;\n- `StopLiveAgent` arrête structured via `shutdown()` et retire la session live ;\n- stop absent => `NOT_FOUND` ;\n- stop ne touche pas handoff/log/provider sessions.\n\nTauri DTO/tests :\n- request camelCase pour `attach_live_agent` et `stop_live_agent` ;\n- response sérialise `kind`; \n- erreurs `INVALID` ids et `NOT_FOUND` live session.\n\nFrontend tests :\n- `Open` focus une cellule visible et n’appelle aucun launch ;\n- `Attach` appelle `attachLiveAgent` puis `mutateLayout(setSession)` sur une cible déterministe ;\n- `Attach` désactivé sans cible ;\n- `Stop` demande confirmation puis appelle `stopLiveAgent`, refresh Work ;\n- `Stop` sur agent busy affiche l’avertissement ;\n- `View conversation` n’affiche que les champs Lot C ;\n- `Copy summary` copie uniquement la preview exposée, pas de log brut ;\n- pas de régression des refresh events Lot A/B/C.\n\nCommandes :\n\n```bash\ncargo test -p application terminal_usecases structured_registry workstate\ncargo test -p app-tauri dto_agents list_live_agents_r0b\nnpm --prefix frontend test -- workstate\nnpm --prefix frontend test -- LayoutGrid singletonAgent agentAlreadyRunning\n```\n\nPuis en validation large :\n\n```bash\ncargo test\nnpm --prefix frontend test\n```\n\n### Décision de cadrage\n\nLot D doit être “actions sur état existant”, pas “nouveau centre de contrôle”. Le seul ajout backend vraiment justifié est de faire porter `attach/stop` par des contrats agent-level qui respectent PTY + structured et l’invariant single-agent. La conversation reste une preview bornée issue de Lot C ; le log brut et la mémoire automatique restent hors périmètre."} +{"id":"419f4fb6-9132-416b-84fc-475d20e60c4c","conversation":"7a2f446c-2efa-0c56-08dd-07cdc5cac19b","atMs":1781977958635,"source":{"kind":"agent","agentId":"a6ced819-b893-4213-b003-9e9dc79b9641"},"role":"prompt","text":"Après Lots Work A/B/C/D mergés dans `develop` @ `c1e99d1`, l'utilisateur demande d'enchaîner sur tout ce qu'il reste. Détermine le prochain chantier prioritaire hors Work et cadre un lot implémentable.\n\nContexte livré:\n- A: read-model/panneau live/busy.\n- B: tickets FIFO par agent.\n- C: summaries conversationnels best-effort.\n- D: actions contrôlées Open/Attach/Stop/View/Copy.\n\nRestes connus mémoire projet:\n- persistance conversationnelle produit/cross-profile plus profonde,\n- handoff/canonical log incrémental,\n- mise à jour automatique mémoire/contexte pendant la vie d'un agent,\n- docs architecture potentiellement obsolètes,\n- intégration MCP réelle/AppImage/e2e.\n\nMerci de choisir le prochain lot prioritaire le plus utile et cadrer précisément:\n- objectif produit,\n- inclus/exclus,\n- frontières domaine/application/infra/Tauri/frontend si applicable,\n- tests attendus,\n- ordre de délégation recommandé.\n\nNe code pas."} +{"id":"8f958013-ea37-49e1-967f-aa3cdab42835","conversation":"7a2f446c-2efa-0c56-08dd-07cdc5cac19b","atMs":1781978071201,"source":{"kind":"agent","agentId":"dce19c75-9669-4e45-b8de-9950025157da"},"role":"response","text":"## Prochain chantier prioritaire : Lot E1 — Auto-memory contrôlée aux checkpoints\n\n### Choix\n\nLe prochain lot hors Work le plus utile est **la capitalisation automatique en mémoire projet**, mais en première marche contrôlée : écrire/mettre à jour la mémoire uniquement quand un agent émet une directive mémoire explicite dans sa réponse de fin de tour.\n\nPourquoi ce lot maintenant :\n- Work A-D donne la visibilité et les actions sur les conversations.\n- Le log/handoff incrémental et le cross-profile sont déjà très avancés dans le code/tests (`RecordTurn`, `FsConversationLog`, `FsHandoffStore`, provider sessions, swap préservant le pair id).\n- La mémoire existe déjà : CRUD, `FsMemoryStore`, recall, injection au lancement, MCP `idea_memory_read/write`.\n- Le vrai trou produit restant est que l’utilisateur ne doit pas avoir à transformer lui-même les apprentissages d’un agent en mémoire durable.\n\nCe lot est volontairement **déterministe et safe** : pas d’extraction LLM libre depuis tout le log, pas d’écriture depuis un résumé ambigu, pas de pollution massive.\n\n### Objectif Produit\n\nQuand un agent termine un tour et indique explicitement une information à mémoriser, IdeA la persiste automatiquement dans `.ideai/memory/` sans action utilisateur.\n\nL’agent peut donc écrire, dans sa réponse finale, une directive structurée du genre :\n\n```markdown\n```idea-memory\nslug: architecture-workstate-actions\ntitle: Work panel actions\ntype: project\ndescription: Work actions are Open/Attach/Stop/View/Copy, without spawning new sessions.\n---\nThe Work panel may focus or attach existing sessions, but must not launch a new agent from Work.\n```\n```\n```\n\nIdeA détecte ce bloc au checkpoint, le valide, puis crée ou remplace la note mémoire correspondante via les ports existants.\n\n### Inclus\n\n- Parser déterministe de blocs `idea-memory` dans les réponses agent.\n- Un use case applicatif `HarvestMemoryFromTurn` branché après un `Response` turn réussi.\n- Écriture via le store mémoire existant, sous garde fichier quand applicable.\n- Publication `MemorySaved` existante pour que l’UI puisse se rafraîchir.\n- Injection d’une courte consigne dans le contexte agent : quand une décision durable est prise, émettre un bloc `idea-memory`.\n- Best-effort strict : une directive invalide ou une erreur mémoire ne fait jamais échouer la réponse, le ticket, ni le handoff.\n\n### Exclus\n\n- Pas d’extraction automatique libre depuis tout `log.jsonl`.\n- Pas de summarizer LLM.\n- Pas de création de “suggestions” à valider dans une nouvelle persistance.\n- Pas de refonte du MemoryPanel.\n- Pas d’auto-modification du contexte global `CLAUDE.md`.\n- Pas de mélange avec l’e2e MCP/AppImage.\n- Pas de changement des règles Work A-D.\n\n### Frontières Hexagonales\n\n**Domaine**\n\nAjouter des value objects purs, sans I/O :\n\n```rust\npub struct MemoryHarvestDirective {\n pub slug: MemorySlug,\n pub title: String,\n pub description: String,\n pub r#type: MemoryType,\n pub body: MarkdownDoc,\n}\n\npub struct MemoryHarvestReport {\n pub found: usize,\n pub saved: usize,\n pub skipped: usize,\n}\n```\n\nLe domaine porte les invariants : slug valide, body non vide, type autorisé. Pas de `tokio`, pas de FS.\n\n**Application**\n\nNouveau use case :\n\n```rust\npub struct HarvestMemoryFromTurn {\n memories: Arc,\n events: Arc,\n // optionnel si on veut passer par le garde existant :\n // guard: Arc,\n}\n\npub struct HarvestMemoryFromTurnInput {\n pub project: Project,\n pub conversation: ConversationId,\n pub agent_id: AgentId,\n pub turn: ConversationTurn,\n}\n```\n\nRègles :\n- ne traiter que `TurnRole::Response` ;\n- parser uniquement le texte du tour courant ;\n- pour chaque directive valide, construire un `Memory` et appeler `MemoryStore::save(project.root, &memory)` ;\n- publier `DomainEvent::MemorySaved { slug }` ;\n- retourner un rapport, mais ne jamais propager l’erreur vers l’orchestration live si appelé en mode checkpoint.\n\nBranchement :\n- dans le chemin où `RecordTurn` est déjà appelé pour la réponse d’un `ask` réussi ;\n- après append/handoff, car le log canonique reste prioritaire ;\n- si harvest échoue, log diagnostic seulement.\n\n**Infrastructure**\n\nAucun nouveau stockage.\n\nRéutiliser :\n- `FsMemoryStore` ;\n- `FsConversationLog` / `FsHandoffStore` restent inchangés ;\n- `FileGuard` si le wiring courant permet de passer par `WriteMemory`, sinon utiliser directement `MemoryStore` mais garder le lot borné aux écritures IdeA internes.\n\n**Tauri**\n\nComposition root :\n- construire `HarvestMemoryFromTurn` avec le `memory_store_port` et `event_bus` existants ;\n- l’injecter dans l’orchestrateur à côté de `RecordTurnProvider`.\n\nPas de nouvelle commande Tauri obligatoire.\n\n**Frontend**\n\nMinimal :\n- `useMemory` doit écouter `MemorySaved` / `MemoryDeleted` / `MemoryIndexRebuilt` et rafraîchir l’index si le panneau mémoire est monté.\n- Aucun nouvel écran.\n- Optionnel : afficher une ligne discrète dans MemoryPanel quand l’index change, mais pas nécessaire pour E1.\n\n### Format Directive Recommandé\n\nBloc fenced unique ou multiple :\n\n```markdown\n```idea-memory\nslug: kebab-case-slug\ntitle: Human title\ntype: project|reference|feedback|user\ndescription: One-line recall hook\n---\nMarkdown body to persist.\n```\n```\n\nContraintes :\n- `slug` obligatoire ;\n- `title` obligatoire ;\n- `description` obligatoire et bornée ;\n- `body` obligatoire ;\n- taille max par bloc, par exemple `8 KiB` ;\n- max blocs par réponse, par exemple `3` ;\n- bloc invalide ignoré individuellement, sans bloquer les autres.\n\n### Sécurité et Qualité Mémoire\n\n- Une réponse ne peut pas écrire hors `.ideai/memory` car elle passe par `MemorySlug` + `MemoryStore`.\n- Pas de chemins arbitraires.\n- Pas de suppression automatique.\n- Remplacement idempotent par slug : une même décision peut être raffinée.\n- Les agents doivent recevoir la consigne : n’émettre un bloc mémoire que pour des décisions durables, conventions, préférences utilisateur ou faits projet stables.\n- Ne pas mémoriser les tâches temporaires, logs d’exécution, tickets, erreurs transitoires.\n\n### Tests Attendues\n\n**Domaine / parser**\n\n- parse un bloc valide ;\n- parse plusieurs blocs ;\n- refuse slug invalide ;\n- refuse body vide ;\n- borne taille et nombre de blocs ;\n- ignore les fences non `idea-memory` ;\n- conserve le Markdown du body sans corruption.\n\n**Application**\n\n- `HarvestMemoryFromTurn` ignore `Prompt` et `ToolActivity` ;\n- une `Response` valide crée/remplace une note mémoire ;\n- publie `MemorySaved` par note sauvegardée ;\n- directives partiellement invalides : sauvegarde les valides, skip les invalides ;\n- erreur `MemoryStore` convertie en rapport/diagnostic sans casser l’appelant checkpoint ;\n- pas de lecture du `log.jsonl` complet.\n\n**Orchestrateur**\n\n- un `idea_ask_agent` réussi avec réponse contenant un bloc `idea-memory` écrit la mémoire après reply ;\n- la réponse rendue au demandeur ne contient pas d’erreur même si la mémoire échoue ;\n- `RecordTurn` continue de produire Prompt + Response comme avant ;\n- pas de harvest sur timeout/cancel/réponse absente.\n\n**Tauri / state**\n\n- composition root câble le harvester ;\n- événement `MemorySaved` relayé comme avant.\n\n**Frontend**\n\n- `MemoryPanel` se rafraîchit sur `MemorySaved` ;\n- pas de refresh sur événements sans rapport ;\n- l’éditeur manuel continue de fonctionner.\n\nCommandes ciblées :\n\n```bash\ncargo test -p domain memory_harvest\ncargo test -p application memory_harvest orchestrator_service\ncargo test -p app-tauri state\nnpm --prefix frontend test -- memory\n```\n\nValidation large :\n\n```bash\ncargo test\nnpm --prefix frontend test\n```\n\n### Ordre de Délégation Recommandé\n\n1. **Architecture** : valider le format `idea-memory`, les invariants et le point de branchement exact après `RecordTurn`.\n2. **DevBackend** : domaine parser + use case `HarvestMemoryFromTurn` + wiring orchestrateur/state.\n3. **DevFrontend** : refresh MemoryPanel sur événements mémoire, sans nouvelle UI lourde.\n4. **QA** : tests ciblés parser/application/orchestrateur/frontend, puis régression Work A-D.\n5. **Git** : revue diff, vérification que le lot ne touche pas MCP/AppImage ni les docs hors corrections nécessaires.\n\n### Suite Après E1\n\nE1 prépare deux lots futurs, sans les mélanger :\n- **E2** : extraction assistée depuis handoff/log avec stratégie substituable, probablement LLM optionnel ou heuristique plus riche.\n- **MCP/AppImage e2e** : validation réelle des outils IdeA dans un agent lancé depuis l’application packagée.\n\nDécision : faire E1 maintenant. C’est le plus petit lot qui transforme les conversations observables en apprentissage durable, tout en respectant les frontières existantes et sans risquer une mémoire automatique bruitée."} diff --git a/.ideai/memory/MEMORY.md b/.ideai/memory/MEMORY.md index b488c00..d8e2ede 100644 --- a/.ideai/memory/MEMORY.md +++ b/.ideai/memory/MEMORY.md @@ -16,3 +16,4 @@ - [checkpoint-delivery-submit-logging-fix](checkpoint-delivery-submit-logging-fix.md) — memory note checkpoint-delivery-submit-logging-fix - [checkpoint-workstate-delegation-queue-lot-b](checkpoint-workstate-delegation-queue-lot-b.md) — memory note checkpoint-workstate-delegation-queue-lot-b - [checkpoint-workstate-conversation-summaries-lot-c](checkpoint-workstate-conversation-summaries-lot-c.md) — memory note checkpoint-workstate-conversation-summaries-lot-c +- [checkpoint-workstate-controlled-actions-lot-d](checkpoint-workstate-controlled-actions-lot-d.md) — memory note checkpoint-workstate-controlled-actions-lot-d diff --git a/.ideai/memory/checkpoint-workstate-controlled-actions-lot-d.md b/.ideai/memory/checkpoint-workstate-controlled-actions-lot-d.md new file mode 100644 index 0000000..dbf654a --- /dev/null +++ b/.ideai/memory/checkpoint-workstate-controlled-actions-lot-d.md @@ -0,0 +1,64 @@ +--- +name: checkpoint-workstate-controlled-actions-lot-d +description: memory note checkpoint-workstate-controlled-actions-lot-d +metadata: + type: project +--- +# Checkpoint — Workstate controlled actions Lot D + +Date: 2026-06-20 + +## État final + +Lot D `workstate controlled actions` terminé, validé QA et mergé localement dans `develop`. + +Branche finale: +- `develop` @ `c1e99d1` — merge local `feature/workstate-controlled-actions`. +- `feature/workstate-controlled-actions` conservée. +- Aucun push, aucune branche supprimée. +- Working tree propre après décision Git. + +## Commits créés + +- `3408c96` — `feat(workstate): actions contrôlées sur le work-state (Lot D backend)` + - Use cases agent-level `AttachLiveAgent` et `StopLiveAgent`. + - Commandes Tauri `attach_live_agent` et `stop_live_agent`. + - Attach rebind PTY/structured sans spawn; stop PTY/structured. + - DTO camelCase et wiring AppState/lib. + +- `1c6441b` — `feat(workstate): UI des actions contrôlées (Lot D frontend)` + - Port/adaptateur agent alignés sur le nouveau contrat attach + stop. + - Panneau Work: Open, Attach, Stop, View conversation, Copy summary. + - Work n'appelle pas `launchAgent`; Stop passe par `stopLiveAgent`; View/Copy n'utilisent que les previews Lot C. + +- `0976648` — `chore(wip): état runtime .ideai (flux conversation live)` + - Runtime isolé des commits feature. + +- `c1e99d1` — merge local dans `develop`. + +## QA verte + +Commandes QA/Git validées: +- `cargo fmt --all -- --check` OK. +- `cargo test -p application --test workstate_actions` OK, 7 passed. +- `cargo test -p application --test workstate` OK, 21 passed. +- `cargo test -p app-tauri --test dto_agents` OK, 25 passed. +- `cargo test -p app-tauri --test list_live_agents_r0b` OK, 5 passed. +- `cargo check -p app-tauri` OK. +- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx` OK, 25 tests. +- `cd frontend && npx vitest run src/adapters/agent.test.ts` OK, 8 tests. +- `cd frontend && npx vitest run src/features/layout/singletonAgent.test.tsx src/features/layout/agentAlreadyRunning.test.tsx` OK, 9 tests. +- `cd frontend && npx tsc --noEmit` OK. + +Warnings restants uniquement côté Vite sur options `esbuild` dépréciées/ignorées au profit de `oxc`, non bloquants. + +## Décisions produit/techniques + +- Actions Lot D opèrent seulement sur l'état existant; pas de lancement d'agent neuf depuis Work. +- Attach ne crée ni session ni cellule; cible déterministe = cellule visible vide côté UI. +- Stop ne supprime ni agent, ni tickets, ni handoff/conversation summary. +- View/Copy restent limités aux previews bornées Lot C; aucun log brut exposé. + +## Suite probable + +La quadrilogie Work A/B/C/D est intégrée. Prochains chantiers restants à cadrer: persistance conversationnelle/cross-profile plus profonde, mise à jour automatique mémoire/contexte pendant la vie des agents, ou synchronisation documentaire architecture selon priorité Architect/Main. \ No newline at end of file