> ## Documentation Index
> Fetch the complete documentation index at: https://docs.decimal.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Authoring Skills

> How to write a skill that triggers at the right times, changes behavior, and proves it honestly — classification, description craft, body craft, and the eval suite.

Anyone can write a `SKILL.md`. Writing one that measurably improves an agent is a craft with learnable rules — this guide teaches them, with a before/after example for each.

A skill's worth is its **lift** (the with-vs-without improvement): how much better your agent scores with the skill loaded than without it. Everything below serves that number — making it real, making it big, and knowing when it's gone.

## 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" — 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 — run your own benchmark):

* A skill saying "write correct, well-structured SQL" → the model already passes every case without it. **Lift: \~0.**
* The same schema plus four house conventions the model cannot guess ("active user = `deleted_at IS NULL`", "revenue = `net_amount` on completed orders only", "fiscal year starts Feb 1") → the model fails without the skill and passes with it. **Lift: large.**

There are two kinds of lift, and you should know which one you're claiming:

| Kind                 | What happens                                                                                         | Example                                                                 |
| -------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| **Capability lift**  | The model *fails* the task without the skill; the skill's knowledge makes it pass.                   | Internal API schema, new SDK (software development kit) migration rules |
| **Conformance lift** | The model *can* do the task but defaults to the wrong **form**; the skill steers it to the standard. | Commit-message conventions, changelog formats, citation styles          |

One corollary you never violate: **never game an eval**. If honest iteration shows no lift, the model is genuinely good at this — re-scope the skill or drop the idea. A manufactured number is worse than no skill.

## Step 1: Classify your skill

Before writing a line of the body, declare the skill's classification. Two **orthogonal axes** decide what evals the skill needs and how long it will live.

```yaml theme={null}
skill-type:  capability | preference   # does the base model lack the ABILITY, or just the FORM?
skill-scope: public | private          # is the knowledge a public standard, or internal to you?
invocation:  model | user | any        # default: model
```

### The two axes

Run this litmus, in order:

> **Could the base model do this task at all?** If **no** → `capability` (you're teaching a missing ability). If **yes, but you want a specific FORM** → `preference` (you're steering the shape). **Then: is that knowledge public or private?** A published standard the base may one day learn → `public`. Internal house knowledge the base will never absorb → `private`.

* **`skill-type: capability`** — the base model genuinely can't do the task yet: parse a particular PDF layout, call an internal API, know an internal DB schema. The skill teaches a **missing ability**.
* **`skill-type: preference`** — the base model *can* do the task; the skill only steers the **form** to a chosen shape: a PR-description format, a commit convention, a review checklist, a naming scheme. The skill encodes a **chosen process or style**.
* **`skill-scope: public`** — a standard the base may absorb as models improve (RFC 9457, Conventional Commits, BEM).
* **`skill-scope: private`** — internal knowledge the base will never learn from training data (your DB schema, your PR format).

The two axes give four quadrants:

|                | **public**                                                                                                                                                                            | **private**                                                                       |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| **capability** | **Temporary** — retires when the base model catches up. The one quadrant that expires: the platform re-tests it monthly and surfaces it as a retire-candidate. *(legacy `model-gap`)* | **Durable** — an internal ability the base never learns. *(legacy `proprietary`)* |
| **preference** | Adopted public standard — Conventional Commits, BEM. Durable; retires only if base models start emitting it unprompted. *(legacy `convention`)*                                       | House style — your PR format, your review checklist. Durable; it's yours to keep. |

**Only `(capability, public)` expires.** A private ability or a chosen house form stays useful indefinitely; only public capability decays, because a public capability is exactly the thing a future base model can pick up. That one quadrant is why the platform re-benchmarks on a **monthly cadence** — when the base catches up, with ≈ without, and the skill is flagged to retire.

<Note>
  Legacy `skill-type: model-gap | proprietary | convention` frontmatter still parses — it maps to `(capability, public)`, `(capability, private)`, and `(preference, public)` respectively — so existing skills keep working without an edit.
</Note>

### Choosing a preference skill to author

A good preference skill encodes a process or format **many teams would recognize** — a PR-description structure, a code-review checklist, a commit convention, a docstring style. Avoid obscure standards picked *only* because the base model doesn't default to them: a skill that lifts solely because it teaches a rule almost nobody uses is closer to gaming the benchmark than teaching something useful (see [Step 5](#step-5-keep-the-number-honest)).

Name and frame skills **generically** — `pr-description`, `code-review-checklist`, `commit-format` — and do **not** tie a skill to a fictional company (no "Acme PR format"). A preference skill should read as a normal, reusable skill any team could install, not a private artifact dressed up as a public one. (Genuinely internal house style is fine — that's a `private`-scope skill; just don't invent a fake company to make a public standard look proprietary.)

Edge cases — classify by where the **graded** knowledge lives:

* A house style guide built on a public standard: if your eval grades the public standard's rules, it's `(preference, public)`; if it grades your private additions, it's `private` scope — `capability` or `preference` depending on whether the base could produce them unaided at all.
* Framework-migration rules are `(capability, public)` and simply *retire* when models catch up — they don't turn into preferences.

**Before** (unclassified thinking): "our commit-message skill is a style preference, it lives forever."
**After**: "the base can already write a commit message, just not in our shape, and we grade the public Conventional Commits spec → `skill-type: preference`, `skill-scope: public`. Litmus checked: the model *can* do the task, we're steering the form, and the standard is public. It needs conformance cases plus a re-test on model upgrades, and it retires only if a future model emits `feat: add csv export` unprompted."

### Automatic or on-demand?

The `invocation` field decides who fires the skill:

|                         | `model` (Automatic)                                   | `user` (On-demand)                                                        |
| ----------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------- |
| Fired by                | The model, based on the description                   | An explicit call only                                                     |
| Context cost            | Description is in the skill menu on every routed turn | **Zero until invoked** — never occupies your agent's context until called |
| Trigger cases (Step 4)  | Required                                              | Skipped — nothing can misfire                                             |
| Description written for | Search retrieval + the model's menu                   | A human browsing a list                                                   |
| Body style              | Constraints, not steps                                | Step-by-step playbooks allowed — the user chose to run this workflow      |

`any` takes the stricter rule from each row. Default to `model`; choose `user` for workflows a person runs intentionally (release procedures, cleanup scripts) where automatic triggering would only be noise. Outcome evals are required either way — invocation mode never waives lift.

<Note>
  Claude Code spells on-demand as `disable-model-invocation: true`. DecimalAI treats that as equivalent to `invocation: user` and round-trips both spellings on import and export.
</Note>

## Step 2: Write the description as the trigger

Trigger problems cause most skill failures in the wild — and rewriting the description alone fixes most of them. The description is not documentation. It is the **firing mechanism**, and two separate steps read it before your skill can act:

1. **Retrieval** — the router embeds your description and matches it against the user's query. If it isn't retrieved, the skill can never fire. It needs the concrete nouns and verbs users actually type.
2. **Menu selection** — retrieved skills are shown to the model as name + description rows, and the model picks one. Your row has to beat its neighbors.

(When you export to Claude Code or Cursor, the same text is judged by that harness's own trigger machinery — one string serves all readers.)

Four rules:

* **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 — and both are measured by your trigger cases (Step 4).

**Before**: `description: Helps with documents`
**After**: `description: Create, edit, and analyze .docx files — tracked changes, comments, formatting, text extraction. Use when the user works with Word documents. Do NOT use for PDFs or plain-text/markdown files.`

**Before**: `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.`

<Tip>
  **The revision loop:** when trigger cases fail, revise the DESCRIPTION only and re-run them. Never touch the body to fix a trigger problem — the body carries the lift measurement, the description carries the trigger. Keeping them separate means fixing one never invalidates the other.
</Tip>

For `invocation: user` skills, write for a human scanning a list instead: 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. Four rules.

### Directives, not essays

Models follow instructions; they skim past background prose. Give the reason behind a rule in one clause when it helps generalization — not a paragraph.

**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 use `client.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→after pair per rule cluster, showing the model's wrong default and the conforming output. A 5-line example beats 5 paragraphs of explanation.

### Constraints, not steps

Describe the outcome and the constraints, not a step-by-step path — a rigid script strips the model's ability to recover from surprises. **If exact, fragile steps are genuinely required, write a `scripts/` program instead of prose**: code is deterministic; prose pretending to be code is neither.

**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."

Exception: on-demand (`invocation: user`) playbooks may be procedural — the user explicitly asked for that workflow.

### Put depth in references/, exact steps in scripts/

A skill is a folder, loaded in three layers with three different costs:

| Layer                            | Loaded                                 | What belongs there                                                                                                          |
| -------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Frontmatter (name + description) | On every routed turn                   | The trigger (Step 2). Nothing else.                                                                                         |
| `SKILL.md` body                  | When the skill activates               | The rules, the worked examples, do/don'ts — lean                                                                            |
| `references/` + `scripts/`       | Only when the agent opens or runs them | `references/`: deep tables, long edge-case catalogs. `scripts/`: exact or fragile operations, executed instead of narrated. |

Placement rules: keep `references/` files one level deep, give any reference over \~100 lines a table of contents, and make the body **point** at each reference with when-to-open guidance ("per-language edge cases: `references/edge-cases.md`") — an unmentioned reference is never read.

<Warning>
  **Where lift is measured:** the benchmark loads the `SKILL.md` body only — `references/` are not loaded during the A/B run. Anything that must *prove* lift belongs in the body; references are for depth at runtime.
</Warning>

**Before**: one 480-line `SKILL.md` — 30 rules, a 200-line per-country exceptions table, and a
three-paragraph essay on why formatting matters.

**After**: a \~110-line body carrying the 30 rules and two worked examples; the exceptions table
moved to `references/exceptions.md` with a pointer ("per-country exceptions:
`references/exceptions.md`"); the essay deleted — it failed the no-op test below.

### Length is a cost

The body is injected into your agent's context on every activation — every line is paid for in tokens, and the A/B benchmark measures that cost. There is **no minimum length and no "sweet spot"**: a 60-line skill with real knowledge lifts fine, and past roughly a thousand lines a skill tends to measure as a no-op because bloat buries the directives.

The editing tool is the **no-op test**: remove the line — does output change? If not, delete it.

**Before**: "Be thorough and precise. Write clean, high-quality, maintainable commit messages that your teammates will appreciate." (removing this changes nothing → delete)
**After**: the line is gone; the exact type-token list it was crowding stays.

## Step 4: Build the eval suite

No skill ships on vibes. Two manual runs is not testing — bad skills don't crash, they quietly corrupt outputs. The suite has two independent parts: **graded cases** (does the skill change behavior?) and **trigger cases** (does it fire at the right times?). See [skillevaluation](/guides/skillevaluation) for the `eval.yaml` format and runner.

### Compose the golden prompts

Start with 10–20 prompts and extend from failures — every production bug becomes a permanent case. Compose deliberately, three kinds:

* **Happy path** — the primary intended workflows, one per major rule in the skill, so every check gets cases where it matters. 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 the model already handles. A suite made only of the model's worst cases flatters the skill; the easy passes honestly dilute the lift.
* **Production-shaped** — prompts with the mess of real use: surrounding context, irrelevant detail, imperfect phrasing.

### If a program can check it, make it a validator

Every check in a case is one of exactly two kinds — never blended:

* **`validators` — code decides.** Shell commands run against the response; the exit code is the verdict. Regex, parsers, executing the emitted SQL and diffing rows. Deterministic, free, milliseconds. Put everything a program *can* check here.
* **`expectations` — an LLM (large language model) judge decides.** Qualitative properties only: tone, faithfulness, judgment calls.

If you can write it as a regex, it's a validator, not an expectation.

### The expectation litmus

For every expectation, ask: **"would a generic 'be careful and well-structured' instruction also pass this?"** If yes, it's testing general competence, not your skill's knowledge — rewrite it into a specific, binary, observable property only the skill supplies.

**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

<Note>
  Available from **skillevaluation 0.3.0** (the `should_trigger` field).
</Note>

Graded cases prove the skill helps *when loaded*; trigger cases prove it loads *at the right times*. Write **at least 6**, and they never count toward your graded-case total:

* **3 `should_trigger: true`** — phrased from three different angles (different vocabulary, different intent framing, different surface form), so recall isn't one lucky phrasing.
* **3 `should_trigger: false` near-misses** — one adjacent topic, one same-keywords-different-intent, one generic task. Far-away negatives ("write a poem") teach nothing; the near-miss is the test.

```yaml theme={null}
# trigger cases for the commit-formatter skill
- prompt: "I fixed the race in the retry queue — write the commit message"   # angle: direct ask
  should_trigger: true
- prompt: "what should I put in the message for this change before I push?"  # angle: no git vocabulary
  should_trigger: true
- prompt: "clean up this commit line: 'Fixed Stuff.'"                        # angle: rewrite existing
  should_trigger: true
- prompt: "write the PR description for this branch"                         # near-miss: adjacent artifact
  should_trigger: false
- prompt: "why did this commit break the build? git bisect says it's mine"   # near-miss: same keywords, different intent
  should_trigger: false
- prompt: "summarize what changed in this diff"                              # near-miss: generic task
  should_trigger: false
```

These roll up to a trigger recall (should-fire cases that fired) and a false-fire rate (should-not-fire cases that fired). When one fails, run the Step 2 revision loop — description only. On-demand (`invocation: user`) skills skip this section entirely.

### Repeat runs — average out the luck

Model behavior is probabilistic; one run per case measures luck. Run the suite with `--runs 3`: the whole suite executes three independent times and the per-case results are averaged by **mean**, so the headline pass-rate's expected value doesn't depend on the count — more runs just tighten the confidence. Repetition is a runner flag, not a per-case field, so no single case can be weighted more than another. (This replaced the old per-case `trials`/pass^k knob in skillevaluation 0.6.0.)

## Step 5: Keep the number honest

The benchmark catches many problems automatically. These are the ones only you can catch.

**Never leak the rule into a test prompt** — including by paraphrase or by example. A graded prompt must not name, state, paraphrase, or show a sample of the conforming output; otherwise you're measuring the prompt, not the skill.

**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."

Keep case prompts disjoint from the body's worked examples too — a shared example is a leak by another route.

**Don't stack the deck.** A suite composed only of the model's worst cases flatters the skill. Include the easy cases it already passes; they keep the lift honest.

**The with-skill arm must never score worse.** If loading the skill drops any score below the no-skill baseline, the skill is actively hurting — fix it or cut the offending rule. Low-quality skills measurably reduce accuracy; "mostly helps" is not good enough.

**Teach real conventions, not invented ones.** Every graded token should be something a practitioner following the convention *without your skill* would produce. A made-up tag vocabulary scores spectacular lift (the model fails every case without it, by construction) and teaches nothing anyone asked for. Big lift from invented rules is gaming, and only the author can catch it.

**One behavior per skill.** If the topic has parallel variants (per-venue templates, per-language style guides), scope to one variant or split into one skill per variant — cases graded against a different variant become noise while the headline stays high.

**Honest drops beat no-lift skills.** If the model already does it, drop the idea and note why. A healthy authoring pipeline drops a meaningful fraction of its candidates.

## Lifecycle: ablation and retirement

**Skills ablation** means testing your agent with and without the skill loaded — the same eval suite, run in both arms. It's how lift is measured at birth, and how retirement is detected later. The schedule follows the quadrant you declared in Step 1:

| Quadrant                                   | Re-test when                          | Expected trajectory                             | Retirement                                                                                                                                                        |
| ------------------------------------------ | ------------------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **capability · public** (was model-gap)    | Monthly cadence + every model release | Lift decays as base models absorb the knowledge | **Expected** — the only quadrant that retires. When with ≈ without, the base caught up; the platform surfaces it as a retire-candidate so you reclaim the tokens. |
| **capability · private** (was proprietary) | On model upgrades (sanity check)      | Lift roughly constant                           | Not expected. A lift drop means the eval or your org's knowledge changed — investigate, don't retire.                                                             |
| **preference · public** (adopted standard) | On model upgrades                     | Stable unless model defaults shift              | Only if base models start emitting the standard unprompted. The same suite doubles as your regression suite.                                                      |
| **preference · private** (house style)     | On model upgrades                     | Stable                                          | Not expected — your house form is yours to keep.                                                                                                                  |

Reading an ablation (numbers illustrative — run your own):

* with 94% / without 32% → +62 lift: real knowledge, keep it loaded.
* with 96% / without 95% → \~0 lift: the model absorbed it — retire the skill; every activation now costs tokens for nothing.

To retire a published skill, unpublish it from the registry (or mark its frontmatter `stability: deprecated` so consumers see the state before you do). The registry shows each published skill's re-verification history ("Verified on ⟨model⟩ — re-tested ⟨date⟩"), so consumers can see whether a skill's lift is fresh or stale — see [Community Registry](/guides/registry).

## Self-check before you publish

1. `skill-type`, `skill-scope`, and `invocation` declared, and you can say why (Step 1).
2. Description states what + when + "Do NOT use for…", third person, no keyword stuffing (Step 2).
3. Every rule is a directive; zero passive essays (Step 3).
4. Constraints, not steps — or a script where steps must be exact (Step 3).
5. Worked examples cover every rule cluster: wrong default → conforming output (Step 3).
6. Every line survives the no-op test; depth moved into `references/` (Step 3).
7. Anything that must prove lift is in the body, not references (Step 3).
8. Graded set composed: happy path + near-miss negatives + production-shaped (Step 4).
9. Automatic skills: at least 6 trigger cases (3 fire / 3 near-miss no-fire), outside the graded set (Step 4).
10. Everything code can check is a validator; expectations are qualitative-only and pass the litmus (Step 4).
11. `--runs 3` — the verdict you act on is a mean over repeats, not a single lucky run (Step 4).
12. No prompt leaks the rule; the lift comes from real, adopted knowledge (Step 5).
13. You know what this skill's ablation should show a year from now (Lifecycle).

## Related

<CardGroup cols={2}>
  <Card title="Skills guide" icon="puzzle-piece" href="/guides/skills">
    The SKILL.md format, discovery, routing, sync, and observability.
  </Card>

  <Card title="skillevaluation" icon="flask" href="/guides/skillevaluation">
    The eval.yaml format and the open A/B runner that measures lift.
  </Card>

  <Card title="Community Registry" icon="store" href="/guides/registry">
    Publish gates, badges, trigger health, and re-verification history.
  </Card>

  <Card title="SkillScore" icon="chart-line" href="/guides/skillscore">
    How published skills are scored from benchmark, live, rating, and adoption evidence.
  </Card>
</CardGroup>
