"""Fusion ACC-4.5 — conversion d'Agents & Skills Flowdeck au format commun (contrat ACC-0.3, `dracodev-commons/contrats/skill-format.md`). Preuve du format : trois presets de la galerie Flowdeck convertis en `skills-communs//SKILL.md` (compte-rendu-reunion, rapport-hebdo, resume-document). Ils doivent être conformes au contrat, se convertir en champs Flowdeck et s'installer dans un workspace via le chemin existant (`skill_gallery.upsert_skill`). """ from pathlib import Path import pytest from app.services import skill_format, skill_gallery RACINE = Path(__file__).resolve().parent.parent CONVERTIS = ["compte-rendu-reunion", "rapport-hebdo", "resume-document"] def _lire(nom: str) -> str: return (RACINE / "skills-communs" / nom / "SKILL.md").read_text(encoding="utf-8") def test_trois_skills_convertis_conformes(): for nom in CONVERTIS: skill = skill_format.analyser_skill_md(_lire(nom)) assert skill_format.valider(skill) == [], (nom, skill_format.valider(skill)) assert skill["meta"]["name"] == nom assert skill["meta"]["apps"] == ["flowdeck"] assert skill["meta"]["version"] == "1.0.0" # Toute écriture est déclarée, avec approbation explicite. actions = {e["action"] for e in skill["meta"]["ecritures"]} assert actions == {"flowdeck.create_document", "flowdeck.write_blocks"} def test_conversion_vers_flowdeck_et_installation(client): for nom in CONVERTIS: skill = skill_format.analyser_skill_md(_lire(nom)) champs = skill_format.vers_champs_flowdeck(skill) assert champs["name"] == nom assert set(champs["allowed_tools"]) == { "search_workspace", "read_document", "create_document", "write_blocks"} assert "Procédure" in champs["prompt_template"] assert "Garde-fous" in champs["prompt_template"] row, cree = skill_gallery.upsert_skill( champs, workspace_id=None, created_by=1) assert cree is True and row["name"] == nom def test_validation_rejette_les_ecarts(): incomplet = skill_format.analyser_skill_md( "---\nname: mauvais nom\napps: [flowdeck]\n---\n\n## Procédure\n\nx\n") erreurs = skill_format.valider(incomplet) assert any("kebab" in e for e in erreurs) assert any("description" in e for e in erreurs) assert any("Quand l'utiliser" in e for e in erreurs) with pytest.raises(ValueError): skill_format.vers_champs_flowdeck(incomplet) def test_ecritures_obligatoires_si_outil_ecrivant(): texte = ( "---\nname: supprime-tout\ndescription: x\nversion: 1.0.0\n" "apps: [flowdeck]\ndomaines: [pro]\noutils: [flowdeck.delete_page]\n---\n\n" "## Quand l'utiliser\n\nx\n\n## Procédure\n\nx\n\n## Garde-fous\n\nx\n") erreurs = skill_format.valider(skill_format.analyser_skill_md(texte)) assert any("ecritures obligatoire" in e for e in erreurs) def test_aller_retour_depuis_un_preset(): preset = skill_gallery.get_gallery("compte-rendu-reunion") texte = skill_format.depuis_preset_flowdeck(preset, domaines=["pro"]) skill = skill_format.analyser_skill_md(texte) assert skill_format.valider(skill) == [] champs = skill_format.vers_champs_flowdeck(skill) assert champs["name"] == "compte-rendu-de-reunion" assert "create_document" in champs["allowed_tools"]