Skip to main content

Anatomy of a skill

This page is for people changing the skills, not using them. It covers what a skill is made of and the one contract that governs every change to one. The step-by-step procedures live in dev/ in the repository; this is the map.

What a skill directory holds​

skills/infrahub-managing-schemas/
├── SKILL.md # Entry point: overview, workflow, rule index
├── rules/ # One file per rule, plus _sections.md and _template.md
├── examples.md # Complete, ready-to-use patterns
├── reference.md # Property and format tables
└── validation.md # Skill-specific validation guidance (not every skill)

Some skills also include scripts/, for work that is better done by code than by prose. infrahub-converting-netbox-device-types is the example: its converter is a bundled Python program the skill runs.

One directory is different. skills/infrahub-common/ is not a skill you invoke. It holds references every other skill points at: GraphQL query syntax, the .infrahub.yml format, the marketplace reference, the Profiles versus Object Templates distinction, and shared rules. Every skill that loads it pays for all of it, so a reference only two skills need belongs in those two skills instead.

What a rule looks like​

Rules are small and independently addressable, so the assistant reads only the ones the task needs. Each includes frontmatter and follows a fixed shape:

---
title: Relationship identifiers must match on both sides
impact: CRITICAL
tags: relationships, schema
---

## Relationship identifiers must match on both sides

Impact: CRITICAL

One-sentence statement of the rule.

### Why it matters

Two or three sentences naming the concrete consequence: the loader
rejection, the silent reference failure, the data that ends up in the
wrong shape.

### Incorrect

```yaml
# Bad example
```

### Correct

```yaml
# Good example
```

The filename includes a category prefix, and rules/_sections.md is the index that defines those prefixes for the skill. rules/_template.md is the starting point for a new one.

The Rule = Test contract​

This is the part that matters most, and the part a first contribution usually misses.

A new rule ships with its grader check and its eval task in the same change. Not a follow-up, not a later PR.

The reason is what happens otherwise:

SituationWhat goes wrong
A rule with no graderThe next refactor of the skill's prose drops the constraint, and no test fails. The rule rots silently.
A grader that cannot failWorse. It reports the rule as covered forever, so nobody looks again.
A grader that contradicts its ruleThe check is what scores, so it wins by default and the rule rots unnoticed. Decide which side moves.

The full seven steps are in dev/guides/adding-a-rule.md, and the rule that enforces them is dev/guidelines/rule-equals-test.md. That guideline loads automatically when you touch a rule, a grader, or eval.yaml.

The trees a skill spans​

One skill is not one directory. A change to it usually touches several. Below, <name> is the skill name without its infrahub- prefix, so for infrahub-managing-schemas it is managing-schemas.

TreeHolds
skills/infrahub-<name>/The skill itself
graders/<name>/Deterministic Python that scores model output
eval.yamlEvery eval task, for every skill, in one root file
evaluations/infrahub-<name>.jsonGenerated from eval.yaml by scripts/sync-evals.py
docs/docs/skills-reference/<name>.mdxThe reference page on this site
docs/sidebars.tsThe sidebar entry scripts/check-docs-sidebar.py enforces

A new skill also has to appear on more surfaces than these, from the README.md and AGENTS.md tables to this site's landing page, router, and the release manifest. uv run invoke lint fails a skill missing from one. The full list, the shape a reference page takes, and which surfaces go stale on a behavior change are in dev/guidelines/skill-registration.md.

A note on testing your own edit​

Editing a skill here and then invoking it does not test your edit. Three ways to exercise a skill load three different copies:

HowLoadsWhat it tests
Invoking infrahub-* in a sessionThe installed plugin under ~/.claude/plugins/cache/The published skill, not your edit
skillgradeThe working tree, copied into a sandboxThe prose, with triggering bypassed
Reading skills/infrahub-<name>/SKILL.md and following itThe working treeThe prose, manually

uv run invoke freshness shows how far apart the first two have drifted.

Note what none of them covers: triggering. Eval prompts tell the model to read the skill at a given path, so they exercise a skill's rules but never its description, which is the field that decides whether the skill fires at all.

Go deeper​

For how skills load at runtime, from the reader's side, see How it works.