diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 13d887b..29de2a6 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2397,4 +2397,24 @@ Ok(declared_main || declared_icon || rel.as_str().starts_with("assets/")) **Débloque #135** : le périmètre d'audit (écriture confinée à l'install, désinstallation 100%) est celui décrit ci-dessus ; #135 vérifie que `RelativePath::new` (rejette déjà `..` et absolu, `crates/domain/src/plugin.rs`) est bien appliqué côté install, pas seulement côté serve. +### 22.2 #138 — Persistance plugin-owned hors projet & purge à la désinstallation + +**Constat.** `sdk/IdeaSDK/src/runtime.ts` déclare déjà `ActivateContext.storage?: PluginStorage` avec `get/set/delete` clé-valeur JSON-serializable, et l'exemple `hello-plugin` l'utilise (`ctx.storage?.get("helloPlugin.ownerAgentId")`, `src/index.ts`). Mais **ce champ n'est jamais peuplé** : `frontend/src/plugins/runtime/loader.ts` ne câble que `logger`, `subscriptions`, `services` (≈ lignes 249-256) — `ctx.storage` vaut toujours `undefined` à l'exécution, silencieusement. Côté Rust, aucun port ni commande n'existe pour cette primitive (`grep PluginStorage crates/` → rien). Faute d'API réelle, l'exemple de référence détourne `ctx.services.workspace`/`ctx.services.config` pour écrire son état interne (compteurs `launches`, flag `enabled`) sous `.ideai/hello-plugin.txt` et `.ideai/hello-plugin.json` — exactement le pattern que le SDK doit cesser d'enseigner par défaut. + +**Décision — deux familles de données, jamais mélangées :** +- **Project-owned** : fichiers du workspace que le plugin modifie *volontairement et explicitement* pour l'utilisateur/le projet (ex. générer un fichier de config réel du projet). Reste sur `ctx.services.workspace.*` / `ctx.services.config.*`, dans le sandbox projet existant (`RelativePath`, confiné au project root). Ce chemin n'est pas fautif en soi — il est fautif quand il sert à stocker de l'état *interne* du plugin. +- **Plugin-owned** : préférences, cache, dernière sélection, index interne, config interne — tout ce qui n'a de sens que pour le plugin lui-même. Ne doit **jamais** vivre dans le project root ni sous `.ideai/`. Vit sous app data, dans un répertoire **frère** de `plugins/installed//` : `app_data/plugins/data//`. Séparé de `installed/` pour que réinstall/mise à jour du package (qui peut re-écrire `installed//` en entier) ne touche jamais aux données de l'utilisateur, et pour que la désinstallation ait une deuxième racine univoque à purger. + +**API canonique : `ctx.storage` seul, pas de second API document.** `ctx.storage.set(key, value)` avec des valeurs JSON couvre déjà le besoin de document structuré que `ctx.services.config` était détourné pour servir — ajouter une deuxième API "document structuré plugin-scopé" ferait doublon avec `ctx.storage` sans bénéfice. `ctx.services.config` reste réservé au project-owned (fichiers réels du projet que le plugin est explicitement chargé de gérer). + +**Cycle de vie :** +- Création/lecture : `ctx.storage.get/set/delete` proxie une commande Tauri (ex. `plugin_storage_get`/`plugin_storage_set`/`plugin_storage_delete`) qui lit/écrit un store scopé par `pluginId` sous `plugins/data//` (forme de stockage — un fichier JSON unique ou un fichier par clé — laissée à l'implémentation de #139 ; la frontière de répertoire est le contrat figé, pas le format interne). +- Suppression : `plugin_uninstall` (`crates/app-tauri/src/plugins.rs:142`) doit, en plus de la purge déjà couverte par #135 (`plugins/installed/` + entrée registre), supprimer intégralement `plugins/data//`. Les fichiers project-owned que le plugin a écrits dans le workspace ne sont **jamais** touchés par l'uninstall — ce sont des données du projet, pas du plugin. + +**Débloque #139** : +1. Implémenter `ctx.storage` de bout en bout : port domaine + adapter infra scopés à `plugins/data//`, commandes Tauri, câblage réel dans `loader.ts` (aujourd'hui absent), confinement identique en esprit à #133/#135 (jamais d'écriture hors `plugins/data//`). +2. Réaligner `hello-plugin` : les compteurs internes (`launches`, `enabled`, `ownerAgentId`) sont conceptuellement plugin-owned → migrer vers `ctx.storage`. Garder au plus un exemple clairement étiqueté "fichier projet réel" via `workspace`/`config` pour montrer que ce chemin existe toujours, sans qu'il reste l'exemple par défaut de persistance interne. +3. `sdk/IdeaSDK/README.md` : section « Structured Config Documents » à corriger pour ne plus donner `.ideai/hello-plugin.json` comme exemple d'état interne — remplacer par un exemple `ctx.storage`, et documenter noir sur blanc la séparation project-owned/plugin-owned de ce §22.2. +4. Preuve requise : test de purge (installer, écrire via `ctx.storage`, désinstaller, vérifier `plugins/data//` disparu) et absence de tout chemin `.ideai/...` dans les exemples SDK par défaut. + *Document maintenu par l'Agent Architecture — base du jalon « cadrage architecture » avant tout code applicatif.*