Files
IdeA/.ideai/tickets/82/carnet.md
Blomios 98fb05447d chore(ideai): état runtime — tickets, mémoire, tâches de fond
État d'exécution accumulé (tickets créés/mis à jour hors #43, notes de
mémoire, tâche de fond) capturé au moment du commit de la feature.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 07:37:19 +02:00

25 KiB

issueRef, version, updatedBy, updatedAt
issueRef version updatedBy updatedAt
#82 6
kind agent_id
agent a6ced819-b893-4213-b003-9e9dc79b9641
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 :

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

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.