Files
IdeA/.ideai/conversations/7a2f446c-2efa-0c56-08dd-07cdc5cac19b/handoff.md
Blomios 7c71544692 chore(wip): état runtime .ideai + mémoire (checkpoint Lot C résumés conversation)
Conversations live, layouts, MEMORY.md et note checkpoint
workstate-conversation-summaries-lot-c.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 19:32:18 +02:00

83 KiB
Raw Blame History

upTo, objective
upTo objective
e1535768-8ce1-41b7-bc7e-e549fe28ea00 CADRAGE ARCHITECTURE (pas de code) — Support de la délégation inter-agents (idea_ask_agent/idea_reply) pour les profils CODEX.

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 lutilisateur crée beaucoup dagents il atteint une limite de requests/login. De plus un agent jamais lancé peut échouer lors dune délégation car il nest pas authentifié. Questions: 1) quelle solution respecte larchitecture hexagonale/SOLID pour partager lauth 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 dagent ? 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 <projectRoot> à 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, cest conforme à larchitecture 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 dexécution du projet, pas un détail Tauri/Codex. - StructuredSessionFactory::start peut transmettre cette donnée vers ladapter runtime concret : cest précisément le rôle de la couche application/composition dorchestrer les ports avec le contexte résolu. - Ajouter --add-dir <projectRoot> dans CodexExecSession est au bon endroit si CodexExecSession est ladapter infrastructure/presentation-backend spécifique à Codex CLI. Le flag --add-dir est un détail technique de ladapter Codex, donc il ne doit pas remonter dans le domaine. - Conditionner cet ajout à workspace-write est cohérent : lintention métier est “la session peut écrire dans le workspace résolu”, limplé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 ladapter 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. Ladapter 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 lexé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. LUI demande un lancement dagent/session ; la politique de sandbox reste dans les services/adapters backend. Formulation recommandée côté modèle : plutôt quun champ trop Codex-spécifique, utiliser un nom neutre comme workspace_roots, writable_roots, project_root, ou ExecutionScope { cwd, project_root, sandbox_mode }. Puis ladapter Codex traduit ExecutionScope + workspace-write en codex exec --add-dir <projectRoot>.
  • Prompt: Contexte: l'utilisateur veut terminer les chantiers ouverts d'IdeA, en autonomie, via le cycle Main -> Architect -> Git -> Dev -> QA -> Git. Main ne code pas. Decision Git deja obtenue: - Branche courante: feature/orchestrator-designation. - Worktree dirty important (~75 fichiers) mais analyse Git: mono-theme = chantier orchestrateur/designation + nouveau sink diagnostic crates/application/src/diag.rs pour le rendez-vous inter-agents. - Ne pas stash/switch. Premier chantier logique: finir orchestrator-designation sur cette branche, puis QA, puis Git commit/merge develop. Etat memoire projet pertinent: - Chantiers restants: agent=session vivante coherente, persistance conversationnelle, handoff cross-profile, live-state partage, refresh memoire/contexte a chaud, UX delegations/conversations, documentation architecture. - Le chantier actuel semble etre une brique d'orchestration/designation, avec modifications dans domain/application/infrastructure/app-tauri/frontend terminal write portal et permissions/sandbox/session structured. Ta mission Architect: 1. Cadre le chantier courant orchestrator-designation a partir de son nom et de l'etat Git ci-dessus: objectif architectural probable, frontieres de couches, contrats/ports/DTO a verifier, invariants attendus. 2. Donne a Main un plan de delegation concret: ce que DevBackend doit verifier/finaliser, ce que DevFrontend doit verifier/finaliser, ce que QA doit tester. 3. Donne des criteres de fin stricts pour considerer ce chantier ferme. 4. Indique s'il faut mettre a jour architect.md maintenant ou attendre la fin de la stabilisation. Ne code pas. Reponds avec idea_reply si cette tache est deleguee via IdeA.
  • Prompt: Tu es sollicité par Main pour reprendre après le checkpoint AppImage 0.3.0. Contexte utile: le chantier orchestrator-designation est intégré localement sur develop (develop ahead origin, AppImage 0.3.0 produite). Le prochain chantier recommandé par Git est feature/agent-skill-awareness, mais il faut d'abord trancher son chevauchement avec une branche/initiative feature/agent-skills mentionnée dans les notes. Merci de cadrer précisément: 1) objectif produit/technique de agent-skill-awareness, 2) différence avec agent-skills si visible depuis le repo ou les notes, 3) frontières backend/frontend/docs, 4) contrats/ports/DTO à créer ou modifier, 5) risques et ordre d'implémentation. Ne code pas; donne un cadrage actionnable pour Git puis DevBackend/DevFrontend/QA.
  • Response: ## Cadrage feature/agent-skill-awareness État repo constaté : develop est ahead origin et contient déjà L12 agent-skills dans le code courant (Skill, SkillStore, CRUD, assignation agent↔skill, injection des bodies dans le convention file). La branche feature/agent-skill-awareness diverge avant lintégration orchestrator-designation; son diff est très large et mêlé à des changements runtime/anciens. Recommandation Git : ne pas merger la branche brute. Rejouer sélectivement les contrats utiles sur une branche neuve depuis develop. ## 1) Objectif produit/technique Objectif produit : un agent doit savoir quil possède des skills assignés et comment les utiliser, sans que lutilisateur doive le lui rappeler. Aujourdhui L12 injecte le contenu complet des skills dans # Skills, mais cest bas dans le contexte, lu comme documentation, et pas présenté comme affordance/action disponible. Objectif technique minimal : transformer linjection “dump de contenu” en modèle “à la MCP” pour les profils MCP : - section haute altitude # Skills disponibles, juste après lorchestration/capacités IdeA, listant les skills assignés par name + description ; - outil MCP read-only idea_skill_read(name) pour charger le body complet à la demande ; - champ optionnel description sur Skill, avec fallback déterministe via effective_description() ; - rétrocompat des anciens skills/index.json sans description ; - conserver un mode non-MCP compatible. Point à trancher avec Architecture : soit garder lancien dump complet pour mcp == None, soit intégrer cette découverte au futur binaire universel idea/skill built-in. Ne pas mélanger avec la refonte C-univ complète. La note de branche ajoute aussi un brief inconditionnel “capacités IdeA” dans compose_convention_file. Attention : ARCHITECTURE.md récent pousse une décision plus structurante : remplacer la prose libre dorchestration par un skill built-in Orchestration IdeA (SkillScope::Builtin). Donc, pour ce chantier, je recommande de ne pas reprendre tel quel le brief inconditionnel sauf comme correctif transitoire validé par Architecture. Le périmètre propre de agent-skill-awareness doit rester : “découverte et lecture des skills assignés”. ## 2) Différence avec feature/agent-skills agent-skills visible dans le repo = L12, déjà intégré fonctionnellement : - domaine : Skill, SkillScope::{Global, Project}, SkillRef, Agent.skills/ManifestEntry.skills ; - port : SkillStore ; - infra : FsSkillStore global + projet ; - application : CreateSkill, UpdateSkill, ListSkills, DeleteSkill, AssignSkillToAgent, UnassignSkillFromAgent ; - backend Tauri + DTO ; - frontend : features/skills, SkillGateway, assignation dans AgentsPanel ; - lancement : LaunchAgent résout les skills assignés et injecte leurs bodies dans compose_convention_file. agent-skill-awareness = couche par-dessus L12 : - rend les skills visibles comme capacités nommées ; - évite de forcer le body complet en contexte MCP ; - expose une lecture explicite du body via idea_skill_read; - ajoute description comme méta courte, pas une nouvelle famille de skills. Donc ce nest pas un doublon de agent-skills; cest une amélioration dergonomie runtime. Mais son ancienne branche embarque des changements qui chevauchent des zones modifiées depuis, donc reprise manuelle. ## 3) Frontières backend/frontend/docs Backend Domaine : - ajouter description: Option<String> à Skill avec #[serde(default)] ; - ajouter Skill::with_description et Skill::effective_description() ; - préserver with_content() en conservant la description ; - éventuellement ajouter OrchestratorCommand::ReadSkill { name, requester? } et action skill.read dans OrchestratorRequest::validate si on garde le routage MCP via modèle orchestrator. Backend Application : - CreateSkillInput reçoit description: Option<String> ; - UpdateSkill doit idéalement pouvoir modifier description aussi, pas seulement content, sinon le frontend ne peut pas éditer la méta ; - créer ReadSkill use case read-only sur le port existant SkillStore, pas de nouveau port ; - résolution par nom : project scope dabord, global ensuite ; ambiguïté dans un même scope = erreur typée ; absent = not found ; case-insensitive. Backend Lifecycle : - modifier compose_convention_file pour les profils MCP : section # Skills disponibles en amont, lignes déterministes **name** — description, mentionnant idea_skill_read(name=...) ; - garder ordre déterministe des skills assignés selon le manifest ; - décider comportement non-MCP. Option conservatrice : garder le dump # Skills complet seulement en non-MCP pour zéro régression. Option cible architecture : passer par skill built-in + futur CLI idea, mais cest un autre lot. Infrastructure / MCP : - ajouter idea_skill_read au catalogue MCP et au mapping tool → OrchestratorCommand::ReadSkill ; - dispatcher dans OrchestratorService vers ReadSkill, retour inline Markdown ; - câbler dans state.rs/composition root ; - mettre à jour les tests de compteur/catalogue MCP, actuellement sensibles aux nombres fixes. App-Tauri DTO/commands : - SkillDto bénéficie du champ automatiquement si Skill sérialise camelCase ; - CreateSkillRequestDto et UpdateSkillRequestDto doivent porter description?: string | null ; - commandes UI existantes create_skill/update_skill restent les mêmes noms. Frontend : - domain Skill ajoute description?: string | null ; - CreateSkillInput ajoute description?: string; updateSkill doit permettre de passer description + content, pas seulement content ; - SkillEditor ajoute un champ court “Description” ; en edit, description éditable ; - SkillsPanel peut afficher description sous le nom ; - mocks + tests RTL à adapter. Docs : - ARCHITECTURE.md : ajouter la décision “awareness MCP” sous §14.2 ou laligner avec §16 si le skill built-in devient la voie cible ; - agents-dev/L12-skills.md ou note dédiée : préciser que L12 crée/assigne/injecte, et que agent-skill-awareness ajoute description + affordance + read tool. ## 4) Contrats / ports / DTO À créer/modifier : - domain::Skill { description: Option<String> } avec serde default. - Skill::effective_description() -> String : description non vide trimée, sinon première ligne non vide du body sans # initial. - SkillStore : pas de nouveau port. Les impls list/get/save transportent simplement le champ. - FsSkillStore index : ajouter description dans IndexEntry, #[serde(default)], écriture camelCase. Le body reste dans md/<id>.md. - CreateSkillInput { name, description, content, scope, project_root }. - UpdateSkillInput à faire évoluer vers { scope, skill_id, description, content, project_root } ou variante patch explicite. Je conseille simple remplacement complet description + content pour rester aligné UI. - nouveau use case ReadSkill { SkillStore } : input { name, project_root }, output MarkdownDoc ou DTO { name, scope, contentMd } si on veut plus de traçabilité. Branche historique renvoyait seulement MarkdownDoc; acceptable pour MCP. - OrchestratorRequest: action/type skill.read, champ name requis. - OrchestratorCommand::ReadSkill { name, requester? }. Le requester est utile si la couche MCP veut garder la symétrie avec context/memory, mais le use case nen a pas besoin. - MCP tool idea_skill_read: input { name: string }, output contenu Markdown inline. - DTO Tauri/TS : Skill.description?: string | null, create/update requests. À ne pas créer : - pas de SkillAwarenessStore ; - pas de nouveau port dédié ; - pas de mécanisme CLI propriétaire ; - pas de lancement/exécution de skill depuis lUI. ## 5) Risques et ordre dimplémentation Risques : - la branche historique est obsolète vis-à-vis de orchestrator-designation; merge brut très risqué ; - conflit conceptuel avec la décision architecture SkillScope::Builtin / “Orchestration IdeA” : ne pas figer de prose libre qui sera supprimée juste après ; - double noms de skills : project shadow global doit être documenté ; doublons intra-scope doivent produire une erreur claire ; - assignation à chaud : un skill assigné pendant une session ne devient visible quaprès relaunch/régénération du convention file, sauf mécanisme futur ; - ancien index sans description : serde default obligatoire ; - tests MCP avec compteurs hardcodés fragiles ; préférer assertion par présence de tool. Ordre recommandé : 1. Git : créer une branche neuve depuis develop, par exemple feature/agent-skill-awareness-v2; ne cherry-pick que si les hunks sont triviaux, sinon réimplémenter depuis la note. Garder feature/agent-skill-awareness comme référence de design/test, pas comme base de merge. 2. DevBackend T1 : domaine + serde + tests purs (description, fallback, with_content conserve description, legacy JSON). 3. DevBackend T2 : FsSkillStore, use cases create/update/list, DTO Tauri. Tests store legacy + roundtrip. 4. DevBackend T3 : ReadSkill + OrchestratorCommand::ReadSkill + validation skill.read. Tests résolution project/global/ambiguous/not found. 5. DevBackend T4 : MCP catalogue/mapping/service/state. Tests tool map + dispatch + catalogue par nom. 6. DevBackend T5 : compose_convention_file awareness. Tests : MCP avec skills affiche # Skills disponibles, descriptions, appel idea_skill_read, pas de body complet si décision retenue ; non-MCP conserve baseline/dump complet ; zéro skill omet la section ; ordre avant persona ou juste après orchestration selon décision. 7. DevFrontend T6 : champ description dans domain/ports/adapters/mock/SkillEditor/SkillsPanel/tests. Ne pas toucher à lexécution de skills. 8. Docs T7 : architecture + agents-dev, en explicitant la frontière avec futur SkillScope::Builtin. 9. QA T8 : cargo test --workspace, pnpm test, puis e2e AppImage : agent neuf + skill assigné + profil MCP voit # Skills disponibles, appelle idea_skill_read, reçoit le body ; profil non-MCP ne régresse pas. Verdict court : agent-skills = socle déjà intégré ; agent-skill-awareness = affordance + lecture à la demande. Reprendre le chantier, oui, mais en branche neuve depuis develop, avec périmètre serré et sans avaler le vieux diff.
  • Prompt: Reprise chantier feature/agent-skill-awareness-v2 après hotfix livraison délégation/logs submit. Contexte: branche actuelle feature/agent-skill-awareness-v2; dirty attendu avec fichiers runtime .ideai/* et changements code du hotfix listés dans la mémoire checkpoint-delivery-submit-logging-fix. Le checkpoint précédent disait de cadrer agent-skill-awareness vs agent-skills avant implémentation. Merci de fournir le cadrage architecture: objectif exact, frontières backend/frontend, contrats/DTO/ports à toucher, et ordre d'implémentation recommandé. Ne code pas.
  • Response: ## Cadrage architecture — feature/agent-skill-awareness-v2 ### Verdict de cadrage Il faut distinguer strictement deux sujets : - agent-skills / L12 = fonctionnalité déjà existante : entité Skill, scopes Global|Project, CRUD, assignation agent↔skill dans le manifeste, injection des bodies assignés dans le convention file à lactivation. - agent-skill-awareness = rendre lagent conscient du système de skills IdeA et de la manière correcte de les utiliser/contribuer, sans réimplémenter L12. Donc le chantier recommandé nest pas un nouveau store, ni une nouvelle UI CRUD, ni un mécanisme de slash-commands fournisseur. Cest une évolution de composition de contexte agent + éventuellement une petite surface de découverte orchestrateur, gardée séparée. ### Objectif exact MVP recommandé : à chaque activation dagent, le convention file doit expliquer explicitement : 1. Les sections sous # Skills sont les workflows assignés à cet agent, utilisables quand pertinents. 2. Un skill assigné est du contexte opérationnel, pas une commande magique ni une sous-tâche fournisseur. 3. Si lagent identifie un workflow réutilisable, il doit passer par lorchestration IdeA (idea_create_skill côté MCP, ou skill.create côté protocole fichier), jamais écrire directement dans .ideai/skills/. 4. Les skills non assignés ne doivent pas être injectés intégralement à tous les agents : lassignation reste la frontière de contexte. Ce MVP ferme le flou awareness vs skills : on garde L12 comme source métier, on améliore la compréhension de lagent au moment où son contexte est généré. ### Frontières backend À toucher : - crates/application/src/agent/lifecycle.rs - compose_convention_file(...) est le point naturel : fonction pure, déjà responsable de Project root, orchestration, contexte projet, persona, skills, mémoire, handoff. - Ajouter une sous-section courte, stable et testable, par exemple dans # Orchestration IdeA ou juste avant # Skills : ## Usage des skills IdeA. - Garder lordre actuel : project root → orchestration/awareness → contexte projet → persona → skills → mémoire → handoff. - Tests application de composition dans le même fichier ou suite existante : - agent sans skills : la consigne awareness peut exister, mais pas de section # Skills si la liste est vide, pour préserver le contrat actuel. - agent avec skills : la consigne précède les bodies et les bodies restent en ordre manifeste. - mcp_enabled=true : mentionner les outils idea_create_skill / outils natifs IdeA. - mcp_enabled=false : mentionner le protocole fichier skill.create. À ne pas toucher pour le MVP : - Pas de nouveau SkillStore. - Pas de nouveau port domaine. - Pas de nouveau DTO Tauri. - Pas de mutation du manifeste. - Pas de changement des invariants Skill, SkillRef, Agent.skills. - Pas de scope Builtin tant que le projet reste sur létat réel actuel : larchitecture historique en parle, mais le code courant a déjà une prose dorchestration dynamique et le pivot structuré/MCP est livré différemment. Réintroduire Builtin serait un autre chantier. ### Frontières frontend MVP : aucune frontière frontend obligatoire. LUI skills existe déjà via : - frontend/src/domain/index.ts : Skill, SkillRef, SkillScope. - frontend/src/ports/index.ts : SkillGateway. - frontend/src/adapters/skill.ts : list/create/update/delete/assign/unassign. - frontend/src/features/skills/* : panneau et view-model L12. Éventuel ajustement UI, à garder non bloquant : afficher dans le panneau skills une phrase sobre du type “Les skills assignés sont injectés au lancement de lagent”. Pas de nouveau gateway pour ça. ### Contrats/DTO/ports à toucher MVP recommandé : - Domaine Rust : aucun nouveau type requis. - Ports Rust : aucun nouveau port. - Application : seulement la fonction pure de composition du convention file et ses tests. - Infrastructure : aucun changement. - Tauri DTO/commands : aucun changement. - Frontend DTO/ports : aucun changement. Contrats existants à respecter : - domain::Skill { id, name, content_md, scope }. - domain::SkillRef { skill_id, scope } stocké sur lagent/manifeste. - SkillStore::list/get/save/delete reste la seule abstraction de persistance. - LaunchAgent::resolve_skills reste best-effort pour les dangling refs : un skill supprimé ne bloque pas le launch. - compose_convention_file reste pure/I-O free. ### Option séparée : découverte typée des skills par agent À ne faire que si le produit veut que les agents découvrent les skills non assignés. Dans ce cas, ne pas injecter tous les bodies. Ajouter plutôt une surface compacte : - OrchestratorCommand::ListSkills { scope: Option<SkillScope> } ou ListSkills avec scope requis. - Alias protocole : skill.list. - Outil MCP : idea_list_skills(scope?). - OrchestratorService réutilise le SkillStore existant et renvoie une liste compacte { id, name, scope }, pas contentMd par défaut. Ce serait un lot séparé, parce que cela touche domain/src/orchestrator.rs, application/src/orchestrator/service.rs, ladapter MCP / watcher, et éventuellement les DTO de réponse. Ce nest pas nécessaire pour corriger lawareness des skills assignés. ### Ordre dimplémentation recommandé 1. Stabiliser le texte de contrat - Rédiger une section courte “Usage des skills IdeA”. - Deux variantes : MCP (idea_create_skill) et fichier (skill.create). - Garder linterdiction des subagents natifs dans la section orchestration existante. 2. Modifier compose_convention_file uniquement - Ajouter un helper pur si utile, par exemple compose_skill_awareness(mcp_enabled: bool) -> &'static str. - Ne pas déplacer la section # Skills ni changer la forme des bodies. 3. Ajouter les tests de composition - No-skills : pas de # Skills, awareness présente. - With-skills : awareness + bodies dans lordre. - MCP vs file : bonne consigne de création/contribution. 4. Vérifier non-régression launch - Tests ciblés application agent/lifecycle. - Puis cargo test -p application agent ou le subset existant pertinent. - Pas besoin de Vitest sauf si un texte UI est ajouté. 5. Lot optionnel discovery seulement après validation du MVP - Décider explicitement si un agent doit pouvoir lister les skills non assignés. - Si oui, implémenter skill.list / idea_list_skills comme nouvelle entrée orchestrateur compacte. ### Point dattention Le fichier ARCHITECTURE.md contient encore des sections historiques sur un vieux plan Builtin skill + idea CLI + inbox/outbox, mais létat réel du code est déjà sur AskAgent, Reply, sessions structurées, MCP bridge, mailbox/input mediator. Pour ce chantier, il faut suivre létat réel du code et ne pas rouvrir lancien plan. Le bon axe est : améliorer la conscience contextuelle des agents sur les skills existants, pas refaire lorchestration.
  • Prompt: Inventaire architecture des chantiers applicatifs restant en cours après intégration locale de agent-skill-awareness-v2. Références mémoire: remaining-work-idea-agent-control-ide liste comme suites possibles: registre session/singleton agent, live-state partagé projet, handoff/canonical conversation log cross-session/cross-profile, UX conversations/délégations, MCP IdeA-only en flux réel, mise à jour mémoire/contexte automatique, restrictions profils supportés. Merci de classer ce qui est déjà livré vs encore actif, choisir le prochain chantier à reprendre en premier, et fournir un cadrage architectural initial. Ne code pas.
  • Response: ## Inventaire post agent-skill-awareness-v2 Contexte observé : la branche locale est develop...origin/develop [ahead 15], avec seulement des fichiers runtime .ideai/conversations/* dirty. Je considère donc agent-skill-awareness-v2 intégré localement et je reclasse depuis le code actuel, pas depuis la mémoire seule. ### Déjà livré / à considérer fondation acquise 1. Registre session / singleton agent - Livré côté application : TerminalSessions, StructuredSessions, agrégateur LiveSessions, session_for_agent, node_for_agent, rebind_agent_node. - Livré côté Tauri/UI : list_live_agents, attach_live_agent, guards frontend de lancement singleton. - Statut : fondation livrée, à durcir uniquement par tests de flux réel. 2. Messagerie inter-agents / FIFO / réponse synchrone - Livré : AgentMailbox, InputMediator, AgentBusyChanged, tickets, idea_reply, résolution par ticket, timeout/cancel. - OrchestratorService::ask_agent et reply existent, avec conversation par paire. - Statut : livré applicativement, UX encore perfectible. 3. MCP IdeA-only en flux backend - Livré : bridge idea mcp-server, endpoint app-tauri, serveur MCP, outils idea_*, runtime MCP injecté au launch, badge source mcp/file côté UI. - Statut : livré côté infrastructure/app, reste validation produit en flux réel et polish observabilité. 4. Handoff / canonical conversation log cross-session / cross-profile - Plus avancé que la mémoire ne le dit : domain/src/conversation_log.rs définit ConversationLog, HandoffStore, HandoffSummarizer, ProviderSessionStore. - Infra livrée : FsConversationLog, FsHandoffStore, FsProviderSessionStore, HeuristicHandoffSummarizer. - App livrée : RecordTurn, injection handoff au LaunchAgent, persistance provider session, séparation pair id IdeA vs engine session id. - Statut : architecture et première implémentation livrées ; reste qualité de résumé, couverture UX, audit de complétude de tous les chemins de record. 5. Restrictions profils supportés - Livré partiellement : profils structurés Claude/Codex, structured_adapter, materializes_idea_bridge, garde guard_mcp_bridge_supported, profils sélectionnables. - Statut : règle technique présente, reste formulation produit/UI des capacités et fallbacks. 6. Agent skill awareness - Après intégration locale : livré comme couche de contexte, sans nouveau store ni DTO. agent-skills reste L12, awareness reste composition de convention file. ### Encore actif / pas complètement produit 1. UX conversations / délégations - Backend riche, frontend encore dispersé entre agents/layout/terminal/chat/events. - Manque une lecture utilisateur claire : qui a demandé quoi, à qui, état FIFO, réponse reçue, conversation par paire, reprise. - Statut : actif, prochain meilleur chantier. 2. Live-state partagé projet - Il existe un live-state runtime : sessions, busy, mailbox, events. - Il nexiste pas encore un modèle applicatif unifié, consultable et éventuellement persistable, du “travail en cours” projet. - Statut : partiellement livré en runtime, pas encore comme read-model produit. 3. Mise à jour mémoire/contexte automatique pendant la vie dun agent - Injection au launch : livrée. - Outils contexte/mémoire et FileGuard : présents. - Promotion automatique, signal/bruit, mise à jour continue : pas livrés. - Statut : actif mais à repousser après UX, car il faut dabord rendre les fils et décisions visibles. 4. Handoff/canonical log qualité produit - Socle livré, mais pas encore “expérience de conversation retrouvée” garantie dans tous les cas. - Résumeur heuristique seulement ; UX de consultation/reprise à clarifier. - Statut : fondation livrée, produit actif. ## Prochain chantier recommandé Je recommande de reprendre en premier : UX conversations & délégations, avec un read-model live-state minimal. Raison : les briques backend existent maintenant. Continuer par mémoire automatique ou nouveaux mécanismes MCP risque dempiler de linvisible. Le prochain verrou produit est de rendre lorchestration compréhensible et opérable : lutilisateur doit voir ses “employés IA” travailler, attendre, déléguer, répondre et reprendre. Ce chantier peut aussi servir daudit dintégration : sil manque un événement ou une donnée backend, on lajoute proprement comme read-model, au lieu de découvrir les trous plus tard. ## Cadrage architectural initial ### Objectif Fournir une vue produit unifiée des conversations et délégations dun projet : - agents vivants et cellule hôte, - état idle/busy/limited/starting si disponible, - délégations en cours et en attente par agent, - dernière requête/réponse utile, - conversation User↔Agent ou Agent↔Agent associée, - capacité à ouvrir/rattacher la cellule concernée. Le but nest pas encore de créer un “journal complet” frontend. Le journal canonique existe côté .ideai/conversations; le premier lot doit exposer un état opérationnel scannable. ### Frontière backend Préférer un use case de lecture applicatif plutôt que de faire recomposer le frontend depuis dix events. Nouveau read-model applicatif proposé : rust ProjectWorkState { live_agents: Vec<LiveAgentState>, conversations: Vec<ConversationThreadSummary>, delegations: Vec<DelegationState>, } Port/domain à éviter au départ : pas de nouveau store durable tant quon lit les registres existants. Le read-model peut composer : - LiveSessions / TerminalSessions / StructuredSessions, - InputMediator::busy_state, - ConversationRegistry, - AgentMailbox si une méthode dinspection propre est ajoutée, - ConversationLog / HandoffStore en lecture best-effort pour les résumés. Si inspection mailbox nécessaire, ne pas exposer limpl InMemoryMailbox : ajouter un petit port ségrégué, par exemple AgentQueueSnapshot, ou étendre prudemment AgentMailbox avec une méthode read-only si limpact reste faible. ### Frontière Tauri / DTO Ajouter une commande de lecture, pas une mutation : - get_project_work_state(projectId) -> ProjectWorkStateDto DTOs camelCase, stables et tolérants aux champs absents : - LiveAgentStateDto { agentId, name, nodeId?, sessionId?, kind: "pty"|"structured", busy, limited? } - DelegationStateDto { ticketId, requesterAgentId?, targetAgentId, taskPreview, status: "queued"|"delivered"|"awaitingReply"|"replied"|"timedOut", source: "mcp"|"file"|"user"?, createdAtMs? } - ConversationThreadSummaryDto { conversationId, left, right, lastTurnPreview?, handoffPreview?, updatedAtMs? } Ne pas exposer tout log.jsonl dans ce lot. Préviews seulement. ### Frontière frontend Ajouter une feature dédiée, sans mélanger avec TerminalView : - frontend/src/features/workstate/ ou features/conversations/ - hook useProjectWorkState(projectId) consommant un nouveau WorkStateGateway - panneau dans ProjectsView ou extension du sidebar Agents : “Work” / “Conversations” Le frontend doit continuer à consommer des ports TS, pas invoke() directement : - domain/index.ts : types miroirs DTO. - ports/index.ts : WorkStateGateway. - adapters/workState.ts : Tauri invoke. - mock gateway + tests Vitest. ### Ordre dimplémentation recommandé 1. Lot A — read-model minimal sans nouvelle persistance - Backend command get_project_work_state qui agrège live agents + busy state + conversations connues. - Frontend panneau simple affichant agents live/busy et dernières délégations connues. 2. Lot B — queue/delegation snapshot propre - Ajouter linspection read-only de mailbox/FIFO si le Lot A ne peut pas montrer les tickets en attente. - Tester FIFO visible : deux demandes vers le même agent apparaissent dans lordre. 3. Lot C — conversation summaries depuis log/handoff - Lire ConversationLog::last et HandoffStore::load best-effort. - Afficher preview et reprise sans charger tout le journal. 4. Lot D — actions UX - “Ouvrir/rattacher lagent”, “voir la conversation”, “arrêter”, “copier résumé”, selon capacités déjà existantes. 5. Lot E — mémoire/contexte automatique - Seulement après visibilité des fils : proposer/persister les apprentissages utiles depuis les conversations vers mémoire/contexte via FileGuard. ### Contraintes à respecter - Ne pas faire du frontend la source de vérité du live-state. - Ne pas polluer .ideai/memory/ avec le journal conversationnel. - Ne pas dupliquer les registres session existants. - Garder PTY brut et structured sessions substituables dans le read-model. - Toute écriture mémoire/contexte doit rester derrière les use cases/FileGuard existants. ## Synthèse Le cœur “agent control IDE” est maintenant largement livré côté backend : singleton, mailbox/FIFO, MCP IdeA-only, log/handoff/provider sessions. Le chantier prioritaire nest plus dinventer ces mécanismes, mais de les rendre visibles et pilotables. Je reprendrais donc par UX conversations/délégations + read-model live-state projet minimal, puis seulement ensuite la mise à jour automatique mémoire/contexte.
  • Prompt: Nous passons à l'implémentation du chantier UX conversations/délégations + read-model live-state minimal. Merci de transformer ton cadrage en périmètre de Lot A implémentable maintenant. Donne les contrats exacts backend/Tauri/frontend, les fichiers probables, ce qui est inclus/exclu, et les tests attendus. Objectif: premier lot utile, minimal, sans nouvelle persistance durable si possible. Ne code pas.
  • Response: ## Lot A — ProjectWorkState minimal, implémentable maintenant ### Objectif du lot Livrer une première vue utile et read-only du travail agent en cours dans un projet, sans nouvelle persistance durable : - lister les agents du projet avec leur état live, - indiquer cellule/session hôte quand lagent tourne, - indiquer idle / busy à partir du médiateur dentrée, - exposer le ticket courant si busy, - permettre au frontend dafficher un panneau “Work” simple et scannable. Ce lot ne cherche pas encore à afficher lhistorique, les previews de log, ni la file FIFO complète. Il pose le read-model minimal qui rend létat agent visible. ## Inclus - Nouvelle commande Tauri read-only get_project_work_state(projectId). - Agrégation backend depuis les stores/registres existants : manifest agents, LiveSessions, InputMediator::busy_state. - DTO frontend miroir + gateway TS. - Panneau frontend minimal dans la sidebar, probablement onglet Work. - Tests backend DTO/commande + tests frontend hook/panel. ## Exclus - Aucune nouvelle persistance .ideai/. - Pas de lecture log.jsonl / handoff.md dans le Lot A. - Pas dinspection complète de la mailbox/FIFO. - Pas de mutation : pas de stop/reply/attach depuis ce panneau dans le premier lot. - Pas de migration de list_live_agents existant. - Pas de refonte des panneaux Agents/Terminal/Chat. ## Contrat backend application Le plus minimal peut rester dans app-tauri en composition de DTO, mais je recommande un petit use case application pour garder Tauri adapter mince. ### Nouveau module probable - crates/application/src/workstate/mod.rs - export dans crates/application/src/lib.rs ### Types application proposés rust pub struct GetProjectWorkState { contexts: Arc<dyn AgentContextStore>, live: Arc<LiveSessions>, input: Arc<dyn InputMediator>, } pub struct GetProjectWorkStateInput { pub project: Project, } pub struct ProjectWorkState { pub agents: Vec<AgentWorkState>, } pub struct AgentWorkState { pub agent_id: AgentId, pub name: String, pub profile_id: ProfileId, pub live: Option<LiveWorkSession>, pub busy: AgentBusyState, } pub struct LiveWorkSession { pub node_id: NodeId, pub session_id: SessionId, pub kind: LiveSessionKind, } pub enum LiveSessionKind { Pty, Structured, } ### Important sur kind LiveSessions::live_agents() agrège déjà PTY + structured mais ne porte pas le kind. Pour Lot A, deux options : 1. Option minimale : omettre kind du DTO. Suffisant pour afficher “live”. 2. Option préférable : ajouter une méthode read-only à LiveSessions, sans casser lexistant : rust pub fn live_agent_entries(&self) -> Vec<LiveAgentEntry> pub struct LiveAgentEntry { pub agent_id: AgentId, pub node_id: NodeId, pub session_id: SessionId, pub kind: LiveSessionKind, } Garder live_agents() existant pour compatibilité avec list_live_agents. ### Algorithme use case 1. manifest = contexts.load_manifest(&project).await? 2. Convertir chaque entry en Agent via entry.to_agent(). 3. Construire une map agent_id -> live entry depuis LiveSessions. 4. Pour chaque agent : - busy = input.busy_state(agent.id) - live = live_map.get(agent.id) - produire AgentWorkState 5. Trier par name ou conserver lordre manifeste. Recommandation : conserver ordre manifeste pour stabilité avec list_agents. ## Contrat Tauri ### Nouveau DTO dans crates/app-tauri/src/dto.rs rust #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct ProjectWorkStateDto { pub agents: Vec<AgentWorkStateDto>, } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct AgentWorkStateDto { pub agent_id: String, pub name: String, pub profile_id: String, pub live: Option<LiveWorkSessionDto>, pub busy: BusyStateDto, } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct LiveWorkSessionDto { pub node_id: String, pub session_id: String, pub kind: LiveSessionKindDto, } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub enum LiveSessionKindDto { Pty, Structured, } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase", tag = "state")] pub enum BusyStateDto { Idle, Busy { ticket: String, since_ms: u64 }, } Si léquipe veut réduire encore le lot : supprimer kind et LiveSessionKindDto. ### Nouvelle commande dans crates/app-tauri/src/commands.rs rust #[tauri::command] pub async fn get_project_work_state( project_id: String, state: State<'_, AppState>, ) -> Result<ProjectWorkStateDto, ErrorDto> Comportement : - resolve_project(&project_id, &state).await? - appeler state.get_project_work_state.execute(...) - mapper en DTO Erreurs : - INVALID si projectId invalide, - NOT_FOUND si projet inconnu, - STORE si manifeste illisible. ### Wiring AppState Fichiers probables : - crates/app-tauri/src/state.rs - ajouter pub get_project_work_state: Arc<GetProjectWorkState>. - construire avec IdeaiContextStore, LiveSessions::new(terminal_sessions, structured_sessions), input_mediator. - crates/app-tauri/src/lib.rs - enregistrer commands::get_project_work_state dans invoke_handler. ## Contrat frontend ### Types dans frontend/src/domain/index.ts ts export interface ProjectWorkState { agents: AgentWorkState[]; } export interface AgentWorkState { agentId: string; name: string; profileId: string; live?: LiveWorkSession | null; busy: WorkBusyState; } export interface LiveWorkSession { nodeId: string; sessionId: string; kind: "pty" | "structured"; } export type WorkBusyState = | { state: "idle" } | { state: "busy"; ticket: string; sinceMs: number }; Si backend omet kind, retirer kind ici aussi. ### Port dans frontend/src/ports/index.ts ts export interface WorkStateGateway { getProjectWorkState(projectId: string): Promise<ProjectWorkState>; } export interface Gateways { // existants... workState: WorkStateGateway; } ### Adapter Tauri Nouveau fichier : frontend/src/adapters/workState.ts ts export class TauriWorkStateGateway implements WorkStateGateway { getProjectWorkState(projectId: string): Promise<ProjectWorkState> { return invoke<ProjectWorkState>("get_project_work_state", { projectId }); } } Puis wiring : - frontend/src/adapters/index.ts : instancier workState: new TauriWorkStateGateway(). - frontend/src/adapters/mock/index.ts : ajouter MockWorkStateGateway. ### Feature frontend Nouveau dossier recommandé : - frontend/src/features/workstate/useProjectWorkState.ts - frontend/src/features/workstate/ProjectWorkStatePanel.tsx - frontend/src/features/workstate/index.ts - frontend/src/features/workstate/workstate.test.tsx Hook : ts export interface ProjectWorkStateViewModel { state: ProjectWorkState | null; busy: boolean; error: string | null; refresh: () => Promise<void>; } Refresh initial + refresh sur events existants : - agentLaunched - agentExited - agentBusyChanged - orchestratorRequestProcessed - éventuellement agentProfileChanged ### UI minimale Dans ProjectsView.tsx : - ajouter SidebarTab = ... | "work" - ajouter { id: "work", label: "Work" } - afficher ProjectWorkStatePanel projectId={active.id} si actif. Contenu minimal du panneau : - ligne par agent : nom, live/offline, busy/idle. - si live : afficher sessionId court ou nodeId court. - si busy : afficher ticket court + âge approximatif facultatif. - empty state : “No agents yet.” - erreur et bouton refresh. Pas dactions dans Lot A. Le panneau est observabilité seulement. ## Fichiers probables Backend : - crates/application/src/workstate/mod.rs nouveau - crates/application/src/lib.rs - crates/application/src/terminal/registry.rs si ajout live_agent_entries() avec kind - crates/app-tauri/src/dto.rs - crates/app-tauri/src/commands.rs - crates/app-tauri/src/state.rs - crates/app-tauri/src/lib.rs Frontend : - frontend/src/domain/index.ts - frontend/src/ports/index.ts - frontend/src/adapters/workState.ts nouveau - frontend/src/adapters/index.ts - frontend/src/adapters/mock/index.ts - frontend/src/features/workstate/useProjectWorkState.ts nouveau - frontend/src/features/workstate/ProjectWorkStatePanel.tsx nouveau - frontend/src/features/workstate/index.ts nouveau - frontend/src/features/workstate/workstate.test.tsx nouveau - frontend/src/features/projects/ProjectsView.tsx ## Tests attendus ### Rust application Nouveau test application, selon conventions existantes : - cargo test -p application workstate Cas : 1. Projet avec deux agents, aucun live, aucun busy → deux rows live=None, busy=Idle. 2. Agent live PTY → row porte live.session_id, live.node_id, kind=Pty si kind inclus. 3. Agent busy → row porte Busy { ticket, since_ms }. 4. Agent absent du manifeste mais présent dans registry → ignoré dans le read-model projet. ### Rust app-tauri Selon structure actuelle, ajouter dans tests de wiring/commands : - commande enregistrée dans invoke_handler, - DTO sérialise en camelCase : agentId, profileId, sinceMs, sessionId, nodeId, kind. Commandes de vérification probables : - cargo test -p app-tauri --test orchestrator_wiring - ou test dédié si les commands sont déjà testées ailleurs. ### Frontend Vitest Nouveau workstate.test.tsx : 1. Le panneau affiche No agents yet. quand liste vide. 2. Il affiche agent idle/offline. 3. Il affiche agent live + busy avec ticket court. 4. Un event agentBusyChanged déclenche refresh. 5. ProjectsView expose longlet Work et rend le panneau pour le projet actif. Commande ciblée : bash npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx ### Typecheck bash npx tsc --noEmit cargo check -p app-tauri ## Notes de rigueur - Le Lot A doit rester read-only. Les actions “ouvrir/rattacher”, “stop”, “voir conversation” sont Lot D. - Ne pas lire ConversationLog dans Lot A : dès quon affiche des previews de conversation, on bascule vers Lot C. - Ne pas ajouter une persistance live_state.json : le live-state est dérivé des registres runtime existants. - Ne pas dupliquer list_live_agents; le nouveau read-model peut lutiliser conceptuellement, mais il doit ajouter le busy state et le nom agent, donc il mérite sa commande dédiée. - Si LiveSessions est difficile à injecter tel quel, injecter les deux registres et agréger dans le use case, mais garder lagrégation dans application, pas dans Tauri ni frontend.
  • Prompt: Lot A ProjectWorkState minimal est intégré localement dans develop (3bfb932). Cadrage demandé pour le Lot B du chantier UX conversations/délégations + read-model live-state: queue/delegation snapshot propre. Objectif: exposer dans le read-model les délégations/tickets en attente/en cours de façon read-only, sans nouvelle persistance durable et sans refonte FIFO. Merci de définir précisément: source de vérité backend, éventuelle extension read-only de AgentMailbox/InputMediator, DTOs, impacts frontend, inclus/exclus, tests attendus. Ne code pas.
  • Response: ## Lot B — Queue / Delegation Snapshot Read-Only Objectif : enrichir ProjectWorkState avec les tickets actuellement présents dans la FIFO par agent, en lecture seule, sans nouvelle persistance durable et sans changer la sémantique enqueue/resolve/cancel. ### Source de vérité backend La source de vérité des délégations/tickets est la mailbox existante, pas le frontend et pas un nouveau store : - domain::mailbox::Ticket porte déjà id, source, conversation, requester, task. - infrastructure::InMemoryMailbox possède déjà les VecDeque<Slot> par AgentId. - InputMediator::busy_state(agent) reste la source de vérité du ticket en cours. Donc : - queue/order = snapshot de InMemoryMailbox ; - status inProgress/queued = dérivé en croisant la queue avec InputMediator::busy_state ; - live session = déjà Lot A via LiveSessions ; - manifest boundary = toujours AgentContextStore.load_manifest(project). Ne pas reconstruire une deuxième FIFO dans ProjectWorkState. ## Extension read-only recommandée Ne pas étendre InputMediator : il est lautorité busy/livraison, pas lautorité de contenu de queue. Éviter aussi de grossir le port mutateur AgentMailbox si possible. Ajouter un port ségrégué read-only dans domain/src/mailbox.rs : rust #[derive(Debug, Clone, PartialEq, Eq)] pub struct QueuedTicketSnapshot { pub id: TicketId, pub source: InputSource, pub conversation: ConversationId, pub requester: String, pub task: String, pub position: u32, } pub trait AgentQueueSnapshot: Send + Sync { fn queue_for(&self, agent: AgentId) -> Vec<QueuedTicketSnapshot>; } Implémenter AgentQueueSnapshot pour InMemoryMailbox en clonant uniquement les données de Ticket, jamais les oneshot::Sender. Pourquoi ce choix : - ISP : le read-model ne dépend pas de enqueue/resolve/cancel. - Aucun changement de comportement FIFO. - Les tests peuvent fournir un fake AgentQueueSnapshot sans implémenter tout AgentMailbox. Alternative acceptable si léquipe préfère moins de types : ajouter fn snapshot_for(&self, agent: AgentId) -> Vec<QueuedTicketSnapshot> directement à AgentMailbox, avec impl par défaut vide. Mais cest moins propre : les ports mutateur et lecture restent mélangés. ## Modèle application Fichier principal : crates/application/src/workstate/mod.rs. Étendre GetProjectWorkState : rust pub struct GetProjectWorkState { contexts: Arc<dyn AgentContextStore>, live: Arc<LiveSessions>, input: Arc<dyn InputMediator>, queue: Arc<dyn AgentQueueSnapshot>, } Étendre les structs : rust pub struct AgentWorkState { pub agent_id: AgentId, pub name: String, pub profile_id: ProfileId, pub live: Option<LiveWorkSession>, pub busy: AgentBusyState, pub tickets: Vec<AgentTicketState>, } pub struct AgentTicketState { pub ticket_id: TicketId, pub conversation_id: ConversationId, pub position: u32, pub status: TicketWorkStatus, pub source: TicketWorkSource, pub requester_label: String, pub task_preview: String, pub task_len: usize, } pub enum TicketWorkStatus { InProgress, Queued, } pub enum TicketWorkSource { Human, Agent { agent_id: AgentId }, } Dérivation status : rust let busy_ticket = self.input.busy_state(agent.id).ticket(); status = if busy_ticket == Some(ticket.id) { TicketWorkStatus::InProgress } else { TicketWorkStatus::Queued }; task_preview : générer côté application pour éviter denvoyer tout le prompt au panneau. Recommandation : trim + remplacer whitespace runs par espace + couper à 160 caractères. Garder task_len pour indiquer quil y a plus. Important : conserver lordre FIFO via position, ne pas retrier les tickets. ## Tauri / DTO Étendre crates/app-tauri/src/dto.rs. rust #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct AgentWorkStateDto { pub agent_id: String, pub name: String, pub profile_id: String, pub live: Option<LiveWorkSessionDto>, pub busy: AgentBusyState, pub tickets: Vec<AgentTicketStateDto>, } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct AgentTicketStateDto { pub ticket_id: String, pub conversation_id: String, pub position: u32, pub status: TicketWorkStatusDto, pub source: TicketWorkSourceDto, pub requester_label: String, pub task_preview: String, pub task_len: usize, } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub enum TicketWorkStatusDto { InProgress, Queued, } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase", tag = "kind")] pub enum TicketWorkSourceDto { Human, Agent { agent_id: String }, } Wire JSON attendu : json { "agents": [ { "agentId": "...", "name": "Architect", "profileId": "...", "live": { "nodeId": "...", "sessionId": "...", "kind": "pty" }, "busy": { "state": "busy", "ticket": "...", "sinceMs": 1234 }, "tickets": [ { "ticketId": "...", "conversationId": "...", "position": 0, "status": "inProgress", "source": { "kind": "agent", "agentId": "..." }, "requesterLabel": "Main", "taskPreview": "Analyser...", "taskLen": 842 } ] } ] } La commande Tauri reste la même : rust get_project_work_state(project_id) -> ProjectWorkStateDto Aucune nouvelle commande nécessaire. ## Wiring AppState Dans crates/app-tauri/src/state.rs : - garder lArc<InMemoryMailbox> déjà créé au composition root ; - le passer à GetProjectWorkState::new(...) comme Arc<dyn AgentQueueSnapshot> ; - continuer à passer le même Arc<InMemoryMailbox> au MediatedInbox et à lorchestrateur comme AgentMailbox. But : un seul objet InMemoryMailbox, deux vues de port : rust let inmemory_mailbox = Arc::new(InMemoryMailbox::new()); let mailbox = Arc::clone(&inmemory_mailbox) as Arc<dyn AgentMailbox>; let queue_snapshot = Arc::clone(&inmemory_mailbox) as Arc<dyn AgentQueueSnapshot>; Puis : rust GetProjectWorkState::new(contexts, live_sessions, input_mediator, queue_snapshot) ## Frontend contracts Étendre frontend/src/domain/index.ts : ts export type TicketWorkStatus = "inProgress" | "queued"; export type TicketWorkSource = | { kind: "human" } | { kind: "agent"; agentId: string }; export interface AgentTicketState { ticketId: string; conversationId: string; position: number; status: TicketWorkStatus; source: TicketWorkSource; requesterLabel: string; taskPreview: string; taskLen: number; } export interface AgentWorkState { agentId: string; name: string; profileId: string; live?: LiveWorkSession; busy: WorkBusyState; tickets: AgentTicketState[]; } WorkStateGateway ne change pas : ts getProjectWorkState(projectId: string): Promise<ProjectWorkState>; Adapter Tauri inchangé sauf types. Mock : MockWorkStateGateway accepte désormais tickets dans ses seeds. Pour compat tests, soit mettre tickets: [] dans les fixtures, soit normaliser dans le mock au retour. ## UI Lot B Fichier principal : frontend/src/features/workstate/ProjectWorkStatePanel.tsx. Afficher sous chaque agent : - badge Live/Offline, Busy/Idle existant ; - si tickets.length > 0, une mini-liste compacte : - #1 In progress ou #2 Queued, - requester : Human ou label requester / source agent, - preview du task, - ticket court en monospace. UX minimale : - Agent sans ticket : rien ou texte discret “No queued tickets”. - Ticket inProgress : badge warning, cohérent avec Busy. - Ticket queued : badge neutral. - Pas daction de résolution/annulation dans Lot B. Refresh : - Garder refresh manuel. - Garder refresh sur événements Lot A : agentBusyChanged, agentLaunched, agentExited, orchestratorRequestProcessed. - Ajouter refresh sur delegationReady, car cest le signal existant le plus proche dun ticket qui démarre physiquement côté UI. Limite connue du Lot B : un ticket ajouté derrière un agent déjà busy peut ne pas émettre de nouvel agentBusyChanged ni delegationReady immédiatement. Il sera visible au refresh manuel ou au prochain événement. Une vraie notification agentQueueChanged est un Lot B+ ou Lot C, pas indispensable pour le snapshot read-only. ## Inclus - Port read-only AgentQueueSnapshot. - Impl snapshot de InMemoryMailbox, sans mutation. - Extension de ProjectWorkState avec agents[].tickets. - Statuts inProgress/queued dérivés, pas stockés. - Task preview bornée, pas full prompt. - UI compacte des tickets par agent. - Tests de snapshot FIFO et read-model. ## Exclus - Pas de agentQueueChanged dans ce lot, sauf si léquipe juge indispensable pour UX live immédiate. - Pas dannulation de ticket depuis UI. - Pas de résolution manuelle depuis UI. - Pas de persistance queue dans .ideai/. - Pas de lecture ConversationLog / handoff.md. - Pas dhistorique des tickets terminés. - Pas de statut delivered : delegation_delivered est aujourdhui un log/ack best-effort, pas une donnée persistée dans la mailbox. Ne pas lexposer comme état tant quil na pas de source de vérité. - Pas de refonte des chemins ask_agent, submit_human_input, idea_reply. ## Point de vigilance technique Le snapshot va rendre visibles les tickets réellement restés dans InMemoryMailbox. Cest précisément utile, mais cela peut révéler des tickets qui ne sont pas drainés sur certains chemins de succès. À vérifier par tests avant de conclure le lot : - succès idea_reply PTY : ticket retiré ; - timeout/cancel : ticket retiré par BusyTurnGuard ; - succès structured Final : le ticket ne doit pas rester dans la queue ; - submit humain fire-and-forget : décider sil doit apparaître comme ticket ou être exclu du snapshot. Pour le MVP UX “délégations”, je recommande dafficher uniquement les tickets dont source == Agent. Si léquipe veut afficher aussi les entrées humaines, le libellé UI doit parler de “tickets dentrée”, pas seulement de délégations, et il faut clarifier leur drainage. ## Tests attendus ### Domain / infrastructure mailbox Fichiers : - crates/domain/src/mailbox.rs - crates/infrastructure/src/mailbox/mod.rs Tests : 1. snapshot_preserves_fifo_order_and_positions : deux tickets A→B, positions 0, 1. 2. snapshot_carries_ticket_metadata : source, conversation, requester, task présents. 3. snapshot_is_read_only : appeler snapshot ne change pas pending ni head_ticket. 4. snapshot_updates_after_resolve_ticket : le ticket résolu disparaît. 5. snapshot_updates_after_cancel_head : le head annulé disparaît, le suivant devient position 0. ### Application workstate Fichier : crates/application/tests/workstate.rs. Tests : 1. Agent sans queue → tickets == []. 2. Deux tickets pour un agent manifeste → tickets dans lordre FIFO. 3. busy.ticket == first → premier ticket InProgress, second Queued. 4. busy == Idle → tous tickets Queued. 5. Queue pour agent absent du manifeste → ignorée. 6. task_preview tronque et task_len conserve la longueur originale. 7. Source agent → DTO application contient TicketWorkSource::Agent { agent_id }; source human → Human si inclus. ### App-tauri DTO / command Fichiers : - crates/app-tauri/src/dto.rs - tests existants type dto_agents.rs ou nouveau test DTO. Tests : 1. Sérialisation camelCase : ticketId, conversationId, requesterLabel, taskPreview, taskLen. 2. Enum status sérialisée en inProgress / queued. 3. Source agent sérialisée en { "kind": "agent", "agentId": "..." }. 4. get_project_work_state renvoie tickets imbriqués avec les agents. ### Frontend Fichier : frontend/src/features/workstate/workstate.test.tsx. Tests : 1. Agent avec ticket inProgress affiche badge In progress, preview et ticket court. 2. Agent avec deux tickets affiche lordre #1, #2. 3. Agent sans tickets naffiche pas de liste parasite. 4. Event delegationReady déclenche un refresh. 5. Mock gateway accepte et renvoie tickets. Commandes de vérification : bash cargo test -p infrastructure mailbox --lib cargo test -p application --test workstate cargo test -p app-tauri --test dto_agents npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx npx tsc --noEmit cargo check -p app-tauri ## Résumé dimplémentation Le Lot B est une extension de projection : InMemoryMailbox expose une vue snapshot, GetProjectWorkState lagrège par agent manifeste, Tauri sérialise tickets, le panneau Work les affiche. Aucun nouveau stockage, aucune nouvelle queue, aucune mutation UX.
  • 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 daction 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/<conversationId>/handoff.md). Source durable secondaire : ConversationLog (.ideai/conversations/<conversationId>/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 lorchestration, 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 lajouter plus tard, mais Lot C doit se limiter aux conversations visibles par les tickets pour éviter douvrir 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<dyn HandoffStore> et Arc<dyn ConversationLog>, ou Option<Arc<dyn ConversationPreviewPort>> si on veut rendre lenrichissement 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 <project_root>/.ideai/conversations/ ; - si FsHandoffStore::load renvoie une erreur de parsing, lapplication la convertit en preview unavailable/partial, pas en AppError global ; - FsConversationLog::last est déjà tolérant aux lignes corrompues, donc cest 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<AgentWorkState>, pub conversations: Vec<ConversationWorkSummary>, } pub struct ConversationWorkSummary { pub conversation_id: ConversationId, pub status: ConversationPreviewStatus, pub objective_preview: Option<String>, pub summary_preview: Option<String>, pub summary_len: usize, pub up_to: Option<TurnId>, pub recent_turns: Vec<ConversationTurnWorkPreview>, } 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 lerreur 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: <objectivePreview> ; - sinon résumé : <summaryPreview> ; - sinon dernier tour : Last: <textPreview> ; - 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 na 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 dadapter dédié apparaît, mais éviter den 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 lorganisation 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 daction. Le backend reste source dautorité 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 lutilisateur peut agir sur létat déjà visible. Périmètre minimal prioritaire : - retrouver ou afficher la cellule dun 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 dun 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 lagent est live mais sa cellule hôte nest 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 lutilisateur 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 lagent après confirmation. - Fonctionne pour PTY et structured. - Ne supprime pas lagent, 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 lagent nest 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 linterprè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<LiveAgent> doit retourner kind ; - ajouter AgentGateway.stopLiveAgent(projectId, agentId): Promise<StoppedLiveAgent> ; - 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 dappeler 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 ; cest 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 linvariant “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 dun 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 lUI 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 nappelle 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 lavertissement ; - View conversation naffiche 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 linvariant 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.