The skillsvault.json manifest
skillsvault.json is a small JSON file you place next to SKILL.md. It is additive — a harness that doesn't understand skillsvault simply ignores it — and it's what turns a portable skill into a governable one. Its name and targets are the fields a policy matches on, and publish reads name, version, intent, targets and description from it. Most of the rules below are recommendations: publishing enforces only a valid name and version, and the rest feeds the skill's compliance status (compliant / non_compliant) shown in the console.
Where it lives
crm-entry/
├── SKILL.md
└── skillsvault.json ← here, beside SKILL.md
Schema
{
"skillsvault_schema": 1, // compliance check. Schema version (currently 1).
"manifest_id": "mf_91ac3d", // compliance check. Keep it the same across versions.
"name": "acme/crm-entry", // enforced. "<namespace>/<skill-name>", lowercase.
"version": "1.4.0", // enforced. Semver.
"namespace": "acme", // compliance check. The part before "/" in name.
"intent": "crm-data-entry", // compliance check. One canonical machine intent.
"targets": ["hubspot"], // recommended (may be []). Systems the skill touches.
"description": "Create and update HubSpot contacts from a deal brief.",
"supersedes": "mf_77de00", // optional. Not used by skillsvault today.
"x": { } // optional. Org-defined extension bag; ignored by matching.
}
Field rules
Three levels apply. Enforced means publishing refuses the version (422) when the rule fails. Compliance check means the version still publishes, but a failure marks the skill non_compliant in the console. Recommended is convention: nothing checks it, but keeping to it makes policies easier to write.
| Field | Type | Rule |
|---|---|---|
skillsvault_schema | int | Compliance check: must be 1. |
manifest_id | string | Compliance check: must be present. Recommended: mf_ followed by lowercase letters and digits, kept the same through every version. |
name | string | Enforced: ^[a-z0-9][a-z0-9-]*/[a-z0-9][a-z0-9-]*$. Compliance check: it ends in the SKILL.md frontmatter name. |
version | string | Enforced: semver (MAJOR.MINOR.PATCH, optionally with a pre-release or build suffix). A version, once published, can't be re-published with different content (409) — bump it for every change. |
namespace | string | Compliance check: name starts with <namespace>/. The namespace a skill is filed under always comes from name; publishing into one your org doesn't have yet creates it, within your plan's namespace limit. |
intent | string | Compliance check: must be present. Recommended: kebab-case, short, one canonical intent per skill. |
targets | string[] | Recommended: declare at least one (an empty list is a compliance warning, not a failure), each lowercase kebab-case. |
description | string | Optional. Used only when the SKILL.md frontmatter has no description — see What publishing reads and writes. |
supersedes | string | Optional. Not used by skillsvault today. |
x | object | Optional. Free-form extension data; never used for matching. |
Identity is the name
A skill's identity in the registry is its namespaced name: versions are grouped under it, and a Specific skill policy matches it. manifest_id is recorded with each version's manifest, but nothing matches or groups on it, and skillsvault doesn't check that it stays the same between versions. Keep it stable anyway, so the manifest stays a faithful record. Renaming a skill (a new name) makes it a different skill.
Why these fields matter
Policies don't match on free text — they match on stable, declared fields, in priority order:
| The policy targets… | …via |
|---|---|
one specific skill (matcher manifest_id, "Specific skill") | the namespaced name, e.g. acme/crm-entry |
| everything in a namespace | the part of name before the / |
| anything touching a system | targets (e.g. target:teamleader) |
Because these are declared and stable, the gate's decision is deterministic and explainable: a skill is blocked because its name, namespace or a target is named in an active policy, not because a model guessed. The manifest_id field (mf_…) is the skill's permanent id in the manifest; the gate doesn't match on it. Keep targets honest — they are the contract a governance officer writes rules against.
What publishing reads and writes
When you run skillsvault publish, it reads name, version, intent, targets, and description from skillsvault.json (--name and --version override the first two) and sends the whole manifest with the version. The registry stores the SKILL.md frontmatter description as the skill's description; the manifest's description is used only when the frontmatter has none.
A few fields are owned by the registry — you don't set them, and a value you supply is ignored:
- the content hash of the published bytes (how versions stay immutable),
- the author and publish time,
- the compliance/lint result the console displays.
Minimal valid manifest
The smallest manifest that passes the compliance check:
{
"skillsvault_schema": 1,
"manifest_id": "mf_3f9c12",
"name": "acme/crm-entry",
"version": "1.0.0",
"namespace": "acme",
"intent": "crm-data-entry",
"targets": [],
"description": "Create and update HubSpot contacts from a deal brief."
}