Verificación de activación del skill
Un skill solo vale la pena si el modelo lo alcanza en el momento adecuado. Esa decisión se toma casi por completo a partir de la description del frontmatter del skill. Por lo tanto, "¿se activa mi skill?" es una pregunta medible, y así es como se mide: construye un conjunto etiquetado de solicitudes de usuario, ejecuta un agente real contra cada una y puntúa si el skill fue utilizado.
La solicitud del usuario es: $ARGUMENTS
Paso 1 — Localizar el skill objetivo
$ARGUMENTS puede ser una ruta a un SKILL.md, una ruta al directorio del skill, un nombre de skill o estar vacío.
- Vacío: busca con glob
.claude/skills/*/SKILL.mdy**/skills/*/SKILL.md. Una coincidencia → úsala. Varias → listarlas y preguntar cuál. Ninguna → decirlo y parar. - Un directorio: usa el
SKILL.mddentro de él. - Un nombre: encuentra el directorio del skill que coincida.
El nombre simple del skill es el nombre del directorio que contiene SKILL.md. Lee la description del frontmatter y mantenla presente: esa cadena es lo que el modelo compara, por lo que es la entrada principal para el diseño de filas y la cosa a la que acabarás recomendando ediciones.
Mide su longitud mientras estés allí — dos presupuestos separados la truncan, y cualquiera de los dos produce un resultado de baja recuperación (recall) que parece exactamente una mala redacción.
- Truncamiento por skill.
descriptionywhen_to_usese concatenan y cortan en un presupuesto de caracteres fijo — 1,536 caracteres, configurable mediante la opciónskillListingMaxDescChars. El texto desencadenante más allá del corte no puede afectar la activación en absoluto, por lo que es como si no existiera. - El presupuesto total del listado, que importa más exactamente en los repositorios que ejecutan conjuntos de pruebas de activación. El listado siempre contiene cada nombre de skill, pero su presupuesto total de caracteres escala aproximadamente al 1% de la ventana de contexto del modelo, compartido entre todos los skills que el usuario tiene instalados. Cuando se desborda, Claude Code descarta descripciones empezando por los skills que invocas con menos frecuencia.
El segundo tiene una consecuencia que vale la pena afirmar claramente: en un repositorio con muchos skills, un skill puede obtener una recuperación casi nula con una descripción perfectamente buena, porque su descripción nunca estuvo en el listado. Reescribir la redacción entonces no arregla nada. Y el orden de eliminación es primero los menos invocados, así que un skill recién creado — que por definición rara vez se invoca, y es exactamente sobre lo que alguien ejecuta este conjunto — es la víctima más probable. Eso es un sesgo sistemático contra el skill bajo prueba.
Palancas, si el problema es el listado: skillListingBudgetFraction (el 1% predeterminado), la variable de entorno SLASH_COMMAND_TOOL_CHAR_BUDGET (un recuento de caracteres fijo) y skillOverrides configurado como "name-only" para liberar presupuesto de skills que no necesitas que coincidan.
Si el skill no tiene description en su frontmatter, detente e informa eso como el hallazgo: un skill sin descripción nunca puede ser invocado por el modelo, por lo que un conjunto de pruebas obtendría una recuperación cero por construcción y no te diría nada que no sepas ya.
Paso 2 — Confirmar que coder-eval está instalado
Ejecuta coder-eval --version. Instalar este plugin no instaló la CLI, y el conjunto no se puede validar ni ejecutar sin ella.
Si falta, sigue ${CLAUDE_PLUGIN_ROOT}/reference/cli-setup.md: ofrece la instalación, pregunta antes de ejecutarla y confirma con coder-eval --version después. Nunca instales sin que te lo pidan y no continúes si el usuario lo rechaza.
Esa referencia también cubre la otra mitad de la verificación de versión: si este proyecto fija una versión de coder-eval y qué hacer cuando la instalada no coincide.
Paso 3 — Verificar qué cubre ya este skill
Antes de diseñar nada, mira lo que el repositorio ya tiene. Localiza el árbol de tareas según ${CLAUDE_PLUGIN_ROOT}/reference/repo-layout.md y búscalo con glob para una tarea que lleve un criterio skill_triggered.
Si un conjunto existente ya menciona este skill, informa su cobertura — cuántas filas positivas y de distracción, en qué suite_thresholds se basa — y ofrece extenderlo añadiendo filas a su conjunto de datos, en lugar de crear un conjunto paralelo. Dos conjuntos para un mismo skill se separan y el usuario paga por ambos en cada ejecución. Crea uno nuevo solo cuando nada cubra este skill, o cuando el usuario elija hacerlo después de ver lo que hay.
Dos cosas que vale la pena informar en lugar de solucionar en silencio:
- Un conjunto existente sin filas de distracción. La precisión es 1.0 por definición, por lo que la mitad de su resultado no tiene sentido. Ofrece añadir distractores — una mejora real tanto frente a crear un conjunto rival como a no decir nada.
- Un conjunto vecino que menciona un skill diferente. Eso no es cobertura. Adelante, crea el nuevo y explica por qué el vecino no contó.
Luego verifica de dónde debería venir agent.plugins. Si un experimento contra el que se resolverá la tarea ya proporciona ese bloque, hazlo heredado y no escribas uno — una tarea que redeclara lo que el experimento proporciona se desvía de él, sin nada que detecte la divergencia. Escribe el bloque propio de la plantilla solo cuando nada exponga ya el skill al entorno aislado. Si existen varios experimentos y no está claro contra cuál se resolverá esta tarea, pregunta en lugar de adivinar.
Paso 4 — Diseñar las filas
Este paso es todo el experimento. El resto es mecánica.
Filas positivas — solicitudes que un usuario real haría de manera plausible y que la descripción afirma cubrir. Parafrasea; nunca copies frases de la descripción. Una fila extraída de la descripción prueba la coincidencia de cadenas, no la activación. Varía el vocabulario e incluye al menos una fila oblicua donde el usuario describa su problema en lugar de la operación que realiza el skill.
Filas de distracción — solicitudes adyacentes que el skill no debería reclamar, especialmente aquellas que comparten vocabulario con la descripción. Estas son las que hacen que la precisión sea significativa.
Filas de skills hermanos (opcional) — solicitudes que pertenecen legítimamente a un skill hermano específico en el mismo repositorio, con expected_skill configurado para ese hermano. En un repositorio con múltiples skills, el enrutamiento incorrecto entre dos skills adyacentes es el fallo común, y una distracción simple solo muestra que ocurrió un fallo, no a dónde fue. Estas filas indican eso.
Agrégalas cuando dos skills tengan temas superpuestos y quieras saber cuál gana. Cuestan filas adicionales, y cada fila es una ejecución completa de agente — así que trátalas como un seguimiento dirigido, no como un valor predeterminado.
Nunca menciones el nombre del skill en una indicación (prompt). Eso prueba la obediencia, no la activación:
- Malo: "Usa el skill pdf-forms para llenar esta solicitud."
- Bueno: "Necesito llenar los campos de este PDF de solicitud y devolverlo."
Esta regla no se ve afectada por las filas de skills hermanos: expected_skill es una etiqueta en el conjunto de datos, leída por el criterio y nunca mostrada al agente. La prompt de la fila aún no debe mencionar ningún skill, incluido el hermano.
Tamaño. Mínimo 3 positivas + 3 distracciones. Apunta a 8–12 de cada una para obtener una señal sobre la que puedas actuar — la recuperación sobre 3 filas se mueve en saltos de 33 puntos, lo que es demasiado grueso para distinguir una regresión real del ruido. La plantilla incluida tiene 6 filas porque es el mínimo ilustrativo, no un objetivo. Cualquier fila de skills hermanos se suma a eso: 8–12 positivas más 8–12 distracciones ya son 16–24 ejecuciones, así que indica el total resultante antes de escribir el conjunto.
Rechaza generar un conjunto sin filas de distracción. Sin negativos, la precisión es 1.0 por definición y la mitad del resultado no tiene sentido. Explica por qué y pide los casos adyacentes en su lugar.
Paso 5 — Escribir el conjunto
Copia los dos archivos de plantilla en el árbol de tareas del usuario, ubicados siguiendo ${CLAUDE_PLUGIN_ROOT}/reference/repo-layout.md (si el repositorio no tiene ninguno, propón una ubicación y pregunta):
${CLAUDE_PLUGIN_ROOT}/reference/templates/activation.yaml${CLAUDE_PLUGIN_ROOT}/reference/templates/activation-rows.jsonl
Luego sustituye, manteniendo el JSONL junto al YAML (las entradas dataset.paths se resuelven relativas al archivo de tarea):
task_id→<nombre-del-skill>-activationskill_name→ el nombre simple del skillexpected_skillde cada fila positiva → el mismo nombre simple; las filas de distracción mantienen"";expected_skillde una fila de skill hermano → el nombre simple de ese hermanopromptde cada fila → las solicitudes diseñadas en el paso 4, un objeto JSON por línea
Luego haz que el skill sea accesible, que es el paso que decide si el conjunto mide algo. La tarea se ejecuta en un entorno aislado nuevo que no contiene ninguno de los archivos del usuario, por lo que al agente no se le ofrecen skills a menos que la tarea indique dónde viven. Ese es el bloque agent.plugins en la plantilla — y es trabajo de la plantilla solo cuando nada expone ya el skill: si el paso 3 encontró un experimento que proporciona ese bloque, hazlo heredado y elimina la copia de la plantilla en lugar de escribir una segunda declaración.
De lo contrario, complétalo. path debe ser una raíz de plugin: un directorio que contenga un subdirectorio skills/, para que el skill se encuentre en <ruta>/skills/<nombre-del-skill>/SKILL.md. Un plugin.json en .claude-plugin es opcional — sin uno, el espacio de nombres predeterminado es el nombre del propio directorio.
Para .claude/skills/pdf-forms/SKILL.md, esa raíz es .claude, no .claude/skills. Apuntar a un directorio simple de directorios de skills no carga nada en absoluto.
Prepara una raíz mínima en lugar de apuntar a .claude directamente. Una raíz de plugin carga TODO el plugin, no solo sus skills: un directorio agents/, commands/ o hooks/ junto a skills/ también se vuelve visible para el agente evaluado. Apunta al .claude de un repositorio y le das al agente todos los subagentes y comandos del proyecto — y un subagente que pueda responder la solicitud es un factor de confusión, no un detalle. Si existe .claude/agents/pdf-expert.md mientras mides pdf-forms, el agente puede delegar en él en lugar de llamar al skill; skill_triggered registra no, y la recuperación cae por una razón que no tiene nada que ver con la descripción del skill. También hace que el número dependa del repositorio, por lo que dos conjuntos ya no son comparables.
Así que construye una raíz que contenga exactamente la unidad bajo prueba y apunta a ella:
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"
Usa cp -R en lugar de ln -s donde los enlaces simbólicos sean problemáticos (Windows, algunas imágenes de CI). Si el usuario prefiere la línea única, export SKILL_SOURCE_PATH="$(pwd)/.claude" sigue funcionando — pero di claramente que también expone todo lo demás bajo .claude, por lo que una baja recuperación puede deberse a los hermanos y no al skill.
Mantenlo como una variable de entorno en lugar de codificar una ruta absoluta en el YAML — el conjunto se confirma y se vuelve a ejecutar en otras máquinas. Si la variable no está configurada, el skill simplemente está ausente, cada fila positiva obtiene un 0 y el resultado es indistinguible de un skill que nunca se activa, así que confirma que está configurada antes de informar cualquier hallazgo de baja recuperación.
skill_name debe ser el nombre simple incluso cuando el skill proviene de un plugin y se invoca como plugin:skill — el verificador elimina el espacio de nombres antes de comparar. Un valor con espacio de nombres aquí obtiene silenciosamente una recuperación cero en cada fila, lo que se lee exactamente como un skill roto.
Debido a que la coincidencia es por nombre simple, no puede sobrevivir a una colisión de nombres. Dos skills llamados init — uno de un plugin, otro incorporado — son la misma cadena para el criterio, por lo que acreditará al que se active como si fuera el que está bajo prueba. Verifica que el nombre sea único entre todo lo instalado antes de confiar en un resultado (/context y /doctor listan el conjunto activo). Una colisión no genera error; mide el skill equivocado. Si existe una, dila en lugar de informar el número — renombrar el skill, o medir donde la colisión esté ausente, son las únicas soluciones honestas.
Para campos de criterio más allá de esta plantilla, lee ${CLAUDE_PLUGIN_ROOT}/reference/criteria.md.
Paso 6 — Validar antes de gastar nada
coder-eval plan <ruta-a-activation.yaml> debe salir con 0. Es solo una verificación de esquema — no lee el archivo de conjunto de datos — así que también confirma que el JSONL esté junto al YAML y tenga un objeto por línea.
Paso 7 — Ejecutar
Cada fila es una ejecución completa de agente: N filas significa N ejecuciones y costo real de tokens. Indica el número de filas y el agente/modelo que se usará, luego pregunta antes de comenzar.
coder-eval run <ruta-a-activation.yaml>
El criterio es agnóstico respecto al agente: detecta que Claude utiliza el skill a través de la herramienta Skill, y cualquier agente sin esa herramienta (Codex, por ejemplo) leyendo los archivos del skill desde el disco; por lo tanto, el mismo conjunto funciona independientemente del agente al que se resuelva la tarea.
Paso 8 — Informar e interpretar
Presenta la recuperación (recall), la precisión (precision), la puntuación F1 y la matriz de confusión, luego explica lo que significan:
- Recuperación baja (no captura filas que debería haber capturado): descarta el truncamiento y la expulsión del listado antes de concluir que la descripción es insuficiente. Ambos producen un resultado de baja recuperación indistinguible de una mala redacción, y ambos son baratos de verificar:
/doctorestima el costo de contexto del listado y sus mayores contribuyentes, y la fila Skills en/contextinforma el tamaño del listado después de aplicar el presupuesto — eso es lo que el modelo realmente recibió. Solo una vez que la descripción está demostrablemente en el listado y dentro del límite por skill, la redacción es la culpable: no nombra las situaciones, tipos de archivo o frases que deberían desencadenarla. - Precisión baja (se activa con distractores): la descripción es excesiva y está robando solicitudes adyacentes. Estrecharla, y di explícitamente para qué no sirve el skill.
- Fallas concentradas en un hermano (con filas de skills hermanos): eso es una disputa de límites entre dos descripciones, no una descripción vaga. Arreglar solo el skill bajo prueba tiende a mover el fallo en lugar de eliminarlo — di explícitamente para qué cada uno de los dos skills no es, y vuelve a ejecutar.
- Ambos altos: informa los números y el recuento de filas, y señala que un conjunto pequeño dice poco — ofrece ampliarlo.
Señala la description del frontmatter como lo que hay que editar, cita las filas específicas que fallaron y sugiere redacciones. No edites el archivo tú mismo — el usuario lo hará después de decidir el texto.