Templates (v7.71.x) : - registre unifié \ emplates\ (migrations 48-49) + TemplateService.instantiate unique (UI, API v2, agent, scheduler) - sélecteur (pilule page vide, menu •••, commande /template), gestionnaire /templates, menu New ▾, From template, base inline dans un document - 141 presets système (59 pages, 42 bases, 15 blocs, 25 lignes), titre auto depuis le template, variables title réservée - récurrences RRULE + scheduler dédupliqué, agent apply_template/list_templates, API /api/templates + /api/v2/fd-templates - correctifs : bouton Templates, centrage fenêtre, filtres CSP, flux de création, variable title - tests : tests/test_fd_templates.py (19) et e2e/templates_picker.spec.js (8) Inclut le travail déjà présent dans le working tree (vues Notion : view_query/view_aggregate/form_projection/geocoding, property_types, database_table, docs agents-skills) et ignore .playwright-mcp/.
367 lines
30 KiB
Markdown
367 lines
30 KiB
Markdown
# Agents & Skills pour Flowdeck — Phase 1 : Le Skill devient une page
|
||
|
||
| Champ | Valeur |
|
||
|---|---|
|
||
| Phase | **1 sur 5** — fondation de toute la solution |
|
||
| Document parent | `architecture-agents-skills-notion-flowdeck.md` v1.1 — en particulier §3.4 (les Skills dans Notion), §4.2 (écarts E1, E3, E4), §10.3–10.4, §13.1, §13.4–13.6 |
|
||
| Version | 1.0 — 9 octobre 2026 |
|
||
| Auteur | Spark, pour Bruno |
|
||
| Statut | Prêt à implémenter |
|
||
| Prérequis | Flowdeck v7.69.8 ; migration courante = 38 |
|
||
| Migration créée | **39** |
|
||
| Dépendances | Aucune (première phase) |
|
||
| Débloque | Phases 2 (routeur de skills), 3 (skills des Custom Agents), 5 (Skills API) |
|
||
|
||
> **Résultat visible à la fin de cette phase.** N'importe quelle page Flowdeck peut devenir un skill ; une collection peut devenir une base de skills avec `Description` / `Files` / `Tags` ; la Library a un onglet *Skills* et un onglet *Discover* avec `Enable for me` ; les skills s'exécutent depuis le chat (`/`), le menu de sélection, le menu slash et le menu de bloc via un moteur unique (le Skill Runner) ; les skills intégrés d'AI Writing sont exposés comme skills ; chaque skill s'exporte en `SKILL.md` téléchargeable pour les agents locaux. Aucun skill existant n'est perdu : les enregistrements actuels et les 17 presets deviennent des pages.
|
||
|
||
---
|
||
|
||
## Table des matières
|
||
|
||
1. [Objectif et principe directeur](#1-objectif-et-principe-directeur)
|
||
2. [Périmètre](#2-périmètre)
|
||
3. [État d'entrée — ce qui existe en v7.69.8](#3-état-dentrée--ce-qui-existe-en-v7698)
|
||
4. [Modèle de données — migration 39](#4-modèle-de-données--migration-39)
|
||
5. [Tickets de travail](#5-tickets-de-travail)
|
||
6. [API livrée par la phase](#6-api-livrée-par-la-phase)
|
||
7. [Comportements détaillés](#7-comportements-détaillés)
|
||
8. [Plan de migration des données existantes](#8-plan-de-migration-des-données-existantes)
|
||
9. [Tests](#9-tests)
|
||
10. [Critères d'acceptation](#10-critères-dacceptation)
|
||
11. [Risques spécifiques et vigilance](#11-risques-spécifiques-et-vigilance)
|
||
12. [Ordre d'exécution](#12-ordre-dexécution)
|
||
13. [Définition de « terminé »](#13-définition-de--terminé-)
|
||
|
||
---
|
||
|
||
## 1. Objectif et principe directeur
|
||
|
||
Le principe (ADR-01 du document parent) : **la page est la source de vérité du skill ; la table `agent_skills` devient son index d'exécution.** Toute l'économie de la phase vient de ce que Flowdeck n'a pas à construire pour un skill ce qu'il a déjà pour une page : éditeur de blocs, ACL (partage, groupes, héritage), historique `page_versions`, corbeille, recherche FTS5 et sémantique, commentaires, Library.
|
||
|
||
Corollaires qui guident chaque ticket :
|
||
|
||
- **Marquer une page ne la déplace pas et ne la copie pas.** C'est l'insertion d'une ligne d'index. Démarquer supprime la ligne, la page ne bouge pas.
|
||
- **Un skill de base est une ligne de collection comme les autres** : son contenu est la page-ombre (`pages.collection_row_id`), ses métadonnées (`Description`, `Files`) sont des propriétés de la ligne.
|
||
- **Aucun nouvel éditeur, aucun nouveau modèle de permissions.** Si un comportement de skill ne peut pas s'exprimer avec les mécanismes de page/collection existants, c'est un signal d'arrêt et de redesign, pas une raison d'ajouter un sous-système.
|
||
|
||
## 2. Périmètre
|
||
|
||
### 2.1 Inclus
|
||
|
||
- Migration 39 (refonte `agent_skills`, `collections.is_skills_db`, `user_skill_enablements`, `skill_runs`, `skill_local_downloads`).
|
||
- Marquer/démarquer une page comme skill ; déplacer une page dans une base de skills.
|
||
- Créer une base de skills (gabarit) ; convertir une collection existante en base de skills (correspondance des propriétés).
|
||
- Bannière de skill sur la page (états, interrupteurs, téléchargement, badge de copie périmée).
|
||
- Library : onglet *Skills* et sous-vue *Discover*, `Enable for me`, désactivation pour soi.
|
||
- Skill Runner : chargement d'un skill (page + fichiers), construction de la consigne, exécution manuelle depuis 4 surfaces (chat `/`, sélection, slash, bloc), journalisation dans `skill_runs`.
|
||
- Skills intégrés : améliorer l'écriture, corriger, expliquer, reformater, traduire, résumer.
|
||
- Création assistée d'un skill par l'Agent (dialogue → création de la page).
|
||
- Export `SKILL.md` (+ fichiers partageables) et import `SKILL.md` ; format `flowdeck-skill` v1 conservé.
|
||
- Migration des skills existants et des 17 presets en pages.
|
||
|
||
### 2.2 Exclu (phases ultérieures)
|
||
|
||
- L'**usage automatique** des skills par l'Agent (routage sur la `Description`) → Phase 2. La `Description` est collectée et indexée dès cette phase, mais rien ne l'exploite encore automatiquement.
|
||
- L'usage des skills par les **Custom Agents** (accès accordés, runs autonomes) → Phase 3.
|
||
- La **comptabilité** d'usage détaillée par run (`agent_runs`, crédits) → Phases 2 et 4. Ici, `skill_runs` journalise avec les références disponibles (conversation, surface, utilisateur) ; la colonne `run_id` définitive arrive en Phase 2 — voir §4.3 pour la parade.
|
||
- L'écriture automatique dans les répertoires des agents locaux (utilitaire compagnon) → question ouverte Q4 du document parent ; cette phase livre le **téléchargement** du bundle et le suivi d'empreinte.
|
||
|
||
## 3. État d'entrée — ce qui existe en v7.69.8
|
||
|
||
| Élément | État | Fichier / mécanisme (architecture v7.69.8, §16.3) |
|
||
|---|---|---|
|
||
| Skills actuels | Prompts paramétrés + outils autorisés, table `agent_skills` | `app/services/skill_gallery.py` |
|
||
| Galerie | 17 presets installables | `skill_gallery.py`, routes `/api/agent/skills` |
|
||
| Format portable | `flowdeck-skill` v1, export/import JSON | idem + `/api/v2/skills/*` |
|
||
| Points d'entrée éditeur | Menu contextuel de bloc → *Skills ›* ; toolbar de sélection → *Skills* (§13.3) ; section Compétences du Menu + (gérer/parcourir) | `page_editor_scripts.js`, `agent_panel_*.js` |
|
||
| AI Writing | 6 actions headless (écrire, résumer, traduire, continuer, autocomplétion, propriétés) | `app/services/ai_writing.py` |
|
||
| Page-ombre de ligne | Chaque ligne de collection peut porter une page de contenu | `pages.collection_row_id` (v6.5) |
|
||
| Indexation sémantique | Embeddings maison, index incrémental, `resource_type` libre | `app/services/semantic_search.py` |
|
||
|
||
## 4. Modèle de données — migration 39
|
||
|
||
### 4.1 Schéma (repris du document parent §10.3–10.4, reséquencé)
|
||
|
||
```sql
|
||
-- Migration 39 — Phase 1
|
||
ALTER TABLE collections ADD COLUMN is_skills_db INTEGER NOT NULL DEFAULT 0;
|
||
-- Correspondance des propriétés dans collections.schema_json :
|
||
-- {"skill_properties": {"description": "<nom prop>", "files": "<nom prop>", "tags": "<nom prop>"}}
|
||
|
||
-- agent_skills est RECRÉÉE (patron : nouvelle table, copie, bascule, §8)
|
||
CREATE TABLE agent_skills_new (
|
||
id INTEGER PRIMARY KEY,
|
||
page_id INTEGER NOT NULL UNIQUE REFERENCES pages(id) ON DELETE CASCADE,
|
||
collection_id INTEGER REFERENCES collections(id),
|
||
name_cached TEXT NOT NULL,
|
||
description TEXT DEFAULT '',
|
||
files_json TEXT DEFAULT '[]',
|
||
tools_json TEXT DEFAULT '[]',
|
||
is_builtin INTEGER NOT NULL DEFAULT 0,
|
||
auto_use_default INTEGER NOT NULL DEFAULT 1,
|
||
editor_menu_default INTEGER NOT NULL DEFAULT 0,
|
||
owner_id INTEGER REFERENCES users(id),
|
||
status TEXT DEFAULT 'ready',
|
||
source TEXT DEFAULT 'page', -- 'page' | 'legacy' | 'import' | 'builtin'
|
||
created_at TEXT NOT NULL,
|
||
updated_at TEXT NOT NULL
|
||
);
|
||
|
||
CREATE TABLE user_skill_enablements (
|
||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||
skill_id INTEGER NOT NULL REFERENCES agent_skills_new(id) ON DELETE CASCADE,
|
||
enabled INTEGER NOT NULL DEFAULT 1,
|
||
auto_use INTEGER, editor_menu INTEGER,
|
||
created_at TEXT NOT NULL,
|
||
PRIMARY KEY (user_id, skill_id)
|
||
);
|
||
|
||
CREATE TABLE skill_runs (
|
||
id INTEGER PRIMARY KEY,
|
||
skill_id INTEGER NOT NULL REFERENCES agent_skills_new(id),
|
||
run_id INTEGER, -- nullable en Phase 1, rattaché en Phase 2 (§4.3)
|
||
conversation_id INTEGER REFERENCES agent_conversations(id),
|
||
user_id INTEGER REFERENCES users(id),
|
||
surface TEXT NOT NULL, -- 'chat' | 'editor' | 'agent' | 'api'
|
||
invocation TEXT NOT NULL, -- 'manual' en Phase 1 ('auto' dès la Phase 2)
|
||
created_at TEXT NOT NULL
|
||
);
|
||
|
||
CREATE TABLE skill_local_downloads (
|
||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||
skill_id INTEGER NOT NULL REFERENCES agent_skills_new(id) ON DELETE CASCADE,
|
||
target TEXT NOT NULL, -- 'claude-code'|'codex'|'cursor'|'gemini'|'grok'|'file'
|
||
page_version TEXT NOT NULL, -- empreinte au téléchargement
|
||
downloaded_at TEXT NOT NULL,
|
||
PRIMARY KEY (user_id, skill_id, target)
|
||
);
|
||
```
|
||
|
||
### 4.2 Règles du schéma
|
||
|
||
1. `page_id UNIQUE` : un skill = une page, sans exception. Le marquage est idempotent (un second marquage renvoie le skill existant).
|
||
2. `ON DELETE CASCADE` depuis `pages` : mettre la page à la corbeille **purge dure** ou la suppression effective retire le skill partout (menus, Library) — comportement attendu, testé (T-19).
|
||
3. Pour un skill de base, `description` et `files_json` sont des **caches relus** depuis les propriétés de la ligne à chaque sauvegarde de la ligne ou de sa page-ombre ; la ligne de collection fait foi.
|
||
4. Pour un skill autonome, `description`/`files_json` sont édités depuis la bannière et stockés ici.
|
||
5. `tools_json` hérite du modèle actuel : un skill peut **restreindre** les outils d'un run, jamais les élargir (intersection appliquée par le moteur, §7.4).
|
||
|
||
### 4.3 La parade `run_id`
|
||
|
||
`agent_runs` n'existe qu'en Phase 2. Pour ne pas bloquer la Phase 1 : `skill_runs.run_id` est nullable, `conversation_id` + horodatage portent la traçabilité immédiate ; la Phase 2 ajoute la contrainte de rattachement par une migration de données (et non de schéma). Ne pas créer de table `runs` temporaire.
|
||
|
||
### 4.4 Indexation
|
||
|
||
À la création/modification d'un skill : écrire ou mettre à jour les lignes `semantic_embeddings` avec `resource_type='skill'`, contenu = `name + description` (le titre seul ne suffit pas à l'appariement futur). Le scheduler d'indexation existant (300 s) est le filet ; l'écriture directe à la sauvegarde donne la fraîcheur nécessaire aux menus.
|
||
|
||
## 5. Tickets de travail
|
||
|
||
*Convention : chaque ticket est indépendamment testable ; « Fichiers » donne les points d'ancrage d'après l'architecture v7.69.8 — à confirmer contre le code réel au démarrage du ticket.*
|
||
|
||
### Socle données
|
||
|
||
**P1-T01 — Migration 39 et recréation d'`agent_skills`.**
|
||
Fichiers : `app/migrations.py` (`@register(39, ...)`), `app/db.py` si la baseline doit refléter le nouvel état pour les installations neuves.
|
||
Travail : créer le schéma du §4 ; la copie des données existantes est dans le même ticket que le script de migration de contenu (P1-T02), exécuté comme étape de la migration (données) — la migration reste une transaction unique.
|
||
Acceptation : sur une base de test v7.69.8 complète, `apply_migrations()` passe à la version 39 sans perte (comptages avant/après identiques pour les skills convertis, §8).
|
||
|
||
**P1-T02 — Migration de contenu : skills existants → pages.**
|
||
Travail : pour chaque ancien skill sans page : créer une page (workspace de l'utilisateur, privée par défaut, dans une base de skills privée créée à la demande — voir P1-T04), y écrire le prompt d'origine **à l'identique** comme corps de page, créer la ligne `agent_skills` avec `source='legacy'`, conserver `tools_json`. Les presets de la galerie ne sont **pas** matérialisés pour tous : ils le sont à l'installation (P1-T13).
|
||
Acceptation : chaque ancien skill reste invocable après migration (test de non-régression sur le jeu de skills de la base de test) ; aucun prompt modifié (comparaison de hash avant/après).
|
||
|
||
**P1-T03 — Service `skills_pages.py` : le cycle de vie page ↔ skill.**
|
||
Fichiers : nouveau `app/services/skills_pages.py` ; évolution de `skill_gallery.py` (qui devient un consommateur du service, pas le propriétaire du modèle).
|
||
Travail : `mark_as_skill(page_id, user)`, `unmark(page_id)`, `get_skill_by_page(page_id)`, `sync_skill_from_row(collection_page)` (recopie Description/Files depuis les propriétés), `sync_skill_from_page(page)` (titre, cache), suppression en cascade déjà portée par le schéma. Tous les appels vérifient l'ACL de la page via `PermissionManager` (marquer exige l'édition de la page).
|
||
Acceptation : les cinq opérations sont couvertes en tests unitaires, y compris les refus d'ACL.
|
||
|
||
### Bases de skills
|
||
|
||
**P1-T04 — Base de skills privée par défaut.**
|
||
Travail : à la première création d'un skill par un utilisateur sans base, créer sa collection `is_skills_db=1` privée (« Mes skills »), avec les propriétés `Description` (text), `Files` (files), `Tags` (multi_select) créées dans le schéma de la collection. Idempotent par utilisateur.
|
||
Acceptation : deux créations successives de skills par le même utilisateur n'ont créé qu'une base.
|
||
|
||
**P1-T05 — Gabarit « Skills » et conversion d'une collection.**
|
||
Fichiers : gabarits de bases (`db_templates`/seed existant), route `PUT /db/{id}/skills-db` (§6).
|
||
Travail : création directe d'une base de type Skills (mêmes propriétés qu'en T04 + vue par défaut triée par nom) ; dialogue de conversion : mapper des propriétés existantes vers `Description`/`Files`/`Tags` ou les créer ; option « activer les pages existantes comme skills pour moi » ; la conversion pose `is_skills_db=1` et crée les lignes `agent_skills` des lignes de la base (page-ombre requise : la créer si absente).
|
||
Acceptation : convertir la base de test « Playbooks » transforme chaque ligne en skill visible dans la Library ; la déconversion (`is_skills_db=0`) retire les lignes d'index sans toucher aux pages (règle du §18 du document parent).
|
||
|
||
**P1-T06 — Marquage depuis une page ordinaire.**
|
||
Fichiers : menu `•••` de page (gabarits de l'éditeur), route `POST /api/agent/pages/{id}/skill`.
|
||
Travail : entrée *Use as a skill* (case à cocher, état relu) ; si la page n'est dans aucune base de skills, le skill est autonome (description éditée via la bannière, T07) ; proposer en un clic « déplacer dans une base de skills » (réutilise le *Move to* existant + création de la ligne dans la base cible).
|
||
Acceptation : marquer, démarquer, re-marquer la même page donne un seul skill à chaque état stable.
|
||
|
||
### Expérience du skill
|
||
|
||
**P1-T07 — Bannière de skill.**
|
||
Fichiers : fragment de l'éditeur de page (au-dessus du corps, comme les bannières existantes), JS de l'éditeur.
|
||
Contenu : badge « Skill », base d'appartenance (lien), état d'activation pour l'utilisateur, interrupteurs *Use automatically* (défaut du skill) et *Add to text editor menu*, champ `Description` (skill autonome) ou lien vers la propriété (skill de base), bouton *Download for local agents*, badge orange « copie locale périmée » si une ligne `skill_local_downloads` de l'utilisateur est en retard d'empreinte.
|
||
Acceptation : la bannière n'apparaît que sur les pages-skills ; ses interrupteurs écrivent les bonnes colonnes (défaut du skill vs choix de l'utilisateur — ne pas confondre, §7.2).
|
||
|
||
**P1-T08 — Library : onglets Skills et Discover.**
|
||
Fichiers : `app/routers/library.py`, `app/templates/library.html`, `static/js/library.js`.
|
||
Travail : onglet *Skills* (skills créés + activés ; colonnes du §7.5 du parent : Nom, Base, Description, Use automatically, Menu éditeur, Propriétaire, Dernière utilisation depuis `skill_runs`) ; sous-vue *Discover* (skills lisibles non activés) avec bouton `Enable for me` par ligne ; recherche par nom/description ; les interrupteurs de l'onglet écrivent `user_skill_enablements`.
|
||
Acceptation : un skill partagé avec moi apparaît dans Discover, pas dans Skills ; après `Enable for me`, il bascule ; le désactiver le retire de mes menus sans affecter les autres utilisateurs (test multi-utilisateurs).
|
||
|
||
**P1-T09 — Le Skill Runner.**
|
||
Fichiers : nouveau `app/services/skill_runner.py`.
|
||
Travail : `load_skill(skill_id, user)` → {corps Markdown de la page (blocs éligibles sérialisés comme le fait le Context Builder), fichiers extraits (texte des fichiers `shareable`, plafonnés par budget), tools_json} ; `run(skill_id, input, surface, user, conversation_id)` → construit la consigne (page du skill en couche « consigne de tâche »), appelle le moteur existant en run contraint (outils = intersection), écrit `skill_runs`, renvoie la sortie structurée {texte, blocs, skill_name}. Mode LLM `offline` : sortie déterministe de test.
|
||
Acceptation : un run de skill produit une ligne `skill_runs` complète ; le nom du skill est présent dans la réponse rendue (préparation de l'invariant I5).
|
||
|
||
**P1-T10 — Invocation dans le chat : menu `/`.**
|
||
Fichiers : `agent_panel_*.js`, route `GET /api/agent/skills/menu`, compositeur du panneau.
|
||
Travail : taper `/` ouvre la liste (skills activés de l'utilisateur, recherche par nom) ; `/nom` filtre directement ; `+` lance la création assistée (T14) ; sélectionner un skill l'attache au prochain message (pastille retirable) et le run passe par le Runner (T09).
|
||
Acceptation : `/` sans skill activé affiche les intégrés + l'entrée de création ; un skill forcé par `/` n'est jamais remplacé par un autre (le routeur automatique n'existe pas encore, mais le contrat est posé).
|
||
|
||
**P1-T11 — Invocation dans l'éditeur : sélection, slash, bloc.**
|
||
Fichiers : `page_editor_scripts.js` (les trois points d'entrée existent — les rebrancher sur le Runner), registre des types de blocs.
|
||
Travail : déclarer `skill_eligible` par type de bloc (liste du document parent §13.4) ; menu de sélection : skills avec `editor_menu` effectif d'abord, intégrés ensuite, entrée *Manage Skills* ; menu de bloc → *Skills* (bloc + contenu imbriqué) ; slash `/skill` uniquement sur ligne non vide ; résultat en **aperçu diff** pour les transformations (accepter/refuser/réessayer), insertion sous le bloc pour les générations.
|
||
Acceptation : les trois gestes produisent le même résultat à entrée égale ; un bloc non éligible ou vide n'offre pas de skills.
|
||
|
||
**P1-T12 — Skills intégrés sur AI Writing.**
|
||
Fichiers : `app/services/ai_writing.py`, seed des skills intégrés.
|
||
Travail : créer les 6 pages-skills intégrées (`is_builtin=1`, `source='builtin'`, workspace système ou seed par utilisateur — décision : seed **par workspace**, page système non éditable, désactivable par utilisateur via les enablements) : Améliorer l'écriture, Corriger, Expliquer, Reformater, Traduire, Résumer — chacune appelle le service AI Writing existant via le Runner plutôt que de dupliquer les prompts ; l'autocomplétion inline reste sur le service direct (latence).
|
||
Acceptation : le menu de sélection d'un nouvel utilisateur affiche exactement les 6 intégrés, dans l'ordre ; désactiver « Corriger » le retire pour cet utilisateur seul.
|
||
|
||
### Portabilité
|
||
|
||
**P1-T13 — Galerie rebranchée sur les pages.**
|
||
Travail : installer un preset = créer la page-skill correspondante dans la base privée de l'utilisateur (corps = le contenu du preset, `Description` rédigée pour un futur appariement — relire les 17 descriptions pendant ce ticket) + enablement activé. La galerie existante devient une vue de presets-pages ; l'ancien format d'installation est abandonné après migration (T02).
|
||
Acceptation : réinstaller les 17 presets sur un compte de test donne 17 pages éditables ; modifier la page modifie le comportement du skill au run suivant.
|
||
|
||
**P1-T14 — Création assistée par l'Agent.**
|
||
Travail : depuis le chat (`/` → `+` ou « crée un skill qui… »), un run guidé de l'Agent personnel pose les questions minimales (travail, entrées, règles, exemples, sortie) puis appelle un outil interne de création de page-skill dans la base privée ; l'utilisateur est amené sur la page créée pour relire.
|
||
Acceptation : le parcours complet produit un skill invocable sans quitter le chat autrement que pour la relecture.
|
||
|
||
**P1-T15 — Export `SKILL.md` et suivi de copie locale.**
|
||
Fichiers : `app/services/skill_export.py` (nouveau), route `GET /api/agent/skills/{id}/download`.
|
||
Travail : sérialisation Markdown (frontmatter `name`/`description` + corps de page) ; bundle ZIP = `SKILL.md` + fichiers `shareable=1` ; écriture d'une ligne `skill_local_downloads` (target `file` par défaut, cible nommée si choisie) avec l'empreinte = hash du contenu sérialisé + `updated_at` de la page ; l'empreinte courante est recalculée à la lecture pour le badge (T07).
|
||
Acceptation : le `SKILL.md` exporté est relu par le test d'import (T16) sans perte de nom/description/corps.
|
||
|
||
**P1-T16 — Import `SKILL.md` et compatibilité v1.**
|
||
Travail : `POST /api/agent/skills/import` accepte un `SKILL.md` (frontmatter analysé) ou un bundle, crée une page-skill (`source='import'`) dans la base privée, range les fichiers joints en propriété `Files` ; le format `flowdeck-skill` v1 reste accepté et produit le même résultat (page + ligne d'index, `tools_json` conservé).
|
||
Acceptation : les trois sources (page existante, `SKILL.md` externe, JSON v1) convergent vers le même objet skill.
|
||
|
||
### Finitions
|
||
|
||
**P1-T17 — API v2 des skills alignée.**
|
||
Travail : `GET /api/v2/skills` et `/{id}` exposent `page_id`, `description`, `collection_id` ; négociation de contenu : `Accept: text/markdown` renvoie la sérialisation `SKILL.md` ; le JSON v1 reste le défaut (compatibilité des clients existants).
|
||
Acceptation : les tests API v2 actuels passent inchangés ; un test nouveau couvre la variante Markdown.
|
||
|
||
**P1-T18 — Documentation in-app et aide.**
|
||
Travail : entrée d'aide du produit (section Skills : page, base, Library, Discover, agents locaux), textes de la bannière et des dialogues en français, noms d'objets en anglais conformément au document parent.
|
||
Acceptation : revue des libellés (pas de « compétence » imposé dans l'UI là où le produit dit *Skill*).
|
||
|
||
## 6. API livrée par la phase
|
||
|
||
| Route | Ticket |
|
||
|---|---|
|
||
| `POST /api/agent/pages/{id}/skill` · `DELETE …` | T06 |
|
||
| `PUT /db/{id}/skills-db` (conversion) | T05 |
|
||
| `GET /api/agent/skills/menu` | T10 |
|
||
| `PUT /api/agent/skills/{id}/enablement` | T08 |
|
||
| `POST /api/agent/skills/{id}/run-chat` · `POST …/run-editor` | T09–T11 |
|
||
| `GET /api/agent/skills/{id}/download` | T15 |
|
||
| `POST /api/agent/skills/import` | T16 |
|
||
| `GET /api/v2/skills` · `GET /api/v2/skills/{id}` (étendus) | T17 |
|
||
|
||
Toutes les routes internes suivent les conventions existantes : cookie-auth, CSRF complet, contrôle d'ACL dans le routeur, erreurs au format du produit.
|
||
|
||
## 7. Comportements détaillés
|
||
|
||
### 7.1 Qui peut faire quoi
|
||
|
||
| Action | Droit requis |
|
||
|---|---|
|
||
| Marquer une page | Éditer la page |
|
||
| Éditer un skill | Éditer sa page (rien de plus, rien de moins) |
|
||
| Activer un skill pour soi | Lire sa page |
|
||
| Exécuter un skill | Lire sa page + être activé (ou être son créateur) |
|
||
| Convertir une base | Admin/owner de la collection |
|
||
| Désactiver le skill d'un autre | Impossible : la désactivation est toujours « pour soi » ; retirer pour tous = supprimer la page ou la démarquer par un éditeur |
|
||
|
||
### 7.2 Défaut du skill vs choix de l'utilisateur
|
||
|
||
Deux niveaux, à ne jamais écraser l'un par l'autre : les colonnes `*_default` d'`agent_skills` (posées par le propriétaire) et les surcharges nullable de `user_skill_enablements` (posées par chaque utilisateur). Résolution : surcharge utilisateur si non NULL, sinon défaut du skill. La bannière édite le **défaut** (si l'utilisateur peut éditer la page) ; l'onglet Library édite la **surcharge**.
|
||
|
||
### 7.3 Contenu éligible d'un bloc de skill
|
||
|
||
Le corps du skill est la page entière sérialisée, mais l'**entrée** d'un run éditeur respecte l'éligibilité (§13.4 du parent) : types listés seulement, contenu imbriqué inclus, blocs vides ignorés. Le Runner reçoit l'entrée déjà filtrée — il ne connaît pas l'éditeur.
|
||
|
||
### 7.4 Contrainte d'outils
|
||
|
||
`outils_du_run = outils_autorisés_par_la_politique ∩ (outils_du_skill si tools_json non vide, sinon tous)`. Un skill ne peut ni ajouter un outil MCP, ni lever une approbation. Test dédié obligatoire (T-19).
|
||
|
||
### 7.5 Le skill supprimé en cours de route
|
||
|
||
Si la page part à la corbeille entre l'affichage du menu et le run : le Runner répond par une erreur propre « skill indisponible », le menu se rafraîchit, aucun run fantôme dans `skill_runs`.
|
||
|
||
## 8. Plan de migration des données existantes
|
||
|
||
1. **Sauvegarde** : snapshot SQLite par le service de backup existant avant le déploiement (procédure habituelle des migrations risquées).
|
||
2. **Inventaire** : compter les skills existants par utilisateur et par type ; photographier les hashes des prompts.
|
||
3. **Migration 39** : schéma + T02 en une transaction ; en cas d'échec, rien n'est appliqué (patron des migrations Flowdeck).
|
||
4. **Vérification post-migration** (script jetable, pas de code livré) : comptages avant/après, hashes des corps de pages générées = hashes des prompts d'origine, échantillon de 5 skills invoqués en mode offline.
|
||
5. **Bascule d'UI** : les anciens écrans de gestion des skills (galerie enregistrements) sont retirés dans le même déploiement ; pas de période de double modèle.
|
||
|
||
## 9. Tests
|
||
|
||
*Pyramide existante : pytest (TestClient, base temporaire), Playwright pour les gestes réels. Tous les tests de skill doivent passer en mode LLM `offline`.*
|
||
|
||
| # | Niveau | Cas |
|
||
|---|---|---|
|
||
| T-01 | Unitaire | Marquer/démarquer : idempotence, unicité `page_id`, refus sans droit d'édition |
|
||
| T-02 | Intégration | Migration 39 sur fixture v7.69.8 : comptages, hashes, skills legacy invocables |
|
||
| T-03 | Intégration | Conversion d'une collection : propriétés mappées ou créées, lignes → skills, déconversion propre |
|
||
| T-04 | Intégration | Skill de base : modifier la propriété `Description` de la ligne met à jour le cache du skill |
|
||
| T-05 | Intégration | Discover/Enable : visibilité par ACL, activation personnelle, désactivation sans effet sur autrui |
|
||
| T-06 | Intégration | Runner : ligne `skill_runs` écrite ; nom du skill dans la sortie ; intersection d'outils respectée |
|
||
| T-07 | Intégration | Un skill dont `tools_json` tente d'ajouter un outil hors politique : l'outil n'est pas disponible au run |
|
||
| T-08 | Intégration | Export `SKILL.md` → import : nom, description, corps et fichiers conservés |
|
||
| T-09 | Intégration | Badge de copie périmée : téléchargement, puis édition de la page → badge présent ; re-téléchargement → badge absent |
|
||
| T-10 | E2E | Sélectionner un texte → menu Skills → Corriger → diff → accepter : le texte est remplacé, l'historique de page contient la version |
|
||
| T-11 | E2E | Chat : `/` → choisir un skill → pastille visible → réponse produite |
|
||
| T-12 | E2E | Supprimer la page d'un skill affiché dans un menu ouvert : erreur propre au run, pas d'écran cassé |
|
||
| T-13 | Non-régression | Les tests existants des routes skills (cookie et v2) passent ; le format v1 s'importe toujours |
|
||
|
||
## 10. Critères d'acceptation
|
||
|
||
- [ ] Sur une installation migrée, l'inventaire des skills avant/après est identique (noms, prompts) et chaque skill est devenu une page éditable.
|
||
- [ ] Un playbook existant (page ordinaire) devient un skill invocable en moins de cinq gestes, sans copier-coller.
|
||
- [ ] Une équipe partage une base de skills ; un membre la découvre dans *Discover*, l'active pour lui, et l'invoque depuis l'éditeur.
|
||
- [ ] Les quatre surfaces d'invocation (chat `/`, sélection, slash, bloc) passent par le même Runner (vérifiable : elles écrivent le même format de `skill_runs`).
|
||
- [ ] Un skill exporté en `SKILL.md` se réimporte sans perte ; le badge signale une copie locale périmée après édition de la page.
|
||
- [ ] Aucun run de skill ne peut utiliser un outil que la politique ou le skill ne lui donne pas.
|
||
- [ ] Le mode LLM `offline` exécute tous les parcours de test de la phase.
|
||
|
||
## 11. Risques spécifiques et vigilance
|
||
|
||
| Risque | Vigilance |
|
||
|---|---|
|
||
| La recréation d'`agent_skills` casse les clés étrangères existantes (références depuis d'autres tables) | Inventaire des FK entrantes **avant** T01 ; la recréation suit le patron SQLite (désactiver les FK pendant la copie, `foreign_key_check` après) |
|
||
| Les pages générées par la migration polluent les *Recents* et la recherche des utilisateurs | Marquer les pages migrées avec leur date d'origine ; ne pas toucher `recents` ; la recherche les indexe comme des pages normales (c'est voulu), mais le titre est préfixé du nom du skill d'origine, sans décor |
|
||
| Deux bases privées créées en concurrence (double premier skill) | Contrainte d'unicité applicative + création dans la même transaction que le premier skill (T04) |
|
||
| Les descriptions des 17 presets, écrites pour des humains, sont trop vagues pour le futur routeur | T13 impose leur relecture dès cette phase (le routeur de la Phase 2 en dépend) |
|
||
| Confondre défaut du skill et choix utilisateur dans l'UI | §7.2 + libellés distincts dans la bannière (« pour tous ») et la Library (« pour moi ») |
|
||
|
||
## 12. Ordre d'exécution
|
||
|
||
```text
|
||
T01 ── T02 ── T03 ──┬── T04 ── T05 ── T06 ── T07
|
||
├── T09 ──┬── T10
|
||
│ ├── T11
|
||
│ └── T12
|
||
├── T08
|
||
├── T13 ── T14
|
||
└── T15 ── T16 ── T17
|
||
T18 en fil continu ; tests au fur et à mesure, jamais en bloc final.
|
||
```
|
||
|
||
Le chemin critique est T01 → T03 → T09 : tant que le Runner n'existe pas, aucune surface ne peut être rebranchée.
|
||
|
||
## 13. Définition de « terminé »
|
||
|
||
La phase est terminée quand : les critères du §10 passent sur une base migrée depuis la v7.69.8 **et** sur une installation neuve ; la suite de tests existante est verte ; la documentation in-app (T18) est livrée ; le document parent est annoté (version 1.2 : « Phase 1 livrée », avec les écarts éventuels entre plan et réalisation consignés plutôt que tus).
|
||
|
||
---
|
||
|
||
*Document de phase 1/5 — voir le document parent pour l'analyse fonctionnelle Notion et l'architecture d'ensemble.*
|