# The 4-layer skill format

Source: https://opvs.ai/docs/guides/vendors/four-layer-format

> Where each part of a skill goes (SKILL.md, references, learnings, registry, scripts) and the guidance block the platform turns into every reply's envelope.

A complete skill ships two things in one signed package:

- **The 4-layer format**: content folders an agent reads and drills into.
- **The guidance block** in `client/schema.yaml`: metadata the platform reads to decorate every reply with `_guidance`, `telemetry_signal` and, on request, `_findings`.

Ship only one and the skill is half-built. For the craft of making each layer hold up, read [Write skills that hold up](https://opvs.ai/docs/guides/vendors/skill-quality.md).

## Folder layout

```
skills/<your-skill>/
├── SKILL.md                 ← Layer 1: the router (required)
├── client/
│   └── schema.yaml          ← API surface + guidance block
├── references/              ← Layer 2: procedures (recommended)
│   └── <task-name>.md
├── learnings/               ← Layer 3: lessons with provenance (recommended)
│   └── <yyyy-mm-dd-slug>/
│       ├── metadata.json
│       └── artifacts/what-we-learned.md
├── registry/                ← Layer 4: typed assets agents install (recommended)
│   ├── registry.json
│   └── <item-name>/
│       ├── registry-item.json
│       └── <asset files>
└── scripts/                 ← optional: checks the agent runs on its own work
    └── verify-<thing>.mjs
```

## Layer 1: `SKILL.md`, the router

At most **500 lines**, with frontmatter. The body is a decision tree that points at `references/` files, not a manual. The `description:` line is the trigger: list every verb and noun an agent might meet, then name the sibling skills to use instead.

Put in it: frontmatter (`name`, `description`, `version`, and optionally `category`, `triggers`, `requires_auth`, `vendor`, `entitlements`); at most one `<HARD-GATE>` block for the single riskiest mistake; a table "if the user wants X → read `references/X.md`"; a short list of anti-patterns; and pointers to `learnings/`, `registry/` and `scripts/`.

## Layer 2: `references/`, procedures

One Markdown file per task or per trap, 50 to 200 lines each. Two shapes:

- **WRONG / RIGHT** to teach a trap: the broken version, then the fix, with real values.
- **Steps / Gotchas / Verify** to teach a multi-step job.

Every MUST is followed by **why**, so the agent can judge edge cases. Agents on OPVS read these with `skill_read_reference`; agents on other tools read the files after install.

## Layer 3: `learnings/`, what you learned

One folder per lesson:

- `metadata.json`: `title`, `summary`, `created_at`, `updated_at`, `references[]` (provenance: the issue, incident or change that produced it), and optionally `tags[]` and `stale_after`.
- `artifacts/what-we-learned.md`: the lesson itself, plus any supporting files.

Agents read every summary and open the artifact only when it matches the task, so the summary is the trigger. Provenance in `references[]` is **required** for published learnings. Tell agents in `SKILL.md` that learnings are starting points to check, not ground truth.

## Layer 4: `registry/`, assets agents install

Things the agent installs instead of writing: board templates, document starters, prompt packs, HTML components. Each item has its own folder with a `registry-item.json` and its files.

Required fields in `registry-item.json`: `$schema`, `name`, `type`, `title`, `description`, `version`, `vendor`, `skill`, `files`. Add `preview` for items with a visible surface, and `references[]` provenance for published items. `type` is one of `opvs:block` (drop-in), `opvs:component` (parameterised), `opvs:template` (multi-file starting point) or `opvs:blueprint` (multi-skill workspace setup).

Each item has its own semver, independent of the skill, and belongs to exactly one skill (the `skill` field).

## `scripts/`, optional

Deterministic checks the agent runs on its own work and pastes the output of. Name each one in `SKILL.md` so the agent knows when to run it.

## The guidance block

Add it to `client/schema.yaml`, next to your methods.

```yaml
guidance:                       # skill level
  use: "what this skill is for, one line"
  not:
    - "<sibling-skill> → what to do instead"
  intent_aliases:               # every name a free-text input goes by
    - "query"
    - "search"
  pitfalls:
    - "<the top gotcha, one sentence>"

methods:
  getThing:
    description: Plain-English summary.
    guidance:                   # method level
      use: "when an agent should call this"
      next: "updateThing, listThings"
      warn: "the one thing that catches first-time callers"
      telemetry_signal_default: "warm"
    http: GET /things/{id}
    params:
      id: { type: uuid, required: true, description: The thing's id. }
    response:
      format: yaml
```

Three rules apply to every vendor:

Rule

What it means for you

Declare `intent_aliases`

If any method takes a free-text search input, list every name it goes by under `guidance.intent_aliases`. The platform never guesses. A package with no aliases gets a review warning, because agents cannot find it by the words they use.

`_findings` comes from the platform

When a caller sends `X-OPVS-Audit-Level: all`, the platform adds `_findings` (default `[{audit: "ok"}]`). Set `audit_findings_template` only if your skill has real audit results.

`telemetry_signal_default` defaults to `absent`

Set `cold`, `warm` or `hot` only where the method has natural usage meaning. The generator warns when it is missing.

### Nested object and array parameters

Declare the full shape inline; every generator then emits the nested JSON Schema and agents see the sub-fields.

```yaml
params:
  workflow:
    type: object
    required: true
    description: The workflow to run.
    properties:
      smartlead:
        type: object
        properties:
          connection_id:
            type: string
            required: true          # per-field required, not a sibling list
            description: Smartlead account connection id.
  admin_regions:
    type: array
    items:
      type: object
      properties:
        country: { type: string, required: true, description: ISO country code. }
        cities:  { type: array, items: string }
```

- Each sub-field carries its own `required`. There is no sibling `required: [names]` list.
- `items` is a type name (`items: string`) or a full object spec.
- Validation reaches every level and reports a dotted path. Nesting is capped at 12 levels.
- `oneOf`, `allOf`, `anyOf` and `$ref` pass through but are not validated.
- A schema never restricts what callers send: undeclared keys still reach your API; a mismatch only produces a warning.

## What happens at runtime

- **The envelope is added by the platform.** It reads your `guidance` block and decorates each reply. You write no code for it.
- **The layer files travel with the install.** Agents open `references/`, `learnings/` and `registry/` files directly, with no API call.

## Packaging the layers

Nothing extra is needed in `package.yaml`: the publish command packs the whole package folder (minus a fixed exclude list), so new folders are included and signed automatically. See [The package manifest](https://opvs.ai/docs/guides/vendors/package-manifest.md#what-goes-in-the-bundle).

- Adding the layers to an existing skill is a **minor** version bump.
- Bump a registry item's own `version` when you change a file inside it.

## Questions

**Do I need all four layers?** No. Only `SKILL.md` is required. Ship at least one `references/` file so the agent has somewhere to drill into.

**Can I publish without the guidance block?** Yes, but agents lose the per-method `next` and `warn` hints. Ship it.

**Is a learnings folder the same as **`LEARNINGS.md`**?** No. The [security review](https://opvs.ai/docs/guides/vendors/security-review.md) looks for root files (`README.md`, `SKILL.md`, `CHANGELOG.md`). `learnings/` inside a skill is the structured lesson format.

**My skill teaches a method rather than calling an API. What then?** Publish it **guidance-only**: no `methods:` in `schema.yaml`, rich `references/`. Never declare a method you do not serve, or every call to it fails.

## Next

[Write skills that hold up](https://opvs.ai/docs/guides/vendors/skill-quality.md).
