SKILL.md. Writing one that measurably improves an agent takes a few learnable rules, and this guide gives them with a before and after for each.
A benchmark measures a skill’s lift: how many more test cases pass with the skill loaded than without it. Everything below serves that number: making it real, making it big, and knowing when it’s gone. It’s the benchmark’s measure, not a promise: your own cases decide whether a skill helps your agent.
When you’ve written one, benchmark it, then publish it.
The one rule
A skill lifts only when it supplies knowledge the model can’t infer on its own. Generic advice is not that knowledge. A capable model already knows to “write clean SQL” or “be thorough”, so a skill restating it measures roughly zero. What lifts is the specific, unguessable stuff: your company’s field conventions, a spec’s exact token list, a new API the model hasn’t seen. A pattern we see constantly (numbers illustrative, so run your own benchmark):- A skill saying “write correct, well-structured SQL”: the model already passes every case without it. Lift: about 0.
- The same schema plus four house conventions the model can’t guess (“active user =
deleted_at IS NULL”, “fiscal year starts Feb 1”): the model fails without the skill and passes with it. Lift: large.
One corollary: never game an eval. If honest iteration shows no lift, the model is good at this already, so re-scope the skill or drop the idea.
SKILL.md format
A skill is a folder holding aSKILL.md and optional supporting files. It follows the open agentskills.io specification, so tools such as Claude Code, Cursor and Copilot can read it too.
SKILL.md is YAML frontmatter followed by a markdown body:
decimalai skills verify ./code-review checks a folder the way the native skill loaders read it. It warns about title, category, skill-type, skill-scope and invocation because its format keeps those labels under metadata. The warning blocks nothing, and all of them are read. Stability isn’t a frontmatter field: set it in the skill’s Settings.
license, compatibility and allowed-tools reach the tools that read your file directly. A sync stores them with the version, and your skill’s page shows its license. The registry’s copy isn’t built from them, so people who add your published skill don’t receive them.
What decimalai skills add writes
A registry skill added to a project lands in .agents/skills/<install-name>/, with its identity and labels under metadata:
decimalai-category, decimalai-skill-scope, decimalai-skill-type and decimalai-source appear when the skill has them, and an on-demand skill also gets disable-model-invocation: true. The SDK and the platform read a label from metadata first, then from the top level.
decimalai-skill-id is what marks a folder as the registry’s, so a sync never uploads it. See Skills on disk for the lock file and for connected projects, where add writes no folder.
Step 1: Classify your skill
Before writing the body, declare the skill’s classification. Two independent axes decide what evals it needs and how long it will live.The two axes
Ask in order:Could the base model do this task at all? If no, it’scapability: you’re teaching a missing ability. If yes, but you want a specific form, it’spreference. Then: is that knowledge public or private? A published standard the base model may one day learn ispublic. House knowledge it will never absorb isprivate.
Only
(capability, public) expires, because a public capability is exactly what a future base model can pick up. When it does, with-skill and without-skill score the same.
A good preference skill encodes a process many teams would recognize, such as a PR-description structure. Avoid obscure standards picked only because the base model doesn’t default to them: that lift comes from the rule being unused, which is closer to gaming the benchmark than teaching. Name skills generically (pr-description, commit-format), and don’t invent a fake company to make a public standard look proprietary.
For edge cases, classify by where the graded knowledge lives. A house style guide built on a public standard is public if your eval grades the standard’s rules, and private if it grades your additions.
Automatic or on-demand?
Theinvocation field decides who fires the skill:
any takes the stricter rule from each row. Default to model, and choose user for workflows a person runs on purpose, such as release procedures. Outcome evals are required either way.
Step 2: Write the description as the trigger
The description is not documentation. It is the firing mechanism, and two separate steps read it before your skill can act:- Retrieval: the description is matched against the user’s query. If it isn’t retrieved, the skill can never fire, so it needs the concrete nouns and verbs users actually type.
- Menu selection: retrieved skills are shown to the model as name and description rows, and the model picks one. Your row has to beat its neighbors.
- State the what and the when, in third person: what the skill does, and the situations where it applies.
- Name the artifacts and verbs users type (“git commit message”, “docx”, “migration”), not category words (“documents”, “helper”, “workflow”).
- Add negative scope: one “Do NOT use for …” clause naming the nearest thing this skill must not hijack.
- Don’t keyword-stuff. Every extra topic word widens retrieval and raises false fires.
description: Formats commits nicely
After: description: Format git commit messages to the Conventional Commits spec (type, scope, imperative subject). Use when writing or rewriting a commit message. Do NOT use for PR descriptions, changelogs, or code review comments.
For invocation: user skills, write for a human scanning a list: lead with the outcome (“Clean up stale worktrees and prune merged branches”).
Step 3: Write the body
The body is the knowledge, stated so the model acts on it.Directives, not essays
Models follow instructions and skim past background prose. Give the reason behind a rule in one clause when it helps. Before: “The Interactions API is generally recommended for multi-turn chat scenarios because it handles session state automatically and is the direction the SDK is moving.” After: “Always useclient.interactions.create() for chat. Never use the legacy generate_content API (it drops session state).”
The highest-value section in any body is worked examples: one before and after per rule cluster, showing the model’s wrong default and the conforming output.
Constraints, not steps
Describe the outcome and the constraints, not a step-by-step path, because a rigid script strips the model’s ability to recover from surprises. On-demand playbooks are the exception. Before: “Step 1: Read config.json. Step 2: Extract the port. Step 3: Edit line 4. Step 4: Save the file.” After: “Set the database port in config.json to 5432. The file must still parse. Always run the tests before opening the pull request.”Put depth in references
A skill loads in three layers with three different costs:
If exact steps are genuinely required, write a
scripts/ program instead of prose. Keep bundled files one level deep, and make the body point at each one with when-to-open guidance (“per-country exceptions: references/exceptions.md”), since an unmentioned reference is never read.
Length is a cost
The body enters your agent’s context on every activation, so every line is paid for in tokens, and the benchmark measures that cost. There is no minimum length. The editing tool is the no-op test: remove the line. Does output change? If not, delete it. “Be thorough and precise” changes nothing, so it goes.Step 4: Build the eval suite
Bad skills don’t crash, they quietly corrupt outputs, so two manual runs is not testing. The suite has two parts: graded cases (does the skill change behavior?) and trigger cases (does it fire at the right times?). Theeval.yaml format and the runner are in Benchmark it. These are the decisions only you can make.
Compose the prompts
Start with 10 to 20 and extend from failures, so every production bug becomes a permanent case. A public lift needs at least 8 cases that count. Compose three kinds:- Happy path: the main workflows, one per major rule in the skill. Twenty-two paraphrases of one prompt is not coverage.
- Near-miss negatives: inputs where the model’s wrong default is most tempting, plus easy cases it already handles. Those easy passes honestly dilute the lift.
- Production-shaped: prompts with the mess of real use, such as surrounding context, irrelevant detail and imperfect phrasing.
Decide who grades each check
That last row is the real choice. Only a verified number reaches registry cards and rankings. A skill whose suite has validators can still publish on a pushed result, but its public page shows no verified benchmark. If you want that number, grade with sharp expectations. If only code can tell right from wrong, keep the validators and accept that the result stays unverified.
The expectation litmus
For every expectation, ask: would a generic “be careful and well-structured” instruction also pass this? If yes, it tests general competence, not your skill’s knowledge. Rewrite it into a specific, observable property only the skill supplies. A hosted run counts an expectation only when the with-skill side passes it more often than the without-skill side. Before: “The commit message is clear and well-formatted.” After: “The subject begins with a lowercase type token from the closed set (feat, fix, docs, …), not ‘feature’, ‘add’, or ‘new’, followed by a colon and a single space.”Trigger cases: automatic skills only
Graded cases prove the skill helps when loaded. Trigger cases prove it loads at the right times. Write at least 6, outside your graded total:- 3 that should fire, from different angles: a direct ask, one with no domain vocabulary, one rewriting something that exists.
- 3 near-misses that shouldn’t: an adjacent artifact, the same keywords with a different intent, and a generic task. For a commit-message skill, “write the PR description for this branch” is a near-miss. Far-away negatives (“write a poem”) teach nothing.
Repeat runs
One run per case measures luck. Before acting on a verdict, run with--runs 3: the whole suite repeats and the results average by mean, so no case can weigh more than another.
Step 5: Keep the number honest
The benchmark catches many problems. These are the ones only you can catch. Never leak the rule into a test prompt, including by paraphrase or by example. Otherwise you’re measuring the prompt, not the skill. Keep prompts apart from the body’s worked examples too. Before (leak): “Write a Conventional Commits message for the CSV export feature.” After: “I added an endpoint that lets users export their data as CSV. Write a one-line git commit message.” The with-skill side must never score worse. If loading the skill drops any score below the no-skill baseline, it’s actively hurting. Fix it or cut the offending rule. Teach real conventions, not invented ones. Every graded token should be something a practitioner would produce without your skill. A made-up tag vocabulary scores spectacular lift by construction and teaches nothing anyone asked for. One behavior per skill. If the topic has parallel variants (per-venue templates, per-language style guides), scope to one or split them, or cases graded against another variant become noise.Put a skill you wrote on DecimalAI
A skill has to be in your workspace before a hosted benchmark or a publish can see it. Three ways in:
Name the agent in each. A new skill reaches only the agents you add it to, so one synced with no agent is on none until you add it.
--offer-scope workspace offers a new skill to every agent instead, --dry-run previews, and --no-apply-pulls reports instead of writing to your files. A sync also uploads an eval.yaml beside the skill and the files in scripts/, references/, templates/ and assets/: one level deep, text only, at most 50 files of 500 KB each. See decimalai skills sync.
disk_sync reads project folders such as .agents/skills/, .claude/skills/, .windsurf/skills/ and .continue/skills/. It leaves personal folders such as ~/.claude/skills/ alone, runs in a background thread so it never delays startup, and adds to the agent you named, except a skill someone took off that agent on the dashboard. Don’t also deliver the skill another way.
A change to the frontmatter alone updates the skill’s details without making a new version.
instrument() never writes to disk: the only sync that writes to a file you wrote is a pull in decimalai skills sync.
Lifecycle: re-testing and retirement
A benchmark tests your agent with and without the skill, running the same eval suite both ways. It’s how lift is first measured and how you spot later that a skill has stopped helping. Nothing re-runs it for you, so click Run benchmark or rundecimalai skills benchmark on this schedule:
Reading a result (numbers illustrative):
- With 94%, without 32%: +62 lift. Real knowledge, so keep it loaded.
- With 96%, without 95%: about 0 lift. The model absorbed it, so every activation now costs tokens for nothing.
Self-check before you publish
skill-type,skill-scopeandinvocationdeclared, and you can say why.- Description states what, when and “Do NOT use for…”, in third person.
- Every rule is a directive, with worked examples, and every line survives the no-op test.
- Anything that must prove lift is in the body.
- Graded set mixes happy path, near-miss negatives and production-shaped prompts.
- You chose validators or a verified number, knowingly.
- Automatic skills: at least 6 trigger cases outside the graded set.
- The verdict you act on is a mean over
--runs 3. - No prompt leaks the rule, and the lift comes from real knowledge.
Related
Benchmark it
The eval.yaml format and the open runner that measures lift.
Publish it
The Publish dialog, the checks and their order, and updating later.