Skills: standard operating procedures
This document owns what a Skill is, how one is written, pinned, kept up to date, delivered to each harness and traced. Other documents link here and keep only their own part: the worker Skill at launch, the Skill loader, learned Skills, Skills from extensions and the swarm Skills catalog.
Status:
- Source candidate: local
maia agentloads Project Skills pinned by digest from.maia/maia.json; themaia-workerSkill, injected into every coding Session at launch (D-087, D-105); themaia-orchestrationSkill in themaiaplugin, whose command claims a bundle check keeps true. - Planned: the rest of this page. Mainly S13 (install, status, check and
update through the
skillsCLI, D-170), AI0–AI3 (Maia's own Skills, installed into each detected harness, D-190), S22 (the catalog), FV4 (Project Skills at launch), S49 and S50 (Agents and Routines name their Skills), R11 (the typed Skill loader), R4 (learned Skills) and S73 (Skills from extensions).
What a Skill is
A Skill is a standard operating procedure (SOP) for agents: the steps to do one kind of work with Maia's primitives, and how to prove the work is done. Maia's operations are the verbs (create, send, wait, confirm); a Skill strings them into a procedure (D-026). People write and review Skills like code. Agents follow them, attended or unattended.
Example: a release-prepare Skill says when it applies (before a release
branch is cut), lists the steps (check that the Run's tasks settled, run the
acceptance tests, open the pull request, ask the release owner to confirm),
says how to prove each step, when to stop and ask a person, and what to report.
| Primitive | Holds | Applies | Enforced by |
|---|---|---|---|
Rule (AGENTS.md, Workspace rules (S54)) | constraints and preferences | on every task | the agent; the Node for every rule about safety |
| Skill | the procedure for one kind of task | when that task comes up | nothing: it guides |
| Operation or tool | one typed action with its schema, risk and authorization | when called | the Node |
| Agent (S49) | who does the work: harness, model, instruction, limits and the Skills it may use | at launch | the Node |
| Routine (S50) | when work runs and in what order: a trigger and steps, each naming an Agent and Skills | on its trigger | the Node |
- A Skill grants nothing. It cannot add a tool, widen a grant, choose a Connection, raise a budget or confirm a mutation. What must hold is enforced by the Node, because agents skip rules written only in prompts.
- A Skill never runs. Maia never executes a Skill's files. The agent reads the procedure and calls typed tools, each under its own policy.
- Skill text is untrusted procedure text (instruction sources). It guides an agent and never becomes configuration or authority.
Writing a Skill
A Skill is a folder <skill-id>/ holding SKILL.md, in the common format the
supported harnesses read: front matter with name (kebab-case, equal to the
folder name) and description, then the body, plus optional references one
level deep (portable formats).
The body follows the shape of an SOP:
- When to use, and when not to.
- Inputs the agent needs before it starts.
- Steps, numbered, with exact commands or tool calls for fragile sequences.
- Checks that prove each step and the whole task.
- Escalation: when to stop and ask a person, and whom.
- Report: what to return, in what shape.
Rules:
- The description is the trigger: third person, what and when, at most 1,024 characters.
- One kind of task per Skill, named verb-noun (
release-prepare,pr-review). - At least three evaluation scenarios, written before the Skill (S22).
- Maia's own Skill bundles carry a
registry-lock.json(maia.skill/v1) listing every command they claim. The bundle check (scripts/quality/hps0-skill-bundle-contract.mjs) fails on a claim the registry does not have.
Limits apply at four layers, each on its own:
| Layer | Limit | Owner |
|---|---|---|
| Authoring | description at most 1,024 characters; SKILL.md under 500 lines; references one level deep | this page |
| Package | at most 32 files, 256 KiB per bundle and 64 KiB per Skill file | the bundle check |
| Activation | one loaded body warns at 6 KiB and fails at 12 KiB; all loaded bodies 12 and 24 KiB | initial budgets |
| Bootstrap | the maia-worker Skill at most 2 KiB, injected whole at launch: the one Skill that does not load progressively, because a worker needs it before its first turn | worker Skill at launch |
Where Skills live
| Scope | Location | Pinned by | Status |
|---|---|---|---|
| Built-in | shipped with Maia: plugins/maia/worker-skills/maia-worker, plugins/maia/skills/maia-orchestration | the release's package digest | Source candidate |
| Project | where the skills CLI installs them (.agents/skills/<id>, .claude/skills/<id>), with skills-lock.json | the lock's folder hash | Planned (S13) |
| Machine | each harness's global folder, with the CLI's global lock | the global lock | Planned (S13) |
| Extension | inside the extension | the extension's digest (S73) | Planned |
| Learned | Node-generated resources (R4) | their version record | Planned |
- Qualified identities, no shadowing. Each admitted Skill has one
qualified identity (
builtin:<id>,user:<id>,project:<project-id>/<id>), resolved in the fixed order of resolution and admission. A Project names a user Skill explicitly, a duplicate identity is an error, and a Project Skill cannot redefine a built-in or user one.maia-names are reserved for built-ins. - Discovery is not admission. A Skill found on disk is a candidate.
Project and machine Skills load only from a lock entry whose folder hash
matches; built-in, extension and learned Skills load under the pin the
table above names. Admission records each one in the Project's admitted
snapshot. An unpinned
Skill stays a candidate until the CLI adds it to the lock (
config.import); this replaces D-040's trusted-Project fallback (D-170). - Two built-ins share the name
maia-orchestrationtoday: the plugin Skill for vendor workers and the CLI guidance Skill (apps/cli/guidance/maia-orchestration) that voice pins for local-v1 Runs. S22 renames the guidance Skill and updates its digest pin, descriptor references and tests together. - Until R11 AG1 moves it to the lock, local
maia agentkeeps reading its pins from.maia/maia.json(agent.resources.skills).
Install, pin and update with the skills CLI
Maia has no Skill installer, lock or update checker of its own
(D-040, D-170). It runs the
skills CLI (vercel-labs/skills) at an exact pinned version, with
telemetry off and copies only. The Node carries that version as an exact-pinned
runtime dependency and runs it with its own runtime
(D-193); the commands below show the arguments:
DISABLE_TELEMETRY=1 DO_NOT_TRACK=1 \
skills add <source> --skill <name> -a <agent>... --copy -y
- One pin. The CLI writes
skills-lock.jsonat the Project root: each Skill's source, ref, path in the source and a SHA-256 of its folder. The file has no timestamps and sorted entries, so branches merge cleanly, and it is committed with the Skill folders. It is the Project's only Skill pin. Machine Skills use the CLI's global lock (~/.agents/.skill-lock.json, or$XDG_STATE_HOME/skills/.skill-lock.json). - Every harness from one source. The CLI copies each Skill to where each
harness reads it:
.claude/skillsfor Claude Code,.agents/skillsfor Codex, Pi, Cursor and most others.config.applypasses-afor exactly the harnesses the Project's Agents use on that Node. - Structured reads only. Maia reads
skills list --jsonand the lock files, and recomputes folder hashes with the CLI's algorithm. It never parses the CLI's text output. A test pins the lock formats and the hash against the pinned CLI version. - Detection reads the same list.
maia agents detectreports Skills folders fromskills list --jsonand nothing else, so a folder with no installed Skill is absent (D-190). Detection does not usenpx: a Project could plant the binary it runs. The Node resolves the package from its own install, checks the exact version and that the entry file sits inside the package, and runs it with its own runtime under the resource manager (Linux hard containment; macOS and Windows report the scopeunsupported). The manager rechecks the package files before every start. Nothing is downloaded at run time. - The pin moves by review. The CLI version moves only through a reviewed change to a release that meets the minimum dependency age (release). The recorded pin is 1.7.0.
Is every Skill up to date?
maia skills status answers for each Project, Node and harness. Each Skill
has one state:
| State | Means | Next step |
|---|---|---|
current | the installed copy matches the lock, and the last check found nothing newer | none |
outdated | the source has a newer version than the lock | maia skills update |
modified | the installed copy differs from the lock: someone edited it in place | reinstall it, or keep the edit as a reviewed change |
missing | in the lock, but absent for a harness that should have it | maia config apply |
unpinned | installed, but not in the lock | add it with the CLI, or remove it |
unchecked | the source cannot be checked (a local path, a private or deleted repository, no network); the reason is shown | none |
Each state carries the time of the last check. Status also lists the Sessions still running an older digest: a Session keeps the Skills resolved at its launch, and its next launch takes the new ones.
- Check.
maia skills checkruns the CLI's update in a scratch copy (a scratch worktree for Project Skills, a scratch state directory for machine Skills) and compares the scratch lock with the real one. Each changed hash isoutdated, and a Skill gone from its source is reported. Nothing outside the scratch copy changes. It needs the network, so it never runs at launch; a Routine (S50) can run it on a schedule. - Update.
maia skills update [<name>...]previews first: the Skills that change, old and new digests, a summary of each change, and the Agents and Routines that use them. After a person confirms, it runs the CLI's update in a worktree of the Project, checks that the new hashes equal the previewed ones (else it fails with a conflict and asks for a new preview), and leaves the change (the lock and the Skill folders) for review through the Project's normal flow, with a receipt. Machine Skills update in place after confirmation. Running Sessions are never changed.
How each harness gets its Skills
| Harness | Route | Status |
|---|---|---|
| Codex worker | the worktree's .agents/skills; Codex workers launch with Codex's own Skills list off (D-105), so the maia-worker body arrives as developer instructions, and a resumed thread keeps its first turn's | maia-worker: Source candidate. Project Skills: Planned (FV4), route to qualify |
| Claude Code worker | the worker profile reads no Skills folder (D-087), so the Node passes a launch plugin, one folder per set of digests | maia-worker: Source candidate. Project Skills: Planned (FV4) |
| Pi and Maia agent | the typed Skill loader: a metadata index, loads by exact ID, budgets and a context manifest; the managed worker's restricted projection has no Skills and refuses /skill: today | local pins: Source candidate. Loader: Planned (R11) |
| The user's own harness | the maia plugin: maia-orchestration and maia mcp | Source candidate |
A Session gets exactly the Skills it is eligible for, pinned by digest at
launch: those its Agent names (S49), plus those a Routine step names (S50).
Without an Agent, a coding Session gets only the built-in maia-worker Skill;
.maia/worktree.json with "workerSkills": false turns that off for a
Project.
Using a Skill
- By the agent. The agent sees each eligible Skill's name and description and loads a body when one fits the task. Picking a Skill never grants eligibility.
- By a person. In
maia agent,/skillslists eligible Skills and/skill load <skill-ref>loads one (Planned, R11). A vendor harness keeps its own command for a Skill. - By a Routine. A step names its Agent and instruction and may request
Skills (
requestedSkillRefs), which only narrow what its Agent may use. Each tick resolves them against the snapshot that planned it, so a run uses fixed versions and records their digests; a requested Skill that is missing or not eligible blocks the tick with its reason. An unattended run follows the same procedure a person would.
Tracing Skill use
session showlists what the latest coding launch injected as{ name, digest }(worktree.skills, at most 8, Source candidate). It is a projection of that launch, replaced at the next one, not a history.- Injected is not effective: a resumed Codex thread keeps its first turn's developer instructions. The trace (S12t, D-168) records the Skills each launch staged, and the selected, loaded and effective digests where the harness reports them; where it cannot, the trace says so.
- Each activation's context manifest records every Skill selected and loaded,
with its digest and size, and a
context.skill-selectedevent (Planned, R11). - In a vendor harness, a Skill shows up as that harness's tool call where it
reports one (Claude Code's
Skilltool), kept as trace metadata (D-168). - S22's evaluation scenarios and S62's evals measure a Skill's outcomes; Runs record wall time, tokens, retries and escalations.
Improving a Skill
- A Skill changes like code: a change in a worktree, its evaluation scenarios run, a person reviews and merges, and the lock records the new hash. No Skill changes under running agents.
- An evaluation or a coordinator may propose a change to a Skill. It arrives as a change for review, never applied directly.
- Learned Skills (R4) stay in their own eligibility class. Exporting one into the Project's Skills is an explicit, reviewed change (coordinator learning).
Skill tools at a glance
The plural skills.* operations manage installed Skills on a Node; the
singular skill.* tools act inside one agent activation, as agents and
agent do (D-120).
| Surface | For | Does | Status |
|---|---|---|---|
maia skills status, check, update (skills.status, skills.check, skills.update.preview, .confirm) | people and coordinators; a person confirms updates | inventory and state, upstream check, reviewed update | Planned (S13, D-170) |
maia config plan, apply, drift, import | people | install pinned Skills for each harness on a Node; report drift; adopt an unpinned Skill through the CLI | Planned (S13) |
session show (worktree.skills); .maia/worktree.json workerSkills | anyone who may read the Session | what a launch injected; the per-Project opt-out | Source candidate |
maia agent --skill <path> | a person | load a Skill for a local run | Planned (R11 AG1) |
/skills, /skill load | a person in maia agent | list eligible Skills; load one | Planned (R11) |
list_skills, maia__skill, show_skill_status (operations skill.list, skill.load, skill.status, D-172) | the Maia agent | eligible metadata; load a body; eligibility and digest | Planned (R11 AG2–AG3) |
maia context inspect, lint | people | what loaded; duplicates, oversize and undiscoverable Skills | Planned (R11) |
| Agent Skill references; Routine step Skills | people, through preview and confirmation | eligibility | Planned (S49, S50, FV4) |
maia.agent-definition/v1 skillRefs | the managed worker's definitions | up to 32 references; the one shipped definition has none, and activation does not apply them | Source candidate schema |
| Voice orchestrator prompt | voice | pins the CLI guidance Skill by digest and injects a restricted projection of it | Source candidate |
| Skill views in clients | people | catalog, state and use | Planned (T3) |
Imported from Maia OS docs at 3dc3e07