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/.
30 KiB
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 avecEnable 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 enSKILL.mdté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
- Objectif et principe directeur
- Périmètre
- État d'entrée — ce qui existe en v7.69.8
- Modèle de données — migration 39
- Tickets de travail
- API livrée par la phase
- Comportements détaillés
- Plan de migration des données existantes
- Tests
- Critères d'acceptation
- Risques spécifiques et vigilance
- Ordre d'exécution
- 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 dansskill_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 importSKILL.md; formatflowdeck-skillv1 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. LaDescriptionest 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_runsjournalise avec les références disponibles (conversation, surface, utilisateur) ; la colonnerun_iddé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é)
-- 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
page_id UNIQUE: un skill = une page, sans exception. Le marquage est idempotent (un second marquage renvoie le skill existant).ON DELETE CASCADEdepuispages: mettre la page à la corbeille purge dure ou la suppression effective retire le skill partout (menus, Library) — comportement attendu, testé (T-19).- Pour un skill de base,
descriptionetfiles_jsonsont 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. - Pour un skill autonome,
description/files_jsonsont édités depuis la bannière et stockés ici. tools_jsonhé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
- Sauvegarde : snapshot SQLite par le service de backup existant avant le déploiement (procédure habituelle des migrations risquées).
- Inventaire : compter les skills existants par utilisateur et par type ; photographier les hashes des prompts.
- Migration 39 : schéma + T02 en une transaction ; en cas d'échec, rien n'est appliqué (patron des migrations Flowdeck).
- 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.
- 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 deskill_runs). - Un skill exporté en
SKILL.mdse 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
offlineexé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
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.