Vérification d'activation de compétence
Une compétence ne justifie son existence que si le modèle l'atteint au bon moment. Cette
décision est prise presque entièrement à partir de la description de la compétence dans son frontmatter — donc
« ma compétence se déclenche-t-elle ? » est une question mesurable, et voici comment la mesurer :
construire un ensemble étiqueté de demandes utilisateur, exécuter un agent réel contre chacune d'elles, et scorer
si la compétence a été engagée.
La demande de l'utilisateur est : $ARGUMENTS
Étape 1 — Localiser la compétence cible
$ARGUMENTS peut être un chemin vers un fichier SKILL.md, un chemin vers le répertoire de la compétence, un
nom de compétence, ou vide.
- Vide : glob
./.claude/skills/*/SKILL.mdet**/skills/*/SKILL.md. Une correspondance → l'utiliser. Plusieurs → les lister et demander laquelle. Aucune → le dire et s'arrêter. - Un répertoire : utiliser le fichier
SKILL.mdà l'intérieur. - Un nom : trouver le répertoire de compétence correspondant.
Le nom nu de la compétence est le nom du répertoire contenant SKILL.md. Lire la
description du frontmatter et la garder devant soi : cette chaîne est ce que le modèle
fait correspondre, donc elle est l'entrée principale pour la conception des lignes et la chose que vous finirez par
recommander de modifier.
Mesurer sa longueur pendant que vous y êtes — deux budgets distincts la tronquent, et l'un ou l'autre produit un résultat de faible rappel qui ressemble exactement à une mauvaise formulation.
- Troncature par compétence. La
descriptionetquand_utilisersont concaténées et coupées à un budget de caractères fixe — 1 536 caractères, configurable via le paramètreskillListingMaxDescChars. Le texte de déclenchement au-delà de la limite ne peut pas affecter l'activation du tout, donc il pourrait tout aussi bien ne pas exister. - Le budget de la liste entière, qui importe plus précisément dans les dépôts qui exécutent des suites d'activation. La liste contient toujours chaque nom de compétence, mais son budget de caractères total évolue à environ 1 % de la fenêtre de contexte du modèle, partagé entre toutes les compétences que l'utilisateur a installées. Quand il déborde, Claude Code supprime les descriptions en commençant par les compétences que vous invoquez le moins.
Le second a une conséquence qui mérite d'être énoncée clairement : dans un dépôt avec de nombreuses compétences, une compétence peut obtenir un rappel proche de zéro avec une description parfaitement bonne, car sa description n'a jamais été dans la liste. Réécrire la formulation ne résout alors rien. Et l'ordre de suppression est le moins invoqué d'abord, donc une compétence nouvellement écrite — qui est par définition rarement invoquée, et exactement celle sur laquelle quelqu'un exécute cette suite — est la victime la plus probable. C'est un biais systématique contre la compétence testée.
Leviers, si la liste est le problème : skillListingBudgetFraction (la valeur par défaut de 1 %), la
variable d'environnement SLASH_COMMAND_TOOL_CHAR_BUDGET (un nombre de caractères fixe), et
skillOverrides défini sur "name-only" pour libérer du budget pour les compétences dont vous n'avez pas besoin de correspondance.
Si la compétence n'a pas de description dans son frontmatter, s'arrêter et signaler cela comme
constat — une compétence sans description ne peut jamais être invoquée par le modèle, donc une suite
obtiendrait un rappel nul par construction et ne vous apprendrait rien que vous ne sachiez déjà.
Étape 2 — Confirmer que coder-eval est installé
Exécuter coder-eval --version. Installer ce plugin n'a pas installé la CLI, et la
suite ne peut être validée ni exécutée sans elle.
Si elle est manquante, suivre ${CLAUDE_PLUGIN_ROOT}/reference/cli-setup.md : proposer l'installation,
demander avant de l'exécuter, et confirmer avec coder-eval --version
après. Ne jamais installer sans y être invité, et ne pas continuer si l'utilisateur refuse.
Cette référence couvre également l'autre moitié de la vérification de version — si ce projet épingle une version de coder-eval, et quoi faire quand la version installée ne correspond pas.
Étape 3 — Vérifier ce qui couvre déjà cette compétence
Avant de concevoir quoi que ce soit, regarder ce que le dépôt a déjà. Localiser l'arbre des tâches
selon ${CLAUDE_PLUGIN_ROOT}/reference/repo-layout.md et y faire un glob pour une tâche portant un
critère skill_triggered.
Si une suite existante nomme déjà cette compétence, signaler sa couverture — combien
de lignes positives et de distraction, sur quels suite_thresholds elle se base — et proposer de l'étendre
en ajoutant des lignes à son jeu de données, plutôt que de créer une suite parallèle.
Deux suites pour une même compétence divergent et l'utilisateur paie pour les deux à chaque exécution. Ne créer
que lorsque rien ne couvre cette compétence, ou lorsque l'utilisateur choisit de le faire après avoir vu ce qui existe.
Deux choses qui valent la peine d'être signalées plutôt que d'être contournées silencieusement :
- Une suite existante sans lignes de distraction. La précision est de 1.0 par définition, donc la moitié de son résultat est dénué de sens. Proposer d'ajouter des distracteurs — une véritable amélioration par rapport à la création d'une suite rivale ou à ne rien dire.
- Une suite voisine qui nomme une compétence différente. Ce n'est pas une couverture. Créer et expliquer pourquoi la voisine n'a pas compté.
Puis vérifier d'où doit provenir le bloc agent.plugins. Si une expérience sur laquelle la tâche va
se résoudre fournit déjà ce bloc, l'hériter et ne pas en écrire un — une tâche
qui redéclare ce que l'expérience fournit en diverge, sans rien pour rattraper la divergence. Écrire le bloc propre du modèle
seulement quand rien n'expose déjà la compétence au bac à sable. Si plusieurs expériences existent et qu'il n'est pas clair
sur laquelle cette tâche se résout, demander plutôt que deviner.
Étape 4 — Concevoir les lignes
Cette étape est toute l'expérience. Le reste est mécanique.
Lignes positives — demandes qu'un véritable utilisateur ferait plausiblement et que la description prétend couvrir. Paraphraser ; ne jamais copier le libellé de la description. Une ligne reprise de la description teste la correspondance de chaîne, pas l'activation. Varier le vocabulaire et inclure au moins une ligne oblique où l'utilisateur décrit son problème plutôt que l'opération que la compétence effectue.
Lignes de distraction — demandes adjacentes que la compétence ne devrait pas revendiquer, surtout celles qui partagent du vocabulaire avec la description. C'est ce qui rend la précision significative.
Lignes appartenant à des compétences sœurs (optionnelles) — demandes qui appartiennent légitimement à une autre compétence nommée
dans le même dépôt, avec expected_skill défini sur cette compétence sœur. Dans un
dépôt multi-compétences, le mauvais routage entre deux compétences adjacentes est l'échec courant, et
une simple distraction montre seulement que un dérapage a eu lieu, pas vers où il est allé. Ces lignes
disent où.
Les ajouter lorsque deux compétences ont une thématique qui se chevauche et que vous voulez savoir laquelle gagne. Elles coûtent des lignes supplémentaires, et chaque ligne est une exécution complète de l'agent — donc les traiter comme un suivi ciblé, pas un défaut.
Ne jamais nommer la compétence dans une invite. Cela teste l'obéissance, pas l'activation :
- Mauvais : "Utilise la compétence pdf-forms pour remplir cette demande."
- Bon : "Je dois remplir les champs de ce PDF de demande et le renvoyer."
Cette règle n'est pas affectée par les lignes appartenant à des compétences sœurs : expected_skill est une étiquette dans le
jeu de données, lue par le critère et jamais montrée à l'agent. L'prompt de la ligne ne doit
toujours pas nommer de compétence, y compris la sœur.
Dimensionnement. Minimum 3 positives + 3 distracteurs. Viser 8–12 de chaque pour un signal sur lequel on peut agir — le rappel sur 3 lignes se déplace par bonds de 33 points, ce qui est trop grossier pour distinguer une véritable régression du bruit. Le modèle fourni contient 6 lignes parce que c'est le minimum illustratif, pas un objectif. Les lignes appartenant à des compétences sœurs sont en plus de cela : 8–12 positives plus 8–12 distracteurs font déjà 16–24 exécutions, donc indiquer le total résultant avant d'écrire la suite.
Refuser de générer une suite sans lignes de distraction. Sans négatifs, la précision est de 1.0 par définition et la moitié du résultat est dénuée de sens. Expliquer pourquoi et demander les cas adjacents à la place.
Étape 5 — Écrire la suite
Copier les deux fichiers modèles dans l'arbre des tâches de l'utilisateur, situé en suivant
${CLAUDE_PLUGIN_ROOT}/reference/repo-layout.md (si le dépôt n'en a pas, proposer un
emplacement et demander) :
${CLAUDE_PLUGIN_ROOT}/reference/templates/activation.yaml${CLAUDE_PLUGIN_ROOT}/reference/templates/activation-rows.jsonl
Puis substituer, en gardant le JSONL à côté du YAML (les entrées dataset.paths se résolvent
par rapport au fichier de tâche) :
task_id→<nom-compétence>-activationskill_name→ le nom nu de la compétenceexpected_skillde chaque ligne positive → le même nom nu ; les lignes de distraction gardent""; leexpected_skilld'une ligne appartenant à une compétence sœur → le nom nu de cette sœurpromptde chaque ligne → les demandes conçues à l'étape 4, un objet JSON par ligne
Puis rendre la compétence accessible, ce qui est l'étape qui détermine si la suite mesure quelque chose.
La tâche s'exécute dans un bac à sable frais qui ne contient aucun des fichiers de l'utilisateur, donc
l'agent ne se voit proposer aucune compétence à moins que la tâche ne dise où elles se trouvent. C'est le bloc agent.plugins
dans le modèle — et c'est le travail du modèle seulement lorsque rien n'expose déjà la
compétence : si l'étape 3 a trouvé une expérience fournissant ce bloc, l'hériter et supprimer la copie du
modèle plutôt que d'écrire une seconde déclaration.
Sinon, le remplir. path doit être une racine de plugin : un répertoire contenant un sous-répertoire skills/,
de sorte que la compétence se trouve à <chemin>/skills/<nom-compétence>/SKILL.md. Un
fichier .claude-plugin/plugin.json est optionnel — sans lui, l'espace de noms par défaut est le nom du répertoire lui-même.
Pour ./claude/skills/pdf-forms/SKILL.md, cette racine est ./claude, pas ./claude/skills.
Pointer vers un simple répertoire de répertoires de compétences ne charge rien du tout.
Mettre en place une racine minimale plutôt que de pointer vers ./claude lui-même. Une racine de plugin charge
le PLUGIN ENTIER, pas seulement ses compétences : un répertoire agents/, commands/ ou hooks/ se trouvant à côté de skills/
devient également visible pour l'agent évalué. Pointer vers le ./claude d'un dépôt et vous donnez
à l'agent chaque sous-agent et commande du projet — et un sous-agent qui peut répondre à la demande est un facteur de confusion, pas un détail. Si
./claude/agents/pdf-expert.md existe pendant que vous mesurez pdf-forms, l'agent peut déléguer à lui au lieu d'appeler la
compétence ; skill_triggered enregistre non, et le rappel chute pour une raison qui n'a rien à voir
avec la description de la compétence. Cela rend également le nombre dépendant du dépôt, de sorte que deux suites
ne sont plus comparables.
Donc construire une racine qui contient exactement l'unité sous test, et pointer vers celle-ci :
SKILL_ROOT="$(mktemp -d)"
mkdir -p "$SKILL_ROOT/skills"
ln -s "$(pwd)/./claude/skills/pdf-forms" "$SKILL_ROOT/skills/pdf-forms"
export SKILL_SOURCE_PATH="$SKILL_ROOT"
Utiliser cp -R au lieu de ln -s là où les liens symboliques sont gênants (Windows, certaines images CI). Si
l'utilisateur préfère la ligne unique, export SKILL_SOURCE_PATH="$(pwd)/./claude" fonctionne toujours —
dire clairement qu'elle expose aussi tout le reste sous ./claude, donc un faible rappel peut être dû aux
compétences sœurs plutôt qu'à la compétence elle-même.
En faire une variable d'environnement plutôt que de coder un chemin absolu dans le YAML — la suite est commitée et réexécutée sur d'autres machines. Si la variable n'est pas définie, la compétence est simplement absente, chaque ligne positive obtient 0, et le résultat est indiscernable d'une compétence qui ne se déclenche jamais, donc confirmer qu'elle est définie avant de signaler tout constat de faible rappel.
skill_name doit être le nom nu même lorsque la compétence provient d'un plugin et est
invoquée comme plugin:skill — le vérificateur supprime l'espace de noms avant de comparer. Une
valeur avec espace de noms ici obtient silencieusement un rappel nul sur chaque ligne, ce qui ressemble exactement
à une compétence cassée.
Parce que la correspondance se fait par nom nu, elle ne peut pas survivre à une collision de noms. Deux compétences appelées
init — l'une d'un plugin, l'autre intégrée — sont la même chaîne pour le critère, donc il
créditera celle qui se déclenche comme si c'était celle sous test. Vérifier que le nom est unique parmi tout ce qui est
installé avant de faire confiance à un résultat (/context et /doctor listent l'ensemble actif).
Une collision ne génère pas d'erreur ; elle mesure la mauvaise compétence. Si une existe, le dire plutôt que de rapporter le
nombre — renommer la compétence, ou mesurer là où la collision est absente, sont les seules solutions honnêtes.
Pour les champs de critère au-delà de ce modèle, lire
${CLAUDE_PLUGIN_ROOT}/reference/criteria.md.
Étape 6 — Valider avant de dépenser quoi que ce soit
coder-eval plan <chemin-vers-activation.yaml> doit se terminer avec le code 0. C'est une vérification de schéma uniquement —
elle ne lit pas le fichier de jeu de données — donc confirmer également que le JSONL se trouve à côté du YAML
et a un objet par ligne.
Étape 7 — Exécution
Chaque ligne est une exécution complète de l'agent : N lignes signifie N exécutions et un coût réel en tokens. Indiquer le nombre de lignes et l'agent/modèle qui sera utilisé, puis demander avant de commencer.
coder-eval run <chemin-vers-activation.yaml>
Le critère est agnostique quant à l'agent — il détecte Claude engageant la compétence via l'outil Skill
, et tout agent sans cet outil (Codex, par exemple) en lisant les fichiers de la compétence sur le disque
— donc la même suite fonctionne quel que soit l'agent sur lequel la tâche se résout.
Étape 8 — Rapporter et interpréter
Présenter le rappel, la précision, le F1 et la matrice de confusion, puis dire ce qu'ils signifient :
- Faible rappel (manque des lignes qu'elle aurait dû attraper) : écarter la troncature et l'éviction de liste
avant de conclure que la description sous-revend. Les deux produisent un résultat de faible rappel
indiscernable d'une mauvaise formulation, et les deux sont bon marché à vérifier :
/doctorestime le coût de contexte de la liste et ses plus gros contributeurs, et la ligne Compétences dans/contextrapporte la taille de la liste après l'application du budget — c'est ce que le modèle a réellement reçu. Ce n'est qu'une fois que la description est démontrablement dans la liste et à l'intérieur du seuil par compétence que la formulation est le coupable : elle ne nomme pas les situations, les types de fichiers ou les formulations qui devraient la déclencher. - Faible précision (se déclenche sur les distracteurs) : la description sur-revend et vole des demandes adjacentes. La réduire, et dire explicitement pour quoi la compétence n'est pas faite.
- Les dérapages concentrés sur une seule compétence sœur (avec des lignes appartenant à des compétences sœurs) : c'est un conflit de frontière entre deux descriptions, pas une description vague. Corriger la compétence sous test seule tend à déplacer l'échec plutôt qu'à le supprimer — dire explicitement pour quoi chacune des deux compétences n'est pas faite, et réexécuter.
- Les deux élevés : rapporter les nombres et le nombre de lignes, et noter qu'une petite suite en dit peu — proposer de l'élargir.
Pointer la description du frontmatter comme la chose à modifier, citer les lignes spécifiques qui ont échoué et
suggérer un libellé qui couvrirait ces cas. Ne pas modifier le fichier directement — c'est à l'utilisateur de
décider de la formulation finale. S'il refuse d'éditer le fichier, proposer à la place une règle de post-traitement qui
réécrit la description de la compétence au moment de la construction de la liste.
Une suite de 3 positives + 3 distracteurs avec F1 > 0.8 ne prouve pas qu'une compétence fonctionne. Avec 6 lignes, la limite de confiance à 95 % pour le rappel est de ±34 % ; la plage est aussi large que celle obtenue sans aucune mesure. Présenter l'intervalle de confiance ainsi que le point central, et proposer une suite plus large si le résultat semble décisif mais que l'incertitude est grande.
Avant de finir, supprimer la racine temporaire créée à l'étape 5 (rm -rf "$SKILL_SOURCE_PATH") — les dépendances
de test ne doivent pas traîner sur le système de fichiers de l'utilisateur.