---
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.