#1721·skills

skill-creator: trigger detection reports 0% recall for every skill

Author: jparker-RGCreated Sep 3, 2026Updated Sep 3, 2026

Summary

skill-creator's trigger evaluation reports precision=100% recall=0% for every skill, regardless of how good the description is. run_loop then "improves" the description against evidence that every positive query failed, and reports the result as an optimisation.

This fails silently. Nothing errors, and the output looks like a legitimate measurement of a bad description.

Cause

run_single_query() in skills/skill-creator/scripts/run_eval.py decides whether the skill triggered by watching the claude -p event stream. Three paths return "did not trigger" before the evidence can arrive.

1. Any other tool appearing first is treated as proof of failure (line ~137):

if tool_name in ("Skill", "Read"):
    pending_tool_name = tool_name
    accumulated_json = ""
else:
    return False

A realistic query makes the model look around first — Bash, Glob, Grep. The harness sees that first tool, returns False, and kills the process. This bites hardest on exactly the queries the skill-creator guide asks authors to write: concrete ones, with file paths and backstory.

2. The first content_block_stop ends the whole judgement:

elif se_type in ("content_block_stop", "message_stop"):
    if pending_tool_name:
        return clean_name in accumulated_json

A Read of an unrelated file before the Skill call closes its own block, and the match is decided against that block's accumulated input. A later Skill call is never seen.

3. The non-streaming fallback returns inside its own loop:

for content_item in message.get("content", []):
    ...
    return triggered

The return is inside the for, so only the first tool_use in an assistant message is examined.

Evidence

Found while tuning the description of reveal-doc-style, an internal house-style skill for protocol and regulatory prose at Reveal Genomics — it carries the writing rules and the tracked-changes tooling used on a diagnostic assay's bridge study plan.

Raw stream for a query the harness scored as not triggering:

query:             "i went through the bridge study plan last night and made a bunch of edits in
                    word. can you work out what i changed and roll it into section 7"
tool_use order:    ['Skill', 'Bash', 'Bash', 'Bash', 'Read']
input_json_delta:  '{"skill": "reveal-doc-style-skill-test01'

That query is representative of the whole set: concrete, with a real document and a real section number, which is exactly what makes the model orient itself with Bash before or around the Skill call.

The skill was the first thing the model reached for, and the harness still scored it 0.

Measured on a 20-query trigger set for reveal-doc-style — 11 should-trigger, 9 deliberately near-miss negatives (a warehouse query, a docx-to-markdown conversion, a slide deck, a literature lookup) — serially, one run per query:

shipped patched
positives detected 0 / 11 5 / 11 (original description)
positives detected 9 / 11 (revised description)
false alarms 0 / 9 0 / 9

The patched harness is what showed the original description was under-triggering, and what confirmed a revision fixed it. The shipped harness reported both as identically worthless.

Fix

Keep scanning until the result event: don't treat other tools as disproof, reset per-block state instead of concluding from it, and return from the fallback only after the whole message has been walked. Happy to send a PR if useful.

Also, possibly separate

With --num-workers above 1 the patched harness still returned zeros on queries that pass serially. Not diagnosed; all numbers above are --num-workers 1. Worth checking whether concurrent claude -p processes sharing one .claude/commands/ directory interfere.

Environment

Claude Code on Windows, running the harness under WSL. claude plugin CLI available; anthropic-skills plugin.