← All Articles
ArchitectureSkills•8 min read•Oct 5, 2026

OpenClaw Skill Routing Architecture: Descriptions, Manifests, and Which Skills Each Project Gets to See

Every skill you install is a candidate for every request, in every project, until you decide otherwise.

Open the CLAUDE.md at the root of the repo that builds this site and the first thing in it is a block fenced by two HTML comments, skill-routing:start and its matching end marker. Inside, it says the project's surfaces are a marketing site and content, and it lists five skills in priority order: /design-review, /delight-pass, /qa, /verify, /email-craft. Nobody typed that list into the file. A script generated it from a single routing manifest that lives outside the repo, and a note above the list tells humans not to edit it by hand.

That block is the whole subject of this article.

How a Skill Gets Picked

The mechanics are well covered elsewhere, so briefly. A skill is a folder under ~/.openclaw/skills/ with a SKILL.md inside, plus whatever scripts and examples it needs. The file opens with YAML frontmatter holding a name and a description. At boot the agent sees only those two fields for every installed skill. When a request matches a description, the full body gets read into context and the agent follows it. Community skills come from ClawHub with clawhub install, and the OpenClawKit guide walks through packaging your own.

Every guide I read stops there, at roughly the point where the trouble starts. That loading model is cheap per skill and quietly expensive in aggregate, because the matching step is the model reading a list of descriptions and making a judgment call. Ten skills, the judgment is easy. Sixty skills, several of them overlapping, written by different people (or by the agent itself, months apart), and the agent starts picking the plausible skill instead of the right one.

The Description Is the Router

I think most skill descriptions are written for the wrong reader. They read like a README summary, telling a human what the skill is. The model needs to know when to reach for it, and just as badly, when to leave it alone. I write mine with an explicit trigger phrase and an explicit exclusion:

---
name: design-review
description: >
  Designer's-eye QA of a live page: spacing, hierarchy, components
  that drifted from the rest of the site. Use when: "audit the
  design", "does this look right", "visual QA", "polish this page".
  Do NOT use for broken links or failing forms (that is qa).
---

The Use when: line carries the user's own words, because those are what the request will contain. The Do NOT use line names the neighbour skill by name. That one line resolves more misroutes than anything else I have tried, since the hard cases are almost never a skill that matches nothing. They are two skills that both match.

The routing block in this repo depends on that convention. Proactive suggestion is switched on there, which means a phrase matching a skill's Use when: text surfaces the skill without anyone typing its slash-name. A vague description in that setup fires at random, and a missing one never fires at all.

One Skill Directory, Many Projects

The skills directory is global. The work is not. A content site has no use for a court-document skill, and a legal workspace should never be offered the email-template skill that writes marketing copy. If you leave the directory flat, every project gets every skill, and the only thing keeping them apart is the model's reading of sixty descriptions on every turn.

So I keep one manifest that maps projects to surfaces and surfaces to an ordered skill list. It lives at ~/.openclaw/workspace/skill-routing.yaml, and a small generator (tools/generate-skill-routing-sections.py) rewrites the marked block inside each project's CLAUDE.md. A manifest shaped like this is enough:

# skill-routing.yaml  (shape only, trimmed)
projects:
  openclaw-blueprint:
    surfaces: [marketing-site, content]
    proactive_suggest: true
    skills:            # priority order: first match wins a tie
      - design-review
      - delight-pass
      - qa
      - verify
      - email-craft

Three choices in there are deliberate, and they carry different weight.

The order matters most. /design-review and /qa genuinely overlap on a marketing site, since a broken layout is both a visual problem and a bug, and on this project I want the visual pass first. On an app with real user flows I would flip them. The list order is how one global skill directory produces different behaviour per project without forking a single SKILL.md.

Then the markers. A CLAUDE.md is a file people edit, and a generator that rewrites the whole file will eventually delete somebody's hand-written rule. Rewriting only the text between skill-routing:start and the end marker lets the generator run on every change to the manifest without anyone flinching.

Surfaces are the least important. They are labels, useful mostly when you add a new project and want to copy the skill list from a project of the same kind.

Routing to a Skill That Is Not There

A routing entry that points at a skill missing from disk is worse than no entry, because the agent believes it has a capability it cannot load.

Check the Manifest Against the Disk

I learned the shape of this failure from the model escalation work, where a working script sat unused all summer because the agent's instructions never named it. Skills fail the same way in reverse. The instructions name a skill, the skill got renamed or never finished installing, and the agent improvises something in its place. I run this before the generator, and the generator refuses to write if it fails:

#!/bin/bash
# check-skill-routing.sh: every routed skill must exist and say when to use it
manifest=~/.openclaw/workspace/skill-routing.yaml
skills_dir=~/.openclaw/skills
fail=0
for name in $(yq '.projects[].skills[]' "$manifest" | sort -u); do
  f="$skills_dir/$name/SKILL.md"
  if [ ! -f "$f" ]; then
    echo "MISSING   $name"; fail=1; continue
  fi
  grep -qi 'use when' "$f" || { echo "NO TRIGGER $name"; fail=1; }
done
exit $fail

Run it the other way too, once in a while. Any skill on disk that no project routes to is either dead weight or a gap in the manifest, and you want to know which.

Test Phrasings, Both Directions

Keep a short file of real requests per project with the skill each one should trigger, and a second file of requests that should trigger nothing. Replay both after any change to a description. The second file is the one people skip, and it is the one that catches a greedy description, like a /verify skill that starts claiming every message containing the word "check".

The manifest doubles as a security boundary, which I like more than I expected to. A skill pulled from ClawHub cannot fire inside a project until someone adds it to that project's list, so review happens at the moment of routing rather than at install. The security architecture guide covers what that review should look for.

Internal Links & Further Reading

FAQ

Q: Why not give each project its own skills folder?

You can, and for a client workspace with private skills you should. For shared skills it means copies, and copies drift. A fix to /qa lands in one project and the other four keep the old version. One directory plus a manifest gives you per-project behaviour with a single copy of each skill.

Q: Should the agent be allowed to write its own skills?

Yes, into the directory. Not into the manifest. An agent that notices a repeated workflow and writes it down is doing useful work, and a person deciding which projects get to use that skill is the cheapest review step you will ever add.

The Bottom Line

Write every description with a Use when: line and a Do NOT use line that names the neighbour. Route skills per project from one manifest, in priority order, and generate the routing block between markers so nobody edits it by hand.

Then check the manifest against the disk before every regeneration, because a skill the agent thinks it has is more dangerous than one it knows it lacks.

Get the free OpenClaw deployment checklist

Production-ready setup steps. Nothing you don't need.