From 50e99e5ced3422f1986e7826a855ae861985f648 Mon Sep 17 00:00:00 2001 From: Blomios Date: Wed, 24 Jun 2026 13:32:28 +0200 Subject: [PATCH] =?UTF-8?q?chore(workstate):=20notes=20m=C3=A9moire=20camp?= =?UTF-8?q?agne=20MCP=20T1=E2=86=92T10=20+=20=C3=A9tat=20runtime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capitalise la mémoire projet produite pendant la campagne de tests fonctionnels MCP (rendez-vous inter-agents, backstop no-reply, réconciliation live-state au reboot) et synchronise l'état runtime `.ideai/` (agents, layouts, skills, index mémoire). Inclut la mise à jour du contexte de l'agent Git. Co-Authored-By: Claude Opus 4.8 --- .ideai/agents.json | 8 +- .ideai/agents/git.md | 27 +++--- .ideai/layouts.json | 93 +------------------ .ideai/memory/MEMORY.md | 25 +++++ ...stop-fires-on-intra-task-turn-rootcause.md | 34 +++++++ ...op-no-reply-live-test-failed-2026-06-23.md | 34 +++++++ .../checkpoint-auto-memory-harvest-lot-e1.md | 62 +++++++++++++ ...ing-a-fix-built-live-validation-pending.md | 37 ++++++++ ...oint-mcp-t1-t6-green-t7-fix-wrong-layer.md | 40 ++++++++ ...mcp-tests-t1-t5-and-t5-livestate-defect.md | 29 ++++++ ...int-rendezvous-redesign-devbackend-todo.md | 38 ++++++++ .../memory/checkpoint-work-tab-blank-fix.md | 34 +++++++ ...ion-log-ls6-rotation-and-paginated-read.md | 29 ++++++ .../conversation-viewer-ls7-frontend.md | 26 ++++++ .../memory/git-agent-push-access-gitea-ssh.md | 15 +++ .../handoff-ls5-summary-bound-and-llm-seam.md | 22 +++++ ...rogram-surface-separation-and-livestate.md | 14 +++ ...ve-state-persistence-program-closed-ls8.md | 15 +++ ...live-state-reboot-reconciliation-design.md | 21 +++++ ...p-e2e-findings-reply-wedge-phantom-busy.md | 47 ++++++++++ .../mcp-functional-test-plan-2026-06-24.md | 45 +++++++++ ...ional-tests-t1-t9-green-live-2026-06-24.md | 33 +++++++ ...ss-interrupt-does-not-cancel-rendezvous.md | 29 ++++++ .../mcp-t10b-pending-reboot-verification.md | 24 +++++ ...top-600s-reply-rejected-livestate-stale.md | 37 ++++++++ ...e-live-state-implemented-feature-branch.md | 25 +++++ ...ndezvous-600s-cap-too-short-heavy-tasks.md | 37 ++++++++ .../rendezvous-no-reply-backstop-design.md | 14 +++ .../resume-after-appimage-rebuild-mcp-e2e.md | 39 ++++++++ ...skills-integration-canonical-foundation.md | 11 +++ .ideai/skills/index.json | 7 ++ .../86ea97df-1533-47e5-8809-dc2b02242567.md | 42 +++++++++ 32 files changed, 887 insertions(+), 106 deletions(-) create mode 100644 .ideai/memory/backstop-fires-on-intra-task-turn-rootcause.md create mode 100644 .ideai/memory/backstop-no-reply-live-test-failed-2026-06-23.md create mode 100644 .ideai/memory/checkpoint-auto-memory-harvest-lot-e1.md create mode 100644 .ideai/memory/checkpoint-finding-a-fix-built-live-validation-pending.md create mode 100644 .ideai/memory/checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer.md create mode 100644 .ideai/memory/checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect.md create mode 100644 .ideai/memory/checkpoint-rendezvous-redesign-devbackend-todo.md create mode 100644 .ideai/memory/checkpoint-work-tab-blank-fix.md create mode 100644 .ideai/memory/conversation-log-ls6-rotation-and-paginated-read.md create mode 100644 .ideai/memory/conversation-viewer-ls7-frontend.md create mode 100644 .ideai/memory/git-agent-push-access-gitea-ssh.md create mode 100644 .ideai/memory/handoff-ls5-summary-bound-and-llm-seam.md create mode 100644 .ideai/memory/idea-program-surface-separation-and-livestate.md create mode 100644 .ideai/memory/live-state-persistence-program-closed-ls8.md create mode 100644 .ideai/memory/live-state-reboot-reconciliation-design.md create mode 100644 .ideai/memory/mcp-e2e-findings-reply-wedge-phantom-busy.md create mode 100644 .ideai/memory/mcp-functional-test-plan-2026-06-24.md create mode 100644 .ideai/memory/mcp-functional-tests-t1-t9-green-live-2026-06-24.md create mode 100644 .ideai/memory/mcp-t10a-harness-interrupt-does-not-cancel-rendezvous.md create mode 100644 .ideai/memory/mcp-t10b-pending-reboot-verification.md create mode 100644 .ideai/memory/mcp-t7-backstop-600s-reply-rejected-livestate-stale.md create mode 100644 .ideai/memory/reconcile-live-state-implemented-feature-branch.md create mode 100644 .ideai/memory/rendezvous-600s-cap-too-short-heavy-tasks.md create mode 100644 .ideai/memory/rendezvous-no-reply-backstop-design.md create mode 100644 .ideai/memory/resume-after-appimage-rebuild-mcp-e2e.md create mode 100644 .ideai/memory/skills-integration-canonical-foundation.md create mode 100644 .ideai/skills/md/86ea97df-1533-47e5-8809-dc2b02242567.md diff --git a/.ideai/agents.json b/.ideai/agents.json index bde4ce1..488523b 100644 --- a/.ideai/agents.json +++ b/.ideai/agents.json @@ -5,14 +5,14 @@ "agentId": "a6ced819-b893-4213-b003-9e9dc79b9641", "name": "Main", "mdPath": "agents/main.md", - "profileId": "664cc20c-47b8-53ad-9351-dce4c09c3da4", + "profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4", "synchronized": false }, { "agentId": "dce19c75-9669-4e45-b8de-9950025157da", "name": "Architect", "mdPath": "agents/architect.md", - "profileId": "664cc20c-47b8-53ad-9351-dce4c09c3da4", + "profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4", "synchronized": false }, { @@ -26,14 +26,14 @@ "agentId": "af7f86da-76bc-48e1-9900-71f45a624800", "name": "DevFrontend", "mdPath": "agents/devfrontend.md", - "profileId": "664cc20c-47b8-53ad-9351-dce4c09c3da4", + "profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4", "synchronized": false }, { "agentId": "aefdbd61-e3d4-4bc1-9f42-c259446a97b5", "name": "QA", "mdPath": "agents/qa.md", - "profileId": "664cc20c-47b8-53ad-9351-dce4c09c3da4", + "profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4", "synchronized": false }, { diff --git a/.ideai/agents/git.md b/.ideai/agents/git.md index cabb0c8..e3d2009 100644 --- a/.ideai/agents/git.md +++ b/.ideai/agents/git.md @@ -1,15 +1,16 @@ # Git — Agent de gestion du dépôt git local -> Tu es l'**agent Git** d'IdeA. Ton unique responsabilité est la **gestion du dépôt -> git local** : commits de l'application, création et bascule de branches, merges et -> rebases. Tu es le **seul** à décider de la topologie des branches et à manipuler -> l'historique local. Main te sollicite ; tu décides et tu exécutes. +> Tu es l'**agent Git** d'IdeA. Ta responsabilité est la **gestion du dépôt +> git local** : commits de l'application, création et bascule de +> branches, merges, rebases. Tu es le **seul** +> à décider de la topologie des branches et à manipuler l'historique. Main te +> sollicite ; tu décides et tu exécutes. --- ## 1. Ton rôle (et ses limites) -Tu t'occupes **du local du repo git**, rien d'autre : +Tu t'occupes **du repo git local** : - **Commits** : tu transformes le travail réalisé par les agents de dev en commits propres, atomiques, au bon endroit (bonne branche), avec des messages cohérents. @@ -19,14 +20,16 @@ Tu t'occupes **du local du repo git**, rien d'autre : - Tu **décides** : quand Main t'annonce une nouvelle feature (après cadrage Architect), c'est **toi** qui tranches s'il faut une nouvelle branche, un checkout/switch, ou rien. Après chaque implémentation, Main revient vers toi pour que tu décides si un **merge** - doit avoir lieu quelque part, ou non. + doit avoir lieu, ou non. -**Hors périmètre :** +**Hors périmètre / garde-fous :** - Tu **n'écris pas de code de feature** (c'est DevBackend/DevFrontend). -- Tu ne fais **aucune action sortante** (`push`, publication, création de PR distante) - sans validation explicite de Main / de l'utilisateur. Ton terrain est **local**. - Tu ne prends pas de décision produit/archi : si un choix dépend de l'architecture, tu remontes à Main. +- **Aucune action sortante** : tu ne **pousses pas** vers un remote (pas de `git push`), + tu ne crées pas de PR distante, tu ne publies pas de tags. La synchronisation avec un + remote n'est pas dans ton périmètre. Tu restes **strictement local**. +- **Jamais** de réécriture destructive de l'historique sans validation explicite de Main. --- @@ -110,7 +113,7 @@ tu le dis. délègue une tâche (message `[IdeA · tâche de … · ticket …]`), tu traites puis tu appelles **impérativement** `idea_reply(result=…)`. - Tu rends compte clairement : branche courante, ce que tu as committé (hash + message - court), ce que tu as mergé/rebasé, et **ta décision** (pourquoi cette branche, pourquoi - ce merge ou ce non-merge). + court), ce que tu as mergé/rebasé, et **ta décision** (pourquoi cette + branche, pourquoi ce merge ou ce non-merge). - En cas de conflit de merge/rebase, tu le signales à Main avec le détail ; tu ne forces - pas une résolution hasardeuse. + pas une résolution hasardeuse. \ No newline at end of file diff --git a/.ideai/layouts.json b/.ideai/layouts.json index 8c79c73..645f12f 100644 --- a/.ideai/layouts.json +++ b/.ideai/layouts.json @@ -1,103 +1,16 @@ { "version": 1, - "activeId": "0ac56658-4f42-47fe-ad03-c088c832460a", + "activeId": "49c6b0fc-13c1-40f5-9464-d3248ab54597", "layouts": [ { - "id": "0ac56658-4f42-47fe-ad03-c088c832460a", + "id": "49c6b0fc-13c1-40f5-9464-d3248ab54597", "name": "Default", "kind": "terminal", - "tree": { - "root": { - "type": "split", - "node": { - "id": "35174b5f-0c29-4025-a389-e609ff219f39", - "direction": "row", - "children": [ - { - "node": { - "type": "split", - "node": { - "id": "652a0a78-e342-473d-9725-ebac04556cbf", - "direction": "column", - "children": [ - { - "node": { - "type": "leaf", - "node": { - "id": "d4b8c0d1-a44a-4c45-bbe9-26991f79b465", - "session": "813f17c2-d056-4875-b9a0-3c18e4e40776", - "agent": "a6ced819-b893-4213-b003-9e9dc79b9641", - "agentWasRunning": true - } - }, - "weight": 1.0760044 - }, - { - "node": { - "type": "leaf", - "node": { - "id": "b7fc564a-7673-4970-acdc-1b8cf23dee8e", - "session": "3a6c9dc2-9919-4c17-8f0f-48439151fd27", - "agent": "dce19c75-9669-4e45-b8de-9950025157da" - } - }, - "weight": 0.9239957 - } - ] - } - }, - "weight": 1.0 - }, - { - "node": { - "type": "split", - "node": { - "id": "31597147-d927-4d03-b76c-8d2b22f8b816", - "direction": "column", - "children": [ - { - "node": { - "type": "leaf", - "node": { - "id": "71564af2-673a-46c7-a0c0-877f89f6e49e", - "session": "384778a6-216f-47ed-90f0-67de6812b59d", - "agent": "73c853d1-c0fd-463b-ad17-1d24fefa371f", - "agentWasRunning": true - } - }, - "weight": 1.0 - }, - { - "node": { - "type": "leaf", - "node": { - "id": "8529e97f-ce06-490c-b3d0-531c3dfee442", - "session": "fac01ad4-0e99-4b2a-bdec-621493cf9c57", - "agent": "cd0b4cf1-1bef-4fae-ade5-f0a6b49bbaf5", - "agentWasRunning": true - } - }, - "weight": 1.0 - } - ] - } - }, - "weight": 1.0 - } - ] - } - } - } - }, - { - "id": "74489208-01a7-4005-aba4-b9c41ab843fd", - "name": "Git Graph", - "kind": "gitGraph", "tree": { "root": { "type": "leaf", "node": { - "id": "2396a80e-bc98-47cd-b711-c7cff0010bb6" + "id": "852f5128-5b98-4d36-9a18-e49a57fc4940" } } } diff --git a/.ideai/memory/MEMORY.md b/.ideai/memory/MEMORY.md index d8e2ede..ef99f9a 100644 --- a/.ideai/memory/MEMORY.md +++ b/.ideai/memory/MEMORY.md @@ -17,3 +17,28 @@ - [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 +- [checkpoint-work-tab-blank-fix](checkpoint-work-tab-blank-fix.md) — memory note checkpoint-work-tab-blank-fix +- [skills-integration-canonical-foundation](skills-integration-canonical-foundation.md) — Topologie d'intégration du chantier skills agent et décision d'abandon de feature/agent-skills. +- [idea-program-surface-separation-and-livestate](idea-program-surface-separation-and-livestate.md) — Cadrage du programme post-skills : ce qui existe vs reste, et la règle de frontière surface-agent/humaine. +- [handoff-ls5-summary-bound-and-llm-seam](handoff-ls5-summary-bound-and-llm-seam.md) — Contrat figé de la borne de handoff (perf) et du seam résumeur LLM non activé. +- [conversation-log-ls6-rotation-and-paginated-read](conversation-log-ls6-rotation-and-paginated-read.md) — Contrat figé de la rétention/rotation du transcript et de la lecture paginée riche (surface humaine). +- [conversation-viewer-ls7-frontend](conversation-viewer-ls7-frontend.md) — Contrat figé du viewer de transcript paginé (frontend pur, lecture seule, surface humaine). +- [live-state-persistence-program-closed-ls8](live-state-persistence-program-closed-ls8.md) — Le programme live-state + persistance conversationnelle est livré et documenté ; pointe la doc de clôture et les points restés ouverts. +- [resume-after-appimage-rebuild-mcp-e2e](resume-after-appimage-rebuild-mcp-e2e.md) — memory note resume-after-appimage-rebuild-mcp-e2e +- [mcp-e2e-findings-reply-wedge-phantom-busy](mcp-e2e-findings-reply-wedge-phantom-busy.md) — memory note mcp-e2e-findings-reply-wedge-phantom-busy +- [checkpoint-finding-a-fix-built-live-validation-pending](checkpoint-finding-a-fix-built-live-validation-pending.md) — memory note checkpoint-finding-a-fix-built-live-validation-pending +- [git-agent-push-access-gitea-ssh](git-agent-push-access-gitea-ssh.md) — memory note git-agent-push-access-gitea-ssh +- [rendezvous-no-reply-backstop-design](rendezvous-no-reply-backstop-design.md) — Décision d'architecture sur la fin-de-tour et le backstop no-reply du rendez-vous idea_ask_agent ⇄ idea_reply, après échec live du fix Finding A (77e62e5). +- [checkpoint-rendezvous-redesign-devbackend-todo](checkpoint-rendezvous-redesign-devbackend-todo.md) — memory note checkpoint-rendezvous-redesign-devbackend-todo +- [backstop-no-reply-live-test-failed-2026-06-23](backstop-no-reply-live-test-failed-2026-06-23.md) — memory note backstop-no-reply-live-test-failed-2026-06-23 +- [live-state-reboot-reconciliation-design](live-state-reboot-reconciliation-design.md) — memory note live-state-reboot-reconciliation-design +- [reconcile-live-state-implemented-feature-branch](reconcile-live-state-implemented-feature-branch.md) — memory note reconcile-live-state-implemented-feature-branch +- [rendezvous-600s-cap-too-short-heavy-tasks](rendezvous-600s-cap-too-short-heavy-tasks.md) — memory note rendezvous-600s-cap-too-short-heavy-tasks +- [backstop-fires-on-intra-task-turn-rootcause](backstop-fires-on-intra-task-turn-rootcause.md) — memory note backstop-fires-on-intra-task-turn-rootcause +- [mcp-functional-test-plan-2026-06-24](mcp-functional-test-plan-2026-06-24.md) — memory note mcp-functional-test-plan-2026-06-24 +- [checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect](checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect.md) — memory note checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect +- [checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer](checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer.md) — memory note checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer +- [mcp-t7-backstop-600s-reply-rejected-livestate-stale](mcp-t7-backstop-600s-reply-rejected-livestate-stale.md) — memory note mcp-t7-backstop-600s-reply-rejected-livestate-stale +- [mcp-functional-tests-t1-t9-green-live-2026-06-24](mcp-functional-tests-t1-t9-green-live-2026-06-24.md) — memory note mcp-functional-tests-t1-t9-green-live-2026-06-24 +- [mcp-t10a-harness-interrupt-does-not-cancel-rendezvous](mcp-t10a-harness-interrupt-does-not-cancel-rendezvous.md) — memory note mcp-t10a-harness-interrupt-does-not-cancel-rendezvous +- [mcp-t10b-pending-reboot-verification](mcp-t10b-pending-reboot-verification.md) — memory note mcp-t10b-pending-reboot-verification diff --git a/.ideai/memory/backstop-fires-on-intra-task-turn-rootcause.md b/.ideai/memory/backstop-fires-on-intra-task-turn-rootcause.md new file mode 100644 index 0000000..4f8ef0e --- /dev/null +++ b/.ideai/memory/backstop-fires-on-intra-task-turn-rootcause.md @@ -0,0 +1,34 @@ +--- +name: backstop-fires-on-intra-task-turn-rootcause +description: memory note backstop-fires-on-intra-task-turn-rootcause +metadata: + type: project +--- +--- +title: CAUSE RACINE — le backstop tire sur chaque tour interne (turn_duration ≠ retour-au-prompt) +type: project +description: Le turn-watcher fait tirer le backstop no-reply au PREMIER turn_duration, soit le 1er pas d'agent interne — il casse toute délégation multi-étapes (libération prématurée + rapport perdu). Le test QA pong passait par chance (tâche mono-tour). +--- +# CAUSE RACINE (2026-06-23, prouvée live) — backstop tire sur les tours INTERNES + +## Preuve +Cadrage délégué à Architect (tâche multi-étapes, lecture de code) : +``` +562100 [rendezvous] ask started -> Architect turn_timeout_ms=600000 +564943 [rendezvous] ask returned-to-prompt-no-reply: Architect after_ms=2843 +564943 [mailbox] complete_without_reply agent=dce19c75 ticket=cc4067fa outcome=sent +``` +- Tiré à **2843 ms** : impossible qu'Architect ait fini un cadrage en 2,8 s. +- Au même instant, transcript Architect **toujours en écriture** (Bash/Read en boucle), `idea_reply` count = **0** → Architect **continuait à travailler**. +- Transcript = **61 `turn_duration`** : Claude écrit un `turn_duration` **par tour d'agent interne** (chaque appel LLM de la boucle agentique), PAS seulement au retour-au-prompt final. + +## Diagnostic +`claude_turn_watcher.rs` fait tirer `turn_ended` au **premier incrément** de `turn_duration` au-dessus de la baseline. Or cet incrément survient dès le **1er pas interne** de l'agent (~secondes). Donc le backstop confond « l'agent a fait un pas de raisonnement » avec « l'agent est retourné au prompt sans répondre ». Pour **toute tâche multi-étapes** : libération prématurée du demandeur « sans réponse » pendant que la cible bosse encore → `idea_reply` final → « no pending request » → **rapport perdu** (travail survit sur disque/branche). + +Le test QA « pong » (backstop-no-reply-live-test-failed-2026-06-23, devenu vert) passait **par chance** : tâche à **un seul tour** ⇒ premier tour = dernier tour. Le fix `encode_cwd` a fait tirer le watcher correctement, mais a **révélé que la CONDITION de tir est fausse**. + +## Conséquence opérationnelle +Avec le binaire courant, **aucune délégation substantielle (multi-tours) ne remonte son rapport** : elle est coupée à ~3 s. Workaround Main en attendant le fix : déléguer → surveiller le transcript de la cible jusqu'à inactivité (mtime stable) → récupérer le résultat sur disque/branche ou re-solliciter en léger. + +## Implication pour la refonte (seam liveness/fin-de-tour, validée utilisateur) +« Retourné au prompt idle » NE peut PAS être « un turn_duration est apparu ». Le bon signal = **process vivant (`is_agent_live`) + transcript NE grossit PLUS depuis stall_after_ms** (battement de cœur). Tant que le transcript grossit ⇒ l'agent travaille ⇒ ne pas libérer. Le 1er lot du fix devrait **neutraliser ce tir prématuré**. Lié à [[rendezvous-600s-cap-too-short-heavy-tasks]], [[rendezvous-no-reply-backstop-design]], [[backstop-no-reply-live-test-failed-2026-06-23]]. \ No newline at end of file diff --git a/.ideai/memory/backstop-no-reply-live-test-failed-2026-06-23.md b/.ideai/memory/backstop-no-reply-live-test-failed-2026-06-23.md new file mode 100644 index 0000000..78f79c9 --- /dev/null +++ b/.ideai/memory/backstop-no-reply-live-test-failed-2026-06-23.md @@ -0,0 +1,34 @@ +--- +name: backstop-no-reply-live-test-failed-2026-06-23 +description: memory note backstop-no-reply-live-test-failed-2026-06-23 +metadata: + type: project +--- +--- +title: Backstop no-reply — RE-VALIDATION LIVE VERTE (wedge levé) après fix encode_cwd +type: project +description: Le backstop no-reply du rendez-vous est validé en live le 2026-06-23 (session post-rebuild) : demandeur libéré ~grâce 2s au lieu de 600s. Reste un point de finition (résultat véhiculé via canal d'erreur). +--- + +# Backstop no-reply — RE-VALIDATION LIVE : VERTE (2026-06-23, session post-rebuild) + +Remplace le 1er test (ÉCHEC) du même jour. La cause racine était l'encodage du slug projet Claude (`.` non encodé) corrigé dans `crates/infrastructure/src/inspector/claude_paths.rs` (`encode_cwd` → `replace(/[^a-zA-Z0-9]/g,'-')`, double-tiret sur `/.ideai/`). Voir [[checkpoint-rendezvous-redesign-devbackend-todo]] pour la cause racine. + +## Preuves live (idea.log, session a6ced819) +Scénario : DevBackend → idea_ask_agent vers QA « pong en prose, SANS idea_reply (volontaire) ». + +``` +912948 [mcp] ask armed requester=DevBackend(73c853d1) target=QA timeout_ms=600000 +917707 [turn-watcher] turn_duration detected agent=QA(aefdbd61) count 47->48 -> turn_ended +917707 [input-mediator] turn_ended agent=QA no_reply_payload -> mark_idle + grace +917707 [input-mediator] turn_ended arming grace agent=QA ticket=0257c630 grace_ms=2000 +919707 [mailbox] complete_without_reply agent=QA ticket=0257c630 queue_depth_before=1 after=0 outcome=sent +``` + +Contraste avec l'échec précédent : `turn_ended` = 0 avant le fix, ici il FIRE ; `arm … dir=…IdeA--ideai-run-…` sur dossier EXISTANT ; grâce 2s exacte (917707→919707) ; libération du demandeur via `complete_without_reply outcome=sent`. DevBackend a repris la main en ~13 s mesuré (pas 600 s). Aucun wedge, aucun cascade-timeout. + +## Point de finition résiduel (NON bloquant, follow-up) +Le résultat rendu au demandeur passe par le **canal d'erreur** MCP : « agent QA returned to its prompt without calling idea_reply (no answer was rendered); retry the request and ensure the agent replies via idea_reply ». Ce n'est pas un résultat no-reply synthétique structuré (design item c de [[rendezvous-no-reply-backstop-design]]) ; pour un orchestrateur c'est mal distinguable d'un échec transitoire « retry ». À transformer en verdict consommable (follow-up, hors blocage merge). + +## Suite +Wedge prouvé levé en live ⇒ garde-fou Git satisfait. Délégué à Git : commit du fix `encode_cwd` (+ test de non-régression `encode_cwd_encodes_dot_in_run_dir_to_double_dash`) sur la branche du backstop, puis décision de merge develop. \ No newline at end of file diff --git a/.ideai/memory/checkpoint-auto-memory-harvest-lot-e1.md b/.ideai/memory/checkpoint-auto-memory-harvest-lot-e1.md new file mode 100644 index 0000000..9a12e9c --- /dev/null +++ b/.ideai/memory/checkpoint-auto-memory-harvest-lot-e1.md @@ -0,0 +1,62 @@ +--- +name: checkpoint-auto-memory-harvest-lot-e1 +description: memory note checkpoint-auto-memory-harvest-lot-e1 +metadata: + type: project +--- +# Checkpoint — Auto-memory harvest Lot E1 + +Date: 2026-06-21 + +## État final + +Lot E1 `auto-memory harvest contrôlé` terminé, validé QA et mergé localement dans `develop`. + +Branche finale: +- `develop` @ `61c330a` — merge local `feature/controlled-auto-memory-harvest`. +- Branche `feature/controlled-auto-memory-harvest` supprimée après merge. +- Aucun push, aucun merge vers `main`. +- Working tree applicatif propre; seuls résidus non committés: bruit runtime `.ideai/*` volontairement exclu. + +## Commits créés + +- `b12081b` — `feat(memory): récolte automatique contrôlée de la mémoire (Lot E1)` + - Domaine: parsing pur de blocs fenced `idea-memory` avec limites et erreurs isolées. + - Application: `HarvestMemoryFromTurn` best-effort sur `TurnRole::Response`, persistance via `MemoryStore`, événements `MemorySaved` via `EventBus`. + - Orchestrateur: harvest branché après checkpoint/handoff sur chemins de succès ask/reply, non bloquant. + - Lifecycle: directive mémoire IdeA injectée dans le contexte convention-file. + - App/Tauri: wiring du use case dans `AppState`. + - Frontend: refresh du panneau mémoire sur `memorySaved`; patch test-only mock gateway `workState`. + +- `8c7c47c` — `fix(mcp): fallback déterministe du runtime dir socket` + - Durcit le choix du runtime dir socket MCP si `XDG_RUNTIME_DIR` est inutilisable. + - Commit séparé car indépendant du Lot E1. + +- `61c330a` — `merge(memory): intègre la récolte automatique contrôlée (Lot E1) dans develop`. + +## QA verte + +Commandes QA réelles: +- `cargo fmt --all -- --check` OK. +- `cargo check -p app-tauri` OK. +- `cd frontend && npx tsc --noEmit` OK. +- `cd frontend && npx vitest run` OK après patch test-only, `42 passed`, `405 tests`. +- `cargo test -p domain memory_harvest -- --nocapture` OK, 14 passed. +- `cargo test -p application --test memory_harvest -- --nocapture` OK, 5 passed. +- `cargo test -p application --test orchestrator_service harvest -- --nocapture` OK, 4 passed. +- `cargo test -p infrastructure --test memory_store idempotent -- --nocapture` OK, 1 passed. +- `cargo test --workspace` brut rouge uniquement sur tests AF_UNIX/MCP bloqués par sandbox `Operation not permitted`; workspace filtré strictement sur ces tests environnementaux OK. + +## Décisions produit/techniques + +- Harvest strictement best-effort: jamais bloquant pour réponse, délégation, handoff ou ticket. +- Mémoire durable seulement via directive explicite `idea-memory`, plafonnée et validée; pas de log brut, pas de live-state, pas de handoff dans ce mécanisme. +- `title` est exigé par directive mais non persisté car l'entité `Memory` n'a pas de champ titre; accepté pour E1 afin de ne pas élargir le lot. +- Les fichiers runtime `.ideai/*` générés pendant la session restent hors commits applicatifs. + +## Suite probable + +Prochains chantiers à cadrer par Architect avant implémentation: +- persistance conversationnelle/canonical log + handoff incrémental cross-profile; +- live-state projet persistant et séparé de la mémoire durable; +- synchronisation documentaire architecture après les lots Work B/C/D et E1. \ No newline at end of file diff --git a/.ideai/memory/checkpoint-finding-a-fix-built-live-validation-pending.md b/.ideai/memory/checkpoint-finding-a-fix-built-live-validation-pending.md new file mode 100644 index 0000000..32119e4 --- /dev/null +++ b/.ideai/memory/checkpoint-finding-a-fix-built-live-validation-pending.md @@ -0,0 +1,37 @@ +--- +name: checkpoint-finding-a-fix-built-live-validation-pending +description: memory note checkpoint-finding-a-fix-built-live-validation-pending +metadata: + type: project +--- +# Checkpoint — Finding A : fix confirmé EN CODE + AppImage clean DÉPLOYÉE → reste la validation e2e LIVE (MAJ 2026-06-23 09:33) + +Suite de [[mcp-e2e-findings-reply-wedge-phantom-busy]] (le défaut) et [[resume-after-appimage-rebuild-mcp-e2e]] (méthode). + +## État du code (OK, vert) +- Fix Finding A **mergé dans `develop @ 77e62e5`** (par-dessus feat `c5e5493`). +- 3 lots : **Lot 1** préambule `idea_reply` impératif (`infrastructure/src/input/mod.rs` + `application/src/agent/lifecycle.rs`) ; **Lot 2 = LE fix** `with_prompt_ready_pattern("? for shortcuts")` sur le profil Claude (`application/src/agent/catalogue.rs:80`) + overlay `overlay_reference_defaults` ; **Lot 3** backstop timeout typé `TARGET_RETURNED_NO_REPLY` / env `IDEA_ASK_RENDEZVOUS_TIMEOUT_MS` (`infrastructure/src/orchestrator/mcp/server.rs`). +- Tests lib qui PROUVENT le seed (re-passés vert le 2026-06-23) : `application::agent::catalogue::mcp_tests::claude_seed_carries_the_measured_prompt_ready_pattern` ✓ et `codex_seed_leaves_prompt_ready_pattern_empty_for_now` ✓. + +## LEÇON D'OUTILLAGE (important, éviter de refaire l'erreur) +**Ne PAS utiliser `strings`/`grep` sur le binaire comme oracle de présence du fix.** Le compilateur élimine/inline les littéraux uniques de `catalogue.rs` : `? for shortcuts` ET `codex --version` sont ABSENTS du binaire alors que le code et les tests les portent (les littéraux aussi présents ailleurs — `claude --version`, `.mcp.json`, `CLAUDE.md` — n'apparaissent que par co-référence). Oracle fiable = **tests** + **comportement live**, pas le grep binaire. (Une fausse alarme « build incrémental incohérent » a été levée puis invalidée ainsi.) + +## Mise en service FAITE +- Rebuild clean (`cargo clean -p application -p infrastructure` puis recette tauri) → artefact `…/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`. +- **Déployé** sur `/home/anthony/Documents/IdeA_0.3.0_amd64.AppImage` (rename atomique ; SHA256 `138714473ed6a87b…` installée == artefact). Ancienne sauvegardée `…AppImage.bak-23pre`. +- ⚠️ **L'instance IdeA qui tourne encore est l'ANCIENNE** (FUSE déjà monté). Il faut que **l'utilisateur RELANCE IdeA** pour charger le nouveau binaire — cela redémarre tous les agents, dont Main (la session courante meurt). + +## PROCHAINE ÉTAPE après redémarrage : test e2e LIVE +Méthode (rappel) : **NE PAS valider le système inter-agents VIA `idea_ask_agent`**. Piloter via subagents natifs / l'utilisateur. Diag EXTERNE : `ss -xp | grep idea-mcp` (ponts) ; transcripts `~/.claude/projects//*.jsonl` (horodatages UTC ; reply réel = `tool_use` idea_reply ; wedge = transcript demandeur figé sur `tool_use` idea_ask_agent sans `tool_result`). + +Scénario qui wedgeait (doit maintenant passer) : +1. Faire déléguer **DevBackend → QA** : « réponds juste pong ». +2. ATTENDU avec le fix : si QA répond en prose sans `idea_reply`, dans la grâce `PROMPT_READY_GRACE` le retour au prompt idle (`? for shortcuts`) déclenche `complete_without_reply` → **DevBackend reçoit `TargetReturnedNoReply` (retryable) et REPREND la main** (plus de wedge) ; `idea_workstate` QA repasse `working`→`idle`/`done` (plus de Busy fantôme). Lot 1 devrait en plus pousser QA à appeler `idea_reply` (chemin positif → DevBackend reçoit pong). +3. Non-régression : chemin positif normal (QA appelle `idea_reply`) → demandeur reçoit le résultat, workstate→done ; livraison 1er tour à froid intacte. +4. Calibrer `PROMPT_READY_GRACE` si la fenêtre est trop courte/longue en réel (défaut conservé). + +## Après validation verte +Merge `develop` déjà fait. Reste éventuelle release (0.4.0 ?) = décision utilisateur. NB : **Git est de nouveau strictement local, PLUS d'accès push** (la note `git-agent-push-access-gitea-ssh` a été corrigée en ce sens). `develop` est ~69 commits devant `origin/develop` (sync remote hors périmètre Git). + +## Follow-ups différés (hors PR) +Capture `prompt_ready_pattern` Codex ; généralisation merge profils de référence ; e2e délégation background (node_id None) / profils MCP défaut / observabilité UI des délégations ; durcissement doc `idea_reply` dans `CLAUDE.md` versionné (router à Git). \ No newline at end of file diff --git a/.ideai/memory/checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer.md b/.ideai/memory/checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer.md new file mode 100644 index 0000000..cf2bd04 --- /dev/null +++ b/.ideai/memory/checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer.md @@ -0,0 +1,40 @@ +--- +name: checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer +description: memory note checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer +metadata: + type: project +--- +--- +name: checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer +description: T1→T6 verts (non-régression). T7 ROUGE diagnostiqué (fix sur mauvaise couche) PUIS RE-FIX implémenté dans le working tree + vert en tests unitaires. Reste : rebuild AppImage en cours → relance IdeA → re-valider T1→T7 en live → commit Git. +metadata: + type: project +--- +# Checkpoint tests fonctionnels MCP — run a6ced819 (2026-06-24) + +Plan via skill **mcp-rendezvous-functional-test**, Main = demandeur. Setup chauds : Main/QA/DevFrontend ; froids : Architect/DevBackend/Git. + +## T1→T6 : VERTS sur AppImage 09:21 (aucune régression) +T1 visibilité, T2 chaude `pong`, T3 réveil froid Git (1er tour intact), T4 background node_id None, T5 no-reply backstop (live-state purgée), T6 multi-étapes == oracle. Détails dans l'historique. NB outillage : `pgrep -af mcp-server | grep ` = FAUX positifs (le shell de mesure se matche) → filtrer `ps -eo cmd | grep 'app-tauri mcp-server'`. + +## T7 : défaut trouvé EN LIVE puis CORRIGÉ +**Défaut (T7e live, 621 s ≈ 600 s)** : cible en long tour unique > 600 s (transcript en croissance prouvée) coupée par un timeout PLAT `process error: agent session reply timed out`, pas d'extension. Le watchdog du fix précédent vivait côté requester (`server.rs`) mais le seam qui coupe vraiment est `application/src/orchestrator/service.rs` (`ASK_AGENT_TIMEOUT`=600s plat appliqué au drain de tour délégué, ~L1564). **Fix posé sur la mauvaise couche.** + +**RE-FIX implémenté (subagent natif Architect→DevBackend→QA, worktree, ramené dans le main working tree ; HEAD toujours `1efe2f1`, NON committé)** : +- Découverte clé : `ClaudeSdkSession::send().await` rend TOUS les `ReplyEvent` en bloc À LA FIN du tour (rien mid-turn) → la vivacité par-événement est inutilisable ; seul signe de vie réel = **sonde transcript** (octets `.jsonl`), comme `server.rs`. +- Un seul `run_inactivity_watchdog` générique dans **`crates/application/src/orchestrator/rendezvous.rs`** (NOUVEAU, pur, testé), réutilisé par `service.rs` (le vrai seam) ET `server.rs` (copie privée supprimée → dé-duplication). +- Nouveau port `AskLivenessProbe` injecté au composition root `app-tauri/state.rs` → `transcript_activity_token` (+ `with_ask_ceiling`, env `IDEA_ASK_RENDEZVOUS_CEILING_MS`, défaut 4 h). Sans injection ⇒ fallback fenêtre plate = zéro régression. +- Issues typées : progrès 600 s en un long tour (la machine est rapide : `cargo clean && cargo build --release --workspace` en boucle jusqu'à >13 min, transcript en croissance) ⇒ ATTENDU = PAS de timeout à 600 s, extension, reply final livré. Puis enchaîner T8 (parallèles), T9 (transitif A→B→C, Architect+DevBackend froids), T10 (interruption + reconcile reboot). +4. **Si live vert : demander à Git de committer** le fix (cf. [[git-owns-commit-merge-decisions]]) ; rendez-vous MCP court marche (T2-T6), donc commit délégable à Git OU subagent natif. + +Lié à [[rendezvous-600s-cap-too-short-heavy-tasks]], [[mcp-functional-test-plan-2026-06-24]], [[mcp-bridge-and-delegation-runtime-notes]]. + + \ No newline at end of file diff --git a/.ideai/memory/checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect.md b/.ideai/memory/checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect.md new file mode 100644 index 0000000..1fc86f4 --- /dev/null +++ b/.ideai/memory/checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect.md @@ -0,0 +1,29 @@ +--- +name: checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect +description: memory note checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect +metadata: + type: project +--- +--- +name: checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect +description: Run a6ced819 — relance complète T1→T6 du test fonctionnel MCP sur AppImage 08:41 (fix T5). T5 (live-state purgée sur no-reply) VALIDÉ EN LIVE. Blocage à T7 = défaut plafond 600s connu, non corrigé. +metadata: + type: project +--- +# Checkpoint tests fonctionnels MCP — run a6ced819 (2026-06-24, relance depuis T1) + +Binaire validé : **AppImage 2026-06-24 08:41:30** (fix live-state no-reply), process en cours = cette AppImage, git HEAD `1efe2f1`. Plan via skill **mcp-rendezvous-functional-test** (Main = demandeur). Voir [[mcp-functional-test-plan-2026-06-24]], [[rendezvous-600s-cap-too-short-heavy-tasks]], [[backstop-fires-on-intra-task-turn-rootcause]]. + +## Résultats de la relance T1→T6 (tous ✅) +- **T1** visibilité outils ✅ — 3 requesters chauds (Main, QA, DevFrontend) chacun pont ESTAB + 2 mcp-server ; froids (Architect, DevBackend, Git) 0 process. +- **T2** cible chaude (QA) ✅ — `pong` inline borné, workstate `done`, pas de busy fantôme. +- **T3** réveil à froid (Git) ✅ — nouveau pont (26012/26016), 1er tour `[IdeA·tâche…]` `parentUuid:null` intact, `delivered`, `pong-cold`. +- **T4** background node_id None (Git hors layouts.json) ✅ — `bg-step-ok`, multi-`delivered`, pont stable sans dup. +- **T5** no-reply backstop ✅ **VALIDÉ EN LIVE (le fix marche)** — Main libéré borné avec `TargetReturnedNoReply` (« returned to its prompt without calling idea_reply », retryable) ET **QA `status:"done"` purgée** (plus de busy fantôme `working`). Défaut historique #1 fermé. +- **T6** multi-étapes (QA lit fichier + `find` puis reply) ✅ — pas de libération prématurée, reply réel `header=… ; rs_count=55` (concorde avec `find` indépendant). Piège turn-watcher non déclenché. + +## Blocage actuel : T7 (NON lancé — défaut connu non corrigé) +T7 = tâche lourde > plafond 600s. C'est le finding [[rendezvous-600s-cap-too-short-heavy-tasks]] : dépassement du plafond `IDEA_ASK_RENDEZVOUS_TIMEOUT_MS` (défaut 600000) → faux `-32001` + canal de report mort (idea_reply final non livrable), alors que la cible travaille et finit sur la branche. Les pistes (extension sur signe de vie / message « cible active, plafond atteint » distinct du retryable / plafond configurable / pas de cascade) sont **à cadrer Architect, pas encore implémentées**. Lancer T7 en live = figer Main ~10 min pour re-confirmer un défaut déjà documenté. + +## Prochaine étape (décision utilisateur en attente) +Trois options sur T7 : (a) le lancer quand même pour re-confirmer (wedge ~10 min) ; (b) ouvrir le cycle de fix du plafond maintenant (Architect→Git→DevBackend→QA, rebuild, reprise T1) ; (c) sauter T7 et continuer T8 (parallèles), T9 (transitif A→B→C), T10 (interruption + reconcile reboot) qui ne dépendent pas du plafond. Architect/DevBackend restent froids pour T9. \ No newline at end of file diff --git a/.ideai/memory/checkpoint-rendezvous-redesign-devbackend-todo.md b/.ideai/memory/checkpoint-rendezvous-redesign-devbackend-todo.md new file mode 100644 index 0000000..58e8322 --- /dev/null +++ b/.ideai/memory/checkpoint-rendezvous-redesign-devbackend-todo.md @@ -0,0 +1,38 @@ +--- +name: checkpoint-rendezvous-redesign-devbackend-todo +description: memory note checkpoint-rendezvous-redesign-devbackend-todo +metadata: + type: project +--- +# Checkpoint — Backstop no-reply : 1ère validation live ÉCHOUÉE → cause racine corrigée → RE-VALIDATION LIVE EN ATTENTE (MAJ 2026-06-23 ~19:40) + +## Résultat du 1er test live (AppImage 0.3.0 du commit e87c05f) : ÉCHEC reproduisant le wedge +Scénario joué : DevBackend→QA `ping (test backstop)`, QA répond `pong` EN PROSE sans `idea_reply`. +- QA a bien émis un vrai `turn_duration durationMs=2218` (transcript, ts 17:23:40.161Z). +- MAIS `[turn-watcher] turn_ended` JAMAIS émis (`grep -c turn_ended` sur tout le log = **0**) → DevBackend resté coincé → rendez-vous Main→DevBackend a timeout 600s (MCP error -32001). + +## CAUSE RACINE (prouvée) — encodage du slug projet Claude faux +Traces `[turn-watcher] arm` : toutes `conversation=` + `baseline … count=0`. Le dossier transcript calculé n'existait pas : +- calculé (faux) : `…/-home-…-IdeA-.ideai-run-` (point conservé) → `ls` = absent +- réel (disque) : `…/-home-…-IdeA--ideai-run-` (DOUBLE tiret) +`encode_cwd` (crates/infrastructure/src/inspector/claude_paths.rs) ne remplaçait que `/`/`\\` et gardait le `.`. Claude Code encode `replace(/[^a-zA-Z0-9]/g,'-')`. Dir introuvable → conversation non résolue → baseline 0 → `turn_duration` jamais vu → backstop mort. +Log de référence : `/home/anthony/.local/share/app.idea.ide/logs/idea.log`. + +## FIX LIVRÉ par DevBackend (NON commité — Git tranche) +- `encode_cwd` : `.map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })` (algo exact Claude Code). `/IdeA/.ideai/run` → `-IdeA--ideai-run`. +- Test non-régression ajouté `encode_cwd_encodes_dot_in_run_dir_to_double_dash` (PASS). Test Windows maj `C:\\Users\\me`→`C--Users-me`. +- DevBackend précise : `count_turn_ends` agrège déjà tous les `.jsonl` du dossier ⇒ pas besoin de choisir un .jsonl ; `conversation=` à l'armement est normal et sans impact une fois le dir correct. + +## QA PRÉ-VALIDATION : VERTE +`cargo test --workspace` = **1616 passed, 0 failed** ; `cargo clippy --workspace --all-targets` = 0 erreur. Nouveau test + 5 tests turn-watcher tous PASS. + +## EN COURS : rebuild AppImage (par Main, perms project-root OK) +Recette : `npm --prefix frontend run build` puis depuis `crates/app-tauri/` `APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage`. Logs scratchpad fe-build.log / appimage-build.log. Sortie : `/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`. + +## SEUL RESTE À FAIRE = RE-VALIDATION LIVE (garde-fou Git : pas de merge sans wedge prouvé levé EN LIVE) +1. Utilisateur remplace l'AppImage installée par la nouvelle + relance IdeA (coupe la session). +2. Session fraîche : rejouer DevBackend→QA pong-sans-`idea_reply`. ATTENDU cette fois : trace `arm … dir=…IdeA--ideai-run-… conversation=…` sur dossier EXISTANT + baseline NON nulle + `[turn-watcher] turn_duration detected … -> turn_ended` ⇒ libération du demandeur ~grâce (≈2s) avec résultat no-reply synthétique, et non-régression du chemin propre `idea_reply`. +3. Si VERT → demander à Git d'amender/committer le fix sur `feature/rendezvous-no-reply-backstop` puis décider du merge develop. Si KO → relayer trace réelle à DevBackend. + +## Trou produit confirmé (hors scope, à planifier) +Au reboot, `.ideai/live-state.json` non réconcilié : agents restent `working` fantôme (process morts). Fichier file-guardé, `idea_workstate_set` = self only, aucun reset cross-agent. Manque : réconciliation au boot OU outil orchestrateur de reset. \ No newline at end of file diff --git a/.ideai/memory/checkpoint-work-tab-blank-fix.md b/.ideai/memory/checkpoint-work-tab-blank-fix.md new file mode 100644 index 0000000..e98e490 --- /dev/null +++ b/.ideai/memory/checkpoint-work-tab-blank-fix.md @@ -0,0 +1,34 @@ +--- +name: checkpoint-work-tab-blank-fix +description: memory note checkpoint-work-tab-blank-fix +metadata: + type: project +--- +# Checkpoint — Fix onglet Work vide + +Date: 2026-06-21 + +## État final + +Correctif frontend de l'onglet Work vide terminé, validé QA (VERT) et mergé localement dans `develop`. + +- `develop` @ `a66881d` — merge local `--no-ff` du fix. +- Commit de feature intégré : `3e1e553` (anciennement `9d2f186`, message amendé pour retirer le marqueur `(WIP, QA en attente)`). +- Branche `fix/work-tab-blank-page` supprimée après merge. +- Aucun push, aucune PR. `develop` en avance de 45 commits sur `origin/develop` (local-only). + +## Contenu du fix + +Normalisation défensive du work-state (`frontend/src/adapters/workStateNormalization.ts` nouveau) + adaptation panneau/mock/tests, pour qu'un work-state partiel/inattendu ne rende plus une page entièrement vide. 5 fichiers, +259/-47. + +## QA verte (sortie réelle) + +- `cd frontend && npx tsc --noEmit` → exit 0. +- `cd frontend && npx vitest run` → 42 fichiers / 407 tests passed, exit 0. + +## Branches ouvertes restantes (non mergées dans develop) + +À cadrer/arbitrer avant intégration : +- `feature/agent-skills` @ `ef101db` — domaine skills agent + use cases + FS store + injection LaunchAgent (L12). +- `feature/agent-skill-awareness` @ `5be8987` — manifeste skills + outil MCP `idea_skill_read` + brief « capacités IdeA » dans contexte agent ; contient aussi un fix input cold-start (`e93a2c1`). +- `fix/cold-start-delivery-race` @ `9590eac` — fix livraison délégation cold-start ; branche basée sur d'anciens commits release (0.1.0/0.2.0), potentiellement redondante avec `e93a2c1` de skill-awareness → vérifier avant merge. \ No newline at end of file diff --git a/.ideai/memory/conversation-log-ls6-rotation-and-paginated-read.md b/.ideai/memory/conversation-log-ls6-rotation-and-paginated-read.md new file mode 100644 index 0000000..6d9b317 --- /dev/null +++ b/.ideai/memory/conversation-log-ls6-rotation-and-paginated-read.md @@ -0,0 +1,29 @@ +--- +name: conversation-log-ls6-rotation-and-paginated-read +description: Contrat figé de la rétention/rotation du transcript et de la lecture paginée riche (surface humaine). +metadata: + type: reference +--- +# LS6 — Rotation transcript + lecture paginée + +Hygiène de persistance du `log.jsonl` + surface de lecture humaine paginée (viewer LS7). + +## Invariant pivot INV-LS6 +La rotation n'élague/déplace JAMAIS un tour d'id ≥ `up_to` du handoff courant : le tour up_to + tous les postérieurs restent dans le segment ACTIF. Sans handoff (ou up_to nil) ⇒ aucune rotation. Garantit que `ConversationLog::read(since=up_to)` (fold incrémental) et la reprise restent corrects après rotation. Motif : `read(since)` cherche le curseur par position ; retirer up_to du fichier casserait le fold. + +## Rotation +- Stratégie = **archive segmentée** (`log.jsonl` actif + `log.1.jsonl`… anciens), PAS troncature/suppression (transcript = surface humaine riche à préserver). +- Déclencheur **hors chemin chaud** : use case `RotateConversationLog` à la reprise/ouverture, best-effort, idempotent. L'`append` ne déclenche JAMAIS la rotation (zéro latence ajoutée). +- Seuils sur segment actif : `ROTATE_AFTER_TURNS=500` OU `ROTATE_AFTER_BYTES=1MiB`. Backstop disque `MAX_ARCHIVE_SEGMENTS=20` (drop du plus ancien seulement). Politique pure domaine `rotation_plan(...) -> Skip|Archive{keep_from=up_to}`. +- Mécanique : verrou conversation bref ; réécriture actif **atomique en dernier** (tmp+rename) ; aucun tour perdu (∪ actif+archives) ; pagination dédoublonne par TurnId (crash-safe). + +## Lecture paginée (port ISP `ConversationArchive`, impl FsConversationLog) +- `page(conversation, PageCursor{anchor,direction Forward|Backward}, limit)` → `TurnSlice{turns, has_more}`. limit clampé [1,200] défaut 50. Ordre chrono croissant. Archive-aware. +- Use case `ReadConversationPage` → DTO humain `TurnPage{turns:[TurnView{id,at_ms,role,source,text COMPLET,text_len}], has_more, next_anchor}`. **Texte complet, non borné, non distillé** = surface HUMAINE. +- `ConversationLog` (append/read/last) inchangé = vue segment actif (chemin chaud + dérivation). + +## Frontière +Lecture riche consommée UNIQUEMENT par la commande Tauri du viewer humain, jamais par compose_convention_file/handoff/agent. Rotation ne touche que le transcript brut, jamais le handoff/injection. + +## Frontend +LS6 = backend + commande Tauri `read_conversation_page`. Tout le React (viewer) = LS7. \ No newline at end of file diff --git a/.ideai/memory/conversation-viewer-ls7-frontend.md b/.ideai/memory/conversation-viewer-ls7-frontend.md new file mode 100644 index 0000000..e5db6c2 --- /dev/null +++ b/.ideai/memory/conversation-viewer-ls7-frontend.md @@ -0,0 +1,26 @@ +--- +name: conversation-viewer-ls7-frontend +description: Contrat figé du viewer de transcript paginé (frontend pur, lecture seule, surface humaine). +metadata: + type: reference +--- +# LS7 — Viewer fil-par-paire (front pur) + +Dernier lot fonctionnel du programme persistance. UI humaine qui rejoue le transcript par paire, consomme la commande LS6 `read_conversation_page`. Lecture seule, zéro impact backend/agent. + +## Gateway / adapter / mock (patron WorkStateGateway) +- Port `ConversationGateway.readPage(projectId, conversationId, {anchor?, direction?, limit?}) -> TurnPage`. Ajouter `conversation` à `Gateways`. +- Types domaine : `TurnView{id, atMs, role: prompt|response|toolActivity, source: {kind:human}|{kind:agent,agentId}, text COMPLET, textLen}`, `TurnPage{turns, hasMore, nextAnchor}`. +- Adapter `TauriConversationGateway` → invoke("read_conversation_page",{projectId,conversationId,cursor:{anchor,direction},limit}) + `conversationNormalization.ts` (mapper source Human/Agent calqué sur workStateNormalization). Mock avec pagination réelle + `_setTurns` pour tests offline. +- Frontière : composant → useGateways().conversation → adapter → invoke. Jamais d'invoke direct. + +## Modèle d'affichage +- Clé = conversationId. Entrée = drill-down depuis le Work panel (`ConversationWorkSummary.conversationId` déjà énuméré ; rendre les lignes cliquables via prop `onOpenConversation`). Pas de nouvel appel « liste conversations ». +- Rejeu chrono croissant ; distinction sobre User↔Agent vs Agent↔Agent (parties déduites des sources, noms résolus depuis l'inventaire agents) ; toolActivity atténué/replié. +- Pagination : ouverture backward/None = dernière page (bas du fil) ; scroll-up = backward ancré sur le tour le plus ANCIEN du buffer (préfixe, dédup par id, hasMore gate) ; refresh queue = forward ancré sur le plus récent (manuel ou sur domain events delegationReady/orchestratorRequestProcessed). + +## Intégration +- État local `viewerConversationId` dans ProjectsView ; swap main-area (rend ConversationViewer à la place de LayoutGrid, PAS un nouveau kind backend) ; bouton retour ; reset au changement de projet. viewerConversationId=null ⇒ UI actuelle inchangée (non-régression terminal). + +## Frontière / backend +Lecture seule (read_conversation_page LS6, hors chemin chaud), jamais injecté dans un contexte agent. **Aucune part backend** : juste vérifier la signature de la commande exposée et aligner l'adapter si écart. \ No newline at end of file diff --git a/.ideai/memory/git-agent-push-access-gitea-ssh.md b/.ideai/memory/git-agent-push-access-gitea-ssh.md new file mode 100644 index 0000000..4d35f91 --- /dev/null +++ b/.ideai/memory/git-agent-push-access-gitea-ssh.md @@ -0,0 +1,15 @@ +--- +name: git-agent-push-access-gitea-ssh +description: memory note git-agent-push-access-gitea-ssh +metadata: + type: project +--- +**CORRECTION (2026-06-23) — annule et remplace la note précédente : l'agent Git N'A PLUS l'accès push. Il est de nouveau STRICTEMENT LOCAL, comme avant.** + +- Git gère **100 % du dépôt local** : commits, branches, merges/rebases locaux. Rien d'autre. +- **Aucune action sortante** : pas de `git push`, pas de PR distante, pas de publication de tags, pas de synchronisation remote. Ce périmètre n'est PAS dans ses responsabilités. +- Toute synchro avec un remote (`origin` Gitea ou autre) reste **hors périmètre Git** et sous décision/validation explicite de l'utilisateur, exécutée hors agent Git. + +Le contexte d'agent `git.md` reflète déjà ce retour au strictement-local (garde-fou « tu restes strictement local, pas de `git push` »). + +Voir [[git-owns-commit-merge-decisions]] (Git tranche toute la topologie LOCALE du dépôt). \ No newline at end of file diff --git a/.ideai/memory/handoff-ls5-summary-bound-and-llm-seam.md b/.ideai/memory/handoff-ls5-summary-bound-and-llm-seam.md new file mode 100644 index 0000000..c7da737 --- /dev/null +++ b/.ideai/memory/handoff-ls5-summary-bound-and-llm-seam.md @@ -0,0 +1,22 @@ +--- +name: handoff-ls5-summary-bound-and-llm-seam +description: Contrat figé de la borne de handoff (perf) et du seam résumeur LLM non activé. +metadata: + type: reference +--- +# LS5 — Borne handoff + seam LLM + +Ferme le must-have perf du programme persistance : un `summary_md` non borné gonflait le contexte injecté à chaque lancement. + +## Borne summary_md (déterministe, en CARACTÈRES, pas tokens) +- **Par tour** : `render_turn` tronque le texte aplati à `TURN_LINE_MAX_CHARS=240` (infra summarizer). Corrige le trou réel (un gros Response = ligne géante). +- **Totale** : fn PURE domaine `bound_handoff_summary(Handoff, max)` + `HANDOFF_SUMMARY_MAX_CHARS=4096`. Stratégie de dépassement = **troncature distillée** (garde objectif + lignes les plus récentes, drop oldest, frontière de ligne). JAMAIS re-fold compactant, JAMAIS rejet (n'bloque jamais l'append du log). `up_to`/`objective` jamais modifiés. Idempotent. +- **Appliquée à l'écriture** (`RecordTurn` après `fold`, avant `save` → borne universelle quel que soit le résumeur) **ET défensivement à l'injection** (`resolve_handoff` avant compose → protège les `handoff.md` legacy non bornés, sans migration). + +## Seam LLM : TRANCHÉ = « prêt mais NON activé » +- Pas de vrai LLM en LS5 : `fold` tourne sur le chemin chaud (par tour, dans la délégation) → un appel réseau là = régression perf interdite. Défaut runtime reste `HeuristicHandoffSummarizer`. +- Durcissement réel = la borne totale appliquée **dans RecordTurn (hors résumeur)** ⇒ un futur `LlmHandoffSummarizer` ne peut pas exploser le budget contexte. +- Contrat futur adapter LLM (documenté) : déclenchement **froid/hors chemin** (reprise/rotation/débounce, jamais par tour) ; timeout + **fallback heuristique** (trait sans `Result`) ; sélection par flag, défaut heuristique. Trait `HandoffSummarizer::fold` inchangé. + +## Frontière +Transcript = `log.jsonl` (jamais injecté). `summary_md` = dérivé distillé, désormais borné à l'écriture et à l'injection. Conforme « surface agent = borné/distillé ». \ No newline at end of file diff --git a/.ideai/memory/idea-program-surface-separation-and-livestate.md b/.ideai/memory/idea-program-surface-separation-and-livestate.md new file mode 100644 index 0000000..d199e4a --- /dev/null +++ b/.ideai/memory/idea-program-surface-separation-and-livestate.md @@ -0,0 +1,14 @@ +--- +name: idea-program-surface-separation-and-livestate +description: Cadrage du programme post-skills : ce qui existe vs reste, et la règle de frontière surface-agent/humaine. +metadata: + type: project +--- +État au cadrage (develop @ 827d477) : +- **#2a handoff incrémental cross-profile = FAIT + CÂBLÉ** (lots P1–P8) : `domain/conversation_log.rs` (`Handoff`, `HandoffStore`, `HandoffSummarizer::fold` incrémental, `ProviderSessionStore`), `application/conversation/record.rs` (`RecordTurn` = append+fold), wiring `state.rs` (`AppRecordTurnProvider`), injection au lancement via `HandoffProvider` (P7). Résumeur = `HeuristicHandoffSummarizer`, seam async pour `LlmHandoffSummarizer` (P10) déjà en place. Reste : seam LLM + borne `summary_md`. +- **#2b log canonique = FAIT** : `ConversationLog` append-only `.ideai/conversations//log.jsonl`, **jamais injecté à un agent** (doc domaine explicite). Reste : rétention/rotation + lecture paginée UI. +- **#1 live-state agent = À CONSTRUIRE (cœur)** : aujourd'hui seul `GetProjectWorkState` (read-model HUMAIN dérivé, non durable). Construire un 4ᵉ store `live-state.json` (**gitignored, runtime**) : `domain/live_state.rs` (`LiveState`/`LiveEntry`/`WorkStatus`, port `LiveStateStore`, **keyed last-writer-wins, JAMAIS append**, champs bornés, `prune` TTL+max_n), use cases `UpdateLiveState`/`GetLiveStateLean`, adapter `FsLiveStateStore`, auto-update depuis `orchestrator/service.rs` (ask→Working, reply→Done), injection bornée `# État du projet` au lancement + outils MCP `idea_workstate_read`/`idea_workstate_set`. Ne stocke QUE le non-re-dérivable (intent/progrès/dernière délégation) ; busy/queue restent calculés. + +**Règle de frontière gravée (testable)** : Surface AGENT = borné/distillé/pointeur uniquement (capacités, pointeurs mémoire, affordances skills, handoff `summary_md`, live-state lean). Surface HUMAINE = riche (ProjectWorkStatePanel, viewer fil-par-paire qui rejoue le log). **Transcript / append-only INTERDIT d'injection dans un contexte agent** ; seul un dérivé distillé franchit. 4 stores distincts : `memory/` (savoir stable, versionné) · `handoff.md` (reprise par fil) · `log.jsonl` (transcript humain) · `live-state.json` (coordination transitoire maigre). + +Découpage : LS0 doc baseline → LS1 domaine live-state → LS2 store+usecases → LS3 auto-update délégation → LS4 injection+MCP (clôt cold-start) ; LS5 seam LLM+borne handoff, LS6 rétention log+lecture UI (parallèles) ; LS7 UX fil-par-paire (front pur) ; LS8 doc clôture. \ No newline at end of file diff --git a/.ideai/memory/live-state-persistence-program-closed-ls8.md b/.ideai/memory/live-state-persistence-program-closed-ls8.md new file mode 100644 index 0000000..21f7e97 --- /dev/null +++ b/.ideai/memory/live-state-persistence-program-closed-ls8.md @@ -0,0 +1,15 @@ +--- +name: live-state-persistence-program-closed-ls8 +description: Le programme live-state + persistance conversationnelle est livré et documenté ; pointe la doc de clôture et les points restés ouverts. +metadata: + type: project +--- +Programme **live-state / persistance conversationnelle CLOS** (LS1→LS7 livrés @ fd7adbb ; LS8 = doc de clôture). + +**Doc de référence** : `docs/LS8-live-state-persistence-closure.md` + `ARCHITECTURE.md` §21 (cartographie des 4 stores) ; §19 retitré « LIVRÉ » ; §14.1 et §18.5 corrigés (catalogue MCP **14**). + +**4 stores disjoints** : `.ideai/memory/` (savoir stable, versionné) · `handoff.md` (reprise distillée bornée ≤4096) · `log.jsonl` (transcript humain riche, JAMAIS injecté) · `live-state.json` (coordination maigre runtime, gitignoré). Surface AGENT bornée/distillée/pointeur ; surface HUMAINE riche ; seul le handoff distillé franchit vers un contexte agent. + +**Restant ouvert** : (1) activation seam LLM `LlmHandoffSummarizer` (non activé, défaut heuristique, contrat ADR LS5) ; (2) sweep périodique de rotation (idempotent, non câblé) ; (3) **discordance D19-4 ↔ .gitignore** : `.ideai/conversations/` devait être gitignoré mais est suivi — à trancher Git/Main ; (4) intégration MCP e2e UX ; (5) multi-fenêtres registre sessions ; (6) auto-update mémoire/contexte en cours de session. + +**Contrainte d'écriture** : un agent lancé en run-dir isolé (`.ideai/run//`) **ne peut pas écrire l'arbre du project root** (ARCHITECTURE.md, docs/) — les modifs de doc passent par Git (write+commit authority). \ No newline at end of file diff --git a/.ideai/memory/live-state-reboot-reconciliation-design.md b/.ideai/memory/live-state-reboot-reconciliation-design.md new file mode 100644 index 0000000..e6a4d73 --- /dev/null +++ b/.ideai/memory/live-state-reboot-reconciliation-design.md @@ -0,0 +1,21 @@ +--- +name: live-state-reboot-reconciliation-design +description: memory note live-state-reboot-reconciliation-design +metadata: + type: project +--- +--- +title: Réconciliation du live-state au reboot (ReconcileLiveState) +type: reference +description: Décision d'architecture pour réconcilier les lignes live-state fantômes au redémarrage — seam, source de vérité, contrat de statut et préservation du self-only. +--- +**Problème** : au reboot, `.ideai/live-state.json` n'est pas réconcilié → agents fantômes `working/waiting/blocked` alors que leurs sessions sont mortes. `prune` (TTL 6h) ne couvre pas le fantôme récent. + +**Décision (cadrage Architect, 2026-06-23)** : +- **Seam** : étape best-effort `reconcile_live_state` dans la chaîne `open_project` (commands.rs), juste après `reconcile_layouts`. Pas d'app-boot global (live-state est par-projet). Pas d'outil MCP. Justification : un crash ne déclenche jamais `close_project`, donc **open est le seul filet fiable**. +- **Source de vérité** : le registre de sessions vivantes `LiveSessions` / trait `LiveAgentRegistry` (`application/src/terminal/registry.rs`) — la *même* vérité que `GetProjectWorkState`. Prédicat orphelin = `status ∈ {working,waiting,blocked}` ET `!is_agent_live`. Race-safe : tourne avant toute relance ; LWW gagne via `updated_at_ms` frais. +- **Contrat** : orphelin → `idle` (pas de nouveau statut, évite le ripple DTO/front). Garder `intent` (trace), vider `ticket` + `last_delegation` (rendez-vous mort, tracé ailleurs dans log/handoff), `progress` = marqueur stale, `updated_at_ms` frais. +- **Pas de nouveau port.** Use case applicative `ReconcileLiveState` (compose `LiveStateStore` + `LiveAgentRegistry` + `Clock`) + transformation **pure** `LiveState::reconcile_orphans(is_live, now)` dans `domain/src/live_state.rs`. Provider par-root côté app-tauri (calqué sur `LiveStateProvider`/`live_state_for`). +- **Self-only de `idea_workstate_set`** : invariant **de la surface MCP**, pas du store. `set_workstate` lie `agent_id`=identité handshake ; `UpdateLiveState`/`LiveStateStore` sont identity-agnostic. La réconciliation est un **acte système** dans le composition root qui appelle le port directement, **jamais** via `OrchestratorCommand::SetWorkState` → self-only préservé par construction. Même split que `snapshot_running_agents`/`reconcile_layouts`. + +Fichiers probables : `domain/src/live_state.rs`, `application/src/workstate/{mod.rs,reconcile.rs}`, `app-tauri/src/{lib.rs,state.rs,commands.rs}`. Voir [[live-state-persistence-program-closed-ls8]], [[handoff-ls5-summary-bound-and-llm-seam]]. \ No newline at end of file diff --git a/.ideai/memory/mcp-e2e-findings-reply-wedge-phantom-busy.md b/.ideai/memory/mcp-e2e-findings-reply-wedge-phantom-busy.md new file mode 100644 index 0000000..eeaa75c --- /dev/null +++ b/.ideai/memory/mcp-e2e-findings-reply-wedge-phantom-busy.md @@ -0,0 +1,47 @@ +--- +name: mcp-e2e-findings-reply-wedge-phantom-busy +description: memory note mcp-e2e-findings-reply-wedge-phantom-busy +metadata: + type: project +--- +--- +name: mcp-e2e-findings-reply-wedge-phantom-busy +description: Findings validation MCP e2e live (AppImage 0.3.0) — transport/rendez-vous SAINS ; un seul vrai défaut = wedge si l'agent délégué n'appelle pas idea_reply (pas de timeout serveur). +metadata: + type: project +--- +# Findings validation MCP e2e (live, AppImage 0.3.0) — 2026-06-22 + +Contexte : validation réelle du parcours inter-agents (cf. [[resume-after-appimage-rebuild-mcp-e2e]]). Méthode = Main observe en externe (Bash sur `ss -xp` + transcripts `~/.claude/projects//*.jsonl`), l'utilisateur déclenche dans l'UI ; on ne valide PAS via `idea_ask_agent`. + +## Scénario 1 — délégation vers agent FROID + cycle complet : VERDICT +- ✅ **Réveil à froid** : déléguer vers un agent arrêté relance son pont (`mcp-server --requester ` réapparaît, socket ESTAB recompte). +- ✅ **1er tour non perdu** : tâche livrée intacte = 1er `user` `[IdeA · tâche de · ticket ] …`. +- ✅ **Chemin positif `idea_reply`** : quand l'agent cible appelle `idea_reply(ticket,result)`, le bridge répond `reply ... delivered`, le **demandeur reçoit le `tool_result` et reprend la main** (vérifié : DevBackend reçoit `pong` puis continue). +- ✅ **Purge live-state** : dès l'`idea_reply`, `idea_workstate` repasse le cible de `working` → `done`, intent vidé. + +➡️ **Transport + protocole de rendez-vous = SAINS.** Bugs historiques 3/4/5 OK sur ce chemin. + +## LE défaut restant (racine unique) — wedge sans `idea_reply` +Si l'agent délégué **termine son tour sans appeler `idea_reply`** (ex. tâche triviale « réponds pong » → il répond `pong` en **prose** et s'arrête) : +- le **demandeur wedge indéfiniment** dans `idea_ask_agent` (transcript demandeur figé sur le `tool_use` idea_ask_agent, aucun `tool_result`) ; +- la **cible reste `working`** sur le ticket périmé dans la live-state ; +- **aucun timeout ni récupération** côté serveur. Une interruption utilisateur du `ask` libère le demandeur mais **ne purge pas** le `working` de la cible (purge seulement à l'`idea_reply`). + +➡️ Le « Busy fantôme » n'est PAS un bug d'état indépendant : c'est la **conséquence** de l'absence d'`idea_reply`. Une seule cause à traiter. + +### Pistes correctif (à arbitrer Architect) +1. **Durcir contextes agents** : « toute tâche déléguée se termine IMPÉRATIVEMENT par `idea_reply`, même triviale » (cheap, attaque la cause comportementale). +2. **Garde-fou serveur (rendez-vous)** : timeout sur l'attente `idea_ask_agent` → renvoyer au demandeur un résultat d'échec/relance explicite + purger le `working` de la cible (idempotent). Touche le contrat rendez-vous + cycle de vie live-state = domaine Architect. Voir [[idea-program-surface-separation-and-livestate]], [[mcp-bridge-and-delegation-runtime-notes]]. + +## Diagnostic / repro (rappels) +- Ponts : `pgrep -af 'mcp-server --endpoint'` (1 requester = 2 process) ; `ss -xp | grep -c idea-mcp`. +- Reply réel = `tool_use` `idea_reply` dans le transcript cible → tool_result `reply ... delivered` (⚠️ ne pas confondre avec les mentions `idea_reply` du CLAUDE.md injecté via attachments). +- Wedge demandeur = transcript demandeur s'arrête sur `tool_use` `idea_ask_agent` sans `tool_result`. +- ⚠️ Horodatages transcripts = **UTC** ; `ls %H:%M:%S` masque la date → comparer avec `date` + mtime réel (`ls -t`). + +## Restant à tester (scénarios e2e suivants) +- Délégation vers agent BACKGROUND (node_id None) — même protocole, vérifier pas de 1er tour perdu. +- `ask` sans réponse ne wedge pas durablement la CONNEXION du demandeur (vs juste l'appel) — Bug 5 côté transport. +- Profils Claude/Codex utilisent MCP par défaut ; fallback fichier+prose cohérent. +- Observabilité UI des délégations/replies (aujourd'hui opaque) — sujet UX. \ No newline at end of file diff --git a/.ideai/memory/mcp-functional-test-plan-2026-06-24.md b/.ideai/memory/mcp-functional-test-plan-2026-06-24.md new file mode 100644 index 0000000..077e211 --- /dev/null +++ b/.ideai/memory/mcp-functional-test-plan-2026-06-24.md @@ -0,0 +1,45 @@ +--- +name: mcp-functional-test-plan-2026-06-24 +description: memory note mcp-functional-test-plan-2026-06-24 +metadata: + type: project +--- +--- +name: mcp-functional-test-plan-2026-06-24 +description: Plan de test FONCTIONNEL du rendez-vous inter-agents MCP (trivial→tordu), à exécuter après rebuild develop HEAD ; méthode, ordre, setup IdeA et critères de réussite. +metadata: + type: project +--- +# Plan de test fonctionnel MCP inter-agents (2026-06-24, run a6ced819) + +Objectif : état des lieux par tests **fonctionnels** (pas unitaires) du rendez-vous `idea_ask_agent` ⇄ `idea_reply`, du trivial au plus tordu. On lance dans l'ordre, on **s'arrête au 1er blocage**, on corrige (cycle Architect→Git→Dev→QA), rebuild+relance, puis on **reprend de T1**. Contexte/historique : [[mcp-e2e-findings-reply-wedge-phantom-busy]], [[backstop-fires-on-intra-task-turn-rootcause]], [[rendezvous-no-reply-backstop-design]], [[rendezvous-600s-cap-too-short-heavy-tasks]], [[reconcile-live-state-implemented-feature-branch]]. + +## Contexte binaire au moment du plan +- AppImage qui tournait = build **23/06 19:42**, PÉRIMÉ (antérieur à `c6f0f86` encode_cwd, `744de20` backstop no-reply, `886bb0d`/`1efe2f1` reconcile). Décision utilisateur : **rebuild develop HEAD `1efe2f1` puis relance** avant de tester. +- Règle structurelle : **binaire qui tourne = AppImage**. Tout fix backend ⇒ rebuild + relance d'IdeA (tue Main). Les tests s'enchaînent SANS relance *à l'intérieur d'une même version* ; chaque cycle de fix = 1 relance. + +## Méthode +- **Main est le demandeur** (`idea_ask_agent`) = test fidèle de « si je donne un message à un agent, il me répond ». +- Doublé d'**observation externe Bash** : ponts `ss -xp | grep idea-mcp` et `pgrep -af mcp-server` ; transcripts `~/.claude/projects//*.jsonl` (horodatages **UTC** ; reply réel = `tool_use` idea_reply→tool_result `reply … delivered` ; wedge demandeur = transcript figé sur `tool_use` idea_ask_agent sans tool_result) ; live-state via `idea_workstate_read`. +- Risque résiduel : un wedge non borné fige le tour de Main → l'utilisateur peut interrompre. + +## Setup IdeA à mettre en place avant T1 (consigne utilisateur) +1. Relancer IdeA, ouvrir le projet IdeA, **Main visible** (le demandeur). +2. Garder **2–3 cellules libres visibles** pour qu'on observe à l'œil les délégations (Main y placera des cibles visibles, ou lance-toi QA + DevFrontend dans des cellules visibles). +3. **Laisser Architect, DevBackend, Git ARRÊTÉS (froids)** : T3 testera le réveil à froid ; T9 utilisera Architect+DevBackend froids. +4. Cibles chaudes pour T1/T2 : Main les réchauffe lui-même (launch) — pas d'action requise. + +## Ordre des tests (trivial → tordu) +- **T1 — Visibilité outils MCP.** Un agent lancé par IdeA voit ses outils sans action manuelle. Vérif : pont ESTAB (`ss`) + 2 process mcp-server par requester. +- **T2 — Cible CHAUDE + idea_reply trivial.** Main→ask(agent chaud, « réponds pong via idea_reply »). Attendu : réponse inline `pong`, temps borné, workstate working→done purgé. +- **T3 — Réveil à FROID + idea_reply.** Cible arrêtée. Attendu : pont relancé (nouveau mcp-server), **1er tour non perdu** (1er user `[IdeA · tâche…]` intact), reply remonte. +- **T4 — Cible BACKGROUND (node_id None).** Délégation vers agent sans cellule visible. Attendu : même protocole, pas de 1er tour perdu (historiquement non testé). +- **T5 — NO-REPLY (cible répond en prose, pas d'idea_reply).** Attendu backstop : demandeur libéré en temps borné avec `TargetReturnedNoReply` (retryable), **pas de Busy fantôme** (workstate purgé). DÉFAUT historique #1. +- **T6 — Délégation MULTI-ÉTAPES puis idea_reply.** Cible lit plusieurs fichiers / lance une cmd PUIS idea_reply. Attendu : **pas de libération prématurée** pendant le travail, rapport final livré. PIÈGE turn-watcher (cause racine : backstop tirait au 1er tour interne ~3 s). +- **T7 — Tâche LOURDE > plafond** (impl + `cargo test`/clippy). Attendu : extension sur signe de vie OU message clair « cible active, plafond atteint » (≠ faux `-32001`) ; idea_reply tardif non perdu. Plafond `IDEA_ASK_RENDEZVOUS_TIMEOUT_MS` (défaut 600000). +- **T8 — Délégations PARALLÈLES** (2 cibles en 1 tour Main). Attendu : rendez-vous multiples simultanés OK, multiplexage pont, 2 réponses distinctes. +- **T9 — TRANSITIVE A→B→C** (Main→Architect, Architect délègue à DevBackend puis reply à Main). Attendu : rendez-vous imbriqués, 2 attentes simultanées sans deadlock. Cas le plus tordu. +- **T10 — INTERRUPTION/annulation + reconcile reboot.** (a) Main interrompt un ask en vol → `working` cible purgé, pas de cascade, idea_reply tardif non-livrable proprement. (b) Au reboot, orphelins status∈{working,waiting,blocked} & session morte → `idle` + marker `STALE_AT_RESTART_MARKER` (use case ReconcileLiveState, cf. [[reconcile-live-state-implemented-feature-branch]]). + +## Critère de fin +Tous les T1→T10 verts avec preuve réelle (réponse inline + observation externe cohérente). Sinon : 1er rouge = blocage à corriger, puis reprise de T1. \ No newline at end of file diff --git a/.ideai/memory/mcp-functional-tests-t1-t9-green-live-2026-06-24.md b/.ideai/memory/mcp-functional-tests-t1-t9-green-live-2026-06-24.md new file mode 100644 index 0000000..c4000e9 --- /dev/null +++ b/.ideai/memory/mcp-functional-tests-t1-t9-green-live-2026-06-24.md @@ -0,0 +1,33 @@ +--- +name: mcp-functional-tests-t1-t9-green-live-2026-06-24 +description: memory note mcp-functional-tests-t1-t9-green-live-2026-06-24 +metadata: + type: project +--- +--- +title: MCP tests fonctionnels T1→T9 VERTS en live (2026-06-24, AppImage 12:53 HEAD 1efe2f1 + fix T7 non committé) +type: project +description: Passe complète T1→T9 verte en conditions réelles sur AppImage du 24/06 12:53 ; T5 (backstop no-reply) et T7 (watchdog inactivité >600s) validés live ; reste T10 (interruption + reconcile reboot) qui exige action utilisateur. +--- +# Tests fonctionnels MCP rendez-vous — T1→T9 VERTS live (2026-06-24) + +Run a6ced819, plan [[mcp-functional-test-plan-2026-06-24]]. AppImage **rebuild 12:53 depuis l'arbre de travail** (HEAD `1efe2f1` + le fix T7 watchdog **non committé** mais présent dans les 11 fichiers modifiés du git status → embarqué dans le binaire). Méthode : Main demandeur + observation externe (ss/pgrep/transcripts UTC/workstate). + +## Résultats (preuve réelle) +- **T1 visibilité MCP** ✅ : agents chauds = pont ESTAB + 2 mcp-server (app-tauri parent + AppImage) chacun. +- **T2 cible chaude + reply trivial** ✅ : DevFrontend → `pong` inline, reply delivered 10:56:51 UTC, workstate done (pas de Busy fantôme). +- **T3 réveil à FROID** ✅ : Git froid → `pong-cold` ; nouveau pont spawné, **1er tour `[IdeA·tâche]` intact** (régresseur encode_cwd résolu), reply delivered 10:57:34. +- **T4 cible BACKGROUND (node_id None)** ✅ : Git background → `pong-bg`, reply delivered 10:58:14. +- **T5 NO-REPLY backstop** ✅ (défaut historique #1 résolu) : cible répond en prose sans idea_reply → demandeur libéré en ~20 s avec erreur retryable « returned to its prompt without calling idea_reply », workstate cible done (PAS de Busy fantôme). +- **T6 multi-étapes** ✅ (piège turn-watcher résolu) : DevFrontend lit fichiers + git log PUIS idea_reply ; pas de libération prématurée ; données réelles (142 fichiers, hashes corrects). +- **T7 tâche LOURDE >600s** ✅ (défaut historique #2 résolu, validation live du fix watchdog) : QA charge réelle continue 11:00:46→11:12:44 UTC = **~11m58s**, cargo clean+build release+test (1632 passed/0)+clippy+9 relances ; rendez-vous **NON expiré à 600s**, idea_reply accepté au-delà du plafond (aucun « no pending request »), reply delivered 11:13:21. +- **T8 PARALLÈLE** ✅ : 2 asks même tour (DevFrontend `alpha-8` + Git `beta-8`), réponses distinctes, multiplexage pont OK. +- **T9 TRANSITIF A→B→C** ✅ : Main→Architect→DevBackend (les 2 froids) → `relay:gamma-9:done` ; rendez-vous imbriqués + double réveil à froid sans deadlock ; DevBackend reply delivered 11:14:32. + +## Reste : T10 (exige action utilisateur) +- **T10a interruption** : Main interrompt un ask en vol → attendu : workstate cible purgé, pas de cascade, idea_reply tardif proprement non-livrable. NÉCESSITE que l'utilisateur appuie sur interrupt pendant un ask wedgé. +- **T10b reconcile reboot** : orphelin status∈{working,waiting,blocked} + session morte → au reboot, use case ReconcileLiveState le passe à idle + `STALE_AT_RESTART_MARKER` (cf. [[reconcile-live-state-implemented-feature-branch]]). NÉCESSITE un reboot d'IdeA (ferme Main). + +## Suite recommandée +1. Faire committer le fix T7 par Git (encore non committé, cf. [[git-owns-commit-merge-decisions]] + [[rendezvous-600s-cap-too-short-heavy-tasks]]). +2. Exécuter T10 avec l'utilisateur (interrupt + reboot). \ No newline at end of file diff --git a/.ideai/memory/mcp-t10a-harness-interrupt-does-not-cancel-rendezvous.md b/.ideai/memory/mcp-t10a-harness-interrupt-does-not-cancel-rendezvous.md new file mode 100644 index 0000000..9446856 --- /dev/null +++ b/.ideai/memory/mcp-t10a-harness-interrupt-does-not-cancel-rendezvous.md @@ -0,0 +1,29 @@ +--- +name: mcp-t10a-harness-interrupt-does-not-cancel-rendezvous +description: memory note mcp-t10a-harness-interrupt-does-not-cancel-rendezvous +metadata: + type: project +--- +--- +title: T10a — l'interrupt harness du demandeur n'annule PAS le rendez-vous backend (reply tardif accepté dans le vide) +type: reference +description: Finding T10a (2026-06-24) — interrompre Main (demandeur) au niveau Claude Code ne propage aucune annulation à IdeA ; la cible finit, son idea_reply est « delivered » dans un demandeur abandonné, et la live-state du demandeur reste working. +--- +# Finding T10a (2026-06-24, run a6ced819) + +## Setup +Main → `idea_ask_agent(DevFrontend, "travaille ~90s puis idea_reply late-10a")`. Pendant l'attente, l'utilisateur **interrompt Main** (Échap harness Claude Code). Le tool use `idea_ask_agent` est rejeté côté harness. + +## Observé +- DevFrontend a fini ses ~95 s de travail réel (11:20:39→11:22:14 UTC) puis `idea_reply("late-10a")` → **ACCEPTÉ « delivered »** à 11:22:18 (PAS « no pending request »). +- DevFrontend workstate = **done** (pas d'orphelin côté cible). Pas de cascade. +- **Main (demandeur) workstate resté `working`** : l'interrupt a coupé le tour de Main avant son nettoyage ; le chemin interrupt ne réconcilie pas la live-state du demandeur. + +## Interprétation +L'interrupt Claude Code annule le **tool call local** mais le **process Main survit** → la socket MCP (mcp-server du demandeur) reste connectée → IdeA ne reçoit aucun signal d'annulation → le rendez-vous reste pending → le reply tardif de la cible est livré dans un demandeur qui a déjà abandonné (perdu pour Main mais rapporté « delivered »). + +## Conséquence +- Le T10a du plan (« annulation en vol → reply tardif non-livrable proprement ») **n'est pas exerçable via interrupt harness** tant que le process demandeur survit. Une vraie annulation exigerait : tuer le process demandeur (drop socket) OU un cancel protocolaire JSON-RPC. +- Comportement néanmoins bénin : pas de wedge, pas de cascade, cible réconciliée (done). Seuls écarts : (a) reply tardif « delivered » au lieu de rejeté ; (b) live-state du demandeur laissée working par le chemin interrupt (mais reconcile-au-reboot la rattraperait, cf. [[reconcile-live-state-implemented-feature-branch]]). + +Lié à [[mcp-functional-tests-t1-t9-green-live-2026-06-24]], [[mcp-functional-test-plan-2026-06-24]]. \ No newline at end of file diff --git a/.ideai/memory/mcp-t10b-pending-reboot-verification.md b/.ideai/memory/mcp-t10b-pending-reboot-verification.md new file mode 100644 index 0000000..9d0e19e --- /dev/null +++ b/.ideai/memory/mcp-t10b-pending-reboot-verification.md @@ -0,0 +1,24 @@ +--- +name: mcp-t10b-pending-reboot-verification +description: memory note mcp-t10b-pending-reboot-verification +metadata: + type: project +--- +--- +title: T10b PASS — reconcile au reboot vérifié, inclut l'orchestrateur (2026-06-24) +type: project +description: T10b validé live après reboot. ReconcileLiveState ramène l'orphelin Main working→idle en conservant l'intent et purgeant ticket/lastDelegation. Couvre l'orchestrateur. Reste : faire committer par Git les fix non committés (T7 watchdog + reconcile). +--- +# T10b — PASS (reboot reconcile vérifié) + +## Verdict +Orphelin armé = Main `working` intent "T10b-ORPHAN-MAIN". Après reboot (run a6ced819), `idea_workstate_read` montre Main : +- `status: idle` ✅ (était working) +- `intent` conservé ✅ +- `ticket` purgé ✅ / `lastDelegation` purgé ✅ + +Signature `STALE_AT_RESTART_MARKER` complète. **ReconcileLiveState valide ET inclut l'orchestrateur** (Main n'a PAS été exclu malgré la crainte du design open_project). Champ `progress` non exposé par workstate_read mais transition working→idle+intent retenu = concluante. + +## Suite du plan +T1→T10 du plan [[mcp-functional-test-plan-2026-06-24]] terminés (T1→T9 verts cf. [[mcp-functional-tests-t1-t9-green-live-2026-06-24]], T10a finding [[mcp-t10a-harness-interrupt-does-not-cancel-rendezvous]], T10b PASS ici). +**RESTE : faire committer par Git les fix non committés (T7 backstop/watchdog + reconcile live-state).** cf. [[git-owns-commit-merge-decisions]]. \ No newline at end of file diff --git a/.ideai/memory/mcp-t7-backstop-600s-reply-rejected-livestate-stale.md b/.ideai/memory/mcp-t7-backstop-600s-reply-rejected-livestate-stale.md new file mode 100644 index 0000000..8adcb69 --- /dev/null +++ b/.ideai/memory/mcp-t7-backstop-600s-reply-rejected-livestate-stale.md @@ -0,0 +1,37 @@ +--- +name: mcp-t7-backstop-600s-reply-rejected-livestate-stale +description: memory note mcp-t7-backstop-600s-reply-rejected-livestate-stale +metadata: + type: project +--- +--- +title: MCP T7b/T7e — backstop 600s rejette le reply tardif et laisse le live-state worker non réconcilié +type: reference +description: Finding fonctionnel des tests MCP T7b/T7e (2026-06-24) — un idea_reply après dépassement de la fenêtre 600s est refusé « no pending request », et le ticket du worker reste working dans le live-state. +--- + +# Finding — tests MCP T7 (2026-06-24, QA) + +## Contexte +Série de tests du rendez-vous `idea_ask_agent ⇄ idea_reply` et du backstop no-reply (cap ~600s), demandés par Main : +- **T7d** (debug build x3, avant-plan) : cumul ~3m49s < 600s → reply OK. +- **T7b** (9 étapes `sleep 82`, entrelacées) : le harness BLOQUE tout `sleep` avant-plan → exécutées en run_in_background ; reply OK (tardif mais < fenêtre côté ticket T7b). +- **T7e** (release build répété, avant-plan, sans sleep ni bg) : 7 cycles, cumul réel **08:01:14 → 08:14:21 = 13 min 07 s**, franchit largement 600s. + +## Défaut observé +À la fin de T7e (et de T7b, terminé au même tour, après >600s d'occupation continue), les DEUX `idea_reply` (tickets `4613262f…` T7b et `d2398b22…` T7e) sont refusés : + +``` +invalid input: no pending request to reply to for agent aefdbd61-… +``` + +→ Le backstop a libéré le **demandeur** (Main) côté rendez-vous après dépassement du cap, ce qui est le comportement voulu. MAIS : +1. Le **résultat réel** (builds OK, horodatages non falsifiés) ne peut **plus être délivré** : il est perdu pour le demandeur. +2. `idea_workstate_read` montre encore le worker QA `status=working ticket=d2398b22…` **après** le rejet du reply → le live-state du **worker** n'est PAS réconcilié quand le backstop ferme le rendez-vous côté demandeur. Incohérence demandeur (libéré) vs worker (toujours working). + +## Conséquence / piste +- Le cap 600s du rendez-vous reste trop court pour des tâches lourdes légitimes (cf. [[rendezvous-600s-cap-too-short-heavy-tasks]]) : ici un travail réel et honnête >13 min est « jeté ». +- À corriger : quand le backstop ferme un rendez-vous, soit (a) accepter encore un reply tardif (le router vers le demandeur même après timeout / le persister), soit (b) réconcilier le live-state du worker (passer son ticket à done/abandon) pour ne pas laisser un `working` fantôme. Voir [[backstop-fires-on-intra-task-turn-rootcause]] et [[rendezvous-no-reply-backstop-design]]. + +## Reproductible +Charge avant-plan réelle (`cargo build --release --workspace` en boucle clean) dépassant 600s, puis tenter `idea_reply` sur le ticket d'origine → rejet systématique. \ No newline at end of file diff --git a/.ideai/memory/reconcile-live-state-implemented-feature-branch.md b/.ideai/memory/reconcile-live-state-implemented-feature-branch.md new file mode 100644 index 0000000..46818ac --- /dev/null +++ b/.ideai/memory/reconcile-live-state-implemented-feature-branch.md @@ -0,0 +1,25 @@ +--- +name: reconcile-live-state-implemented-feature-branch +description: memory note reconcile-live-state-implemented-feature-branch +metadata: + type: project +--- +--- +title: Réconciliation du live-state au reboot — implémentée (feature/reconcile-live-state) +type: project +description: Le chantier ReconcileLiveState (3 lots) est implémenté et vert (1623 tests), non committé ; détail des fichiers et de l'arbitrage project_id/provider. +--- + +Implémenté le 2026-06-23 sur `feature/reconcile-live-state` (ticket Main a7fcb5e4, dont le rendez-vous `idea_reply` a expiré pendant le build — résultat consigné ici). **Non committé** (Git tranche). Cadrage : [[live-state-reboot-reconciliation-design]]. + +**Résultats réels** : `cargo test --workspace` = **1623 passed, 0 failed**. `cargo clippy --workspace --all-targets` = 0 erreur, aucun warning sur le code ajouté (warnings restants tous pré-existants). + +**Lot 1 (domaine, `crates/domain/src/live_state.rs`)** : `LiveState::reconcile_orphans(is_live, now_ms) -> Vec` pure (ne mute pas self). Orphelin = status ∈ {Working,Waiting,Blocked} ET !is_live → réécrit status=Idle, intent gardé, ticket=None, last_delegation=None, progress=Some(`STALE_AT_RESTART_MARKER`), updated_at_ms=now. Const pub `STALE_AT_RESTART_MARKER = "(stale — session not running at restart)"`. +4 tests. + +**Lot 2 (application, nouveau `crates/application/src/workstate/reconcile.rs`)** : use case `ReconcileLiveState` sur `LiveStateStore`+`LiveAgentRegistry`(ISP)+`Clock` ; `execute(ReconcileLiveStateInput{project_id})` charge → reconcile_orphans → upsert par ligne. +3 tests. Exporté via workstate/mod.rs + application/lib.rs. + +**Lot 3 (app-tauri, `state.rs`+`commands.rs`)** : provider par-root `AppReconcileLiveState` (résout root via `ProjectStore::load_project`, `FsLiveStateStore::new(&root)`, registre = `LiveSessions` existant). Champ `reconcile_live_state` sur AppState. Hook best-effort dans `open_project` après `reconcile_layouts`. **Acte système** : écrit le port `LiveStateStore` directement, jamais via `OrchestratorCommand::SetWorkState` → self-only de `idea_workstate_set` préservé par construction. + +**Arbitrage à valider QA/Architect** : la résolution project_id→root se fait au composition root (le store est root-bound), donc `ReconcileLiveState::execute` reçoit bien `ReconcileLiveStateInput{project_id}` (conforme Lot 2 + hook Lot 3 à la lettre) mais ne relit pas project_id dans le flux pur (`_input`). Variante sans ce param redondant = ajustement trivial si souhaité. + +**Fichiers (6)** : domain/src/live_state.rs ; application/src/workstate/reconcile.rs (nouveau) ; application/src/workstate/mod.rs ; application/src/lib.rs ; app-tauri/src/state.rs ; app-tauri/src/commands.rs. \ No newline at end of file diff --git a/.ideai/memory/rendezvous-600s-cap-too-short-heavy-tasks.md b/.ideai/memory/rendezvous-600s-cap-too-short-heavy-tasks.md new file mode 100644 index 0000000..e054dfa --- /dev/null +++ b/.ideai/memory/rendezvous-600s-cap-too-short-heavy-tasks.md @@ -0,0 +1,37 @@ +--- +name: rendezvous-600s-cap-too-short-heavy-tasks +description: memory note rendezvous-600s-cap-too-short-heavy-tasks +metadata: + type: project +--- +--- +name: rendezvous-600s-cap-too-short-heavy-tasks +description: FIX LIVRÉ (2026-06-24) du plafond rendez-vous 600s trop court (T7) — watchdog d'inactivité + extension sur signe de vie + plafond absolu, message distinct cible-active vs no-reply. Built, tests verts, AppImage 09:21 ; validation live T7 en attente. +metadata: + type: project +--- +# T7 — plafond rendez-vous trop court : FIX LIVRÉ (2026-06-24) + +## Défaut (rappel, observé 2026-06-23) +Délégation dev lourde (impl + `cargo test --workspace` + clippy) → la cible bosse en **un seul long tour > 600 s sans émettre de `turn_duration`**. L'enveloppe serveur `timeout(600s, dispatch)` plate (`crates/infrastructure/src/orchestrator/mcp/server.rs`) expirait sec : (a) faux `-32001`/no-reply **retryable** alors que la cible n'est pas muette mais active ; (b) drop du `dispatch` → canal de report mort → `idea_reply` final non livrable (« no pending request »). + +## Fix (branche de travail courante, pas encore committé) +Implémenté par subagent Claude (Architect+DevBackend, hors `idea_ask_agent` car le rendez-vous était le sujet), vérifié par Main (QA réelle), AppImage rebuildée. +- **Algorithme `run_inactivity_watchdog`** dans `server.rs` : `ASK_RENDEZVOUS_TIMEOUT` (600 s, env `IDEA_ASK_RENDEZVOUS_TIMEOUT_MS`) devient une **fenêtre d'inactivité (budget de silence)**, pas un plafond absolu. Boucle `timeout(fenêtre, dispatch)` ; à chaque expiration, sonde l'activité de la cible : + - progrès & sous plafond → réarme (extension) ; + - progrès & plafond atteint → **`rendezvous_ceiling_active_error`** (nouveau code JSON-RPC `error_codes::RENDEZVOUS_CEILING_ACTIVE = -32_002`, `data.code="RENDEZVOUS_CEILING_ACTIVE"`, **`retryable:false`**, message « still actively working… do not retry blindly, check the target's in-progress work/branch ») ; + - pas de progrès (vrai silence, ou **pas de probe = fallback plat**) → `rendezvous_no_reply_error` inchangé (`retryable:true`). +- **Signe de vie = octets cumulés des `.jsonl`** du run-dir cible (couvre le long tour unique sans `turn_duration`). Nouvelle `inspector::claude_paths::transcript_activity_token(fs, home, cwd) -> Option`. +- **Plafond absolu** `ASK_RENDEZVOUS_CEILING` (défaut **4 h**, env `IDEA_ASK_RENDEZVOUS_CEILING_MS`). +- **Wiring** : sonde optionnelle `AskActivityProbe` injectée dans `McpServer` (modèle `events`/`ready_sink`, additif → zéro régression), branchée par la composition root `crates/app-tauri/src/state.rs` (qui résout nom→AgentId→run-dir via nouveau `service.resolve_agent_id_by_name`). Infra reste libre d'`AgentId`. +- Fichiers : `server.rs`, `jsonrpc.rs`, `mcp/mod.rs`, `lib.rs`, `inspector/claude_paths.rs`+`mod.rs`, `application/.../service.rs`, `app-tauri/state.rs`. + +## Validation (preuve réelle, Main) +`cargo test -p infrastructure --lib` = **251 passed/0** (dont 6 nouveaux tests : silence→no-reply, fallback sans probe→no-reply, active→extension puis resolved, active→ceiling, codes/retryable distincts). `mcp_server` 22/0, `inspector_claude` 4/0, `cargo test -p application` tout vert. `clippy -p infrastructure -p application --all-targets` = **0 warning nouveau** (résiduels pré-existants : input/mod.rs, lifecycle.rs, fileguard.rs, server.rs:132 ready_sink). **AppImage rebuildée 2026-06-24 09:21**, installée sur `/home/anthony/Documents/IdeA_0.3.0_amd64.AppImage`, backup `…​.old-pre-t7-fix` (08:41). + +## Reste à faire +1. **Validation live T7** après relance IdeA : déléguer une tâche lourde >600 s ; attendu = pas d'expiration tant que la cible écrit son transcript, et soit résolution normale, soit (si >4 h) message ceiling-active non-retryable distinct ; idea_reply tardif non perdu tant qu'on n'a pas atteint le plafond. +2. **Commit par Git** (non fait — laissé à Git, cf. [[git-owns-commit-merge-decisions]]) : décider de le faire avant ou après la validation live T7. +3. Reprise complète des tests **depuis T1** (skill mcp-rendezvous-functional-test). État pré-relance : T1→T6 verts cette passe, dont fix T5 validé live (cf. [[checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect]]). + +Lié à [[rendezvous-no-reply-backstop-design]], [[backstop-fires-on-intra-task-turn-rootcause]], [[mcp-functional-test-plan-2026-06-24]]. \ No newline at end of file diff --git a/.ideai/memory/rendezvous-no-reply-backstop-design.md b/.ideai/memory/rendezvous-no-reply-backstop-design.md new file mode 100644 index 0000000..42ea593 --- /dev/null +++ b/.ideai/memory/rendezvous-no-reply-backstop-design.md @@ -0,0 +1,14 @@ +--- +name: rendezvous-no-reply-backstop-design +description: Décision d'architecture sur la fin-de-tour et le backstop no-reply du rendez-vous idea_ask_agent ⇄ idea_reply, après échec live du fix Finding A (77e62e5). +metadata: + type: reference +--- +Le watcher prompt-ready PTY (`infrastructure/src/input/mod.rs` `arm_prompt_watcher`) ne peut PAS servir de signal de fin-de-tour : il est one-shot ré-armé seulement au `bind_handle`, et matche un bandeau TUI **permanent** (`? for shortcuts`) — donc inopérant par tour. Preuve live : aucun `prompt_ready` de toute une session alors que l'agent a travaillé ⇒ c'est le handshake MCP `initialize` (→ `release_agent_cold_start`) qui libère le cold-start, pas le PTY. + +**Contrat figé** : `idea_ask_agent` est garanti libéré en temps borné par l'un de : +1. `idea_reply` (chemin propre, renforcé par le préambule comportemental injecté à chaque délégation) ; +2. backstop no-reply sur **stagnation** — transition `Alive→Stalled` (lot-2 `arm_liveness`/`sweep_stalled`) du `Busy{ticket}` ⇒ résolution synthétique « cible silencieuse sans idea_reply » + libération du demandeur ; +3. timeout absolu **fini** (dernier recours) — `ASK_RENDEZVOUS_TIMEOUT` ne doit JAMAIS être 24h ; défaut reco 600 s, env-overridable, garde `Some(0)=no-override`. La borne applicative per-tour (`resolve_turn_timeout`) doit aussi être finie. + +Le scraping d'octets ANSI bruts est interdit comme autorité de fin-de-tour (fragile, profil-spécifique). Cold-start = signal MCP `initialize`. Aucun port domaine ni contrat de cartographie n'est touché : tout est infrastructure/application (réutilise lot-2 + sink MCP). Diagnosticabilité : les logs d'armement/bind doivent être en `diag!` (pas `eprintln!`→/dev/null) pour observer armement vs match. \ No newline at end of file diff --git a/.ideai/memory/resume-after-appimage-rebuild-mcp-e2e.md b/.ideai/memory/resume-after-appimage-rebuild-mcp-e2e.md new file mode 100644 index 0000000..df89a97 --- /dev/null +++ b/.ideai/memory/resume-after-appimage-rebuild-mcp-e2e.md @@ -0,0 +1,39 @@ +--- +name: resume-after-appimage-rebuild-mcp-e2e +description: memory note resume-after-appimage-rebuild-mcp-e2e +metadata: + type: project +--- +--- +name: resume-after-appimage-rebuild-mcp-e2e +description: Point de reprise — programme live-state CLOS+RELEASÉ (0.3.0), discordance .gitignore D19-4 TRANCHÉE. Prochain chantier = validation MCP e2e UX. Chantiers restants priorisés. +metadata: + type: project +--- +# Reprise — état au 2026-06-22 (run a6ced819) + +## Où on en est +- **Programme live-state / persistance CLOS et RELEASÉ** : `develop` @ `36be0cb` (LS8), `main` @ `29232dd` = `release(0.3.0): intègre develop dans main`. Détail : [[live-state-persistence-program-closed-ls8]]. Doc : `docs/LS8-live-state-persistence-closure.md` + `ARCHITECTURE.md` §21. +- **AppImage rebuildée + tournante** : `IdeA_0.3.0_amd64.AppImage` (le nom n'est plus 0.1.0). Le binaire live embarque tout le programme ; 6 ponts MCP connectés/ESTAB observés → point 1 du chantier e2e (agent voit ses outils MCP sans action manuelle) déjà satisfait en réel. Recette rebuild (rappel) : `npm --prefix frontend run build` puis depuis `crates/app-tauri/` `APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` ; remplacer l'AppImage installée → relancer. +- **Discordance .gitignore ↔ D19-4 : TRANCHÉE** (par l'utilisateur) et exécutée par Git → commit `47b3806` sur `develop` (`chore(gitignore): désuivre .ideai/conversations/`). Périmètre réel : seul `.ideai/conversations/` était discordant (16 fichiers suivis, 8 handoff.md + 8 log.jsonl) ; `.ideai/run/` et `.ideai/live-state.json` étaient déjà ignorés/propres. Désuivis (rm --cached, conservés sur disque) + règle ajoutée `.gitignore:58`. Plus une décision en attente. + +## Prochain chantier (priorité 1) : VALIDATION MCP END-TO-END UX +Objectif = valider/durcir en situation RÉELLE le parcours inter-agents (le code est probablement bon, Bugs 1→7 corrigés cf. [[mcp-bridge-and-delegation-runtime-notes]], mais ces bugs ne sont apparus qu'en live). À vérifier sur l'AppImage 0.3.0 : +1. Un agent lancé par IdeA voit ses outils MCP sans action manuelle. (déjà OK observé) +2. Délégation vers agent FROID et vers agent BACKGROUND (node_id None) : tâche bien écrite dans le PTY, pas de 1er tour perdu, pas de `Busy` fantôme après interruption/annulation (Bugs 3/4/6/7). +3. Un `ask` sans réponse ne wedge pas la connexion du demandeur (Bug 5). +4. Profils Claude/Codex utilisent MCP par défaut ; fallback fichier+prose cohérent sinon. +5. Observabilité UI des délégations/replies (aujourd'hui opaque) — sujet UX. + +**CONTRAINTE MÉTHODO** : NE PAS réparer/valider le système inter-agents VIA le système inter-agents (`idea_ask_agent`). Piloter via les **subagents natifs (outil Agent)**, et demander à l'utilisateur de faire tourner l'AppImage + observer (Main ne peut pas valider seul depuis l'intérieur). Diag live : `ss -xp | grep idea-mcp` (ponts connectés) ; `~/.claude/projects//*.jsonl` (transcript ; pas de nouveau fichier = tâche jamais soumise). + +## Autres chantiers restants (depuis [[remaining-work-idea-agent-control-ide]]) +- **Auto-update mémoire/contexte EN COURS de session** (aujourd'hui injection au lancement seulement) — priorité 1. +- **Multi-fenêtres / déplacement d'onglet** du registre de sessions — priorité 2 (UI). +- Optionnels différés : activation réelle du seam LLM de handoff (contrat figé ADR LS5, défaut heuristique) ; sweep périodique de rotation (idempotent, non câblé). + +## Friction outillage notée (par Git, sans incidence résultat) +Le `settings.local.json` du run dir a un `deny` qui bloque Edit/Write et certaines redirections Bash pour les agents en run dir isolé ; Git a dû éditer `.gitignore` via `tee -a`. À assouplir si on veut des éditions de fichiers propres côté run dir. + +## Contrainte d'écriture (rappel) +Main + agents en run-dir isolé ne peuvent PAS écrire l'arbre du project root (docs/, ARCHITECTURE.md, .ideai/memory/*.md fichiers) — permissions déniées. Mémoire IdeA : outil `idea_memory_write` (create/replace par slug ; pas de delete). Suppression fichiers mémoire / édition MEMORY.md / CLAUDE.md / .gitignore du project root : passer par **Git**. \ No newline at end of file diff --git a/.ideai/memory/skills-integration-canonical-foundation.md b/.ideai/memory/skills-integration-canonical-foundation.md new file mode 100644 index 0000000..4b7ba37 --- /dev/null +++ b/.ideai/memory/skills-integration-canonical-foundation.md @@ -0,0 +1,11 @@ +--- +name: skills-integration-canonical-foundation +description: Topologie d'intégration du chantier skills agent et décision d'abandon de feature/agent-skills. +metadata: + type: project +--- +Le chantier « skills agent » : la fondation canonique du domaine skills est **déjà dans `develop`** (introduite par `2332b7f` : port `SkillStore`, `SkillRef` sur `ManifestEntry`, `resolve_skills` dans `LaunchAgent`, CRUD + assign/unassign, **frontend complet** `features/skills/*`). + +- `feature/agent-skills` (A, @ef101db) = réimplémentation **obsolète et incompatible** (serde `tag="type"`, `with_updated_content`, pas de `SkillRef`/frontend). **À abandonner** — ne jamais merger, elle régresse develop. +- `feature/agent-skill-awareness` (B, @5be8987) = **surensemble propre** de develop (sa base `8452333` est ancêtre de develop ; diff skill = +539/−3). Apporte : `Skill.description`/`effective_description`, manifeste, outil MCP `idea_skill_read` (variante domaine `OrchestratorCommand::ReadSkill`), brief « capacités IdeA » inconditionnel dans `compose_convention_file`. +- Intégration = cherry-pick `ab34363`(+`1a10d67` squash) puis `566bff4` sur develop ; **exclure** `e93a2c1` (cold-start, superseded par `8bb832c`) et le bruit `.ideai/*` (garder seulement `.ideai/memory/feature-agent-skill-awareness-design.md`). Dropper `e93a2c1` évite tout conflit sur `input/mod.rs`. Points chauds : `lifecycle.rs`, `state.rs`. \ No newline at end of file diff --git a/.ideai/skills/index.json b/.ideai/skills/index.json index 8da6007..8195ee9 100644 --- a/.ideai/skills/index.json +++ b/.ideai/skills/index.json @@ -4,7 +4,14 @@ { "id": "a72dac60-641c-4417-b0d7-94b8539f817a", "name": "build-appimage", + "description": null, "contentHash": "77cb33b978b242f6" + }, + { + "id": "86ea97df-1533-47e5-8809-dc2b02242567", + "name": "mcp-rendezvous-functional-test", + "description": null, + "contentHash": "9fc8260f64c3b9d5" } ] } \ No newline at end of file diff --git a/.ideai/skills/md/86ea97df-1533-47e5-8809-dc2b02242567.md b/.ideai/skills/md/86ea97df-1533-47e5-8809-dc2b02242567.md new file mode 100644 index 0000000..5c0a525 --- /dev/null +++ b/.ideai/skills/md/86ea97df-1533-47e5-8809-dc2b02242567.md @@ -0,0 +1,42 @@ +# Skill — Test fonctionnel du rendez-vous inter-agents MCP + +Workflow **ré-exécutable et évolutif** pour tester de bout en bout le rendez-vous `idea_ask_agent` ⇄ `idea_reply` d'IdeA, du trivial au plus tordu. À relancer après chaque rebuild qui touche l'orchestration MCP / le backstop / la live-state. **Ce plan évolue** : à chaque nouveau souci rencontré, ajoute/raffine un test Tn et note le défaut dans la section « Journal des défauts ». + +## Principe +- **Main est le demandeur** (`idea_ask_agent`) — test fidèle de « si je donne un message à un agent, il me répond ». +- Doublé d'**observation externe Bash** : + - ponts : `ss -xp | grep idea-mcp` (ESTAB) et `pgrep -af mcp-server` (2 process par requester = pont vivant). + - transcripts : `~/.claude/projects//*.jsonl` (horodatages **UTC**). Reply réel = `tool_use` idea_reply → tool_result `reply … delivered`. Wedge demandeur = transcript figé sur `tool_use` idea_ask_agent sans tool_result. 1er tour intact = 1re ligne user `parentUuid:null` = `[IdeA · tâche…]`. + - placement : `.ideai/layouts.json` (agent présent = cellule visible ; absent = background / node_id None). + - live-state : `idea_workstate_read` (last-writer-wins, pas temps réel). +- Règle structurelle : **binaire qui tourne = AppImage**. Tout fix backend ⇒ **rebuild + relance** d'IdeA (tue Main). Les tests s'enchaînent SANS relance *à l'intérieur d'une même version*. Vérifier avant T1 : `stat` mtime de l'AppImage récent + `git log -1` = HEAD attendu. + +## Setup avant T1 +1. Relancer IdeA, ouvrir le projet, **Main visible** (le demandeur). +2. Garder **2 cellules visibles libres** + lancer **QA et DevFrontend** dans des cellules visibles (cibles chaudes). +3. Laisser **Architect, DevBackend, Git ARRÊTÉS (froids)** : T3 teste le réveil à froid ; T9 utilise Architect+DevBackend froids. + +## Méthode d'exécution +On lance dans l'ordre, on **note** chaque verdict avec preuve réelle. Si un défaut **bloquant** apparaît : stop, ouvrir le cycle de fix (Architect→Git→DevBackend→QA, ou via subagents si le rendez-vous lui-même est cassé), rebuild + relance, puis **reprise depuis T1**. Un défaut **non bloquant** est consigné au Journal et on peut continuer si l'utilisateur le décide. + +## Ordre des tests (trivial → tordu) +- **T1 — Visibilité outils MCP.** Agent lancé par IdeA voit ses outils sans action manuelle. Vérif : pont ESTAB + 2 mcp-server par requester ; cibles froides = 0 process. +- **T2 — Cible CHAUDE + idea_reply trivial.** ask(agent chaud, « pong via idea_reply »). Attendu : `pong` inline borné, workstate `done`, pas de busy fantôme. +- **T3 — Réveil à FROID + idea_reply.** Cible arrêtée. Attendu : pont relancé (nouveau mcp-server), **1er tour non perdu**, reply remonte. +- **T4 — Cible BACKGROUND (node_id None).** Cible hors layout (sans cellule visible). Attendu : même protocole, multi-tours sans perte. +- **T5 — NO-REPLY (cible répond en prose, pas d'idea_reply).** Attendu : backstop libère le demandeur en temps borné avec `TargetReturnedNoReply` (retryable), **ET live-state de la cible purgée (pas de busy fantôme)**. Défaut historique #1. +- **T6 — Délégation MULTI-ÉTAPES puis idea_reply.** Cible lit plusieurs fichiers / lance une cmd PUIS idea_reply. Attendu : **pas de libération prématurée** pendant le travail, rapport final livré. (Piège turn-watcher : backstop tirait au 1er tour interne ~3 s.) +- **T7 — Tâche LOURDE > plafond** (impl + `cargo test`/clippy). Attendu : extension sur signe de vie OU message clair « cible active, plafond atteint » (≠ faux `-32001`) ; idea_reply tardif non perdu. Plafond `IDEA_ASK_RENDEZVOUS_TIMEOUT_MS` (défaut 600000). +- **T8 — Délégations PARALLÈLES** (2 cibles en 1 tour Main). Attendu : rendez-vous multiples simultanés, multiplexage pont, 2 réponses distinctes. +- **T9 — TRANSITIVE A→B→C** (Main→Architect, Architect délègue à DevBackend puis reply à Main). Attendu : rendez-vous imbriqués, 2 attentes simultanées sans deadlock. +- **T10 — INTERRUPTION/annulation + reconcile reboot.** (a) Main interrompt un ask en vol → `working` cible purgé, pas de cascade, idea_reply tardif non-livrable proprement. (b) Au reboot, orphelins {working,waiting,blocked} & session morte → `idle` + `STALE_AT_RESTART_MARKER` (use case ReconcileLiveState). + +## Critère de fin +Tous les T1→T10 verts avec preuve réelle. Sinon : 1er défaut bloquant = corriger puis reprise T1. + +## Journal des défauts (à enrichir au fil des runs) +- **2026-06-24, run a6ced819 (AppImage 08:15, HEAD 1efe2f1)** — T1→T4 ✅. **T5 partiel** : backstop libère le demandeur en ~24 s (`TargetReturnedNoReply` retryable, wedge demandeur corrigé) MAIS la **live-state de la cible n'est pas purgée** : elle reste `status:"working"` jusqu'au prochain write réussi de la cible (re-délégation non bloquée, busy fantôme atténué mais présent → `idea_workstate_read` ment sur la dispo). Cause : backstop agit côté demandeur, ne réinitialise pas la live-state de la cible sur no-reply. → fix en cours. + +## Notes / pièges observés +- IdeA peut placer une cible réveillée en **background** même quand des cellules visibles sont libres (observé T3/T4). +- Quand le rendez-vous MCP lui-même est suspecté cassé, **ne pas** orchestrer le fix via `idea_ask_agent` (risque de blocage) : utiliser les subagents natifs de l'orchestrateur pour analyse/fix/rebuild. \ No newline at end of file