skill-router — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited skill-router (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
What it is: skill-router is the request-time dispatch discipline for choosing which existing skill should handle an agent request, plus which adjacent skills should be co-loaded or excluded.
Mental model: Read the compiled manifest, score activation signals additively, filter by project fit, apply quality and specificity tiebreakers, exclude conflicting ownership, expand dependencies and verification partners, and report coverage gaps instead of guessing.
Why it exists: Skill libraries decay when ambiguous requests silently activate the wrong skill. Routing needs explicit evidence and evals so maintainers can see whether the library covers real requests.
What it is NOT: It is not a one-skill metadata audit, new-skill authoring guide, library-wide infrastructure health audit, or general debugging workflow. It is also not model routing (choosing which LLM handles the request) and not a workflow orchestrator (sequencing multi-step subagent runs) — it is the one-shot decision of which skill's methodology the agent should follow before execution begins.
Adjacent concepts: graph-audit checks one skill or manifest surface; skill-scaffold creates new skills; skill-infrastructure watches library-wide health and routing-miss patterns; eval-driven-development helps design routing evals; context-graph supplies the graph context the router consumes.
One-line analogy: The router is an air-traffic controller for skills: it assigns the request to the right runway, co-loads support, and says when no runway is fit.
Common misconception: A router is not a nearest-neighbor guesser; a no-match result is useful evidence, not a failure to be hidden.
keywords arrays to identify the best candidate, with stopword filtering and per-token dedup so a keyword bag cannot stuff its way to the toptriggers field) — the highest-weighted activation signalpaths arrays for file-activated skills, including gitignore-style negation semanticsdepends_on, one-hop verify_with and broader, and the score-aware suppression guardRouting is adversarial against convenience. The tempting move — "if nothing matches exactly, just pick the closest skill and activate it" — is the one that silently degrades every agent that depends on the router. A wrong skill that activates confidently is worse than a coverage gap that surfaces loudly, because silent wrongness has no signal for anyone to fix. The router's job is to produce either a certain winner or an explicit non-answer, never a confident guess.
Five principles follow from that stance:
scripts/skill-graph-route.js) does not stop at the first matching surface. It scores every surface additively into a single number — a trigger hit is +5, a keyword exact-token match is +3, a keyword substring match is +1, a --path glob hit is +2 — then ranks by the total. Triggers are weighted highest precisely so a declared label dominates, but the score is cumulative: two precise keyword matches (+6) can outrank a single trigger (+5), and that is intended — convergent evidence should win. The intuition "a declared trigger should decide" holds because of the weight gap, not because evaluation halts.project[]) wins over an ambient one only when its declared project[] membership fits the active workspace; otherwise a project-anchored skill out of context is wrong context, not more specific. Certified application_verdict skills get a gentle additive boost, and stale or unverified skills are annotated rather than silently promoted. Never rank by skill age, usage count, or author preference.verify_with and broader are one-hop only, and why narrower is never co-loaded.The router scores each candidate skill across the surfaces below, sums the weights into a single score, and ranks. The surfaces are not a stop-at-first-match chain; their weights encode priority. Graph edges then explain co-loading, dependency expansion, and exclusions on top of the ranked result.
| Weight | Surface | Field consulted | Match rule |
|---|---|---|---|
| +5 | Trigger label | triggers | Exact match against the normalized full query, a retained query token, or a normalized trigger phrase. The dominant signal. |
| +3 | Keyword exact | keywords | Each query content-token earns +3 at most once per skill, no matter how many keyword phrases contain it — this is the keyword-stuffing guard. Keyword tokens shorter than 3 chars are ignored. |
| +1 | Keyword substring | keywords | A query token (≥3 chars) that did not earn an exact match, found as a substring of any keyword phrase, earns +1 once. Run as a separate second pass so a substring credit cannot poison a later exact match. |
| +2 | File path | paths | Only when the caller passes --path. Glob match against the touched path after gitignore-style !negation patterns are subtracted. |
Description semantics is NOT scored by the deterministic harness. The reference router does not text-match the description field; description quality drives routing in the LLM-reasoning runtimes described under "Routing at scale" below. Keep description a sharp routing contract regardless, because that is the surface that decides routing when a native Agent-Skills runtime — not this deterministic harness — does the dispatch.
the, this, how, with, …) are stripped from the query and from keyword phrases before comparison, so a prompt like "fix this" cannot exact-match every keyword phrase containing "this" and trigger library-wide false positives.keywords is capped at 10 in the contract: more phrases cannot buy more score for the same token, so padding the array is wasted authoring effort and a routing smell.Project membership is checked before a score is accepted. A skill with a non-empty project[] only routes when no project filter is active, or when one of its project[] handles matches the active project; otherwise it is excluded by the project filter entirely. On a score tie, a project-anchored skill (non-empty project[]) outranks an ambient one — project-anchoring, not publishability, is the specificity signal. (The retired deployment_target enum and the boolean public gate are not routing-specificity signals; public only governs whether a skill is publishable.)
The router also surfaces quality state. eval_state is an opt-in gate via --min-eval-state (default: no gating), and lifecycle.stale_after_days lets the route explanation mark stale skills instead of hiding freshness risk.
After scoring, the router applies the four-verdict Audit Status in a fixed order. This is the current behavior in scripts/skill-graph-route.js; treat it as the canonical gate semantics:
structural_verdict: FAIL or truth_verdict: BROKEN removes the skill — a genuinely broken skill never routes. Critically, UNVERIFIED does not block: the corpus default is UNVERIFIED, and gating on PASS would delete most of the library. "Unknown" is treated as routable, not as broken.application_verdict — HARMFUL, REDUNDANT, or FALSE_POSITIVE — excludes the skill. MIXED is not gated out (it means "applicable on some cases", not proven-bad). A negative verdict expires so a since-fixed skill is not tombstoned forever: it lapses when the skill's last_changed is newer than the grading receipt, or when the grade is older than 90 days. A negative verdict with no eval_last_run receipt is conservatively treated as still active.application_verdict: APPLICABLE adds +2 and PROVISIONAL adds +1 to the sort key. The boost is a tiebreaker, never an override: a strong keyword match on an UNVERIFIED skill still outranks a weak match on an APPLICABLE one. UNVERIFIED and MIXED are neutral (0).Relation edges run in a fixed order. The canonical exclusion edge is relations.suppresses (ADR-0018; the router reads suppresses first and falls back to the deprecated boundary alias for unmigrated skills):
suppresses another selected skill, the suppressed skill is removed and so are its own co-loads — a suppressed skill must not contribute its verify_with / depends_on / broader partners to the result.boundary_exclusion_removes_stronger_match failure mode, where a weak owner would otherwise evict the genuinely stronger match.skos:broader): when a specific child matches, its broader parent is pulled in for context. narrower is deliberately not co-loaded — a parent matching does not make its children relevant.If no skill matches any surface, the router does not fall back to a default skill. It surfaces a coverage gap and recommends authoring a new skill or broadening an existing skill's keywords array. Silent fallback to a wrong skill is worse than an explicit coverage-gap signal. Capture the missed query as a future routing-eval case so the gap is closed with evidence, not just patched.
The deterministic surface-scoring router teaches the mechanics of explainable dispatch, but it is one of three routing strategies the field uses, and it has a known scaling boundary. Situate it honestly:
suppresses / depends_on edges are applied to the reranked winner as a post-process. This is the shape of the SkillRouter system (74.0% top-1, +6pp over a much larger zero-shot 8B pipeline).The scaling boundary — the body becomes the decisive signal. Surface metadata (keywords, triggers, name, description) is sufficient discrimination only while the library is small and low-overlap. Recent retrieval research finds that in large, highly overlapping pools the full skill body is the decisive routing signal: removing the body degrades top-1 accuracy by roughly 29–44 percentage points across BM25, embedding, and reranking methods, and an attention analysis attributes 91.7% of routing attention to the body versus 7.3% to the name and 1.0% to the description. The lesson for a maintainer: keyword/trigger metadata buys precision in a curated library, but it does not scale to a sprawling, overlapping one — past that point, routing must read the body, and the right move is to split or sharpen overlapping skills, not to keep tuning keyword arrays. (Sources: SkillRouter, arXiv 2603.22455; Anthropic, "Equipping agents for the real world with Agent Skills.")
| Anti-pattern | Problem | Fix |
|---|---|---|
| Silent default fallback | Hides missing expertise and poisons context with a wrong frame; the misroute looks successful to the router. | Surface a coverage gap loudly; recommend a new skill or broader keywords. |
| Keyword bag-stuffing | Padding keywords to win routing. | Per-token credit is capped — repeating a token across phrases earns nothing extra. Sharpen the phrase instead. |
| Ambiguous descriptions / overlapping siblings | The router cannot distinguish two near-identical skills. | Add anti_examples and a suppresses edge naming the owner; at scale, split or sharpen the overlapping skills. |
| Stale verdict poisoning routing | Gating forever on an old HARMFUL/REDUNDANT verdict. | Rely on the 90-day / last_changed-supersession expiry so a since-fixed skill is not tombstoned. |
This skill's routability is tested by skill-graph/scripts/skill-graph-routing-eval.js, which evaluates the skill's manifest examples[], anti_examples[], and relation boundaries after generating a fresh manifest. The harness runs each examples[] prompt and requires this skill as the top-1 winner (positive class), and each anti_examples[] prompt and requires this skill to NOT win (a winner named in relations.suppresses[] is a boundary-target pass; a null winner is an informational coverage gap, not a failure). It emits a confusion matrix (expected → actual) to help maintainers spot overlapping keywords or missing boundary edges, and a skill only earns routing_eval: present when all its example cases pass. The skill also ships application and comprehension evals alongside the skill. The eval prompts test weighted-additive match scoring, explicit coverage-gap behavior, relation-aware routing, and the refusal to fall back to a default. Consumers in other agent runtimes can translate those cases to their own grading harness.
After applying this skill, verify:
## Do NOT Use When do not point to a better skill.| Use instead | When |
|---|---|
| The target skill directly | The correct skill is already known — skip the router and load it |
documentation | The task is writing or structuring doc prose, not routing |
graph-audit | The task is auditing whether routing metadata is consistent, not dispatching a query |
skill-scaffold | The task is authoring a new skill from scratch, not dispatching to an existing one |
skill-infrastructure | The task is analyzing routing-miss patterns or health across the whole library, not one request's dispatch |
context-graph | The task is designing the underlying relation graph the router traverses, not the dispatch decision itself |
| Generic RAG / similarity search | The task needs typed Skill Graph metadata (relations, verdicts, project-fit), not raw text similarity |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.