docs(architecture): §22.2 — persistance plugin-owned hors projet & purge à la désinstallation (#138)
Cadrage architecture du ticket #138 : séparation project-owned/plugin-owned, API canonique ctx.storage seule, stockage sous app_data/plugins/data/<id>/ (frère de installed/), purge intégrale à l'uninstall. Débloque #139. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -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.
|
**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<string>("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/<id>/` : `app_data/plugins/data/<pluginId>/`. Séparé de `installed/` pour que réinstall/mise à jour du package (qui peut re-écrire `installed/<id>/` 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/<pluginId>/` (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/<id>` + entrée registre), supprimer intégralement `plugins/data/<id>/`. 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/<pluginId>/`, commandes Tauri, câblage réel dans `loader.ts` (aujourd'hui absent), confinement identique en esprit à #133/#135 (jamais d'écriture hors `plugins/data/<pluginId>/`).
|
||||||
|
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/<id>/` 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.*
|
*Document maintenu par l'Agent Architecture — base du jalon « cadrage architecture » avant tout code applicatif.*
|
||||||
|
|||||||
Reference in New Issue
Block a user