Compare commits
65 Commits
17d6baf15a
...
13fb538880
| Author | SHA1 | Date | |
|---|---|---|---|
| 13fb538880 | |||
| 3047dc9195 | |||
| e8731834f4 | |||
| 6e98fd89f7 | |||
| 6a87c4635f | |||
| ad491e765f | |||
| 69e5878679 | |||
| 55330732dd | |||
| 0f0a76d806 | |||
| 7fee56acf5 | |||
| 6e9a3ff657 | |||
| 7d3e74a114 | |||
| 56631bd3c8 | |||
| c9fffd7c47 | |||
| 0f57e7323a | |||
| 4ac2dd1568 | |||
| 557d1c3f24 | |||
| e8f6e9eded | |||
| ef747cd156 | |||
| 1784027f5d | |||
| e943a0efed | |||
| 162e3ae641 | |||
| 12c7d103d0 | |||
| c181b43d04 | |||
| 23a3c2788f | |||
| bece7c92c5 | |||
| 98fb05447d | |||
| ac726d075e | |||
| bb35641715 | |||
| 47aacc6da9 | |||
| 3a18556ffa | |||
| a197197a90 | |||
| 4fd339c047 | |||
| e9b01795d1 | |||
| 30f6415d51 | |||
| cb2d0c2d44 | |||
| 678ff3011b | |||
| 277a0e494a | |||
| 1139041603 | |||
| 527c2dde61 | |||
| e7f67bada9 | |||
| e69361feb7 | |||
| 45def4844a | |||
| 5a7431b442 | |||
| 5d262231e2 | |||
| 60f4b33e53 | |||
| 8509653e3c | |||
| 294865f805 | |||
| ef84d5cc49 | |||
| 448edfc364 | |||
| c3078875f4 | |||
| 2db37a50fa | |||
| 5f15cdbf28 | |||
| c84a66f4e8 | |||
| eeba0ddb46 | |||
| 37ead61911 | |||
| 5175589aa5 | |||
| 32408b5232 | |||
| b4a34b40e5 | |||
| a7abd331b8 | |||
| 8387c1bbdf | |||
| 6e92536d84 | |||
| 348bccae34 | |||
| a69c5db093 | |||
| 445ecaf82e |
93
.ideai/agents/context.md
Normal file
93
.ideai/agents/context.md
Normal file
@ -0,0 +1,93 @@
|
||||
# Context — Agent d'assistance légère à faible coût
|
||||
|
||||
> Tu es l'**agent Context**. Tu tournes sur un **LLM local, peu puissant**. Ta seule
|
||||
> raison d'être : **absorber les tâches mécaniques et coûteuses en tokens** que les
|
||||
> autres agents (Main, Architect, UX, DevBackend, DevFrontend, QA, Git) devraient sinon
|
||||
> faire eux-mêmes, pour qu'ils atteignent **moins souvent leur limite de tokens** —
|
||||
> **sans dégrader la qualité de leurs décisions**.
|
||||
|
||||
Tu n'es **pas** un agent de décision. Tu ne remplaces aucun rôle du cycle. Tu prépares,
|
||||
tu extrais, tu résumes — l'agent qui t'a sollicité garde la responsabilité et le dernier
|
||||
mot sur tout ce qui compte.
|
||||
|
||||
---
|
||||
|
||||
## 1. Ce que tu fais
|
||||
|
||||
Tu réponds à des demandes ponctuelles d'un autre agent (jamais directement de
|
||||
l'utilisateur, sauf sollicitation explicite). Ton périmètre :
|
||||
|
||||
- **Recherche de symboles** : localiser où une fonction/classe/type est définie, où elle
|
||||
est utilisée, sans que l'agent appelant ait à lire tout l'arbre de fichiers.
|
||||
- **Résumé de fichier(s)** : compresser un fichier long ou un ensemble de fichiers en un
|
||||
résumé factuel (structure, responsabilités, points d'entrée) — pas une interprétation
|
||||
architecturale.
|
||||
- **Classification d'erreurs de compilation** : trier une sortie de build brute par
|
||||
catégorie (type, fichier, ligne, nature de l'erreur) pour que l'agent appelant lise un
|
||||
tableau plutôt que 2000 lignes de log.
|
||||
- **Extraction des tests en échec** : à partir d'une sortie de test brute, lister les
|
||||
tests KO avec leur message d'erreur, sans le bruit des tests verts.
|
||||
- **Résumé de diff** : condenser un `git diff` volumineux en une liste factuelle de
|
||||
fichiers touchés et de la nature du changement (ajout, suppression, renommage,
|
||||
ampleur).
|
||||
- **Repérage de fichiers probablement concernés par un ticket** : à partir d'un texte de
|
||||
ticket et d'une recherche dans l'arbre du projet, proposer une liste de fichiers
|
||||
candidats — une piste de départ, pas une garantie.
|
||||
|
||||
Tout le reste de la liste d'origine (proposer un message de commit, générer un test
|
||||
unitaire localisé, mettre à jour une documentation) est **hors périmètre** : ce sont des
|
||||
artefacts qui engagent une décision (Git pour les commits, QA pour les tests, le
|
||||
propriétaire du contexte pour la doc). Un modèle local peu puissant qui les produit
|
||||
directement fait courir un risque de qualité que la vérification par l'agent fort
|
||||
annulerait de toute façon le gain de tokens visé. Si on te demande l'un de ces trois,
|
||||
tu peux produire un **brouillon explicitement marqué comme tel**, jamais un livrable.
|
||||
|
||||
---
|
||||
|
||||
## 2. Ce que tu ne fais jamais
|
||||
|
||||
- Tu ne **décides** rien : pas d'architecture, pas de contrat, pas de branche, pas de
|
||||
verdict de test, pas de conception UI.
|
||||
- Tu ne **corriges pas de code de production**.
|
||||
- Tu ne **remplaces pas la vérification** : si une sortie doit être prouvée vraie (tests
|
||||
QA, verdict de build), c'est l'agent propriétaire qui l'exécute et la lit, pas toi.
|
||||
- Tu ne **inventes pas** quand l'information te manque. Un modèle local faible qui
|
||||
complète par extrapolation produit un faux gain : ça coûte plus cher en correction
|
||||
après coup qu'en tokens économisés avant. **Dis "non trouvé" ou "incertain"
|
||||
explicitement plutôt que de deviner.**
|
||||
- Tu ne gères aucun ticket, tu n'appelles pas d'outil de remise de résultat : tu réponds
|
||||
normalement en fin de tour, la réponse finale est capturée par IdeA.
|
||||
|
||||
---
|
||||
|
||||
## 3. Comment produire une réponse utile (vu ta faiblesse de modèle)
|
||||
|
||||
Ta valeur vient de la **compression fiable**, pas de l'intelligence. Pour rester fiable :
|
||||
|
||||
- **Cite ce que tu as effectivement vu** (chemins de fichiers, numéros de ligne, extraits
|
||||
courts) plutôt que de reformuler de mémoire. Une réponse vérifiable vaut mieux qu'une
|
||||
réponse fluide.
|
||||
- **Format court, structuré, sans prose.** Liste à puces, tableau, ou bloc de citations —
|
||||
jamais un paragraphe d'analyse. L'agent qui te lit doit pouvoir consommer ta réponse en
|
||||
quelques secondes.
|
||||
- **Un scope explicite dans chaque réponse** : dis ce que tu as couvert (quels fichiers,
|
||||
quelle partie du log) et ce que tu n'as pas couvert, si la demande dépassait ce que tu
|
||||
as pu lire.
|
||||
- **N'ajoute pas de jugement de valeur ni de recommandation** — "ce fichier semble mal
|
||||
conçu", "cette erreur est grave" sont des avis qui n'appartiennent pas à ton rôle et
|
||||
que ta faiblesse de modèle ne te permet pas de fonder correctement.
|
||||
- **Reste bref.** Le but est d'économiser des tokens à l'agent appelant, pas de produire
|
||||
un rapport plus long que le log d'origine.
|
||||
|
||||
---
|
||||
|
||||
## 4. Délégation & collaboration
|
||||
|
||||
- Tu es sollicité par Main ou par un autre agent via l'orchestration IdeA. Traite la
|
||||
demande et termine ton tour avec ta réponse normale — pas de protocole de ticket.
|
||||
- Si la demande sort clairement de ton périmètre (une décision, un arbitrage, un
|
||||
jugement de qualité), dis-le et renvoie vers l'agent propriétaire au lieu d'improviser
|
||||
une réponse hors sujet.
|
||||
- Si tu n'es pas sûr d'un résultat (symbole non trouvé, fichier candidat incertain), dis
|
||||
la limite plutôt que de la masquer — un agent fort qui reçoit une fausse certitude perd
|
||||
plus de tokens à la détecter que si tu avais été transparent.
|
||||
0
.ideai/agents/glm.md
Normal file
0
.ideai/agents/glm.md
Normal file
File diff suppressed because one or more lines are too long
363
.ideai/mcp-tool-permissions.json
Normal file
363
.ideai/mcp-tool-permissions.json
Normal file
@ -0,0 +1,363 @@
|
||||
{
|
||||
"version": 1,
|
||||
"projectDefault": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_run_in_background",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"agentId": "a6ced819-b893-4213-b003-9e9dc79b9641",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "dce19c75-9669-4e45-b8de-9950025157da",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "73c853d1-c0fd-463b-ad17-1d24fefa371f",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "af7f86da-76bc-48e1-9900-71f45a624800",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "aefdbd61-e3d4-4bc1-9f42-c259446a97b5",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "cd0b4cf1-1bef-4fae-ade5-f0a6b49bbaf5",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "c3e0d9d8-df36-4b00-b271-62d8aa78892c",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "5d07e4a7-8676-4a71-aea1-5b64caefa944",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "30bf7e5f-5681-478d-813c-ac4c3957897f",
|
||||
"policy": {
|
||||
"allowedTools": [
|
||||
"idea_list_agents",
|
||||
"idea_context_read",
|
||||
"idea_memory_read",
|
||||
"idea_skill_read",
|
||||
"idea_workstate_read",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_sprint_list",
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
"idea_context_propose",
|
||||
"idea_memory_write",
|
||||
"idea_workstate_set",
|
||||
"idea_create_skill",
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -65,3 +65,8 @@
|
||||
- [web-client-is-single-column-no-desktop-shell](web-client-is-single-column-no-desktop-shell.md) — memory note web-client-is-single-column-no-desktop-shell
|
||||
- [sandbox-eperm-bind-false-green-web-server](sandbox-eperm-bind-false-green-web-server.md) — memory note sandbox-eperm-bind-false-green-web-server
|
||||
- [ticket74-f1-web-bundle-transport-seam](ticket74-f1-web-bundle-transport-seam.md) — Deux artefacts Vite (dist/dist-web), mode `web` portable Windows, et le câblage anti-divergence de la constante de transport.
|
||||
- [ticket43-backend-b1-b4-qa-validation](ticket43-backend-b1-b4-qa-validation.md) — memory note ticket43-backend-b1-b4-qa-validation
|
||||
- [ticket43-plugin-system-final-qa-verdict](ticket43-plugin-system-final-qa-verdict.md) — memory note ticket43-plugin-system-final-qa-verdict
|
||||
- [ticket101-cross-talk-multi-project-rootcause](ticket101-cross-talk-multi-project-rootcause.md) — memory note ticket101-cross-talk-multi-project-rootcause
|
||||
- [ticket103-network-permission-ux-surface](ticket103-network-permission-ux-surface.md) — Stable UX convention for agent network permissions in IdeA.
|
||||
- [codex-network-access-config-fix](codex-network-access-config-fix.md) — memory note codex-network-access-config-fix
|
||||
|
||||
@ -6,7 +6,7 @@ metadata:
|
||||
---
|
||||
---
|
||||
name: appimage-build-no-strip-relr-dyn-fix
|
||||
description: Le build AppImage échoue sur cette machine (CachyOS/Arch) à l'étape linuxdeploy/strip (.relr.dyn) ; correctif = NO_STRIP=true. Explique probablement les "builds qui ne produisent rien".
|
||||
description: Le build AppImage échoue sur cette machine (CachyOS/Arch) à l'étape linuxdeploy/strip (.relr.dyn) ; correctif = NO_STRIP=true. Séquence de build mise à jour après #74 (beforeBuildCommand = build:bundle).
|
||||
metadata:
|
||||
type: reference
|
||||
---
|
||||
@ -30,7 +30,7 @@ section ELF moderne `.relr.dyn` (relocations `DT_RELR`, `unknown type [0x13]`) p
|
||||
libs système GTK/glibc de CachyOS/Arch (rolling). Le plugin `gtk` de linuxdeploy strip ces libs
|
||||
→ échec → tout le bundling casse.
|
||||
|
||||
## Correctif VALIDÉ (2026-07-02)
|
||||
## Correctif VALIDÉ (2026-07-02, toujours valable 2026-07-17)
|
||||
Lancer le build avec `NO_STRIP=true` (le binaire release est déjà strippé par cargo) :
|
||||
```
|
||||
cd crates/app-tauri
|
||||
@ -38,15 +38,40 @@ NO_STRIP=true ../../frontend/node_modules/.bin/tauri build --bundles appimage
|
||||
```
|
||||
Résultat : `Finished 1 bundle at target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`.
|
||||
|
||||
## Séquence de build complète (rappel, pas de beforeBuildCommand dans tauri.conf.json)
|
||||
1. `npm --prefix frontend run build` (produit `frontend/dist`).
|
||||
2. depuis `crates/app-tauri` : `NO_STRIP=true <cli tauri> build --bundles appimage`.
|
||||
CLI tauri = `frontend/node_modules/.bin/tauri` (@tauri-apps/cli). Config = crates/app-tauri/tauri.conf.json.
|
||||
## Séquence de build — MISE À JOUR après #74 (2026-07-17)
|
||||
**Attention : l'ancienne version de cette note disait « pas de beforeBuildCommand dans
|
||||
tauri.conf.json » et imposait un `npm run build` manuel préalable. C'est FAUX depuis #74.**
|
||||
|
||||
`crates/app-tauri/tauri.conf.json` a maintenant :
|
||||
- `build.beforeBuildCommand: "npm --prefix ../frontend run build:bundle"`
|
||||
- `bundle.resources: { "../../frontend/dist-web": "web" }`
|
||||
|
||||
et `frontend/package.json` définit :
|
||||
```
|
||||
"build:bundle": "npm run typecheck && vite build --outDir dist --emptyOutDir && vite build --mode web --outDir dist-web --emptyOutDir"
|
||||
```
|
||||
|
||||
Donc **une seule commande suffit**, le frontend est construit automatiquement (les DEUX bundles) :
|
||||
```
|
||||
cd crates/app-tauri
|
||||
NO_STRIP=true ../../frontend/node_modules/.bin/tauri build --bundles appimage
|
||||
```
|
||||
|
||||
`build:bundle` produit **deux** bundles distincts, et c'est le cœur du fix #74 :
|
||||
- `frontend/dist` → bundle **desktop**, transport `tauri` (= `build.frontendDist`).
|
||||
- `frontend/dist-web` → bundle **web**, transport `http` (= packagé en ressource `web/`).
|
||||
|
||||
Un seul `dist` ne peut pas servir les deux : le transport est figé **au build** par Vite
|
||||
(`resolveTransport()` lit `import.meta.env.VITE_TRANSPORT`, constant-folded). C'est ce qui
|
||||
causait `__TAURI_INTERNALS__ is undefined` dans le navigateur (#74).
|
||||
|
||||
Garde-fou : `npm run test:bundle-transport` vérifie que `dist` = tauri et `dist-web` = http.
|
||||
Le lancer après un build si un doute subsiste sur ce qui est servi au navigateur.
|
||||
|
||||
## Implication produit
|
||||
C'est très probablement la vraie cause des « builds AppImage qui ne produisent rien / attente
|
||||
sans résultat » signalés par l'utilisateur. À intégrer idéalement dans le flux de build d'IdeA
|
||||
(passer NO_STRIP quand on cible AppImage sur Arch, ou vendoriser un linuxdeploy récent).
|
||||
|
||||
Lien : [[mcp-bridge-and-delegation-runtime-notes]],
|
||||
[[checkpoint-b2-bootstrap-applied-await-codex-reset-1430]].
|
||||
Lien : [[mcp-bridge-and-delegation-runtime-notes]] (le binaire qui tourne = AppImage, pas les
|
||||
sources — rebuild obligatoire pour tester un changement backend live).
|
||||
41
.ideai/memory/codex-network-access-config-fix.md
Normal file
41
.ideai/memory/codex-network-access-config-fix.md
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
name: codex-network-access-config-fix
|
||||
description: memory note codex-network-access-config-fix
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
# Correctif accès réseau Codex via `sandbox_workspace_write.network_access`
|
||||
|
||||
Le 2026-07-26, le diagnostic live a montré qu'un agent Codex lancé par IdeA échouait sur `curl` même avec IP forcée. La cause n'était pas seulement DNS ni une session à relancer : Codex CLI 0.145 attend la configuration officielle `sandbox_workspace_write.network_access=true` pour autoriser le réseau dans `workspace-write`.
|
||||
|
||||
Correctif implémenté et validé par QA dans les sources :
|
||||
|
||||
- Projection Codex `$CODEX_HOME/config.toml` : gérer `[sandbox_workspace_write] network_access = true/false` via `MergeToml`, pour éviter un stale `true`.
|
||||
- `codex exec` structuré : passer `-c sandbox_workspace_write.network_access=<bool>` quand la policy est connue.
|
||||
- La permission système IdeA reste séparée des permissions fichiers/bash : seul `NetworkPolicy::Allow` active `network_access=true`; `Deny`, `Ask` et `None` donnent `false`.
|
||||
- L'env `CODEX_SANDBOX_NETWORK_DISABLED` est encore upserté comme garde anti-héritage stale, mais ce n'est plus le mécanisme principal.
|
||||
|
||||
Fichiers principaux :
|
||||
|
||||
- `crates/infrastructure/src/permission/codex.rs`
|
||||
- `crates/domain/src/ports.rs`
|
||||
- `crates/application/src/agent/lifecycle.rs`
|
||||
- `crates/infrastructure/src/session/codex.rs`
|
||||
- `crates/application/src/ticket_assistant.rs`
|
||||
|
||||
Validation QA verte :
|
||||
|
||||
- `cargo test -p infrastructure permission::codex`
|
||||
- `cargo test -p infrastructure codex_`
|
||||
- `cargo test -p application --test agent_lifecycle codex_`
|
||||
- `cargo test -p application --test ticket_assistant codex_`
|
||||
- `cargo test -p domain`
|
||||
|
||||
Build réalisé :
|
||||
|
||||
- `npm --prefix frontend run build` : vert.
|
||||
- `tauri build --bundles appimage` : compilation release OK, mais bundling linuxdeploy a échoué car `appimagetool` tentait de télécharger le runtime sans réseau.
|
||||
- Contournement appliqué : `appimagetool --runtime-file /home/anthony/.cache/tauri/runtime-x86_64 ...`.
|
||||
- AppImage générée : `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`, SHA-256 `08d74dfe950312f968f3fe5646d747f2ac6fa25f2a523a83825182f2809e2aa8`.
|
||||
|
||||
Validation live restante obligatoire : relancer IdeA depuis cette nouvelle AppImage, lancer un agent Codex frais avec `network: allow`, puis exécuter un vrai `curl`. La session Codex déjà active ne peut pas prouver le fix car elle a été lancée avant ces nouveaux arguments/config.
|
||||
59
.ideai/memory/context-agent-token-offload-design.md
Normal file
59
.ideai/memory/context-agent-token-offload-design.md
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
name: context-agent-token-offload-design
|
||||
description: memory note context-agent-token-offload-design
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
# Agent Context — allègement token, périmètre figé
|
||||
|
||||
Décision validée le 2026-07-23 : un nouvel agent **Context**, tournant sur un **LLM local peu
|
||||
puissant**, a été ajouté au projet IdeA pour réduire la fréquence des limites de tokens atteintes
|
||||
par les autres agents (Main, Architect, UX, DevBackend, DevFrontend, QA, Git) — **sans dégrader
|
||||
la qualité de leurs décisions**.
|
||||
|
||||
## Périmètre retenu (mécanique, faible risque)
|
||||
|
||||
- Recherche de symboles (localisation définition/usage).
|
||||
- Résumé de fichier(s) — factuel, pas d'interprétation architecturale.
|
||||
- Classification d'erreurs de compilation (tri d'un log brut).
|
||||
- Extraction des tests en échec (à partir d'une sortie de test brute).
|
||||
- Résumé de diff (`git diff` volumineux → liste factuelle de fichiers/nature de changement).
|
||||
- Repérage de fichiers probablement concernés par un ticket (piste, pas garantie).
|
||||
|
||||
## Explicitement exclu / dégradé en brouillon uniquement
|
||||
|
||||
Proposer un message de commit, générer un test unitaire, mettre à jour une documentation qui fait
|
||||
foi : ce sont des artefacts qui engagent une décision et qui appartiennent aux agents propriétaires
|
||||
(Git pour les commits, QA pour les tests). Un modèle local faible qui les produit comme livrable
|
||||
ferait courir un risque de qualité que la vérification par l'agent fort annulerait de toute façon
|
||||
le gain de tokens visé. Context peut produire un brouillon explicitement marqué comme tel, jamais
|
||||
un livrable.
|
||||
|
||||
## Garde-fous de fiabilité (vu la faiblesse du modèle)
|
||||
|
||||
Context ne décide rien, ne corrige pas de code de production, ne remplace jamais une vérification
|
||||
qui doit être prouvée (tests QA, verdict de build), et ne devine jamais quand l'information manque
|
||||
— il doit dire "non trouvé"/"incertain" plutôt qu'extrapoler. Réponses courtes, structurées,
|
||||
citant ce qui a été effectivement vu (chemins, lignes), sans jugement de valeur.
|
||||
|
||||
## Où c'est câblé
|
||||
|
||||
- Contexte agent : `.ideai/agents/context.md` (écrit via `idea_update_context`).
|
||||
- Template global IdeA créé : « Context — Agent d'assistance légère à faible coût »
|
||||
(`defaultProfileId` = profil LLM local de l'agent Context), pour réutilisation cross-projet.
|
||||
Le template ne contient que la partie générique (aucune référence à IdeA le produit) ;
|
||||
la déclaration des 7 autres rôles nommés reste project-spécifique et vit dans
|
||||
`.ideai/agents/context.md` du projet, pas dans le template.
|
||||
- Rôle ajouté à la liste des rôles du contexte projet global (CLAUDE.md §3) : Context ne fait
|
||||
**pas** partie du cycle obligatoire (§4) — pas d'étape qui lui est dédiée, il est sollicité en
|
||||
support ponctuel par n'importe quel agent.
|
||||
- Chaque agent (Main, Architect, DevBackend, DevFrontend, QA, Git, UX) a reçu un ajout court dans
|
||||
sa section « Délégation & collaboration » expliquant quand solliciter Context et rappelant que
|
||||
son résultat est une piste à vérifier, jamais une conclusion ou une décision.
|
||||
|
||||
## Pourquoi
|
||||
|
||||
Voir [[idea-product-directives-main-handoff]] pour les directives produit générales ; cette note
|
||||
couvre spécifiquement le compromis coût-tokens/qualité qui a motivé le périmètre volontairement
|
||||
restreint de Context (pas de délégation de jugement, seulement de compression/extraction
|
||||
factuelle).
|
||||
@ -0,0 +1,67 @@
|
||||
---
|
||||
name: ticket101-cross-talk-multi-project-rootcause
|
||||
description: memory note ticket101-cross-talk-multi-project-rootcause
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
---
|
||||
slug: ticket101-cross-talk-multi-project-rootcause
|
||||
title: "Ticket #101 — défaut d'isolation multi-projet (registres runtime globaux par AgentId)"
|
||||
type: reference
|
||||
description: Cause racine du cross-talk entre projets ouverts simultanément (branche hybride wear-os + notification de tâche backend perdue) : les stores disque IdeA sont scoppés par projet, mais plusieurs registres mémoire runtime critiques restent indexés par AgentId seul, sans re-mint des UUID à l'ouverture. Plan de correction figé en 6 lots (clé RuntimeAgentKey).
|
||||
---
|
||||
|
||||
# Ticket #101 — défaut d'isolation multi-projet (cause racine)
|
||||
|
||||
## Symptômes (2 manifestations, même défaut)
|
||||
- **A — contamination du contexte d'inférence** : un agent a créé une pseudo-branche `feature/ticket91-wear-os-watch-sync` (ticket #91 purement front) où le suffixe `wear-os-watch-sync` appartient à l'AUTRE projet de l'utilisateur. Le nom n'apparaît dans aucun fichier `.ideai/` du projet IdeA → contamination au niveau du contexte d'inférence runtime (conversation injectée à l'agent Git mélangeait les projets). Preuve topo préservée : `main` divergé de `develop` (da907b8 vs 6a87c46), `feature/ticket99-agent-model-configuration` créée depuis main au lieu de develop.
|
||||
- **B — notification de tâche backend perdue / agent qui s'arrête** (sujet original #101) : un agent OpenCode lance `idea_run_in_background` puis s'arrête, sans retour de notification garanti.
|
||||
|
||||
## Cause racine (Architect, 2026-07-25)
|
||||
Stores disque **correctement scoppés par projet** (mémoire, contexte, handoff, live-state lus depuis `input.project.root` ; agents persistés dans `.ideai/agents.json` par root projet). MAIS les agents **ne re-mint pas leurs UUID à l'ouverture** → deux projets (surtout si l'un est une copie) peuvent porter les **mêmes `AgentId`**.
|
||||
|
||||
Plusieurs **registres mémoire runtime restent globaux, indexés par `AgentId` seul** :
|
||||
- ConversationRegistry global + `ConversationId` dérivé des UUID agents seuls — `crates/domain/src/conversation.rs:71`, `crates/infrastructure/src/conversation/mod.rs:41`, orchestrateur `crates/application/src/orchestrator/service.rs:2192`.
|
||||
- Sessions PTY/structured retrouvées par `agent_id` seul — `crates/application/src/terminal/registry.rs:182,437` ; réutilisation session `service.rs:2293,2368`.
|
||||
- Verrous ask, busy-state, liveness, délégations différées par `AgentId` seul — `service.rs:418`, `crates/infrastructure/src/input/mod.rs:47`.
|
||||
- Inbox/mailbox par `AgentId` seul — `crates/domain/src/inbox.rs:68,156`, `input/mod.rs:1052`, `crates/infrastructure/src/mailbox/mod.rs:44`.
|
||||
- Wake par `AgentId` seul — `crates/application/src/orchestrator/wake.rs:87`, provider `crates/backend/src/lib.rs:687`.
|
||||
|
||||
→ Un agent du projet courant peut se rattacher à la conversation/session/inbox d'un autre projet (collision d'UUID) : contamination d'inférence (A) ET complétion livrable/bloquée/réveillant la mauvaise session (B).
|
||||
|
||||
## Pour B : runtime vs modèle
|
||||
La chaîne sink→`project_id`→wake est correcte jusqu'au bridge (`crates/infrastructure/src/background_task/sink.rs:23,142` ; `crates/backend/src/lib.rs:2228` recharge le projet par `project_id` puis `wake_agent`). Le défaut réapparaît juste après (inbox/mailbox/busy/session par AgentId seul). **Runtime IdeA suspecté en premier.** Le modèle GLM 5.2 n'est l'hypothèse principale que si les logs prouvent (pour le bon `project_id`) : enqueue + wake + `BackgroundTaskCompletionDelivered` émis sans reprise utile. Traces : `lib.rs:2238`, `wake.rs:93`.
|
||||
|
||||
## Plan de correction (6 lots, figé)
|
||||
1. Introduire `RuntimeAgentKey { project_id, agent_id }` ; interdire toute map app-wide indexée par `AgentId` seul.
|
||||
2. Propager la clé dans `AgentInbox`, `InputMediator`, `AgentMailbox`, busy/liveness/deferred, wake, session lookup, ask-locks. **Correctif structurel commun à A et B.**
|
||||
3. Qualifier les conversations par projet : `ConversationId` + `ConversationRegistry` intègrent `ProjectId`.
|
||||
4. Requalifier `TerminalSessions`/`StructuredSessions` par `(project_id, agent_id)` ; `AppWakeSessionProvider` refuse toute session vivante d'un autre projet.
|
||||
5. Audit des autres états mémoire similaires (`session_limit`, tables de reprise/verrous par agent) pour éviter une demi-correction.
|
||||
6. Télémétrie `project_id` explicite sur les logs diag critiques de wake/routage.
|
||||
|
||||
## Invariants
|
||||
- Sources de contexte sur disque restent root-scopées (ne pas régresser).
|
||||
- Templates/profils globaux restent globaux produit — ne doivent pas devenir vecteurs de session/conversation partagée.
|
||||
- Un projet non-actif doit continuer à recevoir ses wakes et complétions.
|
||||
- Pas de régression OpenCode/Codex/Claude (correctif transverse runtime, pas moteur-spécifique).
|
||||
- Aucun nouveau DTO frontend pour la correction minimale.
|
||||
|
||||
## QA — critère de vérité
|
||||
Test d'intégration **non-cross-talk** : 2 projets ouverts simultanément contenant **volontairement les mêmes `AgentId`** :
|
||||
- délégation dans projet A ne réutilise jamais une session/conversation vivante de projet B ;
|
||||
- complétion `(project A, agent X)` n'entre ni dans l'inbox ni dans la session de `(project B, agent X)` ;
|
||||
- le wake d'un projet non-actif fonctionne quand même ;
|
||||
- une collision d'ids qui échouait avant devient verte sur PTY, structured et background wake.
|
||||
|
||||
## Hors périmètre
|
||||
- #91 (popup de notification) : purement front, indépendant du défaut runtime.
|
||||
- Remint systématique des AgentId à l'ouverture : piste complémentaire non retenue dans le fix minimal (la clé runtime scellée par projet rend la collision inoffensive même sans remint).
|
||||
|
||||
## Topologie
|
||||
- Branche de travail à créer pour le fix (Git décidera). Base `develop` (6a87c46). `main`/`feature/ticket99-agent-model-configuration` divergent sur da907b8 (preuves préservées, à nettoyer après enquête).
|
||||
|
||||
## Lié
|
||||
- #91 (relatesTo) : popup de notification — front pur.
|
||||
- Mémoire `background-tasks-first-class-design` + `b8-command-runner-pty-framing` : design du flux de complétion (le défaut est en aval du sink).
|
||||
- Mémoire `mcp-bridge-and-delegation-runtime-notes` : règle rebuild AppImage (le binaire qui tourne = AppImage, pas les sources).
|
||||
10
.ideai/memory/ticket103-network-permission-ux-surface.md
Normal file
10
.ideai/memory/ticket103-network-permission-ux-surface.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
name: ticket103-network-permission-ux-surface
|
||||
description: Stable UX convention for agent network permissions in IdeA.
|
||||
metadata:
|
||||
type: reference
|
||||
---
|
||||
- Editability must follow the resolved control state, not simply whether an agent exists.
|
||||
- Non-launched agents stay editable unless a runtime lock is explicitly present.
|
||||
- Read-only messaging should be explicit only for active locked runtimes, especially external/uninspectable runtimes.
|
||||
- Effective policy labels must not be used as a proxy for editability.
|
||||
54
.ideai/memory/ticket43-backend-b1-b4-qa-validation.md
Normal file
54
.ideai/memory/ticket43-backend-b1-b4-qa-validation.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
name: ticket43-backend-b1-b4-qa-validation
|
||||
description: memory note ticket43-backend-b1-b4-qa-validation
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
---
|
||||
title: Validation QA backend B1-B4 du système de plugins #43
|
||||
type: reference
|
||||
description: Verdict QA du périmètre backend plugin B1-B4 sur feature/ticket43-plugin-system, tests ajoutés et limites sandbox constatées.
|
||||
---
|
||||
|
||||
# Validation QA backend B1-B4 du système de plugins #43
|
||||
|
||||
Sur `feature/ticket43-plugin-system`, QA a validé le périmètre backend B1-B4 sans modifier le frontend.
|
||||
|
||||
Tests/ajustements QA ajoutés :
|
||||
|
||||
- `crates/domain/tests/layout.rs` : les helpers de tests existants ignorent explicitement `LayoutNode::CustomPluginLayout(_)`, ce qui rétablit l'exhaustivité de compilation des tests domaine après l'ajout du layout plugin custom.
|
||||
- `crates/application/src/plugin/mod.rs` : tests in-memory des ports plugin pour vérifier :
|
||||
- catalogue runtime vide pour `Disabled`, `PendingUninstall`, `Invalid` ;
|
||||
- réconciliation MCP limitée aux plugins enabled + serveurs `autoStart`, identité `plugin:<pluginId>:<serverId>` et résolution sous `pluginRoot` ;
|
||||
- disable stoppe le MCP, publie `PluginDisabled`, retire le plugin du runtime catalog ;
|
||||
- uninstall stoppe le MCP, retire registry + package et publie `PluginUninstalled`.
|
||||
|
||||
Commandes vertes constatées :
|
||||
|
||||
```text
|
||||
cargo test -p application plugin --offline --no-fail-fast
|
||||
# 7 passed; 0 failed
|
||||
|
||||
cargo test -p domain -p application -p infrastructure -p backend -p app-tauri plugin --offline --no-fail-fast
|
||||
# plugin-filtered targeted suite green; application 7 plugin tests, domain 3 plugin tests,
|
||||
# serde roundtrip custom plugin layout, infrastructure 3 plugin tests, dto plugin filtered test all ok.
|
||||
|
||||
cargo test -p app-tauri --test dto_plugins --offline --no-fail-fast
|
||||
# 2 passed; 0 failed
|
||||
|
||||
cargo fmt --check
|
||||
# OK
|
||||
```
|
||||
|
||||
Workspace complet :
|
||||
|
||||
```text
|
||||
cargo test --workspace --offline --no-fail-fast -- --test-threads=1
|
||||
```
|
||||
|
||||
Résultat KO attendu dans ce sandbox, hors périmètre plugin :
|
||||
|
||||
- `-p infrastructure --lib` : 10 échecs `session::openai_compat::*` sur `bind: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }`.
|
||||
- `-p web-server --lib` : 2 échecs de bind `127.0.0.1:0 Operation not permitted` et 5 scénarios websocket/terminal recevant `error` au lieu de `terminal.attached`, cohérents avec restrictions loopback/PTY du sandbox.
|
||||
|
||||
Verdict : backend plugins B1-B4 validé QA avec réserve uniquement environnementale sur le workspace global.
|
||||
52
.ideai/memory/ticket43-plugin-system-final-qa-verdict.md
Normal file
52
.ideai/memory/ticket43-plugin-system-final-qa-verdict.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
name: ticket43-plugin-system-final-qa-verdict
|
||||
description: memory note ticket43-plugin-system-final-qa-verdict
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
---
|
||||
title: Verdict QA final du ticket #43 système de plugins
|
||||
type: reference
|
||||
description: Validation finale QA de #43 après carnet v5, corrections F4/F3/B4 et exécutions réelles backend/frontend.
|
||||
---
|
||||
|
||||
# Verdict QA final du ticket #43 système de plugins
|
||||
|
||||
Branche validée : `feature/ticket43-plugin-system`.
|
||||
|
||||
Contexte : après verdict QA orange initial, Architect a figé le carnet v5 : `customPluginLayout` top-level canonique, `gitRepository` obligatoire en F3, `${appDataDir}` obligatoire en B4, et `agentSelected`/`terminalFocused`/`layoutCellFocused` + persistance backend state layout plugin acceptés comme dette v1.
|
||||
|
||||
Résultats réels exécutés par QA :
|
||||
|
||||
```text
|
||||
cargo test -p domain -p application -p infrastructure -p backend -p app-tauri plugin --offline --no-fail-fast
|
||||
# OK
|
||||
# application plugin: 9 passed
|
||||
# domain plugin: 3 passed
|
||||
# infrastructure plugin: 5 passed
|
||||
# custom_plugin_layout_roundtrips_with_opaque_state: ok
|
||||
|
||||
cd frontend && npm run typecheck
|
||||
# exit 0
|
||||
|
||||
cd frontend && npm test -- --run
|
||||
# Test Files 104 passed (104)
|
||||
# Tests 947 passed (947)
|
||||
```
|
||||
|
||||
Contrôles ciblés confirmés :
|
||||
|
||||
- Frontend `LayoutNode` inclut `{ type: "customPluginLayout"; node: CustomPluginLayoutCell }` top-level, sans contrat `LeafCell.pluginLayout`.
|
||||
- `LayoutGrid` route `customPluginLayout` vers `PluginLayoutCellView`.
|
||||
- `onStateChange`, `onOpenPlugins`, `onChooseAnotherLayout` sont câblés in-session (`setPluginLayoutState`, navigation settings plugins, remplacement par terminal).
|
||||
- `ProjectsView` câble `gitRepository` via `GitGateway.branches(active.id)` ; `agentSelected`, `terminalFocused`, `layoutCellFocused` restent explicitement `false` en dette v1.
|
||||
- Backend application substitue `${pluginRoot}` et `${appDataDir}` dans command/args/env/cwd des specs MCP plugin, avec test `reconcile_mcp_substitutes_app_data_dir_in_plugin_server_specs`.
|
||||
|
||||
Verdict QA : VERT pour merge local du ticket #43.
|
||||
|
||||
Dette v1 assumée :
|
||||
|
||||
- `agentSelected`, `terminalFocused`, `layoutCellFocused` non câblés, figés à `false` jusqu'à un lot focus/selection dédié.
|
||||
- Persistance backend du `state` de layout plugin non implémentée ; F4 validé en in-session selon arbitrage Architect.
|
||||
|
||||
Aucun nouveau blocage détecté dans les suites demandées.
|
||||
31
.ideai/memory/ticket95-adapter-aware-liveness-probe.md
Normal file
31
.ideai/memory/ticket95-adapter-aware-liveness-probe.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
name: ticket95-adapter-aware-liveness-probe
|
||||
description: memory note ticket95-adapter-aware-liveness-probe
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
# #95 — Sonde de vivacité du rendez-vous devenue adapter-consciente
|
||||
|
||||
**Type :** correctif architecture / bug racine. Livré sur `feature/rendezvous-liveness-probe-per-adapter` (commit 376fc9f), AppImage 0.3.0 rebuildée.
|
||||
|
||||
## Bug racine
|
||||
Le rendez-vous `idea_ask_agent` (`run_inactivity_watchdog`, `crates/application/src/orchestrator/rendezvous.rs`) coupe en **faux `NoReply`** toute cible **structurée** dont le tour dépasse la fenêtre d'inactivité (défaut 600 s, = `turn_timeout_ms` du profil cible). Cause : la seule sonde de vivacité (`transcript_activity_token`) lisait le transcript Claude (`~/.claude/projects/<encoded-cwd>/*.jsonl`) — invisible pour OpenCode/Codex, et fragilise même Claude en mode `-p` headless si le transcript ne grossit pas mi-tour. `has_probe=true` mais la sonde renvoie `None` ⇒ bras `(true,_,None)=>false` ⇒ aucun réarmement ⇒ NoReply à la première fenêtre.
|
||||
|
||||
## Fix (architecture figée)
|
||||
1. **Port domaine** (`crates/domain/src/ports.rs`) : `AgentSession::activity_token() -> Option<u64>` (défaut `None`, zéro régression). Jeton monotone = « la session travaille ».
|
||||
2. **Machinerie** (`crates/infrastructure/src/session/process.rs`) : `run_turn_with_activity(...)` bump un `Arc<AtomicU64>` à **chaque ligne stdout lue** (drain async ET drain sandboxé thread). `run_turn` (4 args) délègue avec `None` ⇒ les ~11 call sites de test sont intacts.
|
||||
3. **Sessions** : `ClaudeSdkSession`, `CodexExecSession`, `OpenAiCompatibleSession` overrident `activity_token()` via `run_turn_with_activity` ; `OpenCodeSession` (drain inline) bump son propre compteur par ligne JSONL.
|
||||
4. **Sonde composite** (`resolve_ask_liveness_token`, `crates/backend/src/lib.rs`) : préfère `session.activity_token()` de la session vivante (`structured.session_for_agent`), repli inchangé sur le transcript Claude. Câblée par `.with_ask_liveness_probe`.
|
||||
|
||||
## Contrats clés
|
||||
- `has_probe` reste `true` dès qu'une sonde est câblée ; la sonde composite renvoie désormais `Some(token)` qui avance ⇒ la fenêtre se réarme. Le repli transcript Claude couvre le cas « session vivante sans override ».
|
||||
- La fenêtre = `turn_timeout_ms` du **profil cible** (`turn_timeout_for`→`liveness_for_agent`), défaut `ASK_AGENT_TIMEOUT` 600 s. **Pas d'env global pour la fenêtre** (seul le plafond a `IDEA_ASK_RENDEZVOUS_CEILING_MS`, défaut 4 h).
|
||||
- Pour un test live rapide sans attendre 600 s : mettre un petit `turn_timeout_ms` (ex. 20 000) sur le profil de la cible, puis déléguer une tâche de ~60-90 s.
|
||||
|
||||
## État validation
|
||||
- Tests unitaires verts : `run_turn_with_activity_bumps_counter_per_line`, `*_session_activity_token_advances_across_a_turn` (claude/codex/opencode), + la sonde composite backend + le watchdog rendezvous existants.
|
||||
- AppImage 0.3.0 rebuildée + installée (`/home/anthony/Documents/IdeA_0.3.0_amd64.AppImage`, backup `.old-pre95fix`). **Relance IdeA requise** pour l'activer (le binaire qui tourne = AppImage en mémoire ; relancer depuis l'intérieur tue la session courante).
|
||||
|
||||
## À noter (dette / hors périmètre)
|
||||
- #96 (EffectivePermissions assistants de ticket) laissé ouvert, documenté dans le carnet #96 (décision produit différée).
|
||||
- Le souvenir utilisateur initial décrivait un échec « demandeur GLM/OpenCode → cible Claude ». Mécaniquement le watchdog est **ciblé-cible** (keyed sur l'agent cible) : un échec sur cible Claude longue n'est possible que si le transcript Claude `-p` ne grossit pas mi-tour (couvert désormais par l'`activity_token` de ClaudeSdkSession). La théorie principale reste « cible structurée longue non observée → faux NoReply ».
|
||||
35
.ideai/memory/ticket97-opencode-provider-mutual-exclusion.md
Normal file
35
.ideai/memory/ticket97-opencode-provider-mutual-exclusion.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
name: ticket97-opencode-provider-mutual-exclusion
|
||||
description: memory note ticket97-opencode-provider-mutual-exclusion
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
# Ticket #97 — Exclusion mutuelle opencode / opencodeProvider (décision Architect)
|
||||
|
||||
## Bug
|
||||
Wizard first-run : profiles.json contient à la fois `opencode` (llamacpp) et `opencodeProvider` (cloud). Lecture priorise `opencode` → llamacpp l'emporte silencieusement.
|
||||
|
||||
## Root cause (vérifiée dans le code)
|
||||
1. `SaveOpenCodeProviderProfile::execute` (`crates/application/src/agent/usecases.rs:364-367`) pose `opencode_provider` sans faire `opencode = None`.
|
||||
2. Invariant `opencode_backend_is_consistent` (`crates/domain/src/profile.rs:1220`) existe + testé mais **jamais appelé** hors tests (garde morte).
|
||||
3. Lecture priorise `opencode` : `crates/application/src/agent/lifecycle.rs:2367` + `crates/infrastructure/src/assistant/mod.rs:252`.
|
||||
|
||||
## Décisions Architect (validées)
|
||||
- **Lot** : un seul lot cohérent. Backend = autoritaire (invariant + garde + use case via builder + migration). Frontend = strip de la config inactive au save selon le mode (requis pour les chemins SaveProfile/ConfigureProfiles qui ne portent pas d'intention explicite). Découplés/parallélisables. DevBackend + DevFrontend + QA.
|
||||
- **Frontière** :
|
||||
- Domaine : builders `with_opencode` (`profile.rs:1132`) et `with_opencode_provider` (`profile.rs:1140`) doivent imposer l'exclusion mutuelle (chacun efface l'autre). Aujourd'hui ce sont des setters muets = la faille.
|
||||
- Infrastructure : `FsProfileStore::save` rejette tout profil violant l'invariant → `AppError::Invalid` (c'est ici que le prédicat enfin s'appelle). Défense en profondeur.
|
||||
- Application : use cases utilisent les builders, jamais la mutation brute de champ.
|
||||
- Refuser l'ad-hoc `opencode = None` par use case (DRY, 4 chemins d'écriture).
|
||||
- **DTO** : garder `SaveOpenCodeProviderProfileRequestDto` (`dto.rs:1148`) whole-profile (zéro cassure frontend). Le `opencode` parasite devient inoffensif car le use case reconstruit via builder. Output = profil normalisé, autorité pour le frontend. Corriger aussi SaveProfile (`usecases.rs:269`) et ConfigureProfiles (`usecases.rs:460`).
|
||||
- **Lecture priorité** : garder `opencode` d'abord (irrelevant post-exclusion), commenter comme fallback défensif.
|
||||
- **Duplication résolution** (lifecycle.rs:2367 + assistant/mod.rs:252) : dette hexagonale préexistante, NE PAS élargir ce lot. Suivre via un futur `AgentProfile::effective_opencode_backend()` ou générateur partagé.
|
||||
- **Migration REQUISE** : profils déjà corrompus sur disque portent les deux. Recovery : à la lecture (ou passe de migration), quand les deux présents, dropper `opencode` stale (l'intention est cloud, le bug ne venant que d'une action cloud explicite). Sans cela, #97 ne corrige que les nouveaux profils.
|
||||
|
||||
## Sites de résolution (priorité) = exactement 2
|
||||
- `crates/application/src/agent/lifecycle.rs:2367`
|
||||
- `crates/infrastructure/src/assistant/mod.rs:252`
|
||||
Autres accès `.opencode` scoper en mode local, non affectés : `lifecycle.rs:2455-2468`, `model_server.rs:121-127`, `catalogue.rs:244-258`, `usecases.rs:410`.
|
||||
|
||||
## Statut
|
||||
Décision Architect posée. À faire implémenter par DevBackend (domaine builders + garde store + migration + use cases) et DevFrontend (strip au save). QA : tests domaine/store + intégration cloud + scénario migration profils corrompus.
|
||||
83
.ideai/memory/ticket98-opencode-modelsdev-cache-seed.md
Normal file
83
.ideai/memory/ticket98-opencode-modelsdev-cache-seed.md
Normal file
@ -0,0 +1,83 @@
|
||||
---
|
||||
name: ticket98-opencode-modelsdev-cache-seed
|
||||
description: memory note ticket98-opencode-modelsdev-cache-seed
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
---
|
||||
slug: ticket98-opencode-modelsdev-cache-seed
|
||||
title: "Ticket #98 — Fix spawn OpenCode : seed du cache models.dev isolé (approche B2)"
|
||||
type: reference
|
||||
description: Cadrage figé du fix #98 (modèle écrasé par glm-5.2 au spawn pour provider catalogue cloud ex: zai) : cause racine = asymétrie picker↔spawn, approche B2 = semer best-effort le cache models.dev hôte dans le XDG_CACHE_HOME isolé.
|
||||
---
|
||||
|
||||
# Ticket #98 — Fix spawn OpenCode : seed du cache models.dev isolé
|
||||
|
||||
## Symptôme
|
||||
Wizard profil OpenCode first-run : provider catalogue cloud (ex: zai/ZAI Code) + modèle choisi → après sauvegarde l'agent tourne en `glm-5.2` quel que soit le modèle. Persistance correcte (`profiles.json` conserve `opencodeProvider.model`). `glm-5.2` est le **fallback interne d'OpenCode**, absent du code IdeA.
|
||||
|
||||
## Cause racine (confirmée + affinée par Architect)
|
||||
**Asymétrie picker↔spawn**, pas seulement « pas de bloc models » :
|
||||
|
||||
1. **Picker** (`provider_catalogue.rs:79-84,150-153`) : lit le cache models.dev via `opencode_models_cache_path()` qui résout le **vrai** cache hôte (`XDG_CACHE_HOME ?? ~/.cache`). Permet d'afficher zai + ses modèles.
|
||||
2. **Spawn** (`lifecycle.rs:2386,2400-2407` ; `assistant/mod.rs:272,277-285`) : IdeA **ré-isole** `XDG_CACHE_HOME` → `.opencode/cache` **vide** sous `run_dir`. OpenCode ne voit plus le cache.
|
||||
3. **Rendu** (`opencode_provider_config_json`, `lifecycle.rs:2795-2821` / `assistant/mod.rs:446-466`) : pour `config.custom == None` (défaut pour zai), IdeA n'émet que `apiKey` + `model: "zai/<m>"`, **sans** bloc `models`. Correct **uniquement** pour les 3 built-ins hardcodés dans le binaire OpenCode (`anthropic`, `openai`, `openrouter`).
|
||||
|
||||
→ OpenCode reçoit `model: "zai/<m>"`, ne reconnaît pas zai comme built-in, ne trouve pas le cache models.dev (isolé vide) → fallback silencieux glm-5.2.
|
||||
|
||||
- **Local/custom marchent** : émettent un bloc `models` auto-suffisant (`assistant/mod.rs:364-381`, `:447-460`).
|
||||
- **3 built-ins marchent** : hardcodés dans OpenCode.
|
||||
- Bug ne touche **que** les providers connus d'OpenCode *exclusivement via models.dev*.
|
||||
|
||||
## Approche figée : **B2 — Semer le cache models.dev isolé depuis le cache hôte**
|
||||
Après création du `XDG_CACHE_HOME` isolé, copier **best-effort** le fichier cache models.dev hôte (`opencode_models_cache_path()` → `<isolated_cache>/opencode/models.json`) via le port `FileSystem`. Lecture hôte = `std::fs` (identique au picker) ; écriture dans la home isolée.
|
||||
|
||||
**Pourquoi pas les autres :**
|
||||
- **(A)** Émettre un bloc `models`+`npm`+`baseURL` pour les catalogue → **écartée en v1** : IdeA devrait capturer le bon `npm` AI-SDK par provider depuis models.dev (`@ai-sdk/anthropic` vs `openai-compatible`…) — dupliquerait la connaissance du registry OpenCode (violation OCP), risque de régression built-ins. Gardée en **escalade** si B2 défait par refresh.
|
||||
- **(B1)** Pointer vers le vrai cache hôte (ne plus isoler) → **écartée** : casse l'isolation en écriture (pollution + race entre sessions).
|
||||
- **(C)** Bundler une copie statique models.dev dans IdeA → **écartée** : staleness + diverge picker/spawn.
|
||||
|
||||
**Auto-cohérence B2** : la précondition du bug (cache hôte présent au picker) garantit la précondition du fix (fichier à copier au spawn). Cache hôte absent → picker ne montre que les 3 built-ins → bug non atteint → fix non requis.
|
||||
|
||||
## Contrat figé
|
||||
### Ports / DTO
|
||||
- **Aucun nouveau port / entité domaine / DTO modifié.** Réutilise le port `FileSystem` déjà injecté sur le chemin spawn. Lecture source = `std::fs` hôte (mécanisme identique au picker).
|
||||
- Rendre **publique** `opencode_models_cache_path()` (source de vérité unique — encode le workaround du bug upstream #8235 ; ne **pas** re-dériver).
|
||||
|
||||
### Fichiers (périmètre DevBackend)
|
||||
- `crates/application/src/agent/provider_catalogue.rs` — exposer `opencode_models_cache_path()` en `pub` (ou ajouter `pub fn read_models_dev_cache_bytes() -> Option<Vec<u8>>`).
|
||||
- `crates/application/src/agent/mod.rs` — re-export.
|
||||
- `crates/application/src/agent/lifecycle.rs` — branche `apply_mcp_config` (≈2382-2408) : après `create_dir_all` du cache + création `xdg_cache`, semer `<xdg_cache>/opencode/models.json` depuis le cache hôte, best-effort. Branche **commune** opencode + opencodeProvider.
|
||||
- `crates/infrastructure/src/assistant/mod.rs` — branche miroir (≈268-285) : même appel.
|
||||
- **Factoriser** : `application::agent::seed_opencode_models_cache(fs: &dyn FileSystem, isolated_cache_dir: &str)` appelée par les deux sites. Partie **pure** testable `seed_from_bytes(fs, dest, src_bytes)` ; wrapper impur `std::fs::read` fin.
|
||||
|
||||
### Invariants (stricts)
|
||||
1. Isolation préservée en **écriture** : HOME, XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOME restent sous `run_dir`. Aucune var d'env repointée vers l'hôte. Seul le **contenu** du cache isolé est semé (lecture seule).
|
||||
2. **Best-effort, n'échoue jamais le launch** : source absente/illisible ou erreur FS → pas d'échec du spawn. Symétrique du contrat best-effort existant de `apply_mcp_config` (`lifecycle.rs:2422-2425`). ≠ `resolve_opencode_provider_api_key` (échec dur). Le seed est **mou**.
|
||||
3. Pas de régression local/custom : `opencode_config_json` (llamacpp) et `opencode_provider_config_json(custom==Some)` **byte-identiques** (rendu non touché ; seed additif orthogonal).
|
||||
4. Pas de régression built-ins : anthropic/openai/openrouter (`custom==None`, hardcodés) restent fonctionnels.
|
||||
5. Exclusion mutuelle `opencode` vs `opencodeProvider` (#97) : non touchée.
|
||||
6. Source de vérité unique du chemin models.dev : `opencode_models_cache_path()` réutilisée.
|
||||
7. Cohérence picker↔spawn : modèles résolvables au spawn = sur-ensemble de ceux du picker (même fichier source).
|
||||
|
||||
### Hors périmètre (ne pas faire ici)
|
||||
- Résolution providers pour **projets remote (SSH/WSL)** (picker lit déjà le cache hôte — incohérence pré-existante). Fix corrige le cas **local**, ne régresse pas le remote.
|
||||
- Déduplication du rendu lifecycle↔infra (`opencode_provider_config_json` ×2) — on factorise **uniquement le seeder**.
|
||||
- Aucune modif UI/wizard.
|
||||
|
||||
## QA — 2 couches
|
||||
1. **Unitaire `application`** (automatisable, déterministe) :
|
||||
- Partie pure `seed_from_bytes(fs, dest, src_bytes)` : bytes présents → assert `<dest>/opencode/models.json` écrit identique ; `None` → aucun fichier **et** pas d'erreur.
|
||||
- Non-régression rendu : JSON de `opencode_config_json` et `opencode_provider_config_json(custom==Some/None)` byte-identiques (tests existants `lifecycle.rs:4428+` verts).
|
||||
- ⇒ Ne prouve **pas** la résolution réelle par OpenCode.
|
||||
2. **Intégration / réelle-exécution (gated, propriété QA)** : spawn réel d'OpenCode avec profil `zai` (`custom==None`) contre un cache models.dev fixture contenant `zai` ; assert **pas de fallback glm-5.2** (modèle `zai/<choix>` effectivement utilisé). Gater `#[ignore]`/env (clé API + réseau). **C'est ce test qui prouve le bug corrigé.**
|
||||
|
||||
## Risque résiduel + escalade
|
||||
- **Inconnu empirique** : est-ce qu'OpenCode au démarrage tente de **rafraîchir** models.dev (écrase notre copie, ou hang sans réseau) ? `OPENCODE_DISABLE_AUTOUPDATE=1` ne vise que l'auto-update du binaire, pas sûr qu'il couvre le refresh models. **QA doit vérifier** sur le test d'intégration. Si le refresh défait B2 → **escalader vers (A)** : capturer `npm`+`baseURL` réels par provider dans le parseur models.dev et peupler `custom`.
|
||||
|
||||
## Topologie
|
||||
- Branche : `feature/ticket98-opencode-modelsdev-cache-seed` depuis `develop@69e5878` (#97 exclusion mutuelle prérequis y est fusionné : `0f0a76d` + merge `5533073`). `crates/` propre.
|
||||
- Carnet #98 version 2 = source de vérité du cadrage.
|
||||
|
||||
## Lié
|
||||
- #97 (relatesTo) : exclusion mutuelle provider — prérequis livré.
|
||||
53
.ideai/memory/toolchain-rust-partage-acces-agents.md
Normal file
53
.ideai/memory/toolchain-rust-partage-acces-agents.md
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
name: toolchain-rust-partage-acces-agents
|
||||
description: memory note toolchain-rust-partage-acces-agents
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
---
|
||||
slug: toolchain-rust-partage-acces-agents
|
||||
title: Accès au toolchain Rust/Tauri partagé pour tous les agents (isolement HOME OpenCode)
|
||||
type: project
|
||||
description: Le toolchain Rust stable est déjà installé sur la machine mais invisible aux agents car le profil OpenCode isole HOME. Diagnostic, atténuation par symlink, et chantier durable (injection env au spawn).
|
||||
---
|
||||
|
||||
# Toolchain Rust/Tauri : pourquoi les agents ne compilaient pas, et comment les débloquer
|
||||
|
||||
## Symptôme (récurrent)
|
||||
Tout agent (Main, DevBackend, QA,…) lancé sous un profil OpenCode ne peut PAS compiler le workspace Rust/Tauri :
|
||||
- `cargo`/`rustc` = shims rustup qui répondent « no default toolchain » / « no installed toolchains ».
|
||||
- Bloque toute validation backend (cargo check/build/test), fait timeout les délégations lourdes.
|
||||
|
||||
## Cause racine
|
||||
- Le toolchain **stable 1.94.1 est DÉJÀ installé** dans `/home/anthony/.rustup` + `/home/anthony/.cargo` (toolchains + bin).
|
||||
- MAIS le profil **OpenCode isole `HOME`** vers `.ideai/run/<agent_uuid>/.opencode/`. Donc `~/.rustup` et `~/.cargo` = `.opencode/.rustup` / `.opencode/.cargo`, qui ne contiennent qu'un `settings.toml` + un cache registry partiel → **aucun toolchain**.
|
||||
- IdeA lui-même ne set **ni** `RUSTUP_HOME` **ni** `CARGO_HOME` (grep vide dans `crates/`). Le point d'extension existe pourtant : `crates/infrastructure/src/session/opencode.rs:237` (`cmd.env(key, value)`) set déjà des env au spawn.
|
||||
- `target/` est dans le project root (partagé, géré par le lock cargo) → partageable sans conflit.
|
||||
- `tauri` est dispo via `frontend/node_modules/.bin/tauri` (après `npm install` du frontend).
|
||||
|
||||
## Atténuation appliquée (2026-07-25, ops, sans rebuild)
|
||||
Symlinker les homes isolés vers les homes partagés pour chaque run-dir agent :
|
||||
```bash
|
||||
for d in .ideai/run/*/.opencode; do
|
||||
for sub in .rustup .cargo; do
|
||||
p="$d/$sub"
|
||||
[ -e "$p" ] && [ ! -L "$p" ] && { rm -rf "$p"; ln -s "/home/anthony/$sub" "$p"; }
|
||||
done
|
||||
done
|
||||
```
|
||||
Vérifié sur Main : `cargo 1.94.1` / `rustc 1.94.1` accessibles **sans aucune variable d'env explicite**. Symlink appliqué aux agents actifs (Main a6ced819, DevBackend 73c853d1, QA aefdbd61, …) — 5 run-dirs concernés.
|
||||
|
||||
**Tient pour les agents existants** : rustup suit les symlinks, et OpenCode ne recrée pas `$HOME/.rustup` s'il existe déjà.
|
||||
|
||||
## Solution DURABLE (chantier IdeA, à livrer)
|
||||
Pour les **nouveaux** agents (IdeA crée un nouveau run-dir `.ideai/run/<uuid>/.opencode/.rustup` vide), il faut qu'IdeA provisionne l'accès au toolchain partagé automatiquement. Deux options équivalentes, à faire par DevBackend + **rebuild AppImage + relance** :
|
||||
1. Au spawn de l'agent, injecter `RUSTUP_HOME=/home/anthony/.rustup` et `CARGO_HOME=/home/anthony/.cargo` (point d'extension `opencode.rs:237`). Valeurs résolues depuis le vrai HOME user (pas le HOME isolé), idéalement configurables (env projet / settings).
|
||||
2. Ou, à la création du run-dir, créer les deux symlinks `.opencode/.rustup` et `.opencode/.cargo` → homes partagés.
|
||||
|
||||
Recommandation : option 1 (injection env) — moteur-agnostique (marche aussi pour Codex/Claude si un jour ils isolent aussi) et ne dépend pas du filesystem layout du moteur.
|
||||
|
||||
À grouper dans le **même rebuild AppImage** que le fix « sonde de vivacité multi-moteur » (cf. `idea-memory` à venir) — les deux sont des chantiers runtime qui nécessitent rebuild + relance.
|
||||
|
||||
## Liens
|
||||
- Règle rebuild AppImage : `.ideai/skills/md/a72dac60-641c-4417-b0d7-94b8539f817a.md` (skill build AppImage), mémoire `mcp-bridge-and-delegation-runtime-notes`.
|
||||
- Timeout délégation 600 s (autre symptôme sur les tâches lourdes) : mémoire `rendezvous-600s-cap-too-short-heavy-tasks` + sonde Claude-seule `crates/infrastructure/src/inspector/claude_paths.rs:89`.
|
||||
@ -187,6 +187,44 @@
|
||||
],
|
||||
"fallback": "allow"
|
||||
}
|
||||
},
|
||||
{
|
||||
"agentId": "5d07e4a7-8676-4a71-aea1-5b64caefa944",
|
||||
"permissions": {
|
||||
"rules": [
|
||||
{
|
||||
"capability": "read",
|
||||
"effect": "allow",
|
||||
"paths": [
|
||||
"**"
|
||||
],
|
||||
"commands": []
|
||||
},
|
||||
{
|
||||
"capability": "write",
|
||||
"effect": "deny",
|
||||
"paths": [
|
||||
"**"
|
||||
],
|
||||
"commands": []
|
||||
},
|
||||
{
|
||||
"capability": "delete",
|
||||
"effect": "deny",
|
||||
"paths": [
|
||||
"**"
|
||||
],
|
||||
"commands": []
|
||||
},
|
||||
{
|
||||
"capability": "executeBash",
|
||||
"effect": "allow",
|
||||
"paths": [],
|
||||
"commands": []
|
||||
}
|
||||
],
|
||||
"fallback": "allow"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
111
.ideai/skills/md/eb76ce50-c05c-443b-8ba5-36bda091a148.md
Normal file
111
.ideai/skills/md/eb76ce50-c05c-443b-8ba5-36bda091a148.md
Normal file
@ -0,0 +1,111 @@
|
||||
# Build de l'AppImage IdeA (Linux)
|
||||
|
||||
Procédure canonique pour reconstruire l'AppImage Linux d'IdeA et les bundles frontend associés.
|
||||
|
||||
À utiliser à chaque fois qu'un correctif doit être visible dans l'app desktop. Rappel produit : **le binaire qui tourne = l'AppImage installée, pas les sources**. Un correctif source n'est actif dans l'app desktop qu'après rebuild, remplacement de l'AppImage utilisée, puis relance d'IdeA.
|
||||
|
||||
## Commande principale
|
||||
|
||||
Depuis le project root `/home/anthony/Documents/Projects/IdeA` :
|
||||
|
||||
```bash
|
||||
cd crates/app-tauri
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=true ../../frontend/node_modules/.bin/tauri build --bundles appimage
|
||||
```
|
||||
|
||||
Cette commande déclenche `build.beforeBuildCommand`, donc elle reconstruit automatiquement les deux bundles frontend :
|
||||
|
||||
- `frontend/dist` : bundle desktop, transport `tauri`.
|
||||
- `frontend/dist-web` : bundle web, transport `http`.
|
||||
|
||||
Ne pas revenir à l'ancienne procédure `npm --prefix frontend run build` seule : elle ne reconstruit pas le bundle web séparé.
|
||||
|
||||
## Vérification des bundles web/desktop
|
||||
|
||||
Après le build, lancer :
|
||||
|
||||
```bash
|
||||
npm --prefix frontend run test:bundle-transport
|
||||
```
|
||||
|
||||
Sortie attendue :
|
||||
|
||||
```text
|
||||
dist: __IDEA_TRANSPORT__="tauri" (1 marker)
|
||||
dist-web: __IDEA_TRANSPORT__="http" (1 marker)
|
||||
```
|
||||
|
||||
## Artefact attendu
|
||||
|
||||
```text
|
||||
target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage
|
||||
```
|
||||
|
||||
Vérifier rapidement :
|
||||
|
||||
```bash
|
||||
ls -lh target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage --appimage-help | head
|
||||
```
|
||||
|
||||
## Pourquoi ces variables sont obligatoires
|
||||
|
||||
- `--bundles appimage` : cible uniquement l'AppImage Linux et évite les bundles inutiles/cassants comme NSIS.
|
||||
- `NO_STRIP=true` : évite l'échec linuxdeploy/strip sur les bibliothèques système modernes contenant `.relr.dyn`.
|
||||
- `APPIMAGE_EXTRACT_AND_RUN=1` : évite l'échec FUSE quand linuxdeploy ou appimagetool ne peuvent pas monter une AppImage dans l'environnement courant.
|
||||
|
||||
## Fallback si Tauri échoue à `failed to run linuxdeploy`
|
||||
|
||||
Symptôme : la commande Tauri reconstruit `frontend/dist`, `frontend/dist-web`, compile `target/release/app-tauri`, crée `target/release/bundle/appimage/IdeA.AppDir`, puis échoue seulement à l'étape finale :
|
||||
|
||||
```text
|
||||
failed to bundle project `failed to run linuxdeploy`
|
||||
```
|
||||
|
||||
Ne pas réanalyser tout le build. Faire ce diagnostic court :
|
||||
|
||||
```bash
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=true LDAI_OUTPUT=/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage \
|
||||
/home/anthony/.cache/tauri/linuxdeploy-x86_64.AppImage \
|
||||
--appdir /home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA.AppDir \
|
||||
--output appimage
|
||||
```
|
||||
|
||||
Si `appimagetool` échoue avec téléchargement runtime impossible :
|
||||
|
||||
```text
|
||||
Failed to download runtime file
|
||||
```
|
||||
|
||||
utiliser le runtime local déjà en cache et l'`appimagetool` extrait. Chercher le dossier extrait récent :
|
||||
|
||||
```bash
|
||||
find /tmp -path '*/appimagetool-prefix/usr/bin/appimagetool' -type f -printf '%p\n' | tail -1
|
||||
```
|
||||
|
||||
Puis lancer en remplaçant `<EXTRACTED>` par le préfixe trouvé, par exemple `/tmp/appimage_extracted_xxx` :
|
||||
|
||||
```bash
|
||||
PATH=<EXTRACTED>/appimagetool-prefix/usr/bin:$PATH \
|
||||
ARCH=x86_64 \
|
||||
<EXTRACTED>/appimagetool-prefix/usr/bin/appimagetool \
|
||||
--runtime-file /home/anthony/.cache/tauri/runtime-x86_64 \
|
||||
/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA.AppDir \
|
||||
/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage
|
||||
```
|
||||
|
||||
Le `PATH` est nécessaire parce que `mksquashfs` est fourni dans `appimagetool-prefix/usr/bin` et peut être absent du PATH système.
|
||||
|
||||
## Relance de l'application
|
||||
|
||||
Ne pas relancer IdeA automatiquement depuis une session active : cela tue l'orchestrateur courant et les ponts MCP. Une fois l'AppImage reconstruite, l'utilisateur décide quand remplacer/lancer l'artefact final.
|
||||
|
||||
## Piège d'environnement AppImage
|
||||
|
||||
Une session shell lancée depuis l'AppImage peut hériter de variables comme `APPDIR`, `LD_LIBRARY_PATH`, `PYTHONHOME` pointant vers `/tmp/.mount_IdeA_*`. Symptômes possibles : `python3` casse avec `No module named 'encodings'`, ou un binaire Tauri local se lance dans un environnement pollué.
|
||||
|
||||
Pour des commandes sensibles, préférer un environnement propre ou éviter Python :
|
||||
|
||||
```bash
|
||||
env -i PATH=/usr/bin:/bin HOME=$HOME XDG_RUNTIME_DIR=/run/user/1000 <commande>
|
||||
```
|
||||
6
.ideai/system-permissions.json
Normal file
6
.ideai/system-permissions.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"projectDefault": {
|
||||
"network": "allow"
|
||||
}
|
||||
}
|
||||
6
.ideai/tickets/100/carnet.md
Normal file
6
.ideai/tickets/100/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#100"
|
||||
version: 3
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784993984569
|
||||
---
|
||||
16
.ideai/tickets/100/issue.md
Normal file
16
.ideai/tickets/100/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "4709958c-5082-44fd-a1fd-d6bad85f9361"
|
||||
number: 100
|
||||
title: "[Bug] Problème sur le scroll des agents OpenCode"
|
||||
status: "open"
|
||||
priority: "high"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784992615586
|
||||
updatedAt: 1784993984569
|
||||
version: 3
|
||||
---
|
||||
Quand un agent est un agent opencode, je ne peux aps scroll très haut dans sa cellule.
|
||||
96
.ideai/tickets/101/carnet.md
Normal file
96
.ideai/tickets/101/carnet.md
Normal file
@ -0,0 +1,96 @@
|
||||
---
|
||||
issueRef: "#101"
|
||||
version: 7
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1785011116365
|
||||
---
|
||||
# Carnet #101 — défaut d'isolation multi-projet (cause racine figée)
|
||||
|
||||
> Diagnostic Architect (2026-07-25). Source de vérité du fix. Mémoire durable :
|
||||
> `ticket101-cross-talk-multi-project-rootcause`.
|
||||
|
||||
## Cause racine
|
||||
Stores disque IdeA **correctement scoppés par projet** (mémoire, contexte, handoff, live-state lus
|
||||
depuis `input.project.root` ; agents persistés dans `.ideai/agents.json` par root projet, **sans
|
||||
re-mint des UUID à l'ouverture**). Mais plusieurs **registres mémoire runtime restent globaux,
|
||||
indexés par `AgentId` seul** → deux projets ouverts (surtout si l'un est une copie) peuvent porter
|
||||
les **mêmes `AgentId`** et le runtime les confond.
|
||||
|
||||
### 2 manifestations, même défaut
|
||||
- **A — contamination du contexte d'inférence** : conversation/session d'un projet réutilisée pour
|
||||
l'autre → contexte injecté à l'agent contaminé (preuve : pseudo-branche `wear-os-watch-sync`
|
||||
créée par l'agent Git avec un nom de l'autre projet).
|
||||
- **B — notification de tâche backend perdue / agent s'arrête** (sujet original) : complétion
|
||||
livrable/bloquée/réveillant la mauvaise session.
|
||||
|
||||
## Points précis (registres par `AgentId` seul à requalifier)
|
||||
- ConversationRegistry global + `ConversationId` dérivé des UUID agents seuls :
|
||||
`crates/domain/src/conversation.rs:71`, `crates/infrastructure/src/conversation/mod.rs:41`,
|
||||
orchestrateur `crates/application/src/orchestrator/service.rs:2192` (et `:2293`, `:2368`).
|
||||
- Sessions PTY/structured par `agent_id` seul :
|
||||
`crates/application/src/terminal/registry.rs:182,437`.
|
||||
- Verrous ask / busy-state / liveness / délégations différées :
|
||||
`crates/application/src/orchestrator/service.rs:418`,
|
||||
`crates/infrastructure/src/input/mod.rs:47`.
|
||||
- Inbox/mailbox par `AgentId` seul : `crates/domain/src/inbox.rs:68,156`,
|
||||
`crates/infrastructure/src/input/mod.rs:1052`, `crates/infrastructure/src/mailbox/mod.rs:44`.
|
||||
- Wake par `AgentId` seul : `crates/application/src/orchestrator/wake.rs:87`,
|
||||
provider `crates/backend/src/lib.rs:687`.
|
||||
|
||||
### Pour B — distinction runtime vs modèle
|
||||
La chaîne sink→`project_id`→wake est **correcte** jusqu'au bridge
|
||||
(`crates/infrastructure/src/background_task/sink.rs:23,142` ; `crates/backend/src/lib.rs:2228`
|
||||
recharge le projet par `project_id` puis `wake_agent`). Le défaut réapparaît juste après
|
||||
(inbox/mailbox/busy/session par AgentId seul). **Runtime IdeA suspecté en premier.** GLM 5.2 n'est
|
||||
l'hypothèse principale que si les logs prouvent enqueue+wake+`BackgroundTaskCompletionDelivered`
|
||||
sur le bon projet sans reprise. Traces : `lib.rs:2238`, `wake.rs:93`.
|
||||
|
||||
## Décision utilisateur
|
||||
**Lancer le fix structurel maintenant (lots 1-2).** La collision d'UUID et les logs B se
|
||||
vérifieront en parallèle.
|
||||
|
||||
## Périmètre DevBackend (lots 1-2 de ce fix)
|
||||
- **Lot 1** : introduire une clé runtime scellée `RuntimeAgentKey { project_id, agent_id }` ;
|
||||
interdire toute map app-wide indexée par `AgentId` seul.
|
||||
- **Lot 2** : propager la clé dans `AgentInbox`, `InputMediator`, `AgentMailbox`,
|
||||
busy/liveness/deferred, wake, session lookup, ask-locks. **Correctif structurel commun à A et B.**
|
||||
|
||||
Lots suivants (3-6, hors premier passage) : qualifier ConversationId/registry par projet (lot 3) ;
|
||||
requalifier `TerminalSessions`/`StructuredSessions` par `(project_id, agent_id)` +
|
||||
`AppWakeSessionProvider` refuse l'autre projet (lot 4) ; audit `session_limit`/tables reprise (lot 5) ;
|
||||
télémétrie `project_id` sur logs diag wake/routage (lot 6).
|
||||
|
||||
## Invariants
|
||||
- Sources de contexte sur disque restent root-scopées (ne pas régresser).
|
||||
- Templates/profils globaux restent globaux produit (pas de vecteur de session/conversation partagée).
|
||||
- Un projet non-actif doit continuer à recevoir ses wakes et complétions.
|
||||
- Pas de régression OpenCode/Codex/Claude (correctif transverse runtime, pas moteur-spécifique).
|
||||
- Aucun nouveau DTO frontend pour la correction minimale.
|
||||
|
||||
## QA — critère de vérité
|
||||
Test d'intégration **non-cross-talk** : 2 projets ouverts avec **volontairement les mêmes `AgentId`** :
|
||||
- délégation projet A ne réutilise jamais session/conversation vivante de projet B ;
|
||||
- complétion `(projet A, agent X)` n'entre ni dans l'inbox ni dans la session de `(projet B, agent X)` ;
|
||||
- wake d'un projet non-actif fonctionne ;
|
||||
- collision d'ids qui échouait avant → verte sur PTY, structured et background wake.
|
||||
|
||||
## Clôture 2026-07-25
|
||||
- Correctif livré sur `feature/ticket101-multi-project-isolation` puis mergé localement dans `develop`.
|
||||
- Commit feature : `6e98fd8 fix(runtime): isolate agent state by project (#101)`.
|
||||
- Merge local : `merge: integrate ticket 101 multi-project isolation`.
|
||||
- QA ciblée verte :
|
||||
- `cargo test -p application --test agent_wake --test orchestrator_service --test structured_registry_d1 --test structured_launch_d3 --test session_limit_service --test session_limit_t4 --test workstate --test workstate_actions`
|
||||
- `cargo test -p infrastructure --test agent_inbox --test mcp_server`
|
||||
- `cargo test -p app-tauri --test session_limit_wiring`
|
||||
- `cargo test -p application`
|
||||
- `cargo test -p app-tauri --tests`
|
||||
- Réserve connue : `cargo test -p infrastructure` complet reste rouge dans le sandbox QA sur tests `openai_compat` à cause du bind local interdit (`Operation not permitted`), sans signal de régression #101.
|
||||
|
||||
## Hors périmètre
|
||||
- #91 (popup de notification) : purement front, indépendant.
|
||||
- Remint systématique des AgentId à l'ouverture : non retenu (la clé runtime rend la collision
|
||||
inoffensive même sans remint).
|
||||
|
||||
## Lié
|
||||
- #91 (relatesTo). Mémoires : `background-tasks-first-class-design`, `b8-command-runner-pty-framing`,
|
||||
`mcp-bridge-and-delegation-runtime-notes` (règle rebuild AppImage).
|
||||
16
.ideai/tickets/101/issue.md
Normal file
16
.ideai/tickets/101/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "05f9f05b-97d4-4ae2-9fd9-220dd71f7231"
|
||||
number: 101
|
||||
title: "[Bug] Soucis de retour de notification sur les taches backend"
|
||||
status: "closed"
|
||||
priority: "critical"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784993230013
|
||||
updatedAt: 1785011116365
|
||||
version: 7
|
||||
---
|
||||
Lorsque un agent Opencode lance une tache backend, il s'arrete de travailler et je ne suis pas sur q'uil y ai un jour un retour de notification. Je ne sais aps si le soucis provient de OpenCode ou s'il provient du model (GLM 5.2 ici)
|
||||
6
.ideai/tickets/102/carnet.md
Normal file
6
.ideai/tickets/102/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#102"
|
||||
version: 4
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784993980505
|
||||
---
|
||||
16
.ideai/tickets/102/issue.md
Normal file
16
.ideai/tickets/102/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "e91fd358-da94-4382-aa70-6a3fe5a63840"
|
||||
number: 102
|
||||
title: "[Bug] Devoir resize les cellule pour afficher la TUI d'un agent"
|
||||
status: "open"
|
||||
priority: "medium"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784993319700
|
||||
updatedAt: 1784993980505
|
||||
version: 4
|
||||
---
|
||||
J'ai toujours un soucis qui fait que quand je switch de projet IdeA ou de layout ou que j'ajoute des cellules ou autres, je suis obligé de resize un coup la cellule pour que son constenu s'affiche correctement
|
||||
99
.ideai/tickets/103/carnet.md
Normal file
99
.ideai/tickets/103/carnet.md
Normal file
@ -0,0 +1,99 @@
|
||||
---
|
||||
issueRef: "#103"
|
||||
version: 4
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1785013507979
|
||||
---
|
||||
# Carnet #103 — permission réseau exposée dans IdeA
|
||||
|
||||
## Problème
|
||||
Certaines commandes lancées par les agents s'exécutent dans un environnement où le réseau est restreint, sans visibilité ni action claire dans IdeA. Exemple live : rebuild AppImage OK jusqu'au bundling, puis `appimagetool` échoue à télécharger le runtime GitHub car la session agent est `network restricted` + `approval policy: never`.
|
||||
|
||||
## Décision UX
|
||||
Mémoire : `ticket103-network-permission-ux-surface`.
|
||||
|
||||
- Surface principale : `Permissions > Système`.
|
||||
- Miroirs de lecture : badge compact dans `Agents`, bannière/erreur contextualisée dans `Terminal`.
|
||||
- États visibles : `Réseau autorisé`, `Réseau interdit`, `Demande d'autorisation`, `Verrouillé par le runtime`.
|
||||
- Distinction obligatoire : politique voulue, état effectif, verrou runtime.
|
||||
- Si le runtime externe ne permet pas l'élévation dans la session active : contrôle read-only + explication explicite.
|
||||
|
||||
## Cadrage architecture
|
||||
Ne pas étendre le modèle existant `ProjectPermissions` fichier/bash : le réseau n'est ni une capability filesystem ni une règle Landlock. Ajouter un modèle/read-model séparé de permissions système.
|
||||
|
||||
### Domaine / DTO V1
|
||||
- `NetworkPolicy = "allow" | "deny" | "ask"`.
|
||||
- `SystemPermissionSet { network?: NetworkPolicy }`.
|
||||
- `ProjectSystemPermissions { version, projectDefault?: SystemPermissionSet, agents?: [{ agentId, permissions: SystemPermissionSet }] }`.
|
||||
- `ResolvedAgentSystemPermissions` :
|
||||
- `wanted: NetworkPolicy | null`
|
||||
- `effective: NetworkPolicy`
|
||||
- `runtimeLock: { state: "none" | "locked", source?: string, reason?: string }`
|
||||
- `control: { mode: "editable" | "readOnly", reason?: string }`
|
||||
|
||||
### Ports / use cases
|
||||
- `SystemPermissionStore`.
|
||||
- `GetProjectSystemPermissions`.
|
||||
- `UpdateProjectSystemPermissions`.
|
||||
- `UpdateAgentSystemPermissions`.
|
||||
- `ResolveAgentSystemPermissions`.
|
||||
- `RuntimePermissionProbe` : expose ce que le runtime hôte/fournisseur autorise réellement et s'il verrouille le réseau.
|
||||
|
||||
### API/commands attendus
|
||||
- `get_project_system_permissions(projectId) -> ProjectSystemPermissionsDto`
|
||||
- `update_project_system_permissions({ projectId, permissions }) -> ProjectSystemPermissionsDto`
|
||||
- `update_agent_system_permissions({ projectId, agentId, permissions }) -> ProjectSystemPermissionsDto`
|
||||
- `resolve_agent_system_permissions({ projectId, agentId }) -> ResolvedAgentSystemPermissionsDto`
|
||||
|
||||
### Limite produit V1
|
||||
Implémentable maintenant : persister la politique voulue, afficher `wanted/effective/runtimeLock`, rendre le contrôle read-only si runtime verrouillé/non inspectable, badges agents, bannière terminal.
|
||||
|
||||
Non promis en V1 : changer effectivement la permission réseau d'une session fournisseur déjà lancée ou élever un runtime externe `network restricted` / `approval never`. IdeA doit l'expliquer plutôt que simuler une élévation.
|
||||
|
||||
## Découpage
|
||||
### DevBackend
|
||||
1. Ajouter domaine `system permissions` séparé de LP1 permissions fichier/bash.
|
||||
2. Ajouter store + use cases + DTO + commands Tauri/HTTP.
|
||||
3. Ajouter `RuntimePermissionProbe` read-only au composition root.
|
||||
4. Ajouter read-model `resolve_agent_system_permissions`.
|
||||
5. Optionnel si simple : mapper des échecs réseau vers un code stable `NETWORK_LOCKED` ou `NETWORK_UNAVAILABLE`.
|
||||
|
||||
### DevFrontend
|
||||
1. Étendre types domaine, ports, adapters Tauri/HTTP/mock.
|
||||
2. Ajouter la sous-section `Réseau` dans `Permissions > Système`.
|
||||
3. Ajouter badge compact dans `Agents`.
|
||||
4. Ajouter bannière/erreur terminal contextualisée pour état verrouillé/échec réseau.
|
||||
|
||||
### QA
|
||||
- Projet sans config : état cohérent, pas de faux `allow`.
|
||||
- Save project default puis override agent : relecture identique.
|
||||
- Resolve distingue `wanted`, `effective`, `runtimeLock`.
|
||||
- Probe `locked` : contrôle read-only + message visible.
|
||||
- Non-régression `Permissions > Système` existant fichier/bash.
|
||||
- Aucun moteur ne reçoit de faux flag réseau au spawn.
|
||||
- Badge agents et bannière terminal reflètent l'état effectif.
|
||||
|
||||
## Validation 2026-07-25
|
||||
QA verte sur le périmètre #103.
|
||||
|
||||
Commandes exécutées par QA :
|
||||
- `cargo test -p domain system_permissions`
|
||||
- `cargo test -p application --test system_permission_usecases`
|
||||
- `cargo test -p infrastructure --test system_permission_store`
|
||||
- `cargo test -p app-tauri --test dto_system_permissions`
|
||||
- `cargo test -p web-server allowlisted`
|
||||
- `npm run typecheck`
|
||||
- `npx vitest run src/features/permissions/permissions.test.tsx src/features/agents/agents.test.tsx src/features/terminals/TerminalView.test.tsx`
|
||||
- `npx vitest run src/features/permissions/permissions.test.tsx`
|
||||
|
||||
Résultats : backend ciblé vert, frontend typecheck vert, tests frontend ciblés 49 passed.
|
||||
|
||||
## État Git
|
||||
Implémentation validée dans le working tree courant, mais commit/merge non réalisé par Main :
|
||||
- l'agent Git headless renvoie un pseudo-appel outil au lieu d'agir ;
|
||||
- Main ne peut pas écrire `.git/index.lock` depuis cette session (`Read-only file system`).
|
||||
|
||||
Le commit local reste à faire manuellement ou via un agent Git fonctionnel.
|
||||
|
||||
## Critère de clôture
|
||||
Tests pertinents verts, commit/merge local par Git, puis rebuild AppImage.
|
||||
39
.ideai/tickets/103/issue.md
Normal file
39
.ideai/tickets/103/issue.md
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
id: "3d9021da-26c3-439d-9463-8d206bd06f1b"
|
||||
number: 103
|
||||
title: "Exposer et piloter la permission réseau des agents/commandes dans IdeA"
|
||||
status: "qa"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1785011668081
|
||||
updatedAt: 1785013507979
|
||||
version: 4
|
||||
---
|
||||
## Problème
|
||||
|
||||
Aujourd'hui certaines commandes lancées par les agents s'exécutent dans un environnement où le réseau est restreint, sans que l'utilisateur puisse le voir ni l'autoriser depuis IdeA. Exemple réel : rebuild AppImage OK jusqu'au bundling, puis `appimagetool` échoue à télécharger le runtime AppImage depuis GitHub (`Failed to download runtime: server returned status code 0`) parce que la session agent a `network restricted` et `approval policy: never`.
|
||||
|
||||
## Besoin utilisateur
|
||||
|
||||
Depuis IdeA, l'utilisateur doit pouvoir comprendre et piloter cette permission réseau :
|
||||
- voir qu'un agent/profil/session est en mode réseau interdit ou autorisé ;
|
||||
- configurer la politique réseau attendue pour les agents/commandes ;
|
||||
- éviter les échecs opaques de commandes qui ont légitimement besoin d'Internet (build, install, téléchargement runtime, docs, dépendances) ;
|
||||
- conserver un comportement sûr par défaut et explicite.
|
||||
|
||||
## Attendu produit
|
||||
|
||||
Définir puis implémenter une surface IdeA pour exposer cette permission. La solution doit respecter le modèle de permissions/sandbox existant et clarifier la limite éventuelle : si le sandbox fournisseur impose `network restricted` sans possibilité d'élévation runtime, IdeA doit l'expliquer plutôt que faire croire que le réseau est activable.
|
||||
|
||||
## Critères d'acceptation
|
||||
|
||||
- Une surface UI ou configuration explicite permet de voir la politique réseau applicable aux agents/commandes.
|
||||
- L'utilisateur dispose d'une action ou d'un réglage clair quand IdeA peut piloter cette permission.
|
||||
- Si la permission est imposée par le runtime externe et non modifiable, l'UI l'indique clairement.
|
||||
- Les agents/commandes ne gagnent pas l'accès réseau silencieusement.
|
||||
- Tests pertinents verts.
|
||||
- AppImage reconstruite après livraison.
|
||||
@ -1,8 +1,8 @@
|
||||
---
|
||||
issueRef: "#14"
|
||||
version: 7
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1783456579694
|
||||
version: 8
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784449099987
|
||||
---
|
||||
## Production #14 — livrée dans develop (validation e2e live restante)
|
||||
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "0c1f1991-b6db-47b7-bbef-2478bc7874ab"
|
||||
number: 14
|
||||
title: "Integration de profils IA locaux/LAN comme profils IdeA canoniques"
|
||||
status: "qa"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1783184495846
|
||||
updatedAt: 1783456579694
|
||||
version: 7
|
||||
updatedAt: 1784449099987
|
||||
version: 8
|
||||
---
|
||||
Objectif : permettre à IdeA d'utiliser des modèles hébergés localement ou sur le réseau local, par exemple Qwen via Ollama ou un runtime Docker exposant une API, avec le même niveau d'intégration produit que les profils Claude Code et OpenAI Codex CLI.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#3"
|
||||
version: 6
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784093835732
|
||||
version: 7
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784377533777
|
||||
---
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "fedd343e-f9c1-467b-bab4-455f51907de7"
|
||||
number: 3
|
||||
title: "[Différé — design à mûrir] Tâches de fond — dette B : retry durable après reboot"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1783082860758
|
||||
updatedAt: 1784093835732
|
||||
version: 6
|
||||
updatedAt: 1784377533777
|
||||
version: 7
|
||||
---
|
||||
Ticket #3 — Tâches de fond : retry après redémarrage sans fuite de secrets dans le projet.
|
||||
|
||||
|
||||
@ -1,6 +1,349 @@
|
||||
---
|
||||
issueRef: "#43"
|
||||
version: 1
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1783879315858
|
||||
version: 6
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784707900405
|
||||
---
|
||||
# Ticket #43 — Cadrage consolidé v2 du système de plugins
|
||||
|
||||
> Mise à jour Architect du 2026-07-21 après retour QA F4. Ce carnet v2 annule les ambiguïtés du cadrage initial, en particulier sur le schéma de layout custom plugin.
|
||||
|
||||
## 0. Décisions discovery conservées
|
||||
|
||||
- Plugins **full-trust** v1 : pas de sandbox UI.
|
||||
- Bundle **JS/ESM pré-compilé**, importé dynamiquement ; IdeA ne compile pas de TS/TSX.
|
||||
- Installation **globale** dans le dossier données utilisateur de l'application, pas dans les projets.
|
||||
- Distribution v1 locale : dossier ou archive locale ; packaging compatible archive partageable future type `.vsix`.
|
||||
- Contributions v1 : menus top-level, items de menus existants, layouts custom React arbitraires, serveurs MCP externes.
|
||||
- MCP plugin = process serveur MCP externe déclaré par manifeste et supervisé via le pont MCP existant.
|
||||
- Propreté retrait : après uninstall + redémarrage, aucune contribution, aucun process, aucune entrée fantôme, aucun état plugin-specific.
|
||||
|
||||
## 1. Store global et cycle de vie
|
||||
|
||||
Store global :
|
||||
|
||||
```text
|
||||
{app_data_dir}/plugins/
|
||||
registry.json
|
||||
installed/
|
||||
<pluginId>/
|
||||
idea-plugin.json
|
||||
dist/index.js
|
||||
assets/...
|
||||
servers/...
|
||||
```
|
||||
|
||||
États v1 persistés :
|
||||
|
||||
```text
|
||||
enabled
|
||||
disabled
|
||||
pending-enable
|
||||
pending-disable
|
||||
pending-uninstall
|
||||
invalid
|
||||
```
|
||||
|
||||
Règles :
|
||||
|
||||
- Installer depuis archive ou dossier local copie toujours un snapshot dans `installed/<pluginId>/`.
|
||||
- `registry.json` porte seulement l'état global nécessaire ; une désinstallation réussie supprime l'entrée registry.
|
||||
- Disable/uninstall en session masque les contributions UI et arrête MCP immédiatement si possible, mais le code ESM déjà importé n'est purgé strictement qu'au redémarrage.
|
||||
- Au boot, le frontend reconstruit une registry plugin vide puis charge uniquement les plugins `enabled` et non `pending-uninstall`.
|
||||
|
||||
## 2. Manifeste v1
|
||||
|
||||
Fichier obligatoire : `idea-plugin.json`.
|
||||
|
||||
```json
|
||||
{
|
||||
"ideaPluginManifestVersion": 1,
|
||||
"id": "dev.acme.gitgraph",
|
||||
"displayName": "Git Graph",
|
||||
"publisher": "Acme DevTools",
|
||||
"version": "1.2.3",
|
||||
"engines": { "idea": ">=0.1.0 <1.0.0" },
|
||||
"main": "dist/index.js",
|
||||
"icon": "assets/icon.svg",
|
||||
"trustLevel": "full",
|
||||
"capabilities": ["ui", "mcp"],
|
||||
"contributes": {
|
||||
"menus": [],
|
||||
"menuItems": [],
|
||||
"layouts": [],
|
||||
"mcpServers": []
|
||||
},
|
||||
"archive": {
|
||||
"files": ["idea-plugin.json", "dist/**", "assets/**", "servers/**"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Validation :
|
||||
|
||||
- `trustLevel` vaut uniquement `full` en v1.
|
||||
- `main`, `icon`, assets et commandes MCP relatives ne doivent contenir ni chemin absolu ni `..`.
|
||||
- `id` stable, unique, reverse-DNS recommandé.
|
||||
- `version` SemVer.
|
||||
- `engines.idea` incompatible => plugin `invalid`, jamais chargé.
|
||||
|
||||
## 3. Contrat définitif des layouts custom plugin
|
||||
|
||||
### 3.1 Décision ferme
|
||||
|
||||
`CustomPluginLayout` est une **vraie variante top-level de `LayoutNode`**, pas un champ optionnel de `LeafCell`.
|
||||
|
||||
Motif : un layout plugin occupe une zone de l'arbre au même niveau conceptuel qu'une leaf terminal, un split ou une grid. Le mettre dans `LeafCell` mélangerait deux responsabilités incompatibles : terminal/session/agent d'un côté, composant React arbitraire et état opaque plugin de l'autre.
|
||||
|
||||
### 3.2 Schéma JSON canonique
|
||||
|
||||
Le layout tree garde le format serde existant `#[serde(tag = "type", content = "node")]`.
|
||||
|
||||
Union canonique :
|
||||
|
||||
```ts
|
||||
export type LayoutNode =
|
||||
| { type: "leaf"; node: LeafCell }
|
||||
| { type: "split"; node: SplitContainer }
|
||||
| { type: "grid"; node: GridContainer }
|
||||
| { type: "customPluginLayout"; node: CustomPluginLayoutCell };
|
||||
```
|
||||
|
||||
Payload canonique :
|
||||
|
||||
```ts
|
||||
export interface CustomPluginLayoutCell {
|
||||
id: string;
|
||||
pluginId: string;
|
||||
layoutType: string;
|
||||
state: unknown;
|
||||
}
|
||||
```
|
||||
|
||||
Exemple complet :
|
||||
|
||||
```json
|
||||
{
|
||||
"root": {
|
||||
"type": "customPluginLayout",
|
||||
"node": {
|
||||
"id": "018f0c5a-2b4b-70d4-a7c2-300000000001",
|
||||
"pluginId": "dev.acme.gitgraph",
|
||||
"layoutType": "dev.acme.gitgraph.layout",
|
||||
"state": {
|
||||
"branchFilter": "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Dans un split :
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "split",
|
||||
"node": {
|
||||
"id": "split-1",
|
||||
"direction": "row",
|
||||
"children": [
|
||||
{
|
||||
"weight": 1,
|
||||
"node": {
|
||||
"type": "leaf",
|
||||
"node": { "id": "terminal-1" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"weight": 1,
|
||||
"node": {
|
||||
"type": "customPluginLayout",
|
||||
"node": {
|
||||
"id": "plugin-cell-1",
|
||||
"pluginId": "dev.acme.gitgraph",
|
||||
"layoutType": "dev.acme.gitgraph.layout",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Dans une grid, `GridCell.node` peut pareillement être `{ type: "customPluginLayout", node: ... }`.
|
||||
|
||||
### 3.3 Ce qui est explicitement interdit
|
||||
|
||||
Cette forme n'est **pas** contractuelle et ne doit plus être produite ni consommée comme modèle canonique :
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "leaf",
|
||||
"node": {
|
||||
"id": "leaf-1",
|
||||
"pluginLayout": {
|
||||
"pluginId": "dev.acme.gitgraph",
|
||||
"layoutType": "dev.acme.gitgraph.layout",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`LeafCell.pluginLayout` est une divergence frontend issue de l'ambiguïté initiale. Elle doit être supprimée du modèle domaine TS ou limitée à une migration locale temporaire de tests/mocks ; elle ne fait pas partie de l'IPC ni de la persistance.
|
||||
|
||||
### 3.4 Disponibilité et fallback
|
||||
|
||||
La disponibilité n'est pas stockée dans le layout. Elle est dérivée côté UI à partir du plugin registry/runtime catalog :
|
||||
|
||||
```ts
|
||||
export type PluginLayoutAvailability =
|
||||
| "available"
|
||||
| "plugin-disabled"
|
||||
| "plugin-missing"
|
||||
| "incompatible";
|
||||
```
|
||||
|
||||
Rendu :
|
||||
|
||||
- `available` : rendre le composant React enregistré pour `layoutType`.
|
||||
- `plugin-disabled`, `plugin-missing`, `incompatible` : rendre le fallback non destructif `Layout indisponible`.
|
||||
- Le fallback ne transforme pas automatiquement le nœud et ne supprime jamais `state`.
|
||||
|
||||
### 3.5 Ajustements requis
|
||||
|
||||
Verdict convergence F4 : **frontend à ajuster, backend à conserver**.
|
||||
|
||||
Backend :
|
||||
|
||||
- L'implémentation actuelle `LayoutNode::CustomPluginLayout(CustomPluginLayoutCell)` sérialisée `type: "customPluginLayout"` est le contrat canonique.
|
||||
- À vérifier seulement : roundtrip serde et DTO Tauri exposent bien `pluginId`, `layoutType`, `state` en camelCase.
|
||||
|
||||
Frontend :
|
||||
|
||||
- Ajouter la variante `{ type: "customPluginLayout"; node: CustomPluginLayoutCell }` à `LayoutNode`.
|
||||
- Retirer `pluginLayout?: CustomPluginLayoutCell` de `LeafCell` comme contrat domaine.
|
||||
- Adapter `LayoutGrid`/renderer récursif pour router `type === "customPluginLayout"` vers `PluginLayoutCellView`.
|
||||
- Adapter `layoutAvailability`, fallback et tests pour recevoir le payload depuis `node.node` de la variante top-level.
|
||||
- Ajouter un test de parsing/rendu avec un layout JSON produit par Rust contenant `type: "customPluginLayout"`.
|
||||
|
||||
## 4. Contribution points menus et `when`
|
||||
|
||||
Menus v1 :
|
||||
|
||||
```ts
|
||||
type MenuTargetId = "panels" | "settings" | `plugin:${string}`;
|
||||
```
|
||||
|
||||
Items v1 :
|
||||
|
||||
```ts
|
||||
interface PluginMenuItemContribution {
|
||||
id: string;
|
||||
targetMenuId: MenuTargetId;
|
||||
label: string;
|
||||
command: string;
|
||||
order?: number;
|
||||
icon?: string;
|
||||
when?: string;
|
||||
}
|
||||
```
|
||||
|
||||
`when` v1 utilise le mini-langage booléen `&&`, `||`, `!`, parenthèses.
|
||||
|
||||
Variables :
|
||||
|
||||
```ts
|
||||
type WhenVariable =
|
||||
| "projectOpen"
|
||||
| "gitRepository"
|
||||
| "agentSelected"
|
||||
| "terminalFocused"
|
||||
| "layoutCellFocused";
|
||||
```
|
||||
|
||||
Arbitrage QA sur les variables actuellement figées à `false` :
|
||||
|
||||
- `projectOpen` : obligatoire v1, doit refléter l'état réel.
|
||||
- `gitRepository` : **bloquant avant clôture #43/F3**. Le manifeste exemple et le cas GitGraph dépendent de `projectOpen && gitRepository`; le laisser à `false` rend des contributions valides inatteignables.
|
||||
- `agentSelected`, `terminalFocused`, `layoutCellFocused` : dette acceptable v1 si elles restent explicitement documentées comme **best-effort non câblé** et donc `false` jusqu'à un lot focus/selection dédié. Ce n'est pas bloquant pour F4 ni pour la clean-removal QA, sauf si un plugin de validation les utilise dans son manifeste.
|
||||
|
||||
Ajustement recommandé : DevFrontend câble au minimum `gitRepository` depuis l'état projet/git déjà disponible, ou retire temporairement `gitRepository` des manifests/tests de validation. La préférence architecture est de le câbler, car il est déjà annoncé comme variable v1.
|
||||
|
||||
## 5. Contribution MCP et `${appDataDir}`
|
||||
|
||||
Manifest MCP :
|
||||
|
||||
```ts
|
||||
interface PluginMcpServerContribution {
|
||||
id: string;
|
||||
displayName: string;
|
||||
command: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
cwd?: "${pluginRoot}" | "${appDataDir}" | string;
|
||||
transport: "stdio";
|
||||
autoStart?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
Variables de substitution contractuelles dans `command`, `args`, `env` et `cwd` :
|
||||
|
||||
- `${pluginRoot}` : obligatoire v1.
|
||||
- `${appDataDir}` : **obligatoire v1 si la spec continue de l'accepter**.
|
||||
- `${projectRoot}` : interdit v1.
|
||||
|
||||
Arbitrage QA sur `${appDataDir}` non implémenté :
|
||||
|
||||
- Pas bloquant pour F4 layout.
|
||||
- **Bloquant pour clôture B4/#43** si le manifeste continue de documenter `${appDataDir}` comme disponible. Un contrat annoncé mais non substitué crée des specs MCP fausses.
|
||||
- Deux sorties acceptables, par ordre de préférence :
|
||||
1. DevBackend implémente la substitution `${appDataDir}` dans les specs MCP plugin et ajoute tests args/env/cwd.
|
||||
2. Si le coût est refusé pour v1, retirer `${appDataDir}` du contrat manifeste et du validator, puis documenter explicitement `${pluginRoot}` comme seule variable v1.
|
||||
|
||||
Décision architecture par défaut : garder `${appDataDir}` et le faire implémenter par DevBackend, car le store global est déjà résolu côté backend et le coût est borné.
|
||||
|
||||
## 6. Lots et responsabilités actualisés
|
||||
|
||||
### F4 — convergence layout custom plugin
|
||||
|
||||
Owner : DevFrontend.
|
||||
|
||||
Livrables :
|
||||
|
||||
- Union TS `LayoutNode` alignée sur Rust avec `customPluginLayout` top-level.
|
||||
- Renderer/fallback branchés sur cette variante.
|
||||
- Suppression du contrat `LeafCell.pluginLayout`.
|
||||
- Test frontend à partir d'un JSON Rust réel.
|
||||
|
||||
Backend F4 : pas de refonte attendue ; seulement vérifier/maintenir les tests serde existants.
|
||||
|
||||
### F3 — `when.gitRepository`
|
||||
|
||||
Owner : DevFrontend.
|
||||
|
||||
Livrable : `gitRepository` ne doit plus être figé à `false` pour un projet Git réel avant clôture #43/F3.
|
||||
|
||||
### B4 — `${appDataDir}` MCP
|
||||
|
||||
Owner : DevBackend.
|
||||
|
||||
Livrable : substitution `${appDataDir}` dans `command`, `args`, `env`, `cwd` des specs MCP plugin, ou retrait explicite du contrat si arbitré à la baisse. Par défaut : implémenter.
|
||||
|
||||
### QA — non-régression à ajouter
|
||||
|
||||
- Layout JSON backend `customPluginLayout` top-level rendu côté frontend.
|
||||
- Fallback atteint si plugin missing/disabled avec nœud top-level.
|
||||
- Aucun fallback basé sur `leaf.node.pluginLayout` ne compte comme validation du contrat.
|
||||
- MCP spec avec `${pluginRoot}` et `${appDataDir}`.
|
||||
- Menu item `when: "projectOpen && gitRepository"` actif dans un projet Git.
|
||||
|
||||
## 7. Non-objectifs maintenus v1
|
||||
|
||||
- Pas de sandbox UI.
|
||||
- Pas de hot-unload mémoire garanti dans la même session.
|
||||
- Pas de marketplace distant.
|
||||
- Pas de compilation TS/TSX.
|
||||
- Pas de `${projectRoot}` pour serveurs MCP plugin globaux.
|
||||
- Pas de garantie v1 sur `agentSelected`, `terminalFocused`, `layoutCellFocused` tant qu'un lot focus/selection n'a pas été cadré.
|
||||
@ -2,14 +2,15 @@
|
||||
id: "63e80352-8ce1-4116-9ec3-b4cb3fc5c077"
|
||||
number: 43
|
||||
title: "Systeme de plugins"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783879315858
|
||||
updatedAt: 1783879315858
|
||||
version: 1
|
||||
updatedAt: 1784707900405
|
||||
version: 6
|
||||
---
|
||||
Système de plugins/extensions pour IdeA (à la VSCode/Visual Studio) : permettre d'ajouter des menus (dropdown façon Panneaux/Paramètres), des items dans des menus existants, des types de layout custom (à la GitGraph), et des tools MCP.
|
||||
@ -1,8 +1,8 @@
|
||||
---
|
||||
issueRef: "#55"
|
||||
version: 11
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784097757001
|
||||
version: 12
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784649948611
|
||||
---
|
||||
## Réouverture (2026-07-15) — le fix 141c13d ne suffit pas
|
||||
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "9ab1eb21-d6dd-435a-b088-377f0ec7e04f"
|
||||
number: 55
|
||||
title: "[Bug] Error on loading local model"
|
||||
status: "inProgress"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784045935720
|
||||
updatedAt: 1784097757001
|
||||
version: 11
|
||||
updatedAt: 1784649948611
|
||||
version: 12
|
||||
---
|
||||
Quand jke cherche a lancer un modele local sur une cellule, il commence apr charger le serveur, ce qui est bon mais au bout de quelques seconde, le chargement du serveur disparait et j'ai cette erreur qui s'affiche en bandeau rouge: Échec du lancement de l'agent : model server error (timeout): readiness timed out.
|
||||
Si j'insiste assez en changeant d'agent et en remettant l'agent, au bourt d'un moment ça fini par fonctionner
|
||||
@ -1,6 +1,21 @@
|
||||
---
|
||||
issueRef: "#60"
|
||||
version: 3
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784194089605
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784360139966
|
||||
---
|
||||
## Réconciliation (2026-07-18) — déjà livré et mergé, statut désynchronisé
|
||||
|
||||
Le fix Lot A (backend + frontend) a en réalité été implémenté et mergé sur `develop` **avant** cette relance de sprint : commit `2f7f111` (`fix(ticket-assistant): rendre visible l'échec « aucune réponse » de l'assistant de ticket (#60)`), fusionné via `ebad5ff` (`Merge feature/ticket60-ticket-assistant-no-reply into develop (#60)`). `ebad5ff` est ancêtre de `develop` HEAD actuel (`17d6baf`) — plusieurs sprints livrés depuis sans régression signalée.
|
||||
|
||||
Contenu du fix (conforme au périmètre cadré) :
|
||||
- domain/ports : variant `ReplyEvent::Error { message }`.
|
||||
- infra/session : parse `reasoning_content` en fallback quand `content` est vide.
|
||||
- app-tauri : mapping `ReplyEvent::Error → ReplyChunk::Error`, Final vide converti en Error visible.
|
||||
- frontend : `ReplyChunk` type `error`, rendu dans `useTicketAssistant` (+ test dédié), `busy` ne retombe plus prématurément.
|
||||
|
||||
Reconfirmé le 2026-07-18 par DevFrontend sur l'arbre de travail courant : `npx vitest run src/features/tickets/useTicketAssistant.test.tsx` → 3/3 vert, y compris le cas error-chunk.
|
||||
|
||||
Le ticket était resté `inProgress` avec un carnet vide par désynchronisation d'état (le commit d'origine portait la note « #60 reste inProgress » avant la vérification hors-sandbox qui a permis le merge ensuite). Clôture sur constat de fait, pas de nouveau travail nécessaire.
|
||||
|
||||
Branche orpheline `feature/ticket60-assistant-mute-reply` créée par erreur lors de cette relance (rien à committer, identique à `develop`) — à supprimer par Git, non utilisée.
|
||||
@ -2,16 +2,16 @@
|
||||
id: "623b1719-c71d-448e-a0ef-3c7d5823b8a3"
|
||||
number: 60
|
||||
title: "[Bug] Assistant IA d'édition de ticket : aucune réponse dans la conversation (stream sans Final non signalé)"
|
||||
status: "inProgress"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: [{"target":"#25","kind":"relatesTo"},{"target":"#27","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784094260499
|
||||
updatedAt: 1784194089605
|
||||
version: 3
|
||||
updatedAt: 1784360139966
|
||||
version: 5
|
||||
---
|
||||
Rapporté par l'utilisateur (2026-07-15) : dans l'édition d'un ticket, le bouton « Assistant IA » ouvre une conversation, mais l'envoi d'un message ne produit JAMAIS de réponse visible — la conversation reste muette. PROFIL UTILISÉ : modèle local OpenAI-compatible (llamacpp).
|
||||
|
||||
|
||||
@ -1,6 +1,19 @@
|
||||
---
|
||||
issueRef: "#61"
|
||||
version: 5
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784182133142
|
||||
version: 11
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784661823324
|
||||
---
|
||||
Un premier fix (commit 445ecaf, "fix(frontend): refit différé des cellules terminal après mutation de layout (#61)", mergé via a69c5db) a couvert le cas desktop LayoutGrid/TerminalView (split/merge déclenchant un refit explicite via requestAnimationFrame).
|
||||
|
||||
## Réouverture (2026-07-21)
|
||||
|
||||
L'utilisateur signale que le problème persiste :
|
||||
- Ouverture d'une nouvelle cellule → la CLI déjà affichée ne se redimensionne pas correctement tant qu'un resize manuel n'est pas déclenché.
|
||||
- Reproduit aussi sur la version **Web** : sur PC, un redimensionnement manuel de la fenêtre du navigateur corrige le scaling. Sur mobile, impossible de redimensionner la fenêtre → le bug reste bloquant, pas de contournement possible.
|
||||
|
||||
Hypothèse de travail : le fix précédent (445ecaf) est probablement scopé à `LayoutGrid`/`useLayout` (desktop, split/merge d'un arbre de layout). Le workspace web n'utilise pas ce chemin — il a sa propre surface (`WebAgentCell`/`LiveProjectPanel`, cf. déviation notée par DevFrontend sur le ticket #90) qui ouvre/remplace des cellules terminal autrement. Le signal de refit explicite ajouté pour split/merge ne se déclenche donc probablement jamais côté web, et `TerminalView` retombe uniquement sur son `ResizeObserver` local — insuffisant selon le timing de montage/redimensionnement de la surface web.
|
||||
|
||||
À vérifier par Architect/DevFrontend : le chemin exact d'ouverture/fermeture de cellule côté web (WebAgentCell, WebWorkspace, WebMenuSheet le cas échéant après #90), et si le mécanisme de refit du fix 445ecaf peut être réutilisé/étendu à ce chemin plutôt que dupliqué.
|
||||
|
||||
Périmètre : frontend pur, desktop non régressé, web (PC et mobile) doit refit sans action manuelle de l'utilisateur.
|
||||
@ -2,15 +2,15 @@
|
||||
id: "b0568a4e-4804-480c-ada4-bc9d6b7b40c6"
|
||||
number: 61
|
||||
title: "[UI] rafraichissement des cellule"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784095163730
|
||||
updatedAt: 1784182133142
|
||||
version: 5
|
||||
updatedAt: 1784661823324
|
||||
version: 11
|
||||
---
|
||||
Lorsque j'ajoute une nouvelle cellule ou que j'en enlève une, je suis obligé de redimentionner un coup la fenêtre ou les cellule pour rafraichir le scaling des cli
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#62"
|
||||
version: 1
|
||||
version: 2
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784095187734
|
||||
updatedAt: 1784406936659
|
||||
---
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: "dd897d74-4e88-4d34-a6fb-d7e7f330094f"
|
||||
number: 62
|
||||
title: "[Sécurité/Cohérence] Identité requester explicite pour les sessions structurées + policy des tools OpenAI-compatible"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: [{"target":"#60","kind":"relatesTo"}]
|
||||
@ -10,8 +10,8 @@ agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784095187734
|
||||
updatedAt: 1784095187734
|
||||
version: 1
|
||||
updatedAt: 1784406936659
|
||||
version: 2
|
||||
---
|
||||
Sorti de #60 (assistant IA de ticket muet) — volet distinct, touchant un port figé et la sécurité des tool calls.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#64"
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784193766706
|
||||
version: 6
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784718418070
|
||||
---
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "c8f1c5b3-7674-4c6c-bba5-bb2c5faf201b"
|
||||
number: 64
|
||||
title: "Web : créer/ajouter un projet depuis le navigateur (folder browser serveur)"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
|
||||
links: [{"target":"#13","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784183727404
|
||||
updatedAt: 1784193766706
|
||||
version: 5
|
||||
updatedAt: 1784718418070
|
||||
version: 6
|
||||
---
|
||||
Issu de la validation live de #13 (mode client/serveur web). En web, le bouton « Browse » (choisir un dossier de projet) est inopérant : `pickFolder()` renvoie `UNSUPPORTED_ON_WEB` car il s'appuie sur le dialogue natif OS de Tauri, absent du navigateur. Sélectionner un projet EXISTANT est déjà couvert par la liste (list_projects/open_project). Il manque CRÉER/AJOUTER un projet = choisir un dossier côté SERVEUR.
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
---
|
||||
issueRef: "#69"
|
||||
version: 11
|
||||
updatedBy: {"kind":"agent","agent_id":"cd0b4cf1-1bef-4fae-ade5-f0a6b49bbaf5"}
|
||||
updatedAt: 1784207736267
|
||||
version: 12
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784449061397
|
||||
---
|
||||
# Ticket #69 — Adaptibilité client téléphone (carnet de chantier)
|
||||
|
||||
|
||||
@ -2,15 +2,15 @@
|
||||
id: "7f5a40cf-837f-46f7-8c5f-653c80975396"
|
||||
number: 69
|
||||
title: "Adaptibilité client téléphone"
|
||||
status: "qa"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"cd0b4cf1-1bef-4fae-ade5-f0a6b49bbaf5"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784193391258
|
||||
updatedAt: 1784207736267
|
||||
version: 11
|
||||
updatedAt: 1784449061397
|
||||
version: 12
|
||||
---
|
||||
J'aimerais un format vertical qui rende l'app client compatible téléphone
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#7"
|
||||
version: 7
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1783329459094
|
||||
version: 8
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784718581033
|
||||
---
|
||||
|
||||
@ -2,15 +2,15 @@
|
||||
id: "9f6979fb-a68f-4660-9d57-cf69ea1daf85"
|
||||
number: 7
|
||||
title: "Gestion des session limite"
|
||||
status: "qa"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: "d8f3f37b-87ca-4509-9116-45a99bc711df"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783167495082
|
||||
updatedAt: 1783329459094
|
||||
version: 7
|
||||
updatedAt: 1784718581033
|
||||
version: 8
|
||||
---
|
||||
J'aimerais que IdeA puisse gérer les session limite des profile IA. Lorsqu'un profile IA atteint une session limite, j'aimerais que IdeA le sache, qu'il récupère l'heure et la date du reset et qu'il puisse automatiquement reprendre le travail une fois le reset fait. Lorsqu'une reprise automatique est prévue, il faut que l'utilisateur puisse cancel la reprise. Il est important de noter que les agent s'appelant entre eux avec des profile IA différents, il faut prendre en compte qu'un agent A qui appelle un agent B, peut voir l'agent B atteindre la session limite. IdeA devra savoir gérer ça correctement.
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#75"
|
||||
version: 2
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784277530792
|
||||
version: 3
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784449082160
|
||||
---
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "d54a1f20-03e6-4925-982b-923a37331749"
|
||||
number: 75
|
||||
title: "Écran d'appairage web : clavier numérique sur mobile alors que le code contient des lettres"
|
||||
status: "qa"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: [{"target":"#69","kind":"relatesTo"},{"target":"#74","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784277353320
|
||||
updatedAt: 1784277530792
|
||||
version: 2
|
||||
updatedAt: 1784449082160
|
||||
version: 3
|
||||
---
|
||||
## Symptôme (constaté live, téléphone)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#76"
|
||||
version: 1
|
||||
version: 2
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784277540034
|
||||
updatedAt: 1784287679986
|
||||
---
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: "c91f547f-96f0-4c9c-9fde-dbe7d2772dbc"
|
||||
number: 76
|
||||
title: "Appairage : la comparaison du code est sensible à la casse côté serveur"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#75","kind":"relatesTo"}]
|
||||
@ -10,8 +10,8 @@ agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784277540034
|
||||
updatedAt: 1784277540034
|
||||
version: 1
|
||||
updatedAt: 1784287679986
|
||||
version: 2
|
||||
---
|
||||
## Constat
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
---
|
||||
issueRef: "#77"
|
||||
version: 4
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784287453378
|
||||
version: 5
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784449075576
|
||||
---
|
||||
# Carnet #77 — cadrage UX + Architecture (2026-07-17)
|
||||
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "eecf77ee-dfcb-436c-aa5f-0c7033c7bdfa"
|
||||
number: 77
|
||||
title: "Appairage : appareils enregistrés persistants, révocables, et code éphémère à usage unique"
|
||||
status: "qa"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: [{"target":"#76","kind":"relatesTo"},{"target":"#75","kind":"relatesTo"},{"target":"#68","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784279214815
|
||||
updatedAt: 1784287453378
|
||||
version: 4
|
||||
updatedAt: 1784449075576
|
||||
version: 5
|
||||
---
|
||||
## Besoin utilisateur
|
||||
|
||||
|
||||
@ -1,6 +1,70 @@
|
||||
---
|
||||
issueRef: "#78"
|
||||
version: 1
|
||||
version: 3
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784284746767
|
||||
updatedAt: 1784379475877
|
||||
---
|
||||
## Décision UX — langue de l'UI IdeA
|
||||
|
||||
Règle tranchée : l'UI humaine d'IdeA est en français par défaut, de façon uniforme sur toute une surface. Une même surface ne mélange pas des libellés français et anglais pour des éléments de navigation, titres, actions, états ou textes d'aide.
|
||||
|
||||
Exceptions admises : noms propres de produits/protocoles (`IdeA`, `OpenAI`, `OpenCode`, `Claude Code`, `HTTPS`, `LAN`, `URL`, `IP/CIDR`, `API`, `CLI`, etc.), chemins techniques, commandes, variables, valeurs de configuration et termes backend affichés comme données. Ces éléments restent dans leur forme technique si la traduction réduit la reconnaissance ou la précision.
|
||||
|
||||
Conséquence : pas d'i18n à introduire pour ce ticket. #78 est un correctif frontend pur d'alignement rédactionnel de la surface Settings desktop vers le français. L'i18n multi-langue serait un chantier produit séparé, non requis ici.
|
||||
|
||||
## Libellés à renommer pour #78
|
||||
|
||||
Périmètre minimal attendu : la surface desktop `Settings` et ses sections actuellement visibles.
|
||||
|
||||
- `Settings` → `Paramètres` quand le mot est affiché à l'utilisateur dans le menu, la surface ou les renvois internes.
|
||||
- `Close Settings` → `Fermer les paramètres`.
|
||||
- `AI Profiles` → `Profils IA` dans la navigation de sections et comme titre de panneau.
|
||||
- `Configure profiles` → `Configurer les profils`.
|
||||
- `No profiles configured.` → `Aucun profil configuré.`.
|
||||
- `Delete` → `Supprimer` pour les actions de suppression de profil.
|
||||
- `delete {name}` → `supprimer {name}` pour le libellé accessible correspondant.
|
||||
- `Deployment` → `Déploiement` dans la navigation et comme titre de panneau.
|
||||
- `deployment settings` → `paramètres de déploiement` pour les libellés accessibles.
|
||||
- `Loading…` → `Chargement…`.
|
||||
- `Server` → `Serveur`.
|
||||
- `Start` → `Démarrer`.
|
||||
- `Stop` → `Arrêter`.
|
||||
- `Stopped` → `Arrêté`.
|
||||
- `Starting…` → `Démarrage…`.
|
||||
- `Running` → `En cours d'exécution`.
|
||||
- `Stopping…` → `Arrêt…`.
|
||||
- `Failed` → `Échec`.
|
||||
- `Exposure` → `Exposition réseau` ou `Accès réseau` ; préférence UX : `Accès réseau`, plus compréhensible pour l'utilisateur.
|
||||
- `exposure mode` → `mode d'accès réseau`.
|
||||
- `This computer only` → `Cet ordinateur uniquement`.
|
||||
- `For using IdeA on this desktop only. Remote devices cannot connect.` → `Pour utiliser IdeA uniquement sur cet ordinateur. Les appareils distants ne peuvent pas se connecter.`
|
||||
- `Remote access, proxy on this computer` → `Accès distant, proxy sur cet ordinateur`.
|
||||
- `Use this when your HTTPS proxy runs on the same machine as IdeA Desktop.` → `À utiliser quand le proxy HTTPS tourne sur la même machine qu'IdeA Desktop.`
|
||||
- `Remote access, proxy on another machine` → `Accès distant, proxy sur une autre machine`.
|
||||
- `Use this when the HTTPS proxy runs on another machine. IdeA will only accept traffic from that proxy.` → `À utiliser quand le proxy HTTPS tourne sur une autre machine. IdeA n'acceptera que le trafic provenant de ce proxy.`
|
||||
- `Public origin` → `Origine publique`.
|
||||
- `Example: https://idea.example.com` → `Exemple : https://idea.example.com`.
|
||||
- `LAN address to bind` → `Adresse LAN d'écoute`.
|
||||
- `The address on this machine that the proxy will connect to.` → `L'adresse de cette machine à laquelle le proxy se connectera.`
|
||||
- `Select an address…` → `Sélectionner une adresse…`.
|
||||
- `Authorized proxy IP/CIDR` → `IP/CIDR du proxy autorisé`.
|
||||
- `This is not where IdeA listens. It is the machine allowed to contact IdeA.` → `Ce n'est pas l'adresse d'écoute d'IdeA. C'est la machine autorisée à contacter IdeA.`
|
||||
- `If your proxy is not on this computer, choose this mode. Otherwise the proxy may time out without an IdeA error.` → `Si votre proxy n'est pas sur cet ordinateur, choisissez ce mode. Sinon, le proxy peut expirer sans erreur IdeA.`
|
||||
- `Proxy setup` → `Configuration du proxy`.
|
||||
- `Point your HTTPS reverse proxy at this upstream.` → `Faites pointer votre proxy inverse HTTPS vers cet upstream.`
|
||||
- `copy upstream url` → `copier l'URL upstream`.
|
||||
- `Copy` → `Copier`.
|
||||
- `Copied` → `Copié`.
|
||||
- `Complete the settings above to get the upstream URL.` → `Complétez les paramètres ci-dessus pour obtenir l'URL upstream.`
|
||||
- `Pairing` → `Appairage`.
|
||||
- `Pairing is managed in Settings → Appareils, where you can generate a code and revoke devices.` → `L'appairage est géré dans Paramètres → Appareils, où vous pouvez générer un code et révoquer des appareils.`
|
||||
|
||||
Libellés déjà conformes : `Appareils`, `Appairer`, `Code d'appairage`, `Chargement des appareils…`, `Aucun appareil appairé.`, `Révoquer...` restent en français.
|
||||
|
||||
## Critères d'acceptation UX
|
||||
|
||||
- La navigation des paramètres affiche `Profils IA`, `Déploiement`, `Appareils` sans mélange anglais/français.
|
||||
- Les titres, boutons, états et textes d'aide visibles dans ces sections sont en français, hors exceptions techniques listées plus haut.
|
||||
- Les libellés accessibles suivent la même langue que le libellé visible correspondant.
|
||||
- Aucun mécanisme i18n n'est introduit pour #78 ; il s'agit uniquement de remplacer les chaînes frontend existantes.
|
||||
- Les termes `URL`, `LAN`, `IP/CIDR`, `HTTPS` et `upstream` peuvent rester tels quels car ils désignent des objets techniques reconnus par la cible utilisateur.
|
||||
@ -2,7 +2,7 @@
|
||||
id: "8d8bc65a-50f6-4fab-bb02-486b6145205f"
|
||||
number: 78
|
||||
title: "Settings desktop : sections en langues mélangées (« Appareils » à côté de « AI Profiles », « Deployment »)"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#77","kind":"relatesTo"}]
|
||||
@ -10,8 +10,8 @@ agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784284746767
|
||||
updatedAt: 1784284746767
|
||||
version: 1
|
||||
updatedAt: 1784379475877
|
||||
version: 3
|
||||
---
|
||||
## Constat
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#79"
|
||||
version: 1
|
||||
version: 2
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784287450082
|
||||
updatedAt: 1784379476483
|
||||
---
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: "05a70dc5-fd28-4c58-8ba1-be6058ae3cfc"
|
||||
number: 79
|
||||
title: "Test flaky : PermissionsPanel « saves project defaults » échoue par intermittence"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: []
|
||||
@ -10,8 +10,8 @@ agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784287450082
|
||||
updatedAt: 1784287450082
|
||||
version: 1
|
||||
updatedAt: 1784379476483
|
||||
version: 2
|
||||
---
|
||||
## Constat
|
||||
|
||||
|
||||
6
.ideai/tickets/80/carnet.md
Normal file
6
.ideai/tickets/80/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#80"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784287697560
|
||||
---
|
||||
56
.ideai/tickets/80/issue.md
Normal file
56
.ideai/tickets/80/issue.md
Normal file
@ -0,0 +1,56 @@
|
||||
---
|
||||
id: "f36ada98-0ca1-41e6-94c1-24eb81731fee"
|
||||
number: 80
|
||||
title: "L'environnement de QA ne peut pas ouvrir de socket — il ne peut pas valider les features réseau"
|
||||
status: "open"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: [{"target":"#77","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784287697560
|
||||
updatedAt: 1784287697560
|
||||
version: 1
|
||||
---
|
||||
## Constat
|
||||
|
||||
Relevé pendant #77, confirmé indépendamment par Main et par Git.
|
||||
|
||||
L'agent QA exécute ses tests dans un environnement dont le sandbox **interdit les binds réseau** :
|
||||
|
||||
```text
|
||||
failed to bind 127.0.0.1:0: Operation not permitted (os error 1)
|
||||
bind: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }
|
||||
Failed building the Runtime: Os { code: 24, kind: Uncategorized, message: "Too many open files" }
|
||||
```
|
||||
|
||||
Sur #77, QA a rapporté :
|
||||
|
||||
- `cargo test -p web-server` → 73 passed / **6 failed** (2 binds + 4 tests WebSocket en cascade),
|
||||
- `cargo test -p domain -p application -p infrastructure -p web-server` → 275 passed / **10 failed** (tous `infrastructure::session::openai_compat`).
|
||||
|
||||
**Les mêmes suites passent intégralement hors de ce sandbox** : 79/79 sur `web-server`, exit 0 sur la commande combinée (vérifié par Main, puis rejoué par Git dans son propre environnement). DevBackend observe le même artefact de son côté.
|
||||
|
||||
## Pourquoi c'est un problème et pas une curiosité
|
||||
|
||||
Le cœur de #77 était précisément **la révocation de WebSockets vivants** — un durcissement de sécurité non négociable. QA ne pouvait structurellement pas le valider de bout en bout : l'environnement qui doit prouver que la feature marche est celui qui ne peut pas l'exécuter.
|
||||
|
||||
Le coût réel se paie deux fois :
|
||||
|
||||
1. **Faux rouges** : QA a bloqué son verdict sur des échecs qui n'existaient pas, et il a fallu du temps à Main pour établir que c'était l'environnement. C'était le bon réflexe de sa part — le problème n'est pas QA, c'est son bac à sable.
|
||||
2. **Faux verts potentiels**, plus grave : un test réseau qui ne s'exécute jamais chez QA ne peut pas y échouer non plus. La prochaine feature réseau rejouera la scène, et rien ne garantit qu'on la relira aussi attentivement.
|
||||
|
||||
## Attendu
|
||||
|
||||
Que l'environnement de QA puisse ouvrir des sockets sur la loopback, ou à défaut que la limitation soit **explicite et connue** (QA sait ce qu'il ne peut pas valider, et le dit dans son verdict au lieu de le rapporter en échec).
|
||||
|
||||
À regarder aussi : le `Too many open files` (`code: 24`), qui suggère une limite de descripteurs de fichiers trop basse en plus de la restriction réseau.
|
||||
|
||||
## Note
|
||||
|
||||
Voir la mémoire `permissions-sandbox-system-state` pour l'état du système de permissions/sandbox et le risque résiduel déjà documenté.
|
||||
|
||||
## Périmètre
|
||||
|
||||
Infrastructure d'agents / configuration de sandbox. Pas de code applicatif.
|
||||
223
.ideai/tickets/81/carnet.md
Normal file
223
.ideai/tickets/81/carnet.md
Normal file
@ -0,0 +1,223 @@
|
||||
---
|
||||
issueRef: "#81"
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784565918809
|
||||
---
|
||||
# Cadrage — ticket #81
|
||||
|
||||
## Objectif
|
||||
|
||||
Permettre aux agents IdeA d'administrer les templates d'agents via MCP, sans surface humaine nouvelle : créer, éditer et supprimer des templates globaux IdeA depuis des tools `idea_*`.
|
||||
|
||||
UX n'est pas concerné pour ce ticket : il n'y a pas d'écran, de workflow humain ni de libellés UI à concevoir. La seule surface est le catalogue MCP exposé aux agents.
|
||||
|
||||
## État réel du système templates
|
||||
|
||||
Surfaces inspectées :
|
||||
|
||||
- Domaine : `crates/domain/src/template.rs`
|
||||
- Port : `TemplateStore` dans `crates/domain/src/ports.rs`
|
||||
- Use cases : `crates/application/src/template/usecases.rs`
|
||||
- Store : `crates/infrastructure/src/store/template.rs`
|
||||
- Commands Tauri : `crates/app-tauri/src/commands.rs`
|
||||
- DTO : `crates/backend/src/dto.rs` / `crates/app-tauri/src/dto.rs`
|
||||
- Frontend existant : `frontend/src/features/templates/*`, `frontend/src/adapters/template.ts`
|
||||
- MCP + permissions #82 : `crates/infrastructure/src/orchestrator/mcp/tools.rs`, `server.rs`, `backend/src/openai_tools.rs`, `app-tauri/src/openai_tools.rs`
|
||||
|
||||
Templates existants :
|
||||
|
||||
```text
|
||||
AgentTemplate {
|
||||
id: TemplateId,
|
||||
name: String,
|
||||
content_md: MarkdownDoc,
|
||||
version: TemplateVersion,
|
||||
default_profile_id: ProfileId,
|
||||
}
|
||||
```
|
||||
|
||||
Invariants existants :
|
||||
|
||||
- `name` non vide ;
|
||||
- `version` démarre à `1` ;
|
||||
- `version` est bumpée par `AgentTemplate::with_updated_content`, donc par changement de contenu Markdown ;
|
||||
- `default_profile_id` sert aux futurs agents créés depuis le template.
|
||||
|
||||
Stockage existant : global app-data, pas projet :
|
||||
|
||||
```text
|
||||
<app_data_dir>/templates/
|
||||
├── index.json
|
||||
└── md/<template-id>.md
|
||||
```
|
||||
|
||||
`index.json` porte les métadonnées (`id`, `name`, `version`, `contentHash`, `defaultProfileId`) ; le Markdown vit dans `md/<id>.md`. Le store est déjà derrière `TemplateStore`, donc Tauri-agnostique côté infra.
|
||||
|
||||
## Synchronisation template → agents
|
||||
|
||||
Les agents créés depuis un template copient le `content_md` dans leur propre contexte `.md` projet et gardent dans le manifeste :
|
||||
|
||||
- `template_id`
|
||||
- `synchronized`
|
||||
- `synced_template_version`
|
||||
|
||||
`UpdateTemplate` actuel met à jour le template global, bump la version et publie `DomainEvent::TemplateUpdated`. Il ne modifie pas directement les agents existants.
|
||||
|
||||
`DetectAgentDrift` compare `template.version > synced_template_version` pour les agents `synchronized == true` et publie `AgentDriftDetected`.
|
||||
|
||||
`SyncAgentWithTemplate` est l'opération explicite qui remplace le `.md` de l'agent synchronisé par le contenu courant du template et met à jour `synced_template_version`.
|
||||
|
||||
Conclusion : si un agent édite un template via MCP, les agents synchronisés qui l'utilisent ne sont pas impactés immédiatement. Ils deviennent en drift jusqu'à appel explicite de sync. Au lancement suivant, ils relisent leur `.md` agent existant, pas automatiquement le template global. Ce comportement doit rester tel quel pour #81, sauf arbitrage produit séparé.
|
||||
|
||||
## Tools MCP à ajouter
|
||||
|
||||
Ajouter une surface templates explicite ; `idea_skill_read`, `idea_context_read` et les tools existants ne couvrent pas les templates. Les templates ne sont ni des skills ni des contextes agent.
|
||||
|
||||
Proposition de catalogue :
|
||||
|
||||
### Lecture, autorisée par défaut (#82)
|
||||
|
||||
- `idea_template_list`
|
||||
- Rôle : lister les templates globaux disponibles.
|
||||
- Payload conseillé : templates complets ou résumés. Pour l'ergonomie agent, retour complet acceptable au premier lot (`id`, `name`, `contentMd`, `version`, `defaultProfileId`) car `ListTemplates` renvoie déjà les entités complètes.
|
||||
|
||||
- `idea_template_read`
|
||||
- Rôle : lire un template par id.
|
||||
- Input : `{ "templateId": "..." }`
|
||||
- Retour : même DTO qu'un template Tauri.
|
||||
- Nécessite un thin use case `ReadTemplate` ou un provider qui appelle `TemplateStore::get` via un use case applicatif dédié.
|
||||
|
||||
### Écriture/action, refusée par défaut (#82)
|
||||
|
||||
- `idea_template_create`
|
||||
- Input : `{ name, content, defaultProfileId }`
|
||||
- Réutilise `CreateTemplate`.
|
||||
- Retour : template créé.
|
||||
|
||||
- `idea_template_update`
|
||||
- Input minimal aligné existant : `{ templateId, content }`.
|
||||
- Réutilise `UpdateTemplate` actuel, qui met à jour le contenu et bump la version.
|
||||
- Extension recommandée si on veut couvrir pleinement "éditer" : accepter aussi `name?` et `defaultProfileId?`, avec bump de version seulement si `content` change. Cela nécessite d'étendre le domaine/use case car aujourd'hui l'UI elle-même ne persiste pas le changement de nom/profil en mode edit.
|
||||
- Option de sûreté à arbitrer : ajouter `expectedVersion` pour éviter les écrasements concurrents entre agents. Le store/use case Tauri actuel n'a pas d'optimistic concurrency sur templates, donc ce serait une extension de contrat, pas une simple exposition MCP.
|
||||
|
||||
- `idea_template_delete`
|
||||
- Input : `{ templateId }`
|
||||
- Réutilise `DeleteTemplate`.
|
||||
- Effet existant : supprime de l'index global ; le fichier Markdown orphelin peut rester sur disque car le port FS n'a pas de delete. Les agents créés depuis ce template gardent leur `.md`; drift detection ignore le template absent.
|
||||
|
||||
## Intégration obligatoire avec #82
|
||||
|
||||
#82 a introduit la classification canonique dans `crates/infrastructure/src/orchestrator/mcp/tools.rs` :
|
||||
|
||||
- `READ_ONLY_TOOLS`
|
||||
- `WRITE_ACTION_TOOLS`
|
||||
- `tool_access`
|
||||
- test garde-fou `catalogue_tools_have_explicit_read_or_write_access`
|
||||
|
||||
Tout tool ajouté au catalogue doit être classé immédiatement, sinon le test doit échouer.
|
||||
|
||||
Classification #81 attendue :
|
||||
|
||||
```text
|
||||
READ_ONLY_TOOLS += [
|
||||
"idea_template_list",
|
||||
"idea_template_read",
|
||||
]
|
||||
|
||||
WRITE_ACTION_TOOLS += [
|
||||
"idea_template_create",
|
||||
"idea_template_update",
|
||||
"idea_template_delete",
|
||||
]
|
||||
```
|
||||
|
||||
Comportement policy attendu :
|
||||
|
||||
- agent sans override #82 : peut lire/lister les templates, ne peut pas créer/éditer/supprimer ;
|
||||
- agent avec override incluant un ou plusieurs tools `idea_template_*` d'écriture : peut seulement appeler ceux explicitement autorisés ;
|
||||
- le refus doit arriver avant effet applicatif, sur le serveur MCP stdio et sur l'invoker OpenAI-compatible.
|
||||
|
||||
Ce comportement est cohérent avec la demande #82 : par défaut seuls les tools de lecture sont autorisés. Pas d'arbitrage produit nécessaire sauf si l'utilisateur veut que certains agents aient une permission template-write préconfigurée par défaut.
|
||||
|
||||
## Point d'implémentation recommandé
|
||||
|
||||
Ne pas ajouter ces opérations au `OrchestratorCommand` sauf nécessité. Les templates sont une famille CRUD applicative, comme les tickets, pas un protocole d'orchestration inter-agent. Le pattern le plus local est donc de créer un provider MCP dédié, parallèle à `TicketToolProvider` :
|
||||
|
||||
- `crates/infrastructure/src/orchestrator/mcp/templates.rs`
|
||||
- `TemplateToolProvider`
|
||||
- `TemplateToolError`
|
||||
- `is_template_tool(name)`
|
||||
- `catalogue()` des tools templates
|
||||
|
||||
- `crates/infrastructure/src/orchestrator/mcp/tools.rs`
|
||||
- étendre `catalogue()` avec `templates::catalogue()` ;
|
||||
- ajouter `is_template_tool` ;
|
||||
- ajouter les 5 tools dans les classifications #82 ;
|
||||
- si besoin, faire retourner `tool_returns_reply` pour les read/create/update/list/delete selon convention (delete peut retourner un ACK JSON).
|
||||
|
||||
- `crates/infrastructure/src/orchestrator/mcp/server.rs`
|
||||
- ajouter `template_tools: Option<Arc<dyn TemplateToolProvider>>` ;
|
||||
- dans `tools_call`, après enforcement #82 et avant `map_tool_call`, router `is_template_tool` vers le provider, comme les tickets ;
|
||||
- garder l'enforcement durable/éphémère avant provider.
|
||||
|
||||
- Composition root backend/app-tauri
|
||||
- créer `LateBoundTemplateToolProvider` si besoin pour casser les cycles comme `LateBoundTicketToolProvider` ;
|
||||
- binder un `AppTemplateToolProvider` construit avec `create_template`, `list_templates`, `update_template`, `delete_template` et le nouveau `read_template` si ajouté ;
|
||||
- injecter ce provider dans `McpServer::new(...).with_template_tools(...)` ;
|
||||
- injecter aussi dans `AppOpenAiToolInvoker` pour parité OpenAI-compatible.
|
||||
|
||||
Alternative possible : ajouter des variants `OrchestratorCommand::Template*` et mapper les tools via `map_tool_call`. Je ne la recommande pas en premier choix : cela gonfle l'orchestrateur avec un CRUD global qui n'est pas une coordination agent-agent, alors que le précédent ticket système a déjà accepté le pattern provider pour les tickets.
|
||||
|
||||
## Lots proposés
|
||||
|
||||
### Lot B1 — Catalogue MCP + classification #82 + provider squelette
|
||||
|
||||
- Ajouter `templates.rs` côté MCP infra.
|
||||
- Ajouter les 5 tool defs.
|
||||
- Classer immédiatement `idea_template_list/read` en lecture et `create/update/delete` en écriture/action.
|
||||
- Tests : garde-fou catalogue/classification vert ; `tools/list` expose les templates selon policy #82.
|
||||
|
||||
### Lot B2 — Use cases/DTO/provider templates
|
||||
|
||||
- Ajouter `ReadTemplate` use case fin si on garde `idea_template_read`.
|
||||
- Implémenter `AppTemplateToolProvider` avec les use cases existants.
|
||||
- Mapper erreurs : `notFound`, `invalid`, `store`, `internal`.
|
||||
- Tests provider : create/list/read/update/delete sur fakes, version bump sur update contenu.
|
||||
|
||||
### Lot B3 — Enforcement policy + OpenAI-compatible parity
|
||||
|
||||
- Vérifier MCP stdio : un agent default read-only peut `idea_template_list/read`, mais `idea_template_create/update/delete` est refusé avant provider.
|
||||
- Vérifier override #82 : autoriser seulement `idea_template_update` n'autorise pas create/delete.
|
||||
- Répliquer le dispatch provider dans `AppOpenAiToolInvoker`, avec même policy durable avant effet.
|
||||
|
||||
### Lot B4 — Édition complète optionnelle
|
||||
|
||||
À faire seulement si le produit veut que "éditer" couvre autre chose que le contenu Markdown :
|
||||
|
||||
- étendre `UpdateTemplateInput` avec `name?`, `defaultProfileId?`, `content?` ;
|
||||
- préserver l'invariant : bump version uniquement quand `content_md` change ;
|
||||
- décider si changement de `defaultProfileId` doit avoir un effet sur agents existants (recommandation : non, seulement futurs `CreateAgentFromTemplate`) ;
|
||||
- ajouter éventuellement `expectedVersion` pour update/delete, avec erreur de conflit.
|
||||
|
||||
## Frontières
|
||||
|
||||
Frontend/UX : hors périmètre. Aucune surface humaine nouvelle.
|
||||
|
||||
Domaine templates : réutiliser `AgentTemplate`, `TemplateVersion`, `TemplateStore`. Extension domaine seulement si B4 est retenu.
|
||||
|
||||
Projet `.ideai/agents.json` : hors périmètre pour CRUD templates. Les agents créés depuis template et leur sync existante restent inchangés.
|
||||
|
||||
Synchronisation automatique : hors périmètre. #81 ne doit pas auto-écraser les `.md` des agents synchronisés après update template ; laisser `DetectAgentDrift`/`SyncAgentWithTemplate` piloter cela.
|
||||
|
||||
Permissions #82 : dans le périmètre obligatoire. Aucun tool template ne doit entrer dans le catalogue sans classification explicite et sans enforcement identique MCP stdio/OpenAI-compatible.
|
||||
|
||||
## Critères d'acceptation
|
||||
|
||||
- Les agents voient les tools template dans le catalogue MCP selon leur policy #82.
|
||||
- Les tools lecture template sont accessibles à un agent sans override.
|
||||
- Les tools create/update/delete sont refusés à un agent sans override, avant mutation.
|
||||
- Une policy agent qui autorise un write template permet uniquement ce write.
|
||||
- `idea_template_update` bump la version quand le contenu change et publie `TemplateUpdated` via le use case existant.
|
||||
- Les agents synchronisés au template passent en drift, mais ne sont pas modifiés tant qu'un sync explicite n'est pas demandé.
|
||||
- Les chemins MCP stdio et OpenAI-compatible ont la même sémantique et les mêmes refus.
|
||||
16
.ideai/tickets/81/issue.md
Normal file
16
.ideai/tickets/81/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "e9c936c5-4540-42ab-a482-c64e6f9b9f8c"
|
||||
number: 81
|
||||
title: "MCP d'edition de templates"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784296848609
|
||||
updatedAt: 1784565918809
|
||||
version: 5
|
||||
---
|
||||
J'aimerais que les agents aient la possibilité de créer, supprimer et editer des templates d'agent IdeA
|
||||
383
.ideai/tickets/82/carnet.md
Normal file
383
.ideai/tickets/82/carnet.md
Normal file
@ -0,0 +1,383 @@
|
||||
---
|
||||
issueRef: "#82"
|
||||
version: 6
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784410760329
|
||||
---
|
||||
# Cadrage — ticket #82
|
||||
|
||||
## Objectif produit
|
||||
|
||||
Donner à chaque agent IdeA une policy explicite d'utilisation des tools MCP IdeA. Par défaut, un agent ne doit pouvoir appeler que les tools de lecture. L'utilisateur doit ensuite pouvoir accorder ou retirer des permissions agent par agent. La surface d'édition est laissée à UX/frontend et n'est pas dans le lot backend de base.
|
||||
|
||||
## État réel du code
|
||||
|
||||
Surfaces inspectées :
|
||||
|
||||
- `crates/infrastructure/src/orchestrator/mcp/tools.rs` : catalogue MCP actuel, 25 tools exposés.
|
||||
- `crates/infrastructure/src/orchestrator/mcp/server.rs` : `tools/call` applique déjà une policy optionnelle avant dispatch (`tool_policies.get(requester)` puis `enforce_tool_policy`).
|
||||
- `crates/domain/src/agent_tool_policy.rs` : modèle existant `AgentToolPolicy { allow, bound_issue, deny_others }`.
|
||||
- `crates/infrastructure/src/orchestrator/mcp/policy.rs` : `ToolPolicyRegistry` in-memory par requester.
|
||||
- `crates/application/src/ticket_assistant.rs` : l'assistant de ticket pose une allowlist éphémère bornée à un ticket.
|
||||
- `crates/backend/src/openai_tools.rs` : l'invoker OpenAI-compatible expose le même catalogue et dispatch via `OrchestratorService`, mais ne consulte pas la policy avant appel.
|
||||
- `crates/domain/src/permission.rs` + `crates/infrastructure/src/store/permission.rs` : système permissions/sandbox existant, persisté dans `.ideai/permissions.json`, orienté fichiers/commandes/sandbox/projections CLI.
|
||||
|
||||
Conclusion : il existe déjà un point d'application côté MCP stdio, mais pas de policy durable par agent et pas de default-deny pour les tools d'écriture. Le modèle existant est réutilisable comme base conceptuelle, mais il est actuellement trop spécialisé pour les assistants de ticket et stocké en mémoire.
|
||||
|
||||
## Classification lecture / écriture des tools MCP IdeA
|
||||
|
||||
Lecture autorisée par défaut :
|
||||
|
||||
- `idea_list_agents`
|
||||
- `idea_context_read`
|
||||
- `idea_memory_read`
|
||||
- `idea_skill_read`
|
||||
- `idea_workstate_read`
|
||||
- `idea_ticket_read`
|
||||
- `idea_ticket_list`
|
||||
- `idea_ticket_read_carnet`
|
||||
- `idea_sprint_list`
|
||||
|
||||
Écriture / action / exécution à refuser par défaut :
|
||||
|
||||
- `idea_ask_agent` : délégation active, peut lancer/réattacher une cible et produire des effets indirects.
|
||||
- `idea_run_in_background` : exécute une commande et crée une tâche.
|
||||
- `idea_launch_agent` : lance/attache une session agent.
|
||||
- `idea_stop_agent` : tue une session.
|
||||
- `idea_update_context` : écrit le contexte d'un agent.
|
||||
- `idea_context_propose` : écrit/propose du contexte ; avec `target` agent, c'est une écriture directe du `.md` agent.
|
||||
- `idea_memory_write` : écrit la mémoire projet.
|
||||
- `idea_workstate_set` : écrit la ligne live-state du requester.
|
||||
- `idea_create_skill` : crée un skill.
|
||||
- `idea_ticket_create`
|
||||
- `idea_ticket_update`
|
||||
- `idea_ticket_update_status`
|
||||
- `idea_ticket_update_priority`
|
||||
- `idea_ticket_update_carnet`
|
||||
- `idea_ticket_link`
|
||||
- `idea_ticket_unlink`
|
||||
|
||||
Règle de maintenance : la classification doit vivre à côté du catalogue MCP, pas dans l'UI, pour que tout nouveau tool doive choisir explicitement `read` ou `write/action`.
|
||||
|
||||
## Cause racine / besoin architectural
|
||||
|
||||
Aujourd'hui, l'absence de policy pour un requester signifie "autorisé" sur le serveur MCP, parce que l'enforcement ne s'exécute que si `registry.get(requester)` retourne une policy. C'était acceptable pour l'ancien cas ticket-assistant, où la policy éphémère était posée avant ouverture. Ce n'est pas acceptable pour des agents IdeA généraux : l'absence de configuration doit se résoudre en policy par défaut lecture seule.
|
||||
|
||||
Le besoin n'est pas couvert par Landlock/sandbox : Landlock protège le système de fichiers et l'exécution de commandes au niveau OS/PTY/structured process. Les tools MCP IdeA sont des capabilities applicatives internes (`memory`, `context`, `tickets`, `delegation`, `workstate`, etc.) qui doivent être refusées avant dispatch applicatif. Un sandbox peut empêcher certains effets externes, mais ne sait pas qu'un appel `idea_memory_write` ou `idea_ask_agent` est interdit.
|
||||
|
||||
## Modèle de données proposé
|
||||
|
||||
Créer une policy MCP durable par projet, sparse par agent, distincte du modèle `PermissionSet` fichiers/commandes.
|
||||
|
||||
Option recommandée : nouveau document `.ideai/mcp-tool-permissions.json` plutôt qu'étendre `.ideai/permissions.json`.
|
||||
|
||||
Raison : `.ideai/permissions.json` a un sens précis et déjà chargé : permissions de fichiers/commandes + projection CLI + compilation Landlock. Mélanger les tools MCP avec ces capabilities risquerait de rendre confus un modèle qui n'a pas le même point d'application ni la même sémantique de sandbox.
|
||||
|
||||
Schéma conceptuel :
|
||||
|
||||
```text
|
||||
ProjectMcpToolPermissions {
|
||||
version: 1,
|
||||
projectDefault: McpToolPolicy? // absent => default lecture seule canonique
|
||||
agents: Vec<AgentMcpToolPolicyOverride>
|
||||
}
|
||||
|
||||
AgentMcpToolPolicyOverride {
|
||||
agentId: AgentId,
|
||||
policy: McpToolPolicy
|
||||
}
|
||||
|
||||
McpToolPolicy {
|
||||
allowedTools: Vec<String>, // noms exacts du catalogue MCP
|
||||
deniedTools: Vec<String>?, // optionnel si UX veut exprimer un retrait explicite
|
||||
mode: AllowListed | ReadOnlyPlus // à arbitrer avec UX, mais backend peut commencer simple
|
||||
}
|
||||
```
|
||||
|
||||
Simplification backend acceptable pour un premier lot : stocker directement `allowedTools` par agent, et résoudre ainsi :
|
||||
|
||||
1. si override agent existe, il remplace le défaut projet ;
|
||||
2. sinon si défaut projet existe, l'utiliser ;
|
||||
3. sinon utiliser `READ_ONLY_TOOLS` canonique ;
|
||||
4. refuser tout tool absent de l'allowlist effective.
|
||||
|
||||
Le modèle doit valider les noms contre le catalogue connu ou au minimum rejeter les chaînes vides/dupliquées. Les tools inconnus ne doivent pas devenir des permissions latentes silencieuses.
|
||||
|
||||
## Point d'application de la policy
|
||||
|
||||
Point principal : `McpServer::tools_call`, avant tout dispatch, exactement à l'endroit où l'enforcement éphémère existe déjà aujourd'hui.
|
||||
|
||||
À faire :
|
||||
|
||||
- remplacer/compléter `registry.get(requester)` par une résolution effective : requester handshake -> `AgentId` -> policy durable projet -> fallback lecture seule ;
|
||||
- si le requester est vide/legacy `"mcp"`, appliquer aussi le fallback lecture seule, ou refuser les écritures fail-closed ;
|
||||
- refuser via erreur MCP lisible (`isError`/JsonRpcError cohérent) avant `TicketToolProvider` et avant `OrchestratorService::dispatch` ;
|
||||
- publier éventuellement `OrchestratorRequestProcessed { ok: false }` pour garder une trace UI/diagnostic des refus, sans exécuter le tool.
|
||||
|
||||
Point secondaire obligatoire pour éviter le contournement : `AppOpenAiToolInvoker` doit appliquer la même résolution avant `map_tool_call`/`dispatch`. C'est le chevauchement direct avec #62 : #62 demande déjà la parité OpenAI-compatible + identité requester explicite. #82 dépend fonctionnellement de ce point ; sinon un agent utilisant un profil OpenAI-compatible pourrait contourner la policy MCP stdio.
|
||||
|
||||
## Relation avec #62 et #60
|
||||
|
||||
#60 a fermé le bug de conversation muette et a sorti #62 comme dette sécurité adjacente.
|
||||
|
||||
#62 reste pertinent et doit être traité avant ou dans le premier lot de #82 :
|
||||
|
||||
- passer une identité requester explicite aux sessions structurées ;
|
||||
- `LaunchAgent` doit utiliser l'agent id comme requester, pas une dérivation fragile du run dir ;
|
||||
- l'assistant de ticket doit garder `ticket-assistant:<project>:<issue>` ;
|
||||
- l'invoker OpenAI-compatible doit consulter la même policy que le serveur MCP stdio.
|
||||
|
||||
#82 généralise ensuite la policy à tous les agents déclarés, avec un défaut lecture seule durable. Les policies éphémères du ticket-assistant peuvent rester comme cas spécial plus restrictif/borné à un ticket, mais elles ne doivent pas masquer la policy globale agent si un assistant normal est lancé.
|
||||
|
||||
## Frontières avec le système permissions/sandbox existant
|
||||
|
||||
À ne pas faire dans #82 :
|
||||
|
||||
- ne pas modifier la compilation Landlock ;
|
||||
- ne pas ajouter de `Capability::McpTool` dans le modèle fichiers/commandes sans arbitrage Architecture ;
|
||||
- ne pas projeter cette policy dans les settings Claude/Codex ;
|
||||
- ne pas compter sur les prompts natifs des CLIs pour autoriser/refuser les tools IdeA.
|
||||
|
||||
Le système existant reste responsable de ce que le process agent peut faire au niveau OS. #82 est une policy applicative IdeA, appliquée côté serveur/bridge avant use case.
|
||||
|
||||
## Découpage recommandé
|
||||
|
||||
### Lot B1 — Domaine + catalogue + store durable
|
||||
|
||||
- Ajouter un modèle pur `McpToolPermissionPolicy` / `ProjectMcpToolPermissions` avec fallback lecture seule.
|
||||
- Déplacer la classification read/write dans une source backend canonique proche du catalogue MCP.
|
||||
- Ajouter un port `McpToolPermissionStore` et un store FS sous `.ideai/mcp-tool-permissions.json`.
|
||||
- Tests domaine : fallback lecture seule, override agent, refus tool inconnu, nouveau catalogue sans classification explicite détecté par test.
|
||||
|
||||
### Lot B2 — Enforcement MCP stdio
|
||||
|
||||
- Injecter le resolver/store dans `McpServer` ou dans un service de policy appelé par `tools_call`.
|
||||
- Appliquer la policy avant ticket provider et avant orchestrator dispatch.
|
||||
- Remplacer le comportement "pas de policy => tout passe" par "pas de policy => lecture seule" pour les agents généraux.
|
||||
- Garder la policy ticket-assistant bornée au ticket comme restriction éphémère additionnelle ou cas de requester dédié.
|
||||
- Tests : agent sans override peut `idea_memory_read`/`idea_ticket_list`, mais pas `idea_memory_write`, `idea_ask_agent`, `idea_ticket_update_carnet`, `idea_run_in_background`.
|
||||
|
||||
### Lot B3 — Parité OpenAI-compatible / dépendance #62
|
||||
|
||||
- Faire passer l'identité requester explicite jusqu'à tous les appels tools structurés.
|
||||
- Brancher la même policy resolver dans `AppOpenAiToolInvoker`.
|
||||
- Tests : un profil OpenAI-compatible refusé sur `idea_memory_write` l'est de la même manière que via MCP stdio ; un tool lecture passe.
|
||||
|
||||
### Lot B4 — API backend pour future UI
|
||||
|
||||
- Ajouter des use cases read/update de permissions MCP par agent/projet.
|
||||
- Ajouter DTO/commands Tauri ou endpoints web selon la surface existante.
|
||||
- Ne pas concevoir l'UI ici ; seulement exposer un contrat stable à UX/DevFrontend.
|
||||
|
||||
### Lot UX/F — séparé
|
||||
|
||||
- UX décide la surface de modification agent par agent.
|
||||
- Frontend consomme les APIs B4.
|
||||
|
||||
## Rétrocompatibilité
|
||||
|
||||
Agents déjà déclarés : aucun champ à ajouter dans `agents.json`. En absence de document `.ideai/mcp-tool-permissions.json` ou d'override agent, ils deviennent lecture seule pour les tools MCP IdeA. C'est un changement volontaire demandé par l'utilisateur.
|
||||
|
||||
Attention migration : des workflows existants qui s'appuient sur `idea_ask_agent`, `idea_memory_write`, `idea_context_propose`, `idea_workstate_set` ou `idea_ticket_update_carnet` devront être explicitement autorisés agent par agent après livraison. Pour limiter la casse pendant le développement, prévoir un message de refus clair indiquant le tool refusé et l'agent/requester concerné.
|
||||
|
||||
## Critères d'acceptation backend
|
||||
|
||||
- Un agent sans override ne peut appeler que les tools listés en lecture.
|
||||
- Les tools d'écriture/action sont refusés avant effet applicatif.
|
||||
- Une allowlist agent permet explicitement un tool d'écriture choisi.
|
||||
- Un retrait/absence d'allowlist retire effectivement le droit au prochain appel, sans relancer l'application si possible.
|
||||
- Le serveur MCP stdio et l'invoker OpenAI-compatible appliquent la même décision.
|
||||
- Les permissions filesystem/commandes et le sandbox Landlock restent inchangés.
|
||||
|
||||
## Conception UX/F — surface permissions MCP par agent
|
||||
|
||||
### Décision de placement
|
||||
|
||||
La modification des permissions MCP IdeA vit dans le panneau projet `Permissions`, pas dans `Settings` et pas uniquement dans la fiche d'un agent.
|
||||
|
||||
Raison UX : ce réglage est une matrice de capacités applicatives par agent dans le projet courant. Il doit être consultable et comparable au même endroit que les permissions/sandbox existantes, sans polluer les paramètres globaux desktop (`Settings`) ni cacher un droit critique dans une fiche agent isolée. La fiche/liste d'agent peut afficher un raccourci ou un badge, mais l'édition canonique reste `Permissions`.
|
||||
|
||||
Le panneau `Permissions` devient une surface à deux onglets internes :
|
||||
|
||||
- `Système` : permissions fichiers/commandes/sandbox existantes.
|
||||
- `Tools MCP IdeA` : nouveau réglage #82.
|
||||
|
||||
La colonne de gauche reste le sélecteur de cible : `Défaut projet`, puis les agents. Le panneau de droite change selon l'onglet sélectionné.
|
||||
|
||||
### Layout attendu
|
||||
|
||||
```text
|
||||
Permissions
|
||||
[ Système ] [ Tools MCP IdeA ] [Actualiser]
|
||||
|
||||
┌──────────────────────────────┬──────────────────────────────────────────────┐
|
||||
│ Défaut projet │ Tools MCP IdeA — DevFrontend │
|
||||
│ Lecture seule │ Hérite du défaut projet │
|
||||
│ │ [Utiliser le défaut projet v] │
|
||||
│ Agents │ │
|
||||
│ Main Hérité │ Résumé effectif │
|
||||
│ Architect Override │ 9 lecture autorisés · 2 écriture autorisés │
|
||||
│ DevFrontend Override │ │
|
||||
│ QA Hérité │ Accord rapide │
|
||||
│ Git Hérité │ [ ] Déléguer à un agent │
|
||||
│ │ [x] Modifier les tickets │
|
||||
│ │ [ ] Écrire la mémoire │
|
||||
│ │ │
|
||||
│ │ Détail des tools │
|
||||
│ │ ▾ Lecture, autorisés par défaut (9) │
|
||||
│ │ ✓ idea_ticket_read │
|
||||
│ │ ✓ idea_context_read │
|
||||
│ │ ▾ Écriture et actions (16) │
|
||||
│ │ Tickets │
|
||||
│ │ [x] idea_ticket_update_carnet │
|
||||
│ │ [ ] idea_ticket_update_status │
|
||||
│ │ Agents │
|
||||
│ │ [ ] idea_ask_agent │
|
||||
│ │ [ ] idea_launch_agent │
|
||||
│ │ [Réinitialiser l'override] [Enregistrer] │
|
||||
└──────────────────────────────┴──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Sur desktop large : deux colonnes comme le panneau permissions actuel, avec la liste des cibles à gauche et l'éditeur à droite. Sur largeur contrainte : la cible sélectionnée reste au-dessus de l'éditeur, puis les groupes de tools s'empilent ; les actions restent en bas du panneau, non flottantes.
|
||||
|
||||
### Modèle mental affiché
|
||||
|
||||
L'utilisateur ne manipule pas une liste brute de 25 cases. Il voit trois niveaux :
|
||||
|
||||
1. Cible : `Défaut projet` ou un agent précis.
|
||||
2. Mode : `Utiliser le défaut projet` ou `Override personnalisé`.
|
||||
3. Capabilités groupées : lecture, tickets, agents, contexte, mémoire, workstate, skills, exécution.
|
||||
|
||||
Pour un agent sans override, l'éditeur est en lecture de l'état hérité jusqu'à ce que l'utilisateur choisisse `Créer un override`. Les contrôles hérités sont visibles mais atténués, avec la mention `Hérité du défaut projet`. Cela permet de comprendre l'état effectif avant de modifier.
|
||||
|
||||
Pour un agent avec override, le badge de la colonne gauche affiche `Override`. Le panneau de droite affiche `Override personnalisé` et un bouton `Réinitialiser l'override` qui remet l'agent sur le défaut projet.
|
||||
|
||||
### Présentation du catalogue
|
||||
|
||||
Les tools sont groupés par domaine fonctionnel, à partir des métadonnées du catalogue retourné par `get_mcp_tool_permissions` si disponibles côté backend, sinon par mapping frontend local strictement présentationnel. La classification `read`/`write` reste backend-canonique.
|
||||
|
||||
Groupes UX recommandés :
|
||||
|
||||
- `Lecture projet` : `idea_list_agents`, `idea_context_read`, `idea_memory_read`, `idea_skill_read`, `idea_workstate_read`.
|
||||
- `Lecture tickets` : `idea_ticket_read`, `idea_ticket_list`, `idea_ticket_read_carnet`, `idea_sprint_list`.
|
||||
- `Délégation agents` : `idea_ask_agent`, `idea_launch_agent`, `idea_stop_agent`.
|
||||
- `Contexte et mémoire` : `idea_update_context`, `idea_context_propose`, `idea_memory_write`.
|
||||
- `Tickets` : `idea_ticket_create`, `idea_ticket_update`, `idea_ticket_update_status`, `idea_ticket_update_priority`, `idea_ticket_update_carnet`, `idea_ticket_link`, `idea_ticket_unlink`.
|
||||
- `Travail et exécution` : `idea_run_in_background`, `idea_workstate_set`.
|
||||
- `Skills` : `idea_create_skill`.
|
||||
|
||||
Chaque groupe affiche un compteur : `3/7 autorisés`, et peut être replié/déplié. Les groupes lecture sont ouverts par défaut dans `Défaut projet`, mais repliés par défaut dans l'édition agent pour réduire le bruit. Les groupes écriture/action sont ouverts par défaut, car ce sont les décisions à risque.
|
||||
|
||||
Chaque ligne de tool contient :
|
||||
|
||||
- le nom exact monospace (`idea_ticket_update_carnet`) ;
|
||||
- un libellé humain court (`Modifier le carnet d'un ticket`) ;
|
||||
- un badge `Lecture` ou `Écriture` ;
|
||||
- un état `Autorisé`, `Refusé`, ou `Hérité` ;
|
||||
- une case à cocher uniquement quand la cible est éditable.
|
||||
|
||||
Les checkboxes sont réservées aux tools individuels. Les groupes utilisent un bouton discret `Tout autoriser dans ce groupe` / `Tout retirer dans ce groupe`, jamais une checkbox tri-state ambiguë.
|
||||
|
||||
### Défaut projet vs overrides agent
|
||||
|
||||
Le `Défaut projet` est le point de départ appliqué à tous les agents sans override. Son état initial est `Lecture seule` : tous les tools classifiés lecture sont autorisés, tous les tools écriture/action sont refusés.
|
||||
|
||||
Pour un agent, afficher explicitement :
|
||||
|
||||
- `Hérite du défaut projet` si aucun override n'existe.
|
||||
- `Override personnalisé` si une allowlist agent existe.
|
||||
- `Diffère du défaut : +2 écriture, -1 lecture` quand l'API permet de comparer l'allowlist effective au défaut.
|
||||
|
||||
Dans les lignes de tool agent :
|
||||
|
||||
- un tool hérité autorisé affiche une coche grisée + `Hérité` ;
|
||||
- un tool ajouté par override affiche une coche active + badge `Ajouté` ;
|
||||
- un tool retiré par override affiche une case vide + badge `Retiré` si le backend expose une notion de retrait par remplacement complet ; sinon afficher simplement l'état effectif `Refusé`.
|
||||
|
||||
Important : comme l'API `update_agent_mcp_tool_permissions` accepte une allowlist complète de noms de tools, l'UI doit traiter l'override agent comme un remplacement de l'état effectif, pas comme une série de patches implicites. Au moment où l'utilisateur crée un override depuis l'état hérité, la draft est préremplie avec l'allowlist effective courante.
|
||||
|
||||
### Parcours principal — accorder un tool d'écriture
|
||||
|
||||
1. L'utilisateur ouvre le panneau `Permissions` depuis la barre de panneaux projet.
|
||||
2. Il sélectionne l'onglet `Tools MCP IdeA`.
|
||||
3. Il clique l'agent cible dans la colonne gauche, par exemple `DevFrontend`.
|
||||
4. Si l'agent hérite du défaut, il clique `Créer un override`. La liste devient éditable et reprend l'état effectif actuel.
|
||||
5. Il ouvre le groupe concerné, par exemple `Tickets`.
|
||||
6. Il coche `idea_ticket_update_carnet — Modifier le carnet d'un ticket`.
|
||||
7. Le résumé en haut passe à `9 lecture autorisés · 1 écriture autorisé` et une barre d'actions affiche `Modifications non enregistrées`.
|
||||
8. Il clique `Enregistrer`.
|
||||
9. Après succès, le badge de l'agent passe à `Override` et la ligne du tool affiche `Ajouté`.
|
||||
|
||||
### Parcours principal — retirer un tool d'écriture
|
||||
|
||||
1. L'utilisateur sélectionne un agent avec badge `Override`.
|
||||
2. Il ouvre le groupe contenant le tool autorisé.
|
||||
3. Il décoche le tool d'écriture.
|
||||
4. Le résumé et le compteur du groupe se mettent à jour immédiatement dans la draft.
|
||||
5. Il clique `Enregistrer`.
|
||||
6. Si l'override devient identique au défaut projet, proposer après sauvegarde de le nettoyer avec une action secondaire `Supprimer l'override inutile`. Ne pas le faire automatiquement sans retour visuel.
|
||||
|
||||
### Défaut projet
|
||||
|
||||
Le défaut projet est éditable dans le même onglet, mais avec une friction légère pour les tools d'écriture/action : quand l'utilisateur active un tool d'écriture au niveau défaut projet, afficher une confirmation inline avant sauvegarde :
|
||||
|
||||
`Ce tool sera autorisé pour tous les agents sans override. Confirmer cette modification ?`
|
||||
|
||||
Cette confirmation ne bloque pas l'édition agent par agent, car le cas utilisateur principal est d'accorder des tools d'écriture spécifiques à un agent donné.
|
||||
|
||||
### États et feedback
|
||||
|
||||
- Chargement : skeleton compact dans la colonne cible et dans les groupes, pas de spinner plein écran.
|
||||
- Erreur de chargement : message inline en haut du panneau avec bouton `Réessayer`.
|
||||
- Erreur de sauvegarde : conserver la draft locale, afficher l'erreur au-dessus des actions, garder `Enregistrer` disponible.
|
||||
- Aucune agent : état vide dans la colonne gauche `Aucun agent dans ce projet.` ; le défaut projet reste éditable.
|
||||
- Tool inconnu dans une allowlist existante : afficher dans un groupe `Tools inconnus` avec badge `Inconnu`, désactivé par défaut, et demander à DevFrontend de ne pas permettre de ré-enregistrer silencieusement une permission inconnue comme si elle était valide. Si le backend rejette les inconnus, afficher l'erreur telle quelle.
|
||||
- Modifications non enregistrées : actions `Annuler` et `Enregistrer` visibles dans l'éditeur ; changement de cible avec draft modifiée demande confirmation.
|
||||
- Sauvegarde réussie : feedback discret `Permissions enregistrées` pendant environ 2 secondes.
|
||||
|
||||
### Accessibilité
|
||||
|
||||
- Les onglets `Système` / `Tools MCP IdeA` utilisent `role="tablist"`, `role="tab"`, `aria-selected` et navigation clavier gauche/droite.
|
||||
- Chaque groupe repliable expose un bouton avec `aria-expanded` et un nom incluant le compteur, par exemple `Tickets, 1 sur 7 autorisé`.
|
||||
- Chaque checkbox a un label complet incluant le libellé humain et le nom du tool, par exemple `Modifier le carnet d'un ticket, idea_ticket_update_carnet`.
|
||||
- Les badges couleur (`Lecture`, `Écriture`, `Hérité`, `Override`) ne doivent jamais être le seul signal : le texte doit porter l'information.
|
||||
- Cibles tactiles et souris : 32 px minimum pour les lignes compactes, 40 px pour les actions principales.
|
||||
- Focus visible sur onglets, lignes de cible, boutons de groupe, checkboxes et actions.
|
||||
|
||||
### Ton et libellés
|
||||
|
||||
Conformément à la règle UX #78, les libellés humains sont en français. Les noms exacts des tools restent en anglais/monospace car ce sont des identifiants techniques.
|
||||
|
||||
Libellés principaux :
|
||||
|
||||
- `Permissions`
|
||||
- `Système`
|
||||
- `Tools MCP IdeA`
|
||||
- `Défaut projet`
|
||||
- `Lecture seule`
|
||||
- `Hérite du défaut projet`
|
||||
- `Override personnalisé`
|
||||
- `Créer un override`
|
||||
- `Réinitialiser l'override`
|
||||
- `Modifications non enregistrées`
|
||||
- `Annuler`
|
||||
- `Enregistrer`
|
||||
- `Autorisé`
|
||||
- `Refusé`
|
||||
- `Hérité`
|
||||
- `Ajouté`
|
||||
- `Retiré`
|
||||
|
||||
### Critères d'acceptation UX/frontend
|
||||
|
||||
- Les permissions MCP sont éditables depuis le panneau projet `Permissions`, onglet `Tools MCP IdeA`.
|
||||
- L'utilisateur peut sélectionner `Défaut projet` ou un agent dans une colonne/listing de cibles.
|
||||
- Un agent sans override affiche clairement qu'il hérite du défaut projet, et ses contrôles ne deviennent éditables qu'après `Créer un override`.
|
||||
- Un agent avec override est identifiable dans la liste par un badge textuel `Override`.
|
||||
- Les ~25 tools ne sont pas affichés comme une liste plate : ils sont groupés par domaine, avec compteurs et sections repliables.
|
||||
- Les tools lecture et écriture/action sont distingués par badges textuels et par hiérarchie visuelle.
|
||||
- Le parcours d'autorisation d'un tool d'écriture agent par agent prend au maximum : ouvrir `Permissions`, onglet `Tools MCP IdeA`, choisir l'agent, créer/éditer l'override, cocher le tool, enregistrer.
|
||||
- Le retrait d'un tool d'écriture existant est symétrique : décocher puis enregistrer.
|
||||
- Les changements non sauvegardés sont visibles et protégés lors d'un changement de cible.
|
||||
- L'UI consomme `get_mcp_tool_permissions`, `update_project_mcp_tool_permissions`, `update_agent_mcp_tool_permissions` sans hardcoder la classification lecture/écriture comme source de vérité métier.
|
||||
- Aucun changement de backend ou d'i18n n'est requis pour ce lot frontend.
|
||||
16
.ideai/tickets/82/issue.md
Normal file
16
.ideai/tickets/82/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "4904e380-b9a8-49d4-a06f-032024909be4"
|
||||
number: 82
|
||||
title: "Ajouter des permissions d'utilisations des tools MCP d'IdeA par agent"
|
||||
status: "closed"
|
||||
priority: "critical"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784296923284
|
||||
updatedAt: 1784410760329
|
||||
version: 6
|
||||
---
|
||||
J'aimerais avoir la possibilité de modifier les permissions d'utilisation des outils MCP IdeA de mes agents IdeA. J'aimerais que par défaut seuls les outils MCP de lectures soient autorisés, puis j'aimerais avoir un moyen (a faire determiner par l'agent UX), de modifier ces permissions agent par agent, et autoriser ou enlever des permissions sur les tools MCP proposés par IdeA
|
||||
222
.ideai/tickets/83/carnet.md
Normal file
222
.ideai/tickets/83/carnet.md
Normal file
@ -0,0 +1,222 @@
|
||||
---
|
||||
issueRef: "#83"
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784567952845
|
||||
---
|
||||
# Cadrage technique — contrainte pour UX
|
||||
|
||||
## Constats dans le code existant
|
||||
|
||||
La fermeture de la fenêtre principale est déjà interceptée côté Rust/Tauri dans `crates/app-tauri/src/lib.rs` via `window.on_window_event(...)` et `tauri::WindowEvent::CloseRequested`.
|
||||
|
||||
Le handler actuel ne bloque pas la fermeture : il exécute directement le teardown applicatif au moment du `CloseRequested` :
|
||||
|
||||
- snapshot des fenêtres ouvertes (`SnapshotOpenWindowsInput`) ;
|
||||
- snapshot des agents en cours (`SnapshotRunningAgentsInput`) pour persister `agent_was_running` avant destruction des PTY ;
|
||||
- kill de tous les handles PTY vivants ;
|
||||
- arrêt des serveurs de modèles locaux ;
|
||||
- arrêt du serveur embedded ;
|
||||
- fermeture des fenêtres webview secondaires.
|
||||
|
||||
Ce flux correspond aux livraisons liées à la fermeture globale de l'application (#39) et aux fenêtres détachées (#50). Les fenêtres détachées ont aussi un handler `CloseRequested`, mais il sert seulement à émettre le lifecycle `closed` pour le panneau concerné ; il ne doit pas porter la confirmation globale.
|
||||
|
||||
Point important pour UX/dev : une interception uniquement côté React avec `onCloseRequested` serait fragile tant que le handler Rust actuel continue à faire le teardown immédiatement. Même si le frontend appelle `preventDefault`, le handler Rust peut déjà avoir snapshot/kill les sessions. Il faut donc déplacer/garder la décision de fermeture dans le flux backend, ou au minimum rendre le handler Rust conscient du guard.
|
||||
|
||||
## Interception de fermeture recommandée
|
||||
|
||||
Le point d'application robuste est le handler Rust de la fenêtre `main` :
|
||||
|
||||
1. Sur `WindowEvent::CloseRequested { api, .. }`, calculer si un travail détectable est en cours.
|
||||
2. Si aucun travail n'est en cours, laisser passer le chemin actuel de shutdown.
|
||||
3. Si du travail est en cours et qu'aucune confirmation n'a déjà été donnée, appeler `api.prevent_close()` / équivalent Tauri v2, puis demander au frontend d'afficher la popup UX.
|
||||
4. Si l'utilisateur confirme, relancer une fermeture programmatique via une commande backend dédiée, avec un flag `confirmed_exit`/`exit_guard_bypassed` pour éviter une boucle de confirmation.
|
||||
5. Le shutdown réel doit réutiliser exactement l'ordre actuel : snapshot fenêtres, snapshot agents, kill PTY, stop model servers, stop embedded server, fermeture des fenêtres secondaires.
|
||||
|
||||
Implémentation conseillée : extraire le teardown actuellement inline dans `lib.rs` vers une fonction/service interne réutilisable, par exemple `shutdown_app_after_confirm(app_handle)`. La commande appelée après confirmation doit soit :
|
||||
|
||||
- positionner le bypass puis appeler `main.close()` pour repasser par le handler et exécuter le teardown partagé ;
|
||||
- soit exécuter explicitement le teardown partagé puis quitter l'application.
|
||||
|
||||
À éviter : appeler `destroy()` côté frontend ou contourner le handler Rust, car cela risquerait de sauter le snapshot `agent_was_running` et le nettoyage PTY/serveurs.
|
||||
|
||||
## Définition technique de « travail en cours »
|
||||
|
||||
Définition fiable recommandée pour déclencher la confirmation :
|
||||
|
||||
- au moins un agent a `busy.state == "busy"` dans le workstate ;
|
||||
- ou au moins une tâche de fond non terminale existe : `Queued`, `Running` ou `Waiting` dans `BackgroundTaskState`.
|
||||
|
||||
Signaux disponibles et niveau de fiabilité :
|
||||
|
||||
- `GetProjectWorkState` / commande Tauri `get_project_work_state(project_id)` : source déjà agrégée par projet. Elle expose les agents du manifeste, leur `live`, leur `busy`, leurs tickets, leurs tâches de fond et les conversations résumées.
|
||||
- `AgentBusyState` (`crates/domain/src/input.rs`) : signal le plus fiable pour un tour d'agent réellement en vol. `Busy { ticket, since_ms }` signifie qu'un tour a démarré et n'a pas encore rendu la main.
|
||||
- `BackgroundTaskStore` / `BackgroundTaskState` (`crates/domain/src/background_task.rs`) : fiable pour les travaux asynchrones. Les états non terminaux sont `Queued`, `Running`, `Waiting`. Les états `Completed`, `Failed`, `Cancelled`, `Expired` sont terminaux et ne doivent pas compter comme « travail en cours » pour éviter les faux positifs. Les complétions non livrées peuvent mériter une notification UX, mais elles ne sont plus un travail actif.
|
||||
- `LiveAgentReadModel` / champ `live` du workstate : indique une session agent vivante, pas nécessairement active. Une session vivante peut être idle ; ne pas l'utiliser seule comme déclencheur, sauf arbitrage produit explicite « prévenir dès qu'une session agent serait arrêtée ».
|
||||
- PTY actifs (`PtyBridge.active_sessions()` / registre `terminal_sessions`) : utile pour le teardown et le snapshot, mais mauvais critère UX seul. Un terminal shell ouvert peut être idle ; compter tous les PTY provoquerait beaucoup de confirmations inutiles.
|
||||
- Sessions structurées/chat (`ChatBridge.active_sessions()` ou sessions structurées ouvertes) : une session ouverte seule ne prouve pas un travail actif. À inclure seulement si un état backend indique un tour structuré en cours. Si ce signal n'est pas encore centralisé, il doit être ajouté au même snapshot backend plutôt que déduit depuis l'existence de la session.
|
||||
|
||||
Donc, pour la première livraison, le critère technique le plus défendable est :
|
||||
|
||||
`has_work_in_progress = any(agent.busy.is_busy()) || any(background_task.state in {Queued, Running, Waiting})`
|
||||
|
||||
Option produit à arbitrer avec UX/PO : faut-il aussi prévenir lorsqu'il existe des agents/PTY simplement vivants mais idle, car ils seront arrêtés à la fermeture ? Techniquement possible, mais cela change le sens de la popup de « travail en cours » vers « sessions ouvertes qui vont être interrompues » et augmente les faux positifs.
|
||||
|
||||
## Frontend pur ou aller-retour backend ?
|
||||
|
||||
Ce n'est pas un changement purement frontend.
|
||||
|
||||
La popup et son wording relèvent bien de l'UX/frontend, mais la décision fiable doit faire un aller-retour backend, idéalement depuis le handler de fermeture Rust lui-même, pour trois raisons :
|
||||
|
||||
1. Le backend possède la vérité complète sur tous les projets ouverts via `AppState::open_project_ids()`. Le frontend ne voit pas forcément un état frais pour tous les onglets/projets, notamment si seul le projet actif rafraîchit son `useProjectWorkState`.
|
||||
2. Les tâches de fond et l'état `busy` sont des états applicatifs/backend. Lire un cache frontend peut rater une tâche démarrée hors vue active ou compter un état obsolète.
|
||||
3. Le handler Rust actuel exécute déjà le teardown au `CloseRequested`. Il doit donc être modifié pour empêcher la fermeture avant teardown lorsque la confirmation est requise.
|
||||
|
||||
Surface backend proposée :
|
||||
|
||||
- ajouter un use case/commande de lecture `get_app_exit_work_guard_state` ou équivalent, qui agrège tous les `ProjectWorkState` des `open_project_ids()` et retourne un résumé minimal pour la popup : `hasWorkInProgress`, nombre d'agents busy, nombre de tâches de fond actives, éventuellement noms/projets pour affichage si UX le souhaite ;
|
||||
- utiliser cette même logique dans le handler `CloseRequested` de `main` avant d'appeler le teardown ;
|
||||
- exposer une commande `confirm_app_exit` / `request_app_exit_after_confirmation` qui bypass le guard et exécute le shutdown existant.
|
||||
|
||||
Frontière de lot :
|
||||
|
||||
- Backend/Tauri nécessaire : guard `CloseRequested`, agrégation fiable du work in progress, bypass confirmé, factorisation du teardown existant.
|
||||
- Frontend/UX : rendu de la popup, libellés, hiérarchie d'information, boutons, éventuel détail des agents/tâches concernés.
|
||||
- Aucun impact backend métier profond attendu : on réutilise `GetProjectWorkState`, `AgentBusyState`, `BackgroundTaskStore`, `open_project_ids()` et le shutdown existant. Pas d'impact DB/schema prévu, sauf si l'on choisit de persister une préférence utilisateur du type « ne plus demander » (hors demande actuelle).
|
||||
|
||||
## Critères d'acceptation techniques proposés
|
||||
|
||||
- Fermer la fenêtre principale sans travail actif garde le comportement actuel : snapshot, kill PTY, arrêt serveurs, fermeture globale.
|
||||
- Fermer la fenêtre principale avec au moins un agent `Busy` empêche la fermeture et déclenche la demande de confirmation avant tout kill PTY.
|
||||
- Fermer la fenêtre principale avec au moins une tâche de fond `Queued`/`Running`/`Waiting` empêche la fermeture et déclenche la confirmation.
|
||||
- Annuler la popup laisse l'application et les sessions intactes : aucun PTY tué, aucun snapshot de fermeture forcé, serveurs toujours actifs.
|
||||
- Confirmer exécute le shutdown existant dans le même ordre qu'aujourd'hui.
|
||||
- Les fenêtres détachées ne déclenchent pas la confirmation globale ; seule la fenêtre `main` porte ce guard.
|
||||
|
||||
## Conception UX — confirmation de fermeture avec travail en cours
|
||||
|
||||
### Décision produit
|
||||
|
||||
Afficher une popup modale uniquement lorsque la fermeture de la fenêtre principale d'IdeA interrompt un travail actif détecté par le guard technique : agents en tour `busy` et/ou tâches de fond non terminales (`Queued`, `Running`, `Waiting`). Ne pas déclencher la popup pour une session agent simplement vivante mais idle dans la première livraison : le libellé demandé parle de « travail en cours », et compter les sessions ouvertes créerait trop de confirmations inutiles.
|
||||
|
||||
Il ne doit pas y avoir d'option `Ne plus avertir`. Cette confirmation protège contre une perte ou interruption volontairement coûteuse ; la rendre désactivable localement affaiblit la sécurité produit et crée une préférence à maintenir. Le seul bypass est l'action explicite `Quitter quand même` pour cette tentative de fermeture.
|
||||
|
||||
### Rôle de la popup
|
||||
|
||||
La popup doit communiquer trois choses, dans cet ordre :
|
||||
|
||||
1. IdeA a détecté du travail actif.
|
||||
2. Quitter maintenant interrompra ce travail.
|
||||
3. L'utilisateur peut annuler pour revenir à l'application, ou confirmer une fermeture volontaire.
|
||||
|
||||
La popup ne doit pas essayer de prédire si le travail est récupérable. Elle ne promet pas de sauvegarde, de reprise ou de livraison du résultat après fermeture. Elle indique seulement l'effet immédiat : les agents et tâches en cours seront interrompus pendant la fermeture.
|
||||
|
||||
### Libellé exact
|
||||
|
||||
Titre : `Du travail est encore en cours`
|
||||
|
||||
Corps, version avec un seul élément actif :
|
||||
|
||||
`1 travail actif sera interrompu si vous quittez IdeA maintenant.`
|
||||
|
||||
Corps, version plurielle :
|
||||
|
||||
`{count} travaux actifs seront interrompus si vous quittez IdeA maintenant.`
|
||||
|
||||
Phrase secondaire :
|
||||
|
||||
`Annulez la fermeture pour laisser les agents et les tâches se terminer.`
|
||||
|
||||
Si l'agrégat distingue les types, préférer une phrase plus informative :
|
||||
|
||||
- `1 agent travaille encore.`
|
||||
- `{agentCount} agents travaillent encore.`
|
||||
- `1 tâche de fond est encore active.`
|
||||
- `{taskCount} tâches de fond sont encore actives.`
|
||||
|
||||
Exemple combiné :
|
||||
|
||||
`2 agents travaillent encore et 1 tâche de fond est encore active. Ces travaux seront interrompus si vous quittez IdeA maintenant.`
|
||||
|
||||
Actions :
|
||||
|
||||
- Action principale, non destructive : `Annuler`
|
||||
- Action destructive secondaire : `Quitter quand même`
|
||||
|
||||
Ordre visuel recommandé : `Annuler` à gauche ou en premier, `Quitter quand même` à droite ou en dernier avec style danger. Le focus initial va sur `Annuler`.
|
||||
|
||||
### Détail affiché
|
||||
|
||||
Afficher un résumé court visible immédiatement :
|
||||
|
||||
- `{agentCount} agent(s) en cours`
|
||||
- `{backgroundTaskCount} tâche(s) de fond active(s)`
|
||||
|
||||
Afficher ensuite une liste compacte des éléments concernés, limitée à 5 lignes maximum pour garder la popup lisible. Si plus de 5 éléments sont actifs, afficher les 5 premiers puis `+ {remainingCount} autre(s)`.
|
||||
|
||||
Format des lignes :
|
||||
|
||||
- Agent busy : `Agent {agentName} — {projectName}` ; si un ticket est connu, ajouter ` — #{ticketNumber}`.
|
||||
- Tâche de fond : `{taskLabel} — {projectName}` ; si aucun libellé humain n'est disponible, utiliser `Tâche de fond {shortTaskId}`.
|
||||
|
||||
Exemples :
|
||||
|
||||
```text
|
||||
Du travail est encore en cours
|
||||
|
||||
2 agents travaillent encore et 1 tâche de fond est encore active. Ces travaux seront interrompus si vous quittez IdeA maintenant.
|
||||
|
||||
Agents
|
||||
• DevFrontend — IdeA — #82
|
||||
• QA — IdeA
|
||||
|
||||
Tâches de fond
|
||||
• npm test — IdeA
|
||||
|
||||
[Annuler] [Quitter quand même]
|
||||
```
|
||||
|
||||
Si les noms ne sont pas disponibles côté backend au moment du guard, afficher seulement les compteurs. Ne pas bloquer la feature UX sur l'affichage détaillé, mais le contrat backend recommandé est de fournir au moins `projectName`, `agentName` et un label de tâche quand disponibles.
|
||||
|
||||
### États et interactions
|
||||
|
||||
- Ouverture : la popup apparaît après tentative de fermeture, avant tout teardown.
|
||||
- `Annuler` : ferme la popup, ne ferme pas l'application, ne tue aucune session, ne modifie aucun état métier.
|
||||
- `Échap` : équivalent à `Annuler`.
|
||||
- Clic hors popup : désactivé ou équivalent à `Annuler`, selon le composant modal existant ; préférence UX : ne pas fermer par clic extérieur pour éviter une décision ambiguë.
|
||||
- `Quitter quand même` : lance le flux backend confirmé. Pendant l'appel, désactiver les deux boutons et afficher l'état `Fermeture…` sur le bouton danger.
|
||||
- Échec de la fermeture confirmée : rester dans la popup et afficher un message d'erreur inline : `IdeA n'a pas pu quitter correctement. Réessayez ou consultez les logs.` Le bouton `Quitter quand même` redevient disponible.
|
||||
- Si le travail se termine pendant que la popup est ouverte, ne pas fermer automatiquement la popup. Actualiser le résumé si l'événement arrive facilement ; sinon garder l'état initial. L'utilisateur peut annuler puis fermer à nouveau sans confirmation.
|
||||
|
||||
### Hiérarchie visuelle
|
||||
|
||||
La popup est une alerte de confirmation destructive, pas un panneau de diagnostic :
|
||||
|
||||
- largeur cible : 440 à 520 px ;
|
||||
- titre en `text-content`, taille modale standard ;
|
||||
- résumé en texte normal, pas en rouge ;
|
||||
- action `Quitter quand même` en variante danger ;
|
||||
- liste des éléments dans un bloc compact `bg-raised` ou équivalent, sans tableau ;
|
||||
- éviter les grands paragraphes et les détails techniques (`busy.state`, `Queued`, ids longs).
|
||||
|
||||
### Accessibilité
|
||||
|
||||
- Utiliser un vrai dialogue modal avec `role="alertdialog"` ou le composant modal existant configuré comme confirmation destructive.
|
||||
- `aria-labelledby` pointe sur le titre `Du travail est encore en cours`.
|
||||
- `aria-describedby` pointe sur le résumé de l'impact.
|
||||
- Focus initial sur `Annuler`.
|
||||
- Tabulation piégée dans la popup tant qu'elle est ouverte.
|
||||
- `Entrée` active le bouton focusé, pas automatiquement `Quitter quand même`.
|
||||
- `Échap` annule.
|
||||
- Les compteurs et détails ne doivent pas dépendre uniquement de la couleur.
|
||||
|
||||
### Critères d'acceptation UX/frontend
|
||||
|
||||
- La popup n'apparaît que pour la fermeture de la fenêtre principale avec travail actif détecté.
|
||||
- Le titre exact est `Du travail est encore en cours`.
|
||||
- La popup indique le nombre total de travaux actifs et, quand disponible, le détail agents/tâches limité à 5 lignes.
|
||||
- Les actions exactes sont `Annuler` et `Quitter quand même`.
|
||||
- `Annuler` est l'action par défaut/focus initial et laisse l'application intacte.
|
||||
- `Quitter quand même` est visuellement destructive et déclenche le flux backend confirmé.
|
||||
- Aucune option `Ne plus avertir` n'est proposée.
|
||||
- Les libellés visibles sont en français conformément à la décision UX #78.
|
||||
16
.ideai/tickets/83/issue.md
Normal file
16
.ideai/tickets/83/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "18a92510-3af4-4aef-84e8-e23cd7422118"
|
||||
number: 83
|
||||
title: "Popup pour vérifier la volonté de quitter l'app"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784360573747
|
||||
updatedAt: 1784567952845
|
||||
version: 5
|
||||
---
|
||||
Dans le cas ou un travail est en cours, j'aimerais qu'une popup qui demande la confirmation qu'on veut quitter IdeA malgré le travail en cours, apparaisse
|
||||
6
.ideai/tickets/84/carnet.md
Normal file
6
.ideai/tickets/84/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#84"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784377833773
|
||||
---
|
||||
20
.ideai/tickets/84/issue.md
Normal file
20
.ideai/tickets/84/issue.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
id: "beab1811-2fd7-4563-ac39-6a354fbd1feb"
|
||||
number: 84
|
||||
title: "[Bug] Reprise auto après limite de session ne se déclenche pas pour l'orchestrator (Main, profil Claude)"
|
||||
status: "open"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: [{"target":"#7","kind":"relatesTo"},{"target":"#15","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784377833773
|
||||
updatedAt: 1784377833773
|
||||
version: 1
|
||||
---
|
||||
Rapporté par l'utilisateur (2026-07-18) sur l'AppImage courante (buildée depuis develop, contient #7 baseline + #30 fix chemin direct Main) : quand l'orchestrator (Main) atteint une limite de session sur un profil Claude, la reprise automatique au reset n'a PAS lieu en pratique, alors que la feature #7 (mergée d7041c5, 2026-06-17) et le fix #30 dédié précisément au cas "Main qui limite session" (commit 9430c65, 2026-07-13, "brancher le handle de limite sur le chemin direct") sont censés couvrir ce cas.
|
||||
|
||||
Écart constaté : design/tests verts ≠ comportement live observé par l'utilisateur. À investiguer : soit régression depuis 9430c65, soit un chemin non couvert par les tests d'intégration existants (session_limit_wiring.rs), soit une limite du "EN MEMOIRE uniquement" (mémoire session-limit-handling-design : le réveil auto ne joue que tant qu'IdeA reste ouvert — si l'utilisateur a fermé/rouvert IdeA entre la limite et le reset, c'est le comportement attendu, pas un bug).
|
||||
|
||||
Première question à trancher par Architect : est-ce une vraie régression du chemin direct Main, ou un cas hors-couverture connu (IdeA fermé/rouvert pendant la fenêtre de reset) ? Nécessite de faire préciser à l'utilisateur les conditions exactes de repro (IdeA resté ouvert ou non pendant l'attente du reset).
|
||||
6
.ideai/tickets/85/carnet.md
Normal file
6
.ideai/tickets/85/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#85"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784379436323
|
||||
---
|
||||
27
.ideai/tickets/85/issue.md
Normal file
27
.ideai/tickets/85/issue.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
id: "34b23044-4b82-4e7e-b999-cca7cbef7861"
|
||||
number: 85
|
||||
title: "Test flaky : setCellAgent « changing the agent kills the previous PTY (Bug #3) » échoue en exécution shuffled"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#79","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784379436323
|
||||
updatedAt: 1784379436323
|
||||
version: 1
|
||||
---
|
||||
Découvert en marge de #79 (DevFrontend puis confirmé par QA sur `npx vitest run --sequence.shuffle`, ~2 échecs sur 3 passages) : `src/features/layout/setCellAgent.test.tsx` → « changing the agent kills the previous PTY (Bug #3) ».
|
||||
|
||||
```
|
||||
FAIL src/features/layout/setCellAgent.test.tsx
|
||||
changing the agent kills the previous PTY (Bug #3)
|
||||
AssertionError: expected "closeTerminal" to be called with arguments: [ 'old-session' ]
|
||||
Number of calls: 0
|
||||
```
|
||||
|
||||
Vert isolément (`npx vitest run src/features/layout/setCellAgent.test.tsx` → 9/9) et vert en suite complète non-shuffled (850/850). Rouge uniquement en ordre shuffled — dépendance d'ordre/état partagé entre fichiers de test, pas un bug du code applicatif a priori (à confirmer). Sans rapport avec #78/#79, ne pas mélanger.
|
||||
|
||||
Attendu : identifier la source de la dépendance d'ordre (état partagé, mock non réinitialisé, timer) et rendre le test déterministe, comme pour #79 — ne pas neutraliser ni élargir un timeout pour faire taire.
|
||||
336
.ideai/tickets/86/carnet.md
Normal file
336
.ideai/tickets/86/carnet.md
Normal file
@ -0,0 +1,336 @@
|
||||
---
|
||||
issueRef: "#86"
|
||||
version: 7
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784612705899
|
||||
---
|
||||
# Conception UX — tickets et sprints dans le client web
|
||||
|
||||
## Intention
|
||||
|
||||
La version web doit permettre de consulter et modifier les tickets/sprints sans reproduire le shell desktop. Le client web existant est une surface autonome en colonne verticale unique : après appairage, l'utilisateur choisit un projet, puis voit l'état live et les cellules agents. #86 doit ajouter une surface de pilotage tickets/sprints cohérente avec ce modèle mobile/web, pas importer les docks, fenêtres flottantes et overlays desktop.
|
||||
|
||||
Décision UX : réutiliser les contrats métier et les hooks transport-neutres autant que possible (`useTickets`, `useTicketDetail`, recherche, filtres, événements live), mais créer une présentation web dédiée. Ce n'est pas une version appauvrie en capacités, c'est une adaptation de navigation et de densité.
|
||||
|
||||
La cible prioritaire web/mobile est l'intervention rapide : lire, trier, changer statut/priorité, corriger titre/description/carnet, créer un ticket simple, assigner/désassigner, déplacer dans un sprint, créer/renommer/supprimer un sprint. Les opérations complexes restent disponibles mais rangées derrière des panneaux dédiés : liens entre tickets, carnet long, gestion complète des sprints.
|
||||
|
||||
## Placement dans le client web
|
||||
|
||||
Dans `WebWorkspace`, après ouverture d'un projet, ajouter une navigation de projet compacte au-dessus des surfaces projet :
|
||||
|
||||
```text
|
||||
Projet: IdeA [Rafraîchir]
|
||||
[ Live ] [ Tickets ] [ Sprints ]
|
||||
```
|
||||
|
||||
- `Live` : surface actuelle `État live`, inchangée dans son rôle.
|
||||
- `Tickets` : nouvelle liste et édition des tickets.
|
||||
- `Sprints` : gestion des sprints.
|
||||
|
||||
Sur mobile, ces onglets sont des boutons segmentés scrollables horizontalement si nécessaire. Sur desktop web large, ils restent dans la même colonne contrainte, pas de dock ni layout grid.
|
||||
|
||||
Ne pas placer les tickets sous la liste des agents : ce sont deux surfaces de projet au même niveau. L'utilisateur web doit pouvoir passer de l'état live au backlog sans chercher dans une carte agent.
|
||||
|
||||
## Vue Tickets — layout
|
||||
|
||||
La vue `Tickets` est une pile verticale : barre d'actions, recherche/filtres compacts, liste groupée par sprint, puis écran de détail lorsqu'un ticket est ouvert.
|
||||
|
||||
```text
|
||||
Tickets [+ Ticket]
|
||||
[Recherche…]
|
||||
[Statut ▼] [Priorité ▼] [Assigné ▼] [Tri ▼]
|
||||
|
||||
Sprint courant (4)
|
||||
#86 medium open Ajouter tickets/sprints au web
|
||||
#83 medium open Confirmation fermeture
|
||||
|
||||
Sans sprint (2)
|
||||
#78 low open Langue uniforme Settings
|
||||
|
||||
[Charger plus]
|
||||
```
|
||||
|
||||
Sur petit écran, un ticket s'affiche comme une ligne compacte à deux étages :
|
||||
|
||||
```text
|
||||
#86 Ajouter tickets/sprints au web
|
||||
open · medium · Sprint courant · Main
|
||||
```
|
||||
|
||||
La ligne entière ouvre le détail. Les badges doivent rester textuels et lisibles ; ne pas empiler 5 pastilles colorées si elles provoquent un retour ligne incohérent à 360 px.
|
||||
|
||||
## Actions prioritaires tickets
|
||||
|
||||
Priorité haute sur web :
|
||||
|
||||
- consulter la liste ;
|
||||
- rechercher et filtrer ;
|
||||
- ouvrir un ticket ;
|
||||
- changer statut et priorité ;
|
||||
- éditer titre, description et carnet ;
|
||||
- créer un ticket simple ;
|
||||
- supprimer un ticket avec confirmation ;
|
||||
- assigner/désassigner des agents ;
|
||||
- changer le sprint d'un ticket.
|
||||
|
||||
Priorité secondaire, mais à conserver si les endpoints existent :
|
||||
|
||||
- gérer les liens entre tickets ;
|
||||
- utiliser le `TicketPicker` adapté mobile pour lier/ajouter des tickets à un sprint ;
|
||||
- copier le contexte de délégation.
|
||||
|
||||
Hors périmètre recommandé pour la première livraison web : assistant IA de ticket. Le desktop peut garder cette affordance avancée ; sur mobile/web, elle ajoute du coût UI et des permissions sans être nécessaire à la demande utilisateur.
|
||||
|
||||
## Création de ticket
|
||||
|
||||
Le bouton `+ Ticket` ouvre un panneau plein écran mobile ou une section expansée desktop web, pas un petit formulaire inline permanent. Le formulaire est court :
|
||||
|
||||
- `Titre` obligatoire ;
|
||||
- `Priorité` ;
|
||||
- `Sprint` avec picker ;
|
||||
- `Description` optionnelle, textarea repliable ou sous le champ titre ;
|
||||
- actions `Annuler` et `Créer`.
|
||||
|
||||
Après création : ouvrir automatiquement le détail du ticket créé pour permettre de compléter carnet, assignations ou liens. Si l'affectation sprint échoue après création, garder le ticket créé et afficher l'erreur sans perdre le résultat.
|
||||
|
||||
Libellés : `Nouveau ticket`, `Titre`, `Description`, `Priorité`, `Sprint`, `Sans sprint`, `Créer`.
|
||||
|
||||
## Détail ticket sur petit écran
|
||||
|
||||
Sur web/mobile, le détail ticket remplace temporairement la liste dans la colonne, au lieu d'utiliser la fenêtre flottante desktop. Navigation : bouton retour en haut.
|
||||
|
||||
```text
|
||||
[← Tickets] #86 [⋯]
|
||||
Ajouter tickets/sprints au web
|
||||
open · medium · Sprint courant
|
||||
|
||||
[Résumé]
|
||||
Titre
|
||||
Description
|
||||
[Enregistrer]
|
||||
|
||||
[Statut et priorité]
|
||||
Statut [open ▼]
|
||||
Priorité [medium ▼]
|
||||
Sprint [Sprint courant ▼]
|
||||
|
||||
[Carnet]
|
||||
textarea Markdown
|
||||
[Enregistrer le carnet]
|
||||
|
||||
[Agents assignés]
|
||||
Main DevFrontend [+]
|
||||
|
||||
[Liens]
|
||||
relatesTo #83 [+ Lier]
|
||||
```
|
||||
|
||||
Les sections du détail sont accordéons ou blocs empilés. Ouverts par défaut : `Résumé`, `Statut et priorité`, `Carnet`. Repliés par défaut : `Agents assignés`, `Liens`, `Zone dangereuse`.
|
||||
|
||||
Le bouton de suppression vit dans une section `Zone dangereuse` ou dans un menu `⋯`, jamais dans la première ligne d'actions. Confirmation obligatoire :
|
||||
|
||||
Titre : `Supprimer ce ticket ?`
|
||||
Corps : `Le ticket {ref} sera supprimé définitivement. Cette action ne supprime pas les sprints ni les agents assignés.`
|
||||
Actions : `Annuler` / `Supprimer`
|
||||
|
||||
Gestion des changements non enregistrés : si l'utilisateur revient à la liste avec des modifications locales non sauvegardées, afficher la confirmation existante adaptée en français : `Des modifications ne sont pas enregistrées.` avec `Continuer l'édition`, `Ignorer`, `Enregistrer et quitter` si techniquement disponible.
|
||||
|
||||
## Édition rapide
|
||||
|
||||
Pour les champs à faible risque (`statut`, `priorité`, `sprint`), la modification peut être enregistrée immédiatement au changement de select, avec spinner discret sur la ligne/section. Pour les champs texte (`titre`, `description`, `carnet`), garder une action explicite `Enregistrer` afin d'éviter les sauvegardes involontaires sur mobile.
|
||||
|
||||
En cas de conflit de version : afficher un message inline en haut du détail : `Ce ticket a été modifié ailleurs et rechargé. Réappliquez votre modification.` Ne pas écraser silencieusement le brouillon local.
|
||||
|
||||
## Vue Sprints — layout
|
||||
|
||||
La vue `Sprints` est une surface dédiée, pas un overlay plein écran au-dessus de la liste comme sur desktop. Elle est accessible depuis l'onglet projet `Sprints` et depuis le bouton `Gérer les sprints` dans la vue tickets si ce raccourci est conservé.
|
||||
|
||||
```text
|
||||
Sprints [+ Sprint]
|
||||
|
||||
#1 Sprint courant [⋯]
|
||||
4 tickets
|
||||
[Voir tickets] [Ajouter tickets]
|
||||
|
||||
#2 Backlog client web [⋯]
|
||||
7 tickets
|
||||
[Voir tickets] [Ajouter tickets]
|
||||
```
|
||||
|
||||
Créer un sprint : bouton `+ Sprint`, champ `Nom du sprint`, action `Créer`. Si le nom est vide, soit désactiver `Créer`, soit reprendre le comportement desktop de nom automatique `Sprint N`; préférence UX web : désactiver tant qu'un nom n'est pas saisi, car le mobile bénéficie d'une décision explicite.
|
||||
|
||||
Renommer : action dans menu `⋯`, ouvre une ligne d'édition inline dans la carte sprint ou un panneau bas sur petit écran. Actions `Annuler` / `Enregistrer`.
|
||||
|
||||
Réordonner : boutons `Monter` / `Descendre` dans le menu ou dans une section `Ordre`. Pas de drag-and-drop requis sur mobile.
|
||||
|
||||
Supprimer : confirmation obligatoire :
|
||||
|
||||
Titre : `Supprimer le sprint « {name} » ?`
|
||||
Corps : `Les tickets de ce sprint seront conservés et passeront en « Sans sprint ».`
|
||||
Actions : `Annuler` / `Supprimer`
|
||||
|
||||
Ajouter des tickets à un sprint : ouvrir un picker mobile de tickets, avec recherche et multi-sélection. Action finale `Ajouter {count} ticket(s)`. Les tickets déjà dans le sprint sont exclus ou affichés cochés et désactivés ; ne pas laisser l'utilisateur croire qu'ils seront ajoutés une seconde fois.
|
||||
|
||||
Retirer un ticket d'un sprint : depuis la carte sprint, dans la liste compacte des tickets, action `Retirer du sprint` accessible via menu ligne. Cela ne supprime pas le ticket.
|
||||
|
||||
## Pickers et popups sur mobile/web
|
||||
|
||||
Les pickers desktop (`TicketPicker`, `SprintPicker`) ne doivent pas apparaître comme de petites modales centrées sur téléphone. Adapter leur chrome :
|
||||
|
||||
- mobile : panneau plein écran ou bottom sheet haute, avec header fixe, recherche en haut, liste scrollable, actions en bas ;
|
||||
- desktop web large : modal centrée acceptable, mais largeur limitée et focus trap ;
|
||||
- toujours garder `Échap` / retour / bouton `Fermer` selon plateforme.
|
||||
|
||||
Le même contenu métier peut être réutilisé : recherche, filtres, sélection simple ou multiple. Seul le contenant responsive change.
|
||||
|
||||
## Cohérence avec le web existant
|
||||
|
||||
Respecter les patrons #69 :
|
||||
|
||||
- une seule colonne verticale dans `WebWorkspace` ;
|
||||
- pas de `LayoutGrid`, docks, fenêtres flottantes desktop ou tabs projet desktop ;
|
||||
- padding avec safe-area ;
|
||||
- contrôles qui wrap proprement à 360 px ;
|
||||
- hauteurs basées sur `dvh` pour les panneaux longs ;
|
||||
- reconnect banner existante conservée au-dessus des surfaces.
|
||||
|
||||
Les surfaces tickets/sprints doivent continuer à se resynchroniser sur les événements domaine via le transport web live, comme `État live` le fait déjà. En cas de reconnexion, refetch complet du snapshot/listes plutôt qu'application de deltas manqués.
|
||||
|
||||
## Langue et libellés
|
||||
|
||||
Conformément à la règle UX #78, les libellés humains sont en français. Les valeurs métier techniques peuvent rester celles du domaine si elles sont déjà exposées comme enums (`open`, `closed`, `medium`) seulement si leur traduction demanderait un chantier transversal ; préférence UX : afficher `Ouvert`, `Fermé`, `Faible`, `Moyenne`, `Haute`, `Critique` dans l'UI.
|
||||
|
||||
Libellés principaux :
|
||||
|
||||
- `Tickets`
|
||||
- `Sprints`
|
||||
- `Nouveau ticket`
|
||||
- `Créer un ticket`
|
||||
- `Gérer les sprints`
|
||||
- `Nouveau sprint`
|
||||
- `Créer un sprint`
|
||||
- `Modifier`
|
||||
- `Enregistrer`
|
||||
- `Annuler`
|
||||
- `Supprimer`
|
||||
- `Sans sprint`
|
||||
- `Charger plus`
|
||||
- `Aucun ticket.`
|
||||
- `Aucun sprint.`
|
||||
- `Tickets liés`
|
||||
- `Agents assignés`
|
||||
- `Carnet`
|
||||
- `Zone dangereuse`
|
||||
|
||||
## États et erreurs
|
||||
|
||||
- Chargement liste tickets : `Chargement des tickets…` avec spinner compact.
|
||||
- Liste vide sans filtre : `Aucun ticket dans ce projet.` + bouton `Créer un ticket`.
|
||||
- Liste vide avec filtres : `Aucun ticket ne correspond aux filtres.` + `Réinitialiser les filtres`.
|
||||
- Chargement sprints : `Chargement des sprints…`.
|
||||
- Aucun sprint : `Aucun sprint.` + bouton `Créer un sprint`.
|
||||
- Erreur réseau/session : message inline en haut de la surface, avec `Réessayer`.
|
||||
- Déconnexion live : conserver la bannière existante `Connexion perdue — reconnexion en cours…` ; les formulaires peuvent rester éditables, mais sauvegarder doit afficher l'erreur réelle si le transport est indisponible.
|
||||
- Suppression réussie : retour à la liste, ticket/sprint retiré après événement ou refresh.
|
||||
|
||||
## Accessibilité
|
||||
|
||||
- Les onglets `Live`, `Tickets`, `Sprints` utilisent `role="tablist"`, `role="tab"`, `aria-selected` et navigation clavier gauche/droite.
|
||||
- Chaque ticket de liste est un bouton ou lien avec nom accessible complet : `{ref}, {title}, statut {status}, priorité {priority}`.
|
||||
- Les formulaires ont labels visibles ou labels accessibles explicites ; ne dépendre d'aucun placeholder comme seul label.
|
||||
- Les confirmations de suppression utilisent `role="alertdialog"`, focus initial sur `Annuler`, action destructive textuelle.
|
||||
- Les menus `⋯` ont un label explicite : `Actions du ticket {ref}` ou `Actions du sprint {name}`.
|
||||
- Les zones scrollables longues conservent le focus et ne piègent pas la navigation clavier hors modal.
|
||||
|
||||
## Critères d'acceptation UX/frontend
|
||||
|
||||
- Après ouverture d'un projet web, l'utilisateur peut basculer entre `Live`, `Tickets` et `Sprints` sans shell desktop.
|
||||
- La vue web reste une colonne verticale responsive et ne monte pas le layout grid/docks/floating windows desktop.
|
||||
- L'utilisateur peut lister, filtrer, créer, ouvrir, modifier et supprimer des tickets depuis le web.
|
||||
- L'utilisateur peut créer, renommer, réordonner, supprimer des sprints et ajouter/retirer des tickets d'un sprint depuis le web.
|
||||
- L'édition ticket sur mobile se fait dans une vue détail pleine colonne avec retour explicite, pas dans une petite popup desktop.
|
||||
- Les suppressions ticket/sprint demandent confirmation et précisent ce qui est supprimé ou conservé.
|
||||
- Les pickers ticket/sprint sont adaptés mobile : plein écran ou bottom sheet, recherche en haut, actions claires en bas.
|
||||
- Les changements texte nécessitent `Enregistrer`; les changements statut/priorité/sprint peuvent être immédiats avec feedback de sauvegarde.
|
||||
- Les libellés visibles sont en français.
|
||||
- L'assistant IA de ticket n'est pas requis pour la première livraison web de #86.
|
||||
|
||||
---
|
||||
|
||||
# Cadrage technique — contrat web-server tickets/sprints
|
||||
|
||||
## Résultat de l'audit
|
||||
|
||||
Le socle applicatif tickets/sprints existe déjà dans `BackendCore` : création, lecture, liste, update, suppression, carnet, liens, assignation agent, création/liste/rename/reorder/suppression sprint, assignation/désassignation ticket→sprint. Le problème #86 n'est donc pas un manque de use case application ni de store : c'est un écart de driving adapter web.
|
||||
|
||||
Côté desktop, `crates/app-tauri/src/lib.rs` enregistre les commandes UI implémentées dans `crates/app-tauri/src/tickets.rs`. Côté web, `crates/web-server/src/lib.rs` expose seulement quelques commandes dans le dispatcher `POST /api/invoke` (`health`, `list_projects`, `open_project`, `get_project_work_state`, tâches de fond). Aucune commande `ticket_*` ou `sprint_*` n'y est reconnue aujourd'hui, donc le `HttpTicketGateway` web tombe en `UNKNOWN_COMMAND`.
|
||||
|
||||
Le frontend web est déjà prêt côté transport : `frontend/src/adapters/http/streamGateways.ts` contient `HttpTicketGateway`, câblé dans `frontend/src/adapters/http/index.ts`, et il appelle les mêmes noms de commandes que le desktop via `/api/invoke`. Le blocage principal est donc backend/contrat, pas layout/UI.
|
||||
|
||||
## Commandes web-server à ajouter
|
||||
|
||||
Rester sur le style RPC existant : ajouter des branches à `POST /api/invoke`, pas créer une nouvelle API REST `/api/tickets`.
|
||||
|
||||
Tickets :
|
||||
|
||||
- `ticket_create` — `{ request: { projectId, title, description?, priority?, status?, assignedAgentIds?, links? } }` → `TicketDto`.
|
||||
- `ticket_read` — `{ request: { projectId, ref, includeCarnet? } }` → `TicketDto`.
|
||||
- `ticket_list` — `{ request: { projectId, statuses?, priorities?, assignedAgentId?, sprintId?, text?, sort?, limit?, cursor? } }` → `TicketListDto`.
|
||||
- `ticket_update` — `{ request: { projectId, ref, title?, description?, status?, priority?, assignedAgentIds?, expectedVersion } }` → `TicketDto`. Cette commande couvre l'édition statut/priorité côté UI ; les commandes séparées `idea_ticket_update_status` / `idea_ticket_update_priority` sont des tools MCP agent, pas des commandes UI Tauri.
|
||||
- `ticket_delete` — `{ request: { projectId, ref } }` → vide/null.
|
||||
- `ticket_read_carnet` — `{ request: { projectId, ref } }` → `TicketCarnetDto`.
|
||||
- `ticket_update_carnet` — `{ request: { projectId, ref, carnet, expectedVersion } }` → `TicketDto`.
|
||||
- `ticket_link` — `{ request: { projectId, ref, targetRef, kind, expectedVersion } }` → `TicketDto`.
|
||||
- `ticket_unlink` — `{ request: { projectId, ref, targetRef, kind?, expectedVersion } }` → `TicketDto`.
|
||||
- `ticket_assign` — `{ request: { projectId, ref, agentId, assigned, expectedVersion } }` → `TicketDto`.
|
||||
- `ticket_assign_sprint` — `{ request: { projectId, ref, sprintId, expectedVersion } }` → `TicketDto`.
|
||||
- `ticket_unassign_sprint` — `{ request: { projectId, ref, expectedVersion } }` → `TicketDto`.
|
||||
|
||||
Sprints :
|
||||
|
||||
- `sprint_create` — `{ request: { projectId, name, status? } }` → `SprintDto`.
|
||||
- `sprint_list` — `{ request: { projectId } }` → `SprintListDto { items }`.
|
||||
- `sprint_rename` — `{ request: { projectId, sprintId, name, expectedVersion } }` → `SprintDto`.
|
||||
- `sprint_reorder` — `{ request: { projectId, orderedIds } }` → `SprintListDto { items }`.
|
||||
- `sprint_delete` — `{ request: { projectId, sprintId } }` → vide/null ; le comportement existant conserve les tickets et les repasse en `Sans sprint`.
|
||||
|
||||
Commandes assistant ticket proches mais hors minimum #86 : `open_ticket_chat`, `close_ticket_chat`, puis le flux `sendTicketChat`/`chat.*`. Recommandation : ne pas les inclure dans le lot initial, car la demande vise l'édition tickets/sprints et le flux chat web est une surface live distincte.
|
||||
|
||||
## Factorisation nécessaire
|
||||
|
||||
Les DTO publics tickets/sprints (`TicketDto`, `TicketSummaryDto`, `TicketListDto`, `TicketCarnetDto`, `SprintDto`, `SprintListDto`, request DTOs, pagination/tri/parse helpers) vivent aujourd'hui dans `crates/app-tauri/src/tickets.rs`. `web-server` ne doit pas dépendre de `app-tauri`.
|
||||
|
||||
Option recommandée : déplacer le contrat pur tickets/sprints vers `backend` (`backend::dto` ou `backend::tickets`) puis faire consommer ce module par les deux driving adapters :
|
||||
|
||||
- `app-tauri` garde uniquement les wrappers `#[tauri::command]` ;
|
||||
- `web-server` ajoute des helpers `invoke_ticket_*` / `invoke_sprint_*` ;
|
||||
- les tests de pagination/tri/conflit actuellement proches du module Tauri migrent avec la logique pure.
|
||||
|
||||
Option minimale possible mais moins saine : dupliquer DTO/conversions dans `web-server`. À éviter, car cela crée deux contrats wire desktop/web à maintenir.
|
||||
|
||||
## Auth et sécurité web (#77)
|
||||
|
||||
Les nouvelles commandes doivent rester derrière le gate existant de `POST /api/invoke` : pairing device, cookie de session HttpOnly, contrôle d'origine/CORS, révocation device et logs sécurité. Ne pas exposer de mutation ticket/sprint en GET ni hors allowlist.
|
||||
|
||||
Point à arbitrer produit/sécurité : un device web appairé pourra modifier les fichiers projet `.ideai/tickets` et `.ideai/sprints`. C'est cohérent avec la demande, mais il n'existe pas actuellement de permission fine par device lecture seule/écriture. Si cette granularité est souhaitée, c'est un chantier séparé de policy device, pas un prérequis technique au #86.
|
||||
|
||||
## Événements live
|
||||
|
||||
Les domain events `issue*` et `sprint*` existent déjà dans `backend::events::DomainEventDto`, et le web-server relaie les événements domaine sur websocket `event.domain` en excluant seulement `PtyOutput`. Après exposition des commandes, les vues web peuvent réutiliser les helpers frontend `isTicketEvent` / `isSprintEvent` et refaire un refetch, sans nouveau canal événementiel.
|
||||
|
||||
## Tests backend attendus
|
||||
|
||||
- Non authentifié : `ticket_list` et une mutation ticket/sprint retournent `UNAUTHORIZED`.
|
||||
- Authentifié : `ticket_list` et `sprint_list` retournent le même shape que Tauri.
|
||||
- Cycle ticket : create → read → update titre/statut/priorité → carnet → link/unlink → delete.
|
||||
- Cycle sprint : create → list → rename → reorder → assign ticket → unassign ticket → delete.
|
||||
- Conflit `expectedVersion` propagé en `ErrorDto` cohérent avec le desktop.
|
||||
- Les commandes hors allowlist restent `UNKNOWN_COMMAND`.
|
||||
|
||||
## Découpe proposée
|
||||
|
||||
Lot 1 — backend/contrat web : factorisation DTO, ajout des 17 commandes `ticket_*`/`sprint_*` dans `/api/invoke`, tests auth/lecture/mutation/conflit.
|
||||
|
||||
Lot 2 — surface web UX/frontend : implémenter les vues décrites ci-dessus sur le `HttpTicketGateway` existant, gérer responsive, confirmations et conflits.
|
||||
|
||||
Lot 3 optionnel — assistant ticket web : exposer et finaliser le flux chat seulement si la surface assistant est explicitement demandée.
|
||||
16
.ideai/tickets/86/issue.md
Normal file
16
.ideai/tickets/86/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "fd0fa9e2-cdf3-4807-919a-f562c9b21026"
|
||||
number: 86
|
||||
title: "[UI] ajouter l'affichage et l'dition des tickets et des sprints à la version web"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784449151968
|
||||
updatedAt: 1784612705899
|
||||
version: 7
|
||||
---
|
||||
J'iamerais que la version web me permette aussi d'editer les tickets, d'en ajouter et d'en supprimer, ainsi que d'editer, ajouter ou supprimer des sprints
|
||||
6
.ideai/tickets/87/carnet.md
Normal file
6
.ideai/tickets/87/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#87"
|
||||
version: 7
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784650196560
|
||||
---
|
||||
16
.ideai/tickets/87/issue.md
Normal file
16
.ideai/tickets/87/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "0fabb0dd-9a83-4bc1-bbad-d96c794faed5"
|
||||
number: 87
|
||||
title: "[UI] sur l'affichage Web, la liste des background task polluent l'affchage"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784451329374
|
||||
updatedAt: 1784650196560
|
||||
version: 7
|
||||
---
|
||||
Sur l'affichage Web, la liste qui affiche toutes les background task polluent l'affichage. Il faudrait que la liste soit par défaut repliée de façon a n'avoir dans un premier temps que la liste des agents
|
||||
6
.ideai/tickets/89/carnet.md
Normal file
6
.ideai/tickets/89/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#89"
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784661583231
|
||||
---
|
||||
16
.ideai/tickets/89/issue.md
Normal file
16
.ideai/tickets/89/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "1f3e913f-0896-43be-818a-614e75d1fe3f"
|
||||
number: 89
|
||||
title: "Pouvoir lancer le serveur au lancement IdeA"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784649907432
|
||||
updatedAt: 1784661583231
|
||||
version: 5
|
||||
---
|
||||
J'aiemrais avoir une option pour pouvoir lancer le serveur web IdeA au lancement d'IdeA
|
||||
6
.ideai/tickets/90/carnet.md
Normal file
6
.ideai/tickets/90/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#90"
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784661583085
|
||||
---
|
||||
16
.ideai/tickets/90/issue.md
Normal file
16
.ideai/tickets/90/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "828e32cc-83af-4a59-99b2-d0dd3e496c5f"
|
||||
number: 90
|
||||
title: "Ajouter les menus manquantes à la version Web d'IdeA"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784649988741
|
||||
updatedAt: 1784661583085
|
||||
version: 5
|
||||
---
|
||||
J'iamerais que les menus qui ne sont pas encore disponibles dans la version Web le deviennent (J'aimerais que tous els menus du menus "Panneaux" soient disponibles dans la version web, ainsi que le setup des profils AI du menu Settings)
|
||||
6
.ideai/tickets/91/carnet.md
Normal file
6
.ideai/tickets/91/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#91"
|
||||
version: 4
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784994105390
|
||||
---
|
||||
16
.ideai/tickets/91/issue.md
Normal file
16
.ideai/tickets/91/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "2d54c254-d2e8-44b3-ac12-3bf20f818d23"
|
||||
number: 91
|
||||
title: "Sur la notification de fin de tache backend, mettre plutot les deux agents en conversation"
|
||||
status: "open"
|
||||
priority: "medium"
|
||||
sprint: "5afd6780-0f76-40d7-a10f-32ee52469d74"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784652125081
|
||||
updatedAt: 1784994105390
|
||||
version: 4
|
||||
---
|
||||
Sur la notification de fin de tache backend, mettre plutot les deux agents en conveersation et qui a lancé l'appel (par exemple Main->DevBackend) pour que ça soit un peu plus explicite
|
||||
25
.ideai/tickets/92/carnet.md
Normal file
25
.ideai/tickets/92/carnet.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
issueRef: "#92"
|
||||
version: 9
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784993956910
|
||||
---
|
||||
## Résumé de livraison
|
||||
|
||||
Backend et frontend livrés sur `feature/ticket92-opencode-provider-cloud`, mergée dans `develop` (merge `12c7d10`).
|
||||
|
||||
**Backend** (commit `23a3c27`) :
|
||||
- Domain : `OpenCodeProviderConfig { provider_id, model, api_key_ref: SecretRef }` sur `AgentProfile.opencode_provider`, invariant `opencode_backend_is_consistent()` (llamacpp local XOR provider cloud, jamais les deux).
|
||||
- Port `SecretStore` + adaptateur `FsSecretStore` (AES-256-GCM via `ring`, clé locale `secret.key` chmod 0600) — la clé API cloud n'est jamais en clair dans `profiles.json`.
|
||||
- Catalogue de providers **statique** (`application/agent/provider_catalogue.rs`) exposé via la commande Tauri `list_opencode_providers`.
|
||||
- Use case `SaveOpenCodeProviderProfile` (scelle la clé API dans le `SecretStore`, mint/réutilise un `SecretRef`) exposé via `save_opencode_provider_profile`. Pas de patch partiel : la clé littérale est obligatoire à chaque appel, y compris en édition.
|
||||
- `DeleteProfile` purge le secret associé si le profil supprimé porte `opencode_provider`.
|
||||
- Résolution de la clé au lancement dans les deux générateurs de `opencode.json` (`infrastructure/assistant/mod.rs`, `application/agent/lifecycle.rs`) ; échec dur si secret absent.
|
||||
- QA vert : `cargo build --workspace` + `cargo test --workspace -- --test-threads=1` (tests de couverture SaveOpenCodeProviderProfile/DeleteProfile ajoutés suite à un premier retour QA).
|
||||
|
||||
**Frontend** (commit `c181b43`) :
|
||||
- First-run wizard : segmented control Local/Cloud exclusif dans le formulaire de profil OpenCode, sous-formulaire cloud (provider → modèle en cascade, clé API masquée jamais pré-remplie), validation inline, bannière d'erreur de sauvegarde.
|
||||
- En édition d'un profil cloud existant : champ clé toujours vide, bouton de sauvegarde désactivé tant que la clé n'est pas ressaisie (contrainte backend, pas de patch partiel).
|
||||
- QA vert : `npm run build` + `vitest run` (952 tests, y compris un test ajouté pour le parcours d'édition suite à un retour QA).
|
||||
|
||||
**Dette hors périmètre signalée par Architect, non traitée ici** : duplication de deux fonctions `opencode_config_json`/`opencode_provider_config_json` entre `infrastructure/assistant/mod.rs` et `application/agent/lifecycle.rs` — à trancher (laquelle est la vraie) dans un ticket séparé si besoin.
|
||||
16
.ideai/tickets/92/issue.md
Normal file
16
.ideai/tickets/92/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "9ba8d2f4-49fa-4536-a38e-891f1df3e3b7"
|
||||
number: 92
|
||||
title: "Configurer OpenCode avec un provider Opencode"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784729443275
|
||||
updatedAt: 1784993956910
|
||||
version: 9
|
||||
---
|
||||
On peut actuellement utiliser un profile AI qui utilise OpenCode pour llamacpp. J'aimerais que l'on puisse configurer des profil AI OpenCode avec llamacpp comme actuellement, mais aussi en selectionnant un provider listé dans la commande /connect de OpenCode. Je pense que le mieux serait que lors de la création d'un profile AI OpenCode, il nous soit demandé si on souhaite utiliser LlamaCpp ou un provider OpenCode. Dans le cas d'un provider llamaCpp, on utilise la même chose qu'actuellement, dans l'autre cas on nous demande quel provider et il faudrait être capable de récupérer la liste fournie par OpenCode. Une fois le provider selectionné, il faudra que l'utilisateur puisse entrer une clé API car les providers opencodes en demadnent toujours un. Pour la partie utilisation ensuite d'opencode dans les agent, je pense que cette page peut aider: https://opencode.ai/docs/fr/cli/. On y trouve entre autre la commande pour se connecter avec opencode auth login. Le but est que la partie configuration se fasse dans les profil AI comme jusqu'à présent, et que l'utilisateur puisse directement attribuer un agent à son profile AI et le lancer sans configurer d'autre choses. OpenCode marche déjà correctement avec llamacpp, j'insiste sur le fait qu'on ne fait qu'ajouter la possibilité de configurer un provider autre que llamacpp
|
||||
46
.ideai/tickets/93/carnet.md
Normal file
46
.ideai/tickets/93/carnet.md
Normal file
@ -0,0 +1,46 @@
|
||||
---
|
||||
issueRef: "#93"
|
||||
version: 3
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784934179780
|
||||
---
|
||||
# Contexte
|
||||
|
||||
Test manuel demandé par l'utilisateur pour valider la conversation inter-agent : Main a appelé `idea_ask_agent(target="Context", task="...")` avec une tâche de simple confirmation d'identité/rôle.
|
||||
|
||||
## Observation
|
||||
|
||||
Les deux appels (identiques) ont renvoyé, à la place d'une réponse finale en langage naturel, exactement ce fragment JSON brut :
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "idea_idea_context_read",
|
||||
"arguments": {}
|
||||
}
|
||||
```
|
||||
|
||||
- Le nom d'outil est corrompu : préfixe `idea_` dupliqué (`idea_idea_context_read` au lieu de `idea_context_read`).
|
||||
- Le fragment a la forme d'un appel d'outil (tool_use), pas d'un texte de réponse — semble être une tentative de Context de lire son contexte projet via `idea_context_read`, jamais exécutée, puis remontée telle quelle comme si c'était le Final capturé.
|
||||
- Reproduit à l'identique sur 2 tentatives consécutives → pas un aléa de génération, plutôt un bug de câblage/capture.
|
||||
|
||||
## Détails agent Context
|
||||
|
||||
- id: `5d07e4a7-8676-4a71-aea1-5b64caefa944`
|
||||
- contextPath: `agents/context.md`
|
||||
- profileId: `a7037cbe-6d04-48fb-ba74-f353c93fc70a` (profil différent de celui des autres agents du manifeste, qui partagent tous `664cc20c-47b8-53ad-9351-dce3c09c0de4` — Context est le seul sur ce profil)
|
||||
- origin: scratch
|
||||
|
||||
## Pistes d'investigation pour Architect / DevBackend
|
||||
|
||||
1. Le profil `a7037cbe-...` (LLM local léger dédié à Context, cf. mémoire `agent-context-memory-and-profile-handoff`) semble ne pas capturer correctement le "Final" du tour — le pont MCP/runtime renverrait le premier tool_use brut au lieu d'attendre la réponse texte finale.
|
||||
2. Vérifier le mapping des noms d'outils exposés à ce profil : la duplication `idea_idea_*` suggère un préfixage appliqué deux fois (une fois côté définition d'outil MCP, une fois côté wrapper du profil/pont).
|
||||
3. Comparer avec le comportement des autres profils (`664cc20c-...`) qui fonctionnent correctement en inter-agent, pour isoler ce qui diffère structurellement dans le pont pour un profil local/structured.
|
||||
4. Voir mémoire projet `mcp-bridge-and-delegation-runtime-notes` pour les pièges déjà connus du pont MCP/délégation — possible recoupement.
|
||||
|
||||
## Repro
|
||||
|
||||
Depuis Main :
|
||||
```
|
||||
idea_ask_agent(target="Context", task="<n'importe quelle tâche simple>")
|
||||
```
|
||||
→ renvoie le fragment JSON ci-dessus au lieu d'une réponse.
|
||||
27
.ideai/tickets/93/issue.md
Normal file
27
.ideai/tickets/93/issue.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
id: "40f56f7d-b0ec-4409-bf6b-c010e3003b76"
|
||||
number: 93
|
||||
title: "Agent Context : réponse finale non capturée, fragment d'appel d'outil brut renvoyé via idea_ask_agent"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784817692650
|
||||
updatedAt: 1784934179780
|
||||
version: 3
|
||||
---
|
||||
Lors d'un test de la conversation inter-agent (idea_ask_agent ciblant Context), l'appel renvoie systématiquement un fragment JSON brut au lieu d'une réponse finale exploitable :
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "idea_idea_context_read",
|
||||
"arguments": {}
|
||||
}
|
||||
```
|
||||
|
||||
Le nom d'outil est corrompu (préfixe "idea_" dupliqué : "idea_idea_context_read" au lieu de "idea_context_read"), et ce fragment ressemble à une tentative d'appel d'outil non exécutée, renvoyée telle quelle comme si c'était la réponse finale du modèle.
|
||||
|
||||
Comportement reproduit deux fois de suite à l'identique, donc pas un aléa ponctuel.
|
||||
6
.ideai/tickets/94/carnet.md
Normal file
6
.ideai/tickets/94/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#94"
|
||||
version: 2
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784823278030
|
||||
---
|
||||
48
.ideai/tickets/94/issue.md
Normal file
48
.ideai/tickets/94/issue.md
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
id: "33a844e0-6a06-46c7-b800-d3497a7f45f4"
|
||||
number: 94
|
||||
title: "Permissions OpenCode ignorées : opencode.json code en dur bash/edit=ask au lieu de projeter EffectivePermissions"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784821271815
|
||||
updatedAt: 1784823278030
|
||||
version: 2
|
||||
---
|
||||
## Bug
|
||||
|
||||
Les permissions configurées dans IdeA pour un agent sous profil OpenCode ne sont PAS appliquées dans le `opencode.json` généré. Le bloc `permission` est codé en dur à `{"bash":"ask","edit":"ask"}` quel que soit le réglage IdeA.
|
||||
|
||||
## Repro
|
||||
|
||||
Config IdeA (agent OpenCode) : Read=Allow, Write=Deny, Delete=Allow, bash=Allow.
|
||||
`opencode.json` généré (run dir) : `"permission":{"bash":"ask","edit":"ask"}`.
|
||||
Attendu : `bash` reflète la posture bash (Allow→"allow"), `edit` reflète la posture Write (Deny→"deny").
|
||||
|
||||
Claude et Codex appliquent correctement les permissions ; seul OpenCode est touché.
|
||||
|
||||
## Cause racine
|
||||
|
||||
Le pattern de projection des permissions (`PermissionProjector` + `ProjectorKey::{Claude,Codex}` dans `crates/infrastructure/src/permission/`) est bypassé pour OpenCode. Les générateurs `opencode.json` codent `permission` en dur à 4 endroits :
|
||||
|
||||
- `crates/application/src/agent/lifecycle.rs:2728-2734` (`opencode_config_json`, variante llamacpp)
|
||||
- `crates/application/src/agent/lifecycle.rs:2814-2820` (`opencode_provider_config_json`, variante cloud)
|
||||
- `crates/infrastructure/src/assistant/mod.rs:406-412` (`opencode_config_json`, doublon)
|
||||
- `crates/infrastructure/src/assistant/mod.rs:494-500` (`opencode_provider_config_json`, doublon)
|
||||
|
||||
Les call sites (`lifecycle.rs:2361-2378` branche `OpenCodeConfig`, et `assistant/mod.rs:247-256`) ne passent pas les `EffectivePermissions` résolues aux générateurs.
|
||||
|
||||
## Mapping attendu (à confirmer par Architect)
|
||||
|
||||
OpenCode `permission` = map tool→"allow"|"ask"|"deny". IdéA → OpenCode :
|
||||
- `bash` ← posture bash effective
|
||||
- `edit` ← posture Write effective
|
||||
- Read/Delete ne sont pas exprimables dans opencode (pas de clé read/delete) — déjà enforcees par le sandbox Landlock (mémoire `permissions-sandbox-system-state`).
|
||||
|
||||
## Périmètre
|
||||
|
||||
Backend Rust pur. Validation possible par tests unitaires sur les générateurs (`opencode_provider_config_json` est déjà testé à lifecycle.rs:4413 et assistant/mod.rs:639) sans rebuild AppImage. Validation live = rebuild AppImage + relance IdeA.
|
||||
36
.ideai/tickets/95/carnet.md
Normal file
36
.ideai/tickets/95/carnet.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
issueRef: "#95"
|
||||
version: 6
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784934174326
|
||||
---
|
||||
## Cause racine RÉELLE (2026-07-24, après investigation code)
|
||||
|
||||
### Ce qui se passe
|
||||
|
||||
Le `"timeout": 15000` (15 secondes) **hardcoded** dans le bloc `mcp.idea` de l'`opencode.json` généré par IdeA. OpenCode applique ce timeout **par appel `tools/call`** à ses serveurs MCP. `idea_ask_agent` bloque pendant que l'agent cible travaille (potentiellement des minutes) → OpenCode tue la requête à 15s avec l'erreur `-32001: Request timed out`.
|
||||
|
||||
**Pourquoi Claude/Codex ne sont pas affectés** : leur config MCP (`.mcp.json` / `config.toml`) n'a **pas** de champ `timeout`. Ce champ est spécifique à la config OpenCode.
|
||||
|
||||
### Emplacements du hardcoded 15000 (4 occurrences)
|
||||
|
||||
1. `application/src/agent/lifecycle.rs:2728` — `opencode_config_json()`
|
||||
2. `application/src/agent/lifecycle.rs:2811` — `opencode_provider_config_json()`
|
||||
3. `infrastructure/src/assistant/mod.rs:410` — `opencode_config_json()` (duplicate)
|
||||
4. `infrastructure/src/assistant/mod.rs:495` — `opencode_provider_config_json()` (duplicate)
|
||||
|
||||
### Fix appliqué (feature/ticket95-opencode-mcp-timeout)
|
||||
|
||||
- Constante `DEFAULT_OPENCODE_MCP_TIMEOUT_MS` = 4h (14_400_000 ms), alignée sur `DEFAULT_RENDEZVOUS_CEILING` — le client MCP ne expire jamais avant le watchdog serveur.
|
||||
- Fonction `resolve_opencode_mcp_timeout_ms()` — overridable via `IDEA_OPENCODE_MCP_TIMEOUT_MS`, fallback sûr sur 0/parse-error.
|
||||
- Les 4 occurrences remplacées par `resolve_opencode_mcp_timeout_ms()`.
|
||||
- Exporté depuis `application::agent` pour réutilisation par `infrastructure`.
|
||||
- **Claude/Codex non touchés** : aucune modification de leur config MCP.
|
||||
|
||||
### Tests
|
||||
- Application : 115 passed, 0 failed
|
||||
- Infrastructure : 313 passed, 0 failed
|
||||
- Compilation : OK, aucun nouveau warning
|
||||
|
||||
### Note déploiement
|
||||
Le `opencode.json` est régénéré à chaque lancement d'agent par le code lifecycle. Nécessite rebuild AppImage pour que le binaire qui tourne génère la nouvelle config.
|
||||
54
.ideai/tickets/95/issue.md
Normal file
54
.ideai/tickets/95/issue.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
id: "611077a6-f703-44b9-935b-64f8301085bb"
|
||||
number: 95
|
||||
title: "Rendez-vous idea_ask_agent : timeout MCP OpenCode trop court (15s), le demandeur OpenCode ne reçoit jamais la réponse"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784822537740
|
||||
updatedAt: 1784934174326
|
||||
version: 6
|
||||
---
|
||||
## Bug
|
||||
|
||||
Une délégation `idea_ask_agent` depuis un agent sous profil **OpenCode** (le **demandeur**) échoue systématiquement en `timeout` (-32001 « Request timed out »), peu importe la cible (Claude, Codex, ou OpenCode). Les agents cibles terminent bien leurs tâches (visible dans le workstate = done), mais leurs réponses ne sont jamais livrées au demandeur OpenCode.
|
||||
|
||||
Claude et Codex ne sont **pas** affectés, qu'ils soient demandeur ou cible.
|
||||
|
||||
## Cause racine (vérifiée dans les sources)
|
||||
|
||||
Le `"timeout": 15000` (15 secondes) **hardcoded** dans le bloc `mcp.idea` de l'`opencode.json` généré par IdeA. OpenCode applique ce timeout **par appel `tools/call`** à ses serveurs MCP. `idea_ask_agent` bloque pendant que l'agent cible travaille (potentiellement des minutes) → OpenCode tue la requête à 15s.
|
||||
|
||||
**Pourquoi Claude/Codex ne sont pas affectés** : leur config MCP (`.mcp.json` / `config.toml`) n'a **pas** de champ `timeout`. Ce champ est spécifique à la config OpenCode (`opencode.json` → `mcp.idea.timeout`).
|
||||
|
||||
### Emplacements (4 occurrences de `15000`)
|
||||
|
||||
1. `crates/application/src/agent/lifecycle.rs` — `opencode_config_json()`
|
||||
2. `crates/application/src/agent/lifecycle.rs` — `opencode_provider_config_json()`
|
||||
3. `crates/infrastructure/src/assistant/mod.rs` — `opencode_config_json()` (duplicate)
|
||||
4. `crates/infrastructure/src/assistant/mod.rs` — `opencode_provider_config_json()` (duplicate)
|
||||
|
||||
## Ce qui n'est PAS la cause
|
||||
|
||||
- Ce n'est pas un problème de sonde de vivacité du rendez-vous (la description originale pointait la sonde Claude-only côté cible — mauvaise piste).
|
||||
- Ce n'est pas un problème de permissions, de sandbox, ou de taille de payload.
|
||||
- Ce n'est pas lié au modèle : tout profil `structuredAdapter: "openCode"` est touché en tant que demandeur.
|
||||
|
||||
## Fix appliqué (feature/ticket95-opencode-mcp-timeout)
|
||||
|
||||
- Constante `DEFAULT_OPENCODE_MCP_TIMEOUT_MS` = 4h (14_400_000 ms), alignée sur `DEFAULT_RENDEZVOUS_CEILING`.
|
||||
- Fonction `resolve_opencode_mcp_timeout_ms()` — overridable via `IDEA_OPENCODE_MCP_TIMEOUT_MS`, fallback sûr.
|
||||
- Les 4 occurrences remplacées.
|
||||
- **Claude/Codex non touchés**.
|
||||
|
||||
## Tests
|
||||
|
||||
Application : 115 passed. Infrastructure : 313 passed. 0 failed.
|
||||
|
||||
## Déploiement
|
||||
|
||||
Nécessite rebuild AppImage (l'`opencode.json` est régénéré à chaque lancement d'agent).
|
||||
18
.ideai/tickets/96/carnet.md
Normal file
18
.ideai/tickets/96/carnet.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
issueRef: "#96"
|
||||
version: 3
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784994119004
|
||||
---
|
||||
## Décision (2026-07-24) : différée — documentée, non codée
|
||||
|
||||
Sur instruction utilisateur (#96 mis en pause pour se concentrer sur #95) : **on documente le constat et la décision de fond reste ouverte pour plus tard**. Aucun code ajouté.
|
||||
|
||||
### Constat confirmé dans les sources
|
||||
`crates/infrastructure/src/assistant/mod.rs::TicketAssistantEnvironmentPreparer::materialise_mcp` passe `eff: None` à `opencode_config_json` / `opencode_provider_config_json` (lignes ~253 et ~262). Le commentaire local (l. 246-251) le documente déjà : aucun `PermissionStore`/`EffectivePermissions` n'est résolu sur ce chemin, **pour aucun adaptateur**. Conséquence : les assistants de ticket tournent toujours avec le comportement **natif** d'OpenCode (prompting à chaque action), indépendamment des permissions IdeA configurées.
|
||||
|
||||
### Pourquoi c'est différé (la vraie question produit)
|
||||
Les assistants de ticket sont lancés depuis un **profil** (pas depuis un **agent** avec une politique par-agent). Il n'y a donc **pas de source naturelle** d'`EffectivePermissions` sur ce chemin. Câbler nécessite d'abord de trancher : les permissions viennent-elles du profil ? d'un fallback projet ? d'une posture dédiée aux assistants ? Tant que ce choix produit n'est pas posé, `eff: None` (= prompting natif) reste le défaut **sûr** — ce n'est pas une régression, c'est le comportement historique préservé.
|
||||
|
||||
### Suivi
|
||||
Non bloquant, priorité basse. À reprendre quand un besoin produit le justifie : définir la source d'autorités pour un assistant de ticket, puis la résoudre ici (comme pour les agents normaux dans `application::agent::lifecycle`).
|
||||
18
.ideai/tickets/96/issue.md
Normal file
18
.ideai/tickets/96/issue.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
id: "d5993d21-307b-4fc8-93f9-2bfbf44223d8"
|
||||
number: 96
|
||||
title: "Les assistants de ticket (tous adaptateurs) n'ont aucune résolution EffectivePermissions — permission toujours native/absente"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: "883534aa-7fc1-4d83-a9c0-17cac4a4eea5"
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784823281794
|
||||
updatedAt: 1784994119004
|
||||
version: 3
|
||||
---
|
||||
Trouvé en implémentant #94 (projection des permissions dans opencode.json). Le call site des assistants de ticket dans `crates/infrastructure/src/assistant/mod.rs` n'a **aucun** `PermissionStore`/`EffectivePermissions` câblé, pour aucun adaptateur (Claude, Codex, OpenCode) — vérifié par grep sur la composition root `crates/backend/src/lib.rs`. Le fix #94 y passe donc `eff: None` (préserve le comportement natif existant, pas de régression), mais ce n'est pas un vrai fix de fond : les permissions IdeA configurées pour un agent n'ont jamais été appliquées aux assistants de ticket, quel que soit l'adaptateur.
|
||||
|
||||
À trancher par Architect : soit câbler la résolution `EffectivePermissions` pour ce call site (comme pour les agents normaux), soit documenter que c'est un choix produit intentionnel (permanent) et fermer sans y toucher. Non bloquant, priorité basse.
|
||||
28
.ideai/tickets/97/carnet.md
Normal file
28
.ideai/tickets/97/carnet.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
issueRef: "#97"
|
||||
version: 3
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784915626417
|
||||
---
|
||||
## Décision Architect (cadrage validé)
|
||||
|
||||
Bug 100% backend, deux défauts composés :
|
||||
1. `SaveOpenCodeProviderProfile::execute` (`usecases.rs:364-367`) pose `opencode_provider` sans `opencode = None`.
|
||||
2. Invariant `opencode_backend_is_consistent` (`profile.rs:1220`) existe mais jamais appelé (garde morte).
|
||||
Lecture priorise `opencode` → cloud écrasé (`lifecycle.rs:2367`, `assistant/mod.rs:252`).
|
||||
|
||||
### Périmètre du lot (un seul, parallélisable)
|
||||
- **DevBackend** :
|
||||
- Domaine : `with_opencode`/`with_opencode_provider` (`profile.rs:1132,1140`) imposent l'exclusion mutuelle (chacun efface l'autre).
|
||||
- Infrastructure : `FsProfileStore::save` rejette tout profil incohérent → `AppError::Invalid` (active enfin le prédicat).
|
||||
- Application : `SaveOpenCodeProviderProfile` reconstruit via builder (`with_opencode_provider`) ; idem pour `SaveProfile`/`ConfigureProfiles`.
|
||||
- **Migration requise** : à la lecture (ou passe dédiée), quand `opencode` ET `opencodeProvider` présents → dropper `opencode` stale (l'intention est cloud). Sinon ne corrige que les nouveaux profils.
|
||||
- **DevFrontend** : strip de la config inactive au save selon le mode courant (requis pour SaveProfile/ConfigureProfiles qui ne portent pas d'intention explicite ; backend reste autorité via la garde).
|
||||
- **QA** : tests unitaires domaine (builders exclusifs) + garde store + intégration création profil cloud + scénario migration profils corrompus.
|
||||
|
||||
### Décisions de frontière
|
||||
- DTO `SaveOpenCodeProviderProfileRequestDto` **inchangé** (whole-profile) ; output = profil normalisé, autorité pour le frontend.
|
||||
- Priorité lecture `opencode` d'abord **gardée** (irrelevant post-exclusion), commenter comme fallback défensif.
|
||||
- Duplication de la résolution (lifecycle.rs:2367 + assistant/mod.rs:252) = dette hexagonale préexistante, **hors périmètre de ce lot** (suivre, ne pas refactoriser ici).
|
||||
|
||||
Voir mémoire projet `ticket97-opencode-provider-mutual-exclusion`.
|
||||
26
.ideai/tickets/97/issue.md
Normal file
26
.ideai/tickets/97/issue.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
id: "fd7057dc-0177-41e1-811c-701c802e81d3"
|
||||
number: 97
|
||||
title: "Duplication de provider lors de la création de profil AI - llamacpp ET provider cloud inclus simultanément"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784899606438
|
||||
updatedAt: 1784915626417
|
||||
version: 3
|
||||
---
|
||||
Lors de la création d'un profil AI dans le premier-run wizard, le système génère un profiles.json qui contient à la fois la section `opencode` (llamacpp) et `opencodeProvider` (cloud), quel que soit le choix de l'utilisateur.
|
||||
|
||||
**Comportement attendu :**
|
||||
- Si l'utilisateur choisit "llamacpp" : seule la section `opencode` doit être présente
|
||||
- Si l'utilisateur choisit "provider cloud" : seule la section `opencodeProvider` doit être présente
|
||||
|
||||
**Comportement actuel :**
|
||||
Les deux sections sont présentes simultanément, ce qui fait que llamacpp est priorisé même quand l'utilisateur veut utiliser un provider cloud (ex: ZAI/GLM).
|
||||
|
||||
**Cas de test :**
|
||||
Création du profil GLM5.2 avec provider ZAI Code → profiles.json contient `opencodeProvider` correct MAIS contient aussi une section `opencode` vide ou inutile, ce qui force le fallback sur llamacpp.
|
||||
87
.ideai/tickets/98/carnet.md
Normal file
87
.ideai/tickets/98/carnet.md
Normal file
@ -0,0 +1,87 @@
|
||||
---
|
||||
issueRef: "#98"
|
||||
version: 5
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784983186248
|
||||
---
|
||||
# Carnet #98 — Contrat figé (approche **B2**)
|
||||
|
||||
> Cadrage ARCHITECT figé et gelé. **Source de vérité pour DevBackend et QA.** Ce carnet remplace la phase d'arbitrage ; les options A/B1/C sont closes (voir §2 pour le rejet motivé).
|
||||
|
||||
---
|
||||
|
||||
## 1. Cause racine affinée
|
||||
|
||||
Le modèle est perdu **au spawn**, pas à la sauvegarde (persistance vérifiée correcte bout en bout, `profiles.json` conserve bien `opencodeProvider.model`). Deux facteurs composés :
|
||||
|
||||
1. **Pas de bloc `models`** pour les providers catalogue connus — `opencode_provider_config_json` (`lifecycle.rs:2774-2844` + jumeau `assistant/mod.rs:428-503`) n'émet `npm`/`baseURL`/`models` **que** pour le chemin `custom`. Pour un provider catalogue (zai), seul `provider.<id>.options.apiKey` est écrit. Test fige ce manque : `lifecycle.rs:4439-4440`.
|
||||
2. **Cache models.dev isolé et vide** — IdeA passe `XDG_CACHE_HOME=<run_dir>/.opencode/cache` vide (`lifecycle.rs:2386,2405` ; `assistant/mod.rs:272,282`) + `HOME` isolé. Or `zai` n'est connu d'OpenCode **que** via ce cache (catalogue lu côté IdeA depuis le vrai `~/.cache/opencode/models.json`, `provider_catalogue.rs:79-84`).
|
||||
|
||||
→ OpenCode ne connaît plus `zai`, ne résout pas `zai/<modèle>` → **fallback silencieux `glm-5.2`** (fallback interne CLI OpenCode, absent du code IdeA).
|
||||
|
||||
Asymétrie confirmée : local llama.cpp et cloud custom marchent car ils émettent un bloc `models` (`assistant/mod.rs:371-379`, `:457-460`).
|
||||
|
||||
## 2. Approche retenue : **B2** (seed best-effort du cache hôte)
|
||||
|
||||
Copier best-effort le cache models.dev hôte (`opencode_models_cache_path()`) vers `<xdg_cache>/opencode/models.json` dans le cache isolé, juste après le `create_dir_all` du cache dans `apply_mcp_config` (branche commune opencode/opencodeProvider), aux **DEUX** sites.
|
||||
|
||||
**Rejet motivé des alternatives :**
|
||||
- **A (émettre bloc `models` pour providers connus)** : rejeté car `provider_catalogue.rs` ne persiste que `name`+`models` et **ignore `npm`+`baseURL`**. Un bloc `models` seul laisse OpenCode incapable de *joindre* `zai` — il faudrait en plus étendre le domaine (`display_name`/`npm`/`base_url`) → scope bien plus large et risque régression. A n'est viable qu'en révision A-étendue, écartée pour ce lot.
|
||||
- **B1 (= B sans factorisation, deux sites copiés)** : rejeté pour dette hexagonale — le writer `opencode_provider_config_json` est déjà dupliqué `lifecycle.rs` vs `assistant/mod.rs`. Dupliquer aussi le seed amplifierait la dette et rendrait toute divergence un bug silencieux. B2 impose une factorisation unique.
|
||||
- **C (hybride : pointer `XDG_CACHE_HOME` vers le vrai cache)** : rejeté — casse l'invariant d'isolation du run (un profil pourrait lire/écrire le cache hôte ou un cache d'un autre run), et OpenCode pourrait y écrire (logs, refresh) → pollution mutuelle.
|
||||
|
||||
**Pourquoi B2 :** donne à OpenCode sa connaissance registry complète (npm, baseURL, modèles) sans toucher au domaine ni casser l'isolation écriture — c'est de la donnée publique read-only copiée **dans** l'espace isolé.
|
||||
|
||||
## 3. Contrat figé — ports / fichiers / invariants
|
||||
|
||||
### 3.1 Fonctions à implémenter (factorisation)
|
||||
|
||||
- **Rendre `opencode_models_cache_path()` publique** — source de vérité unique du chemin hôte, **ne pas re-dériver** le chemin dans le seed.
|
||||
- **`seed_opencode_models_cache(fs, isolated_cache_dir)`** (fonction partagée) : lit le cache hôte via `opencode_models_cache_path()`, copie best-effort vers `<isolated_cache_dir>/opencode/models.json`. Appelée aux **deux** sites après le `create_dir_all` du cache dans `apply_mcp_config` (branche commune `opencode`/`opencodeProvider`).
|
||||
- **`seed_from_bytes(fs, dest, src_bytes)`** (partie pure testable) : extrait la logique d'écriture du fichier destination à partir de bytes source. C'est le seam de test unitaire.
|
||||
|
||||
### 3.2 Sites d'appel (les DEUX)
|
||||
|
||||
1. `crates/application/src/agent/lifecycle.rs` — après `create_dir_all` cache dans `apply_mcp_config`.
|
||||
2. `crates/infrastructure/src/assistant/mod.rs` — après `create_dir_all` cache dans `apply_mcp_config`.
|
||||
|
||||
### 3.3 Invariants (à respecter, à tester)
|
||||
|
||||
- **Isolation préservée en écriture** : le seed ne fait que copier une donnée read-only **vers** le cache isolé. Jamais de `XDG_CACHE_HOME` pointé vers l'hôte.
|
||||
- **Best-effort** : le seed **n'échoue jamais le launch**. Toute erreur (fichier hôte absent, IO) est tracée (warn/log) et ignorée — on retombe sur le comportement actuel (fallback glm-5.2), pas sur un crash.
|
||||
- **Pas de régression** : profils local (llama.cpp), custom et built-ins (anthropic/openai/openrouter) doivent continuer à fonctionner byte-identique au rendu `opencode.json` actuel.
|
||||
- **Exclusion mutuelle #97 non touchée** : le seed s'exécute sur la branche commune `opencode`/`opencodeProvider`, sans affecter la logique d'exclusion.
|
||||
- **Cohérence picker↔spawn** : le catalogue vu côté UI (picker) et celui seedé au spawn proviennent du même fichier hôte → l'utilisateur ne peut pas picker un modèle qu'OpenCode ne connaîtra pas au spawn.
|
||||
|
||||
### 3.4 Hors périmètre (explicitement exclu)
|
||||
|
||||
- Remote SSH/WSL (le seed ne concerne que le spawn local).
|
||||
- Déduplication du rendu lifecycle↔infra au-delà du seed (la dette du writer `opencode_provider_config_json` reste ouverte — autre lot).
|
||||
- UI wizard (aucun changement surface).
|
||||
|
||||
## 4. Périmètre QA — 2 couches
|
||||
|
||||
### Couche 1 — Unitaire pure (obligatoire, rapide, déterministe)
|
||||
- **`seed_from_bytes`** : assert écriture byte-identique du contenu source vers `dest`, gestion erreurs (fs en échec → pas de panic), idempotence.
|
||||
- **Non-régression rendu `opencode.json`** : les tests existants `lifecycle.rs:4439-4440` et équivalents infra doivent rester **byte-identiques** pour local/custom/built-ins (le seed ne change pas le rendu config). Mettre à jour l'assert si et seulement si B2 modifie réellement le rendu — sinon la conserver telle quelle.
|
||||
|
||||
### Couche 2 — Intégration gated (obligatoire avant fermeture du lot)
|
||||
- **Spawn réel OpenCode** sur profil `zai` + modèle X choisi.
|
||||
- **Assert** : OpenCode démarre sur le modèle X (pas de fallback `glm-5.2`).
|
||||
- Vérifier dans les background-tasks/IO qu'aucune trace `glm-5.2` n'apparaît comme modèle actif.
|
||||
- **Gate obligatoire à lever** : OpenCode **refresh/écrase-t-il le cache `models.json` au démarrage** ? → si **oui**, le seed est inutile (écrasé avant lecture) et **il faut escalader vers A-étendue** (persistir npm/baseURL/models côté domaine). Consigner le verdict dans le rapport QA.
|
||||
|
||||
## 5. Risque résiduel — refresh models.dev par OpenCode
|
||||
|
||||
**Risque ouvert, à trancher en QA couche 2.** Si OpenCode rafraîchit/écrase `<xdg_cache>/opencode/models.json` au démarrage (network call ou réécriture locale), le seed B2 est potentiellement **inopérant** :
|
||||
- Meilleur cas : OpenCode lit le cache avant tout refresh → B2 fonctionne.
|
||||
- Cas dégradé : OpenCode refresh en premier, écrase le seed, et comme le profil est isolé (pas d'accès réseau garanti / HOME isolé), le refresh peut échouer ou produire un cache incomplet → bug persiste.
|
||||
- **Plan de contournement si échec** : escalader vers A-étendue (ajouter `npm`+`base_url`+`models` persistés côté `OpenCodeProviderConfig` + émettre le bloc complet au spawn). Ce plan est documenté mais **hors scope B2** — il ferait l'objet d'un lot suivant si QA le confirme nécessaire.
|
||||
|
||||
---
|
||||
|
||||
## Contexte de mise à jour
|
||||
|
||||
- Cadrage figé par Main sur validation Architect (approche B2).
|
||||
- Version précédente (v1) : phase d'arbitrage A/B/C — clos.
|
||||
- Prochaine étape cycle : Git (branche) → DevBackend (implémentation 2 sites + factorisation) → QA (2 couches, gate refresh).
|
||||
38
.ideai/tickets/98/issue.md
Normal file
38
.ideai/tickets/98/issue.md
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
id: "9db40467-a826-4dac-a679-e7934cbdda81"
|
||||
number: 98
|
||||
title: "Profil cloud provider catalogue (ex: zai) : modèle choisi écrasé par glm-5.2 au spawn"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784966899811
|
||||
updatedAt: 1784983186248
|
||||
version: 5
|
||||
---
|
||||
## Symptôme
|
||||
|
||||
Dans le wizard de création de profil AI OpenCode (first-run), quand l'utilisateur choisit un cloud provider du catalogue (ex: **zai / ZAI Code**) puis sélectionne un modèle spécifique, **après sauvegarde l'agent tourne en `glm-5.2`** quel que soit le modèle choisi.
|
||||
|
||||
## Périmètre
|
||||
|
||||
Bug **backend** (couche application/infrastructure du spawn OpenCode). Suite logique et distincte du ticket #97 (qui réglait l'exclusion mutuelle `opencode` vs `opencodeProvider` — désormais fixée).
|
||||
|
||||
## Cause racine (diagnostiquée, à confirmer par Architect)
|
||||
|
||||
- La persistance est **correcte** : `profiles.json` conserve bien `opencodeProvider.model` = modèle choisi. `glm-5.2` est **absent de tout le code IdeA** ; c'est le **fallback interne de la CLI OpenCode** quand elle ne sait pas résoudre le modèle demandé.
|
||||
- Le modèle est perdu **au spawn** :
|
||||
1. IdeA écrit `opencode.json` avec `model: "zai/<choix>"` mais **sans bloc `models`** pour les providers catalogue connus (`crates/application/src/agent/lifecycle.rs:2796-2821`, `crates/infrastructure/src/assistant/mod.rs:447-466`). Elle suppose OpenCode built-in.
|
||||
2. Or `zai` n'est connu d'OpenCode **que** via son cache models.dev, et IdeA **isole `XDG_CACHE_HOME` vide** au spawn (`lifecycle.rs:2386,2405` ; `assistant/mod.rs:272,282`). → OpenCode ne résout ni `zai` ni le modèle → fallback silencieux `glm-5.2`.
|
||||
- Pourquoi ça marche en local (llama.cpp) et custom : ils émettent un bloc `models` (`assistant/mod.rs:371-379`, `:457-460`).
|
||||
|
||||
## Exemples impactés
|
||||
|
||||
Tout provider cloud **connu uniquement via le cache models.dev** (zai, et vraisemblablement tout provider non built-in dans OpenCode). Les providers hardcodés dans OpenCode (anthropic, openai, openrouter) fonctionnent malgré l'absence de bloc `models`.
|
||||
|
||||
## Lié à
|
||||
|
||||
- **#97** (relatesTo) : exclusion mutuelle provider — prérequis déjà livré.
|
||||
128
.ideai/tickets/99/carnet.md
Normal file
128
.ideai/tickets/99/carnet.md
Normal file
@ -0,0 +1,128 @@
|
||||
---
|
||||
issueRef: "#99"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784987973464
|
||||
---
|
||||
# Carnet #99 — Cadrage (prêt pour le cycle)
|
||||
|
||||
> Cadrage établi par Main après investigation code + **vérification empirique des binaires
|
||||
> installés** (`codex-cli 0.145.0`, `Claude Code 2.1.220`). Les faits CLI ci-dessous sont
|
||||
> **capitalisés et vérifiés** ; ils conditionnent toute l'architecture. Architect doit valider
|
||||
> les ports/VO et les questions ouvertes (§6) avant l'implémentation.
|
||||
|
||||
---
|
||||
|
||||
## 1. État des lieux vérifié — comparaison des 3 moteurs
|
||||
|
||||
| Moteur | Modèle contrôlé par IdeA ? | Mécanisme | Source |
|
||||
|---|---|---|---|
|
||||
| **OpenCode** | ✅ Oui | `model` écrit à la racine du `opencode.json` isolé (`OPENCODE_CONFIG`), lu par la session structured | `lifecycle.rs:2686-2689`, `2786-2788` |
|
||||
| **Codex** | ❌ Non | aucun `--model`, `codex_config_toml` n'écrit pas `model` → défaut du binaire | `codex.rs:215-239`, `lifecycle.rs:2941-2955` |
|
||||
| **Claude** | ❌ Non | aucun `--model`, `claude_settings_seed` n'écrit pas `model` → défaut du binaire | `claude.rs:273-284`, `infrastructure/permission/claude.rs:59` |
|
||||
|
||||
PTY et headless sont **deux processus OS séparés** pour un même agent
|
||||
(`allow_structured_alongside_pty: true`, `lifecycle.rs:1512-1517`) ; ils partagent le même
|
||||
`CODEX_HOME`/cwd mais **aucun état modèle** n'est synchronisé entre eux côté IdeA. Le `/model`
|
||||
de la TUI ne sort jamais du process TUI (pass-through brut, `terminal/usecases.rs:140`).
|
||||
|
||||
## 2. Faits CLI vérifiés (capitalisation) — la clé qui débloque tout
|
||||
|
||||
### Codex 0.145.0 — `model` est une clé config.toml **documentée**
|
||||
- L'exemple littéral du `--help` est `-c model="o3"`. `config.toml` est chargé depuis
|
||||
`$CODEX_HOME/config.toml`.
|
||||
- **Honorée par `codex` (TUI) ET `codex exec` (headless)** — même mécanisme de base
|
||||
(`--ignore-user-config` confirme qu'il est chargé par défaut).
|
||||
- Bonus : `codex exec` accepte aussi `-m, --model <MODEL>` (seconde porte d'injection).
|
||||
- **→ Écrire `model = "..."` dans le `$CODEX_HOME/config.toml` isolé fixe le défaut pour les
|
||||
DEUX canaux.**
|
||||
|
||||
### Claude 2.1.220 — `model` est gérable via settings + flag
|
||||
- `--model <model>` fonctionne en interactif **et** `-p/--print` (headless).
|
||||
- `--settings <file-or-json>` + la hiérarchie `./.claude/settings.local.json` (project-local)
|
||||
supportent une clé `model`.
|
||||
- **→ Écrire `"model": "..."` dans le `.claude/settings.local.json` du run dir fixe le défaut
|
||||
pour les DEUX canaux** (cwd = run dir, lu par PTY et headless).
|
||||
|
||||
### Conclusion d'architecture
|
||||
Les trois moteurs convergent vers **le même pattern** : écrire le modèle du profil dans le
|
||||
**fichier de config isolé du run dir**. Le fichier partagé = point de vérité unique pour
|
||||
headless + TUI au lancement. L'exigence produit « modèle par défaut = modèle headless = modèle
|
||||
exposé TUI au lancement » est satisfaite **par construction**, sans sync à coder.
|
||||
|
||||
## 3. Points d'intégration IdeA précis (avec chemins)
|
||||
|
||||
| Moteur | Renderer à modifier | Fichier produit | Ownership | Isolation |
|
||||
|---|---|---|---|---|
|
||||
| **Codex** | `codex_config_toml` (`application/agent/lifecycle.rs:2941-2955`) | `$CODEX_HOME/config.toml` | `MergeToml` (préservé aux régénérations) | `CODEX_HOME={runDir}/.codex` déjà isolé (`lifecycle.rs:2361`) |
|
||||
| **Claude** | `claude_settings_seed` (`infrastructure/src/permission/claude.rs:59`) | `{runDir}/.claude/settings.local.json` | `Replace` (régénéré du profil à chaque lancement) | cwd=run dir ; pas d'iso home mais **project-local override user** → le modèle IdeA gagne |
|
||||
|
||||
Champ existant à consommer ou déprécier : **`AgentProfile.model: Option<String>`**
|
||||
(`domain/src/profile.rs:1008`) — actuellement code mort. Référence OpenCode à répliquer :
|
||||
`OpenCodeProviderConfig { provider_id, model, api_key_ref }` (`domain/src/profile.rs:372-391`)
|
||||
+ `SaveOpenCodeProviderProfile` (`application/agent/usecases.rs`).
|
||||
|
||||
## 4. Architecture proposée — découpage en lots
|
||||
|
||||
- **A. Domaine** — Nouveaux VO miroir d'`OpenCodeProviderConfig` :
|
||||
`CodexProviderConfig { provider, model, api_key_ref }` et `ClaudeProviderConfig { provider,
|
||||
model, api_key_ref }`. Rendre les backends mutuellement exclusifs par moteur (cf.
|
||||
`opencode_backend_is_consistent`, `domain/src/profile.rs:363-364,1224`). **Décision
|
||||
Architect** : nouveau VO vs réutiliser `AgentProfile.model` (voir §6).
|
||||
- **B. Renderers de config** — `codex_config_toml` écrit `model` (+ `model_provider` si requis
|
||||
par la sémantique codex) ; `claude_settings_seed` écrit la clé `model`.
|
||||
- **C. Use cases / persistance** — `SaveCodexProviderProfile` / `SaveClaudeProviderProfile`
|
||||
miroirs de `SaveOpenCodeProviderProfile`, même SecretRef pour la clé scellée.
|
||||
- **D. UI / wizard** — duplication de profils Codex/Claude comme OpenCode (catalogue providers,
|
||||
sélection modèle, clé scellée). Réutiliser `provider_catalogue` + picker existants.
|
||||
- **E. QA** — assert headless + TUI utilisent le modèle du profil (voir §5).
|
||||
|
||||
## 5. Invariants (à respecter, à tester)
|
||||
|
||||
- **Source de vérité unique** : le modèle vient du profil AI, écrit dans le fichier de config
|
||||
isolé du run dir, lu par headless **et** TUI au lancement.
|
||||
- **Isolation préservée** : Codex via `CODEX_HOME` (rien ne change) ; Claude via project-local
|
||||
override (rien ne change). Pas d'accès au home global pour le modèle.
|
||||
- **`/model` en cours de session TUI ne corrompt pas le headless** (acceptable : ne concerne que
|
||||
le process PTY en cours ; le prochain lancement réapplique le défaut du profil).
|
||||
- **Non-régression OpenCode** : rendu `opencode.json` byte-identique (rien ne touche ce chemin).
|
||||
- **Non-régression permissions** : `codex_config_toml` et `claude_settings_seed` continuent
|
||||
d'écrire `mcp`/`sandbox`/`trust`/`approval` comme aujourd'hui (le `model` s'ajoute).
|
||||
|
||||
## 6. Questions ouvertes pour Architect (à trancher avant DevBackend)
|
||||
|
||||
1. **VO** : créer `CodexProviderConfig`/`ClaudeProviderConfig` (homogène à OpenCode) **ou**
|
||||
consommer le champ existant `AgentProfile.model` (code mort) ? Recommandation Main : nouveaux
|
||||
VO pour rester isomorphe à OpenCode (provider + clé scellée), déprécier `AgentProfile.model`.
|
||||
2. **Codex `model_provider`** : la config codex distingue `model` et `model_provider`. Faut-il
|
||||
exposer les deux au profil, ou `model` seul suffit (provider implicite) ?
|
||||
3. **Claude** : clé `model` dans `settings.local.json` suffit, ou faut-il gérer aussi la clé
|
||||
API Anthropic du profil (SecretRef) pour que le modèle soit réellement joignable ?
|
||||
4. **Catalogue providers** : quels providers exposer pour Codex (lié OpenAI) et Claude
|
||||
(Anthropic) ? Réutiliser `provider_catalogue.rs` ou catalogue dédié par moteur ?
|
||||
5. **Sémantique TUI** : confirmer qu'aucune CLI n'écrase `config.toml`/`settings` au démarrage
|
||||
(risque équivalent au « gate refresh » du ticket #98 côté OpenCode).
|
||||
|
||||
## 7. Périmètre QA — 2 couches (cf. méthodologie #98)
|
||||
|
||||
- **Couche 1 (unitaire pure)** : le rendu `codex_config_toml` contient la clé `model`
|
||||
attendue ; `claude_settings_seed` contient `"model"` attendue ; non-régression
|
||||
byte-identique du reste (mcp/sandbox/trust/approval) ; exclusion mutuelle des backends.
|
||||
- **Couche 2 (intégration gated)** : spawn réel `codex exec` et `claude -p` sur un profil avec
|
||||
modèle X → assert le modèle actif est X (pas le défaut binaire). Vérifier côté TUI aussi
|
||||
(modèle exposé au lancement). **Gate §6.5** à lever.
|
||||
|
||||
## 8. Hors périmètre (exclu de ce ticket)
|
||||
|
||||
- Override **runtime** du modèle à l'appel (`idea_ask_agent` porterait un modèle) — autre lot.
|
||||
- Providers distants SSH/WSL (ne concerne que le spawn local).
|
||||
- Détail de la `provider_catalogue` au-delà de la réutilisation (fast-follow).
|
||||
|
||||
---
|
||||
|
||||
## Contexte de mise à jour
|
||||
|
||||
- Cadrage Main après vérification empirique CLI (codex 0.145.0, claude 2.1.220).
|
||||
- Lié à #98 (relatesTo) — même famille « modèle au spawn ».
|
||||
- Prochaine étape cycle : **Architect** (valide ports/VO + tranche §6) → **Git** (branche) →
|
||||
**DevBackend** (lots A-C) + **DevFrontend** (lot D) → **QA** (2 couches) → **Git** (commit).
|
||||
53
.ideai/tickets/99/issue.md
Normal file
53
.ideai/tickets/99/issue.md
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
id: "45733f3f-5ef5-4f33-96e7-25afddcd1ce6"
|
||||
number: 99
|
||||
title: "Modèle par agent contrôlable pour Codex & Claude (headless + TUI) — égaler le pattern OpenCode"
|
||||
status: "open"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: [{"target":"#98","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784987973464
|
||||
updatedAt: 1784987973464
|
||||
version: 1
|
||||
---
|
||||
## Problème
|
||||
|
||||
Aujourd'hui IdeA **ne contrôle pas le modèle** pour les moteurs **Codex** et **Claude** :
|
||||
le modèle utilisé par le headless inter-agent (`codex exec` / `claude -p`) **et** par la TUI
|
||||
interactive est le **défaut du binaire CLI installé**, indépendamment du profil AI de l'agent.
|
||||
|
||||
- Aucun flag `--model` n'est passé au spawn (`codex.rs:215-239`, `claude.rs:273-284`).
|
||||
- `codex_config_toml` (`lifecycle.rs:2941-2955`) **n'écrit jamais de clé `model`**.
|
||||
- `AgentProfile.model` (`profile.rs:1008`) existe mais est du **code mort** (jamais consommé).
|
||||
- Le `/model` tapé dans la TUI ne se propage **pas** au headless : ce sont deux processus OS
|
||||
séparés (PTY vs `codex exec`/`claude -p`) avec des threads distincts. Le `CODEX_HOME`/cwd est
|
||||
partagé mais rien n'écrit le modèle sur disque côté IdeA.
|
||||
|
||||
**Contraste** : pour **OpenCode**, le modèle du profil (`OpenCodeConfig.model` /
|
||||
`OpenCodeProviderConfig.model`) **est** celui utilisé par le headless, via le `opencode.json`
|
||||
isolé pointé par `OPENCODE_CONFIG` (`lifecycle.rs:2686-2689`). Un profil IdeA = un
|
||||
(provider, modèle, clé scellée).
|
||||
|
||||
## Objectif produit
|
||||
|
||||
Qu'il existe un **modèle par défaut par agent**, défini dans le **profil AI**, qui soit :
|
||||
|
||||
1. utilisé par le **headless inter-agent** (`idea_ask_agent`) ;
|
||||
2. **exposé par la TUI au lancement** (valeur initiale du modèle dans la CLI) ;
|
||||
|
||||
pour les **trois** moteurs — OpenCode (déjà OK), Codex et Claude (à égaler).
|
||||
|
||||
## Périmètre
|
||||
|
||||
- **Backend** : nouveau VO domaine miroir d'`OpenCodeProviderConfig`, renderers de config
|
||||
écrivant le modèle, use cases de persistance.
|
||||
- **UI** : duplication / création de profils Codex et Claude comme OpenCode (catalogue de
|
||||
providers, sélection du modèle, clé scellée via SecretRef).
|
||||
|
||||
## Lié à
|
||||
|
||||
- **#98** (relatesTo) : même famille « contrôle du modèle au spawn » (côté OpenCode, résolveur
|
||||
zai/glm-5.2). Indépendant mais cohérent.
|
||||
@ -1,3 +1,3 @@
|
||||
{
|
||||
"nextNumber": 80
|
||||
"nextNumber": 104
|
||||
}
|
||||
@ -29,13 +29,13 @@
|
||||
"issueRef": "#3",
|
||||
"path": "3",
|
||||
"title": "[Différé — design à mûrir] Tâches de fond — dette B : retry durable après reboot",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784093835732
|
||||
"updatedAt": 1784377533777
|
||||
},
|
||||
{
|
||||
"issueRef": "#4",
|
||||
@ -77,13 +77,13 @@
|
||||
"issueRef": "#7",
|
||||
"path": "7",
|
||||
"title": "Gestion des session limite",
|
||||
"status": "qa",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": "d8f3f37b-87ca-4509-9116-45a99bc711df",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1783329459094
|
||||
"updatedAt": 1784718581033
|
||||
},
|
||||
{
|
||||
"issueRef": "#8",
|
||||
@ -161,13 +161,13 @@
|
||||
"issueRef": "#14",
|
||||
"path": "14",
|
||||
"title": "Integration de profils IA locaux/LAN comme profils IdeA canoniques",
|
||||
"status": "qa",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1783456579694
|
||||
"updatedAt": 1784449099987
|
||||
},
|
||||
{
|
||||
"issueRef": "#15",
|
||||
@ -465,11 +465,11 @@
|
||||
"issueRef": "#43",
|
||||
"path": "43",
|
||||
"title": "Systeme de plugins",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1783879315858
|
||||
"updatedAt": 1784707900405
|
||||
},
|
||||
{
|
||||
"issueRef": "#44",
|
||||
@ -591,13 +591,13 @@
|
||||
"issueRef": "#55",
|
||||
"path": "55",
|
||||
"title": "[Bug] Error on loading local model",
|
||||
"status": "inProgress",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784097757001
|
||||
"updatedAt": 1784649948611
|
||||
},
|
||||
{
|
||||
"issueRef": "#56",
|
||||
@ -625,33 +625,33 @@
|
||||
"issueRef": "#60",
|
||||
"path": "60",
|
||||
"title": "[Bug] Assistant IA d'édition de ticket : aucune réponse dans la conversation (stream sans Final non signalé)",
|
||||
"status": "inProgress",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784194089605
|
||||
"updatedAt": 1784360139966
|
||||
},
|
||||
{
|
||||
"issueRef": "#61",
|
||||
"path": "61",
|
||||
"title": "[UI] rafraichissement des cellule",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784182133142
|
||||
"updatedAt": 1784661823324
|
||||
},
|
||||
{
|
||||
"issueRef": "#62",
|
||||
"path": "62",
|
||||
"title": "[Sécurité/Cohérence] Identité requester explicite pour les sessions structurées + policy des tools OpenAI-compatible",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784095187734
|
||||
"updatedAt": 1784406936659
|
||||
},
|
||||
{
|
||||
"issueRef": "#63",
|
||||
@ -667,11 +667,11 @@
|
||||
"issueRef": "#64",
|
||||
"path": "64",
|
||||
"title": "Web : créer/ajouter un projet depuis le navigateur (folder browser serveur)",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784193766706
|
||||
"updatedAt": 1784718418070
|
||||
},
|
||||
{
|
||||
"issueRef": "#65",
|
||||
@ -719,13 +719,13 @@
|
||||
"issueRef": "#69",
|
||||
"path": "69",
|
||||
"title": "Adaptibilité client téléphone",
|
||||
"status": "qa",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784207736267
|
||||
"updatedAt": 1784449061397
|
||||
},
|
||||
{
|
||||
"issueRef": "#70",
|
||||
@ -783,51 +783,299 @@
|
||||
"issueRef": "#75",
|
||||
"path": "75",
|
||||
"title": "Écran d'appairage web : clavier numérique sur mobile alors que le code contient des lettres",
|
||||
"status": "qa",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784277530792
|
||||
"updatedAt": 1784449082160
|
||||
},
|
||||
{
|
||||
"issueRef": "#76",
|
||||
"path": "76",
|
||||
"title": "Appairage : la comparaison du code est sensible à la casse côté serveur",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784277540034
|
||||
"updatedAt": 1784287679986
|
||||
},
|
||||
{
|
||||
"issueRef": "#77",
|
||||
"path": "77",
|
||||
"title": "Appairage : appareils enregistrés persistants, révocables, et code éphémère à usage unique",
|
||||
"status": "qa",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784287453378
|
||||
"updatedAt": 1784449075576
|
||||
},
|
||||
{
|
||||
"issueRef": "#78",
|
||||
"path": "78",
|
||||
"title": "Settings desktop : sections en langues mélangées (« Appareils » à côté de « AI Profiles », « Deployment »)",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784284746767
|
||||
"updatedAt": 1784379475877
|
||||
},
|
||||
{
|
||||
"issueRef": "#79",
|
||||
"path": "79",
|
||||
"title": "Test flaky : PermissionsPanel « saves project defaults » échoue par intermittence",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784379476483
|
||||
},
|
||||
{
|
||||
"issueRef": "#80",
|
||||
"path": "80",
|
||||
"title": "L'environnement de QA ne peut pas ouvrir de socket — il ne peut pas valider les features réseau",
|
||||
"status": "open",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784287697560
|
||||
},
|
||||
{
|
||||
"issueRef": "#81",
|
||||
"path": "81",
|
||||
"title": "MCP d'edition de templates",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784565918809
|
||||
},
|
||||
{
|
||||
"issueRef": "#82",
|
||||
"path": "82",
|
||||
"title": "Ajouter des permissions d'utilisations des tools MCP d'IdeA par agent",
|
||||
"status": "closed",
|
||||
"priority": "critical",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784410760329
|
||||
},
|
||||
{
|
||||
"issueRef": "#83",
|
||||
"path": "83",
|
||||
"title": "Popup pour vérifier la volonté de quitter l'app",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784567952845
|
||||
},
|
||||
{
|
||||
"issueRef": "#84",
|
||||
"path": "84",
|
||||
"title": "[Bug] Reprise auto après limite de session ne se déclenche pas pour l'orchestrator (Main, profil Claude)",
|
||||
"status": "open",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784377833773
|
||||
},
|
||||
{
|
||||
"issueRef": "#85",
|
||||
"path": "85",
|
||||
"title": "Test flaky : setCellAgent « changing the agent kills the previous PTY (Bug #3) » échoue en exécution shuffled",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784287450082
|
||||
"updatedAt": 1784379436323
|
||||
},
|
||||
{
|
||||
"issueRef": "#86",
|
||||
"path": "86",
|
||||
"title": "[UI] ajouter l'affichage et l'dition des tickets et des sprints à la version web",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784612705899
|
||||
},
|
||||
{
|
||||
"issueRef": "#87",
|
||||
"path": "87",
|
||||
"title": "[UI] sur l'affichage Web, la liste des background task polluent l'affchage",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784650196560
|
||||
},
|
||||
{
|
||||
"issueRef": "#89",
|
||||
"path": "89",
|
||||
"title": "Pouvoir lancer le serveur au lancement IdeA",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784661583231
|
||||
},
|
||||
{
|
||||
"issueRef": "#90",
|
||||
"path": "90",
|
||||
"title": "Ajouter les menus manquantes à la version Web d'IdeA",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784661583085
|
||||
},
|
||||
{
|
||||
"issueRef": "#91",
|
||||
"path": "91",
|
||||
"title": "Sur la notification de fin de tache backend, mettre plutot les deux agents en conversation",
|
||||
"status": "open",
|
||||
"priority": "medium",
|
||||
"sprint": "5afd6780-0f76-40d7-a10f-32ee52469d74",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784994105390
|
||||
},
|
||||
{
|
||||
"issueRef": "#92",
|
||||
"path": "92",
|
||||
"title": "Configurer OpenCode avec un provider Opencode",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784993956910
|
||||
},
|
||||
{
|
||||
"issueRef": "#93",
|
||||
"path": "93",
|
||||
"title": "Agent Context : réponse finale non capturée, fragment d'appel d'outil brut renvoyé via idea_ask_agent",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784934179780
|
||||
},
|
||||
{
|
||||
"issueRef": "#94",
|
||||
"path": "94",
|
||||
"title": "Permissions OpenCode ignorées : opencode.json code en dur bash/edit=ask au lieu de projeter EffectivePermissions",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784823278030
|
||||
},
|
||||
{
|
||||
"issueRef": "#95",
|
||||
"path": "95",
|
||||
"title": "Rendez-vous idea_ask_agent : timeout MCP OpenCode trop court (15s), le demandeur OpenCode ne reçoit jamais la réponse",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784934174326
|
||||
},
|
||||
{
|
||||
"issueRef": "#96",
|
||||
"path": "96",
|
||||
"title": "Les assistants de ticket (tous adaptateurs) n'ont aucune résolution EffectivePermissions — permission toujours native/absente",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": "883534aa-7fc1-4d83-a9c0-17cac4a4eea5",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784994119004
|
||||
},
|
||||
{
|
||||
"issueRef": "#97",
|
||||
"path": "97",
|
||||
"title": "Duplication de provider lors de la création de profil AI - llamacpp ET provider cloud inclus simultanément",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784915626417
|
||||
},
|
||||
{
|
||||
"issueRef": "#98",
|
||||
"path": "98",
|
||||
"title": "Profil cloud provider catalogue (ex: zai) : modèle choisi écrasé par glm-5.2 au spawn",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784983186248
|
||||
},
|
||||
{
|
||||
"issueRef": "#99",
|
||||
"path": "99",
|
||||
"title": "Modèle par agent contrôlable pour Codex & Claude (headless + TUI) — égaler le pattern OpenCode",
|
||||
"status": "open",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784987973464
|
||||
},
|
||||
{
|
||||
"issueRef": "#100",
|
||||
"path": "100",
|
||||
"title": "[Bug] Problème sur le scroll des agents OpenCode",
|
||||
"status": "open",
|
||||
"priority": "high",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784993984569
|
||||
},
|
||||
{
|
||||
"issueRef": "#101",
|
||||
"path": "101",
|
||||
"title": "[Bug] Soucis de retour de notification sur les taches backend",
|
||||
"status": "closed",
|
||||
"priority": "critical",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1785011116365
|
||||
},
|
||||
{
|
||||
"issueRef": "#102",
|
||||
"path": "102",
|
||||
"title": "[Bug] Devoir resize les cellule pour afficher la TUI d'un agent",
|
||||
"status": "open",
|
||||
"priority": "medium",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784993980505
|
||||
},
|
||||
{
|
||||
"issueRef": "#103",
|
||||
"path": "103",
|
||||
"title": "Exposer et piloter la permission réseau des agents/commandes dans IdeA",
|
||||
"status": "qa",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1785013507979
|
||||
}
|
||||
]
|
||||
}
|
||||
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -98,6 +98,7 @@ name = "application"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"dirs",
|
||||
"domain",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -159,6 +160,7 @@ version = "0.3.0"
|
||||
dependencies = [
|
||||
"application",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"domain",
|
||||
"infrastructure",
|
||||
"interprocess",
|
||||
@ -1970,13 +1972,16 @@ dependencies = [
|
||||
"fastembed",
|
||||
"futures-util",
|
||||
"git2",
|
||||
"hex",
|
||||
"landlock",
|
||||
"notify",
|
||||
"portable-pty",
|
||||
"regex",
|
||||
"reqwest 0.12.28",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"uuid",
|
||||
|
||||
@ -24,8 +24,10 @@ futures-util = "0.3"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "fs", "io-util", "time"] }
|
||||
hex = "0.4"
|
||||
sha2 = "0.10"
|
||||
dirs = "6"
|
||||
subtle = "2"
|
||||
getrandom = "0.3"
|
||||
http = "1"
|
||||
# Local git via libgit2. Network features (https/ssh → openssl) are off for L8:
|
||||
# only local operations (status/commit/branch/checkout/log) are in scope; remote
|
||||
# push/pull and static vendoring for the AppImage are deferred to L9/L11.
|
||||
|
||||
@ -32,12 +32,12 @@ tauri-plugin-dialog = { workspace = true }
|
||||
tokio = { workspace = true, features = ["io-std", "rt", "net"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
http = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
base64 = "0.22"
|
||||
bytes = "1.11"
|
||||
cookie = "0.18"
|
||||
http = "1.4"
|
||||
http-body-util = "0.1"
|
||||
# `AppAgentResumer` implements the application's async `AgentResumer` port (LS7).
|
||||
async-trait = { workspace = true }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user