# Skills: load procedures when the task needs them

A skill packages instructions for a kind of work, usually in `SKILL.md`, alongside optional references, scripts, and templates. A tool schema describes an executable operation; a skill explains how to carry out a workflow using available operations. An extension can add executable behavior or lifecycle hooks. A skill can arrive inside an extension or plugin without becoming executable merely because it was discovered.

Consider a harness with fifty specialist workflows. Putting every procedure into every request consumes context and makes irrelevant instructions compete with the task. **Progressive disclosure** means advertising compact descriptions first, then loading a selected procedure and its relevant resources. Codex, pi, and Goose support this separation through the loading paths below; Sidekick adds a versioned binding model described later. [Codex catalog guidance][c-prompt], [pi prompt formatting][p-format], [Goose skill operation][g-operation].

## Follow one skill through the loop

Suppose the user asks: “Extract the invoice totals from these PDFs.” A teaching trace is:

1. **Discover:** the harness finds `invoice-extraction`, its description, and its source identity. It advertises this metadata to the model.
2. **Select:** the model recognizes a matching description, or the user explicitly invokes the skill. These are separate entry points; an implicit match is not guaranteed.
3. **Load:** the harness injects the procedure, or the model requests its contents through a reader. The procedure directs it to a format reference and an extraction script.
4. **Resolve resources:** the agent loads that reference relative to the skill's location. A remote package needs its provider's reader, not an invented local path.
5. **Act:** the agent requests a command to execute the script. Tool authorization and sandbox enforcement apply at this execution boundary.
6. **Continue:** results return to the ordinary agent loop. Loading a skill did not create a separate autonomous worker.

This trace is a design abstraction. The pinned implementations below do not all produce the same message roles or tool calls.

## File and provider catalog loading

| Harness | Discovery and model selection | Explicit selection and loading |
|---|---|---|
| Codex | Catalogs describe skills with source locators. Prompt guidance directs the model to read a matching skill and relevant resources. Sources can be host files or environment/provider-owned packages. | Structured selections and textual mentions are resolved by the skill subsystem. The extension path reads selected main prompts and adds instruction fragments to turn context. |
| pi | Prompt entries contain name, description, and file location. The model uses an available file-reading mechanism when a description matches. `disable-model-invocation` excludes a skill from this catalog. | `/skill:name arguments` reads the file, strips frontmatter, and expands the input with its body, base directory, and supplied arguments. |
| Goose | The native state-machine operation advertises names and descriptions and exposes `load_skill`, including access to supporting files. | Skill slash commands expand into model-visible user content. Built-in commands and existing recipe commands take precedence over skill command resolution. |

Sources: [Codex catalog instructions][c-prompt] and [explicit injection][c-injection]; [pi catalog filtering][p-format] and [command expansion][p-expand]; [Goose loading][g-operation], [command effects][g-command-effects], and [command precedence][g-commands]. These describe the pinned paths, not every UI or deployment configuration.

## Identity and precedence are behavioral contracts

Two packages can both contain a skill named `review`. Preserve the selected source identity, expose collisions, and make resolution predictable. Display names alone are insufficient for reproducibility.

Codex illustrates why subsystem scope matters: its host selection helper requires an unambiguous plain name, while the extension catalog selector takes the first enabled plain-name match and deduplicates by authority plus package. Both support explicit source selection. Do not describe these as one universal collision rule. [Host selection][c-host-selection], [extension selection][c-selection].

Pi keeps the first discovered name and emits a collision diagnostic containing winner and loser paths; it also deduplicates canonical file paths. Goose's discovery visits roots in order and keeps the first parsed name. Its project roots precede global roots, but duplicate winners *within one root* are unspecified because traversal is unsorted. [Pi loader][p-load], [Goose root order][g-roots] and [discovery contract][g-discovery].

## A loaded procedure is not an execution grant

Distinguish **discovery precedence**—which file wins—from **instruction authority**—which instructions the agent must obey. Finding a project skill does not establish who authored it or authorize every effect it requests. A procedure saying “publish the report” still needs the user's applicable authorization and the harness's execution policy.

Keep tool permissions in an enforced policy layer. Treat skill metadata as declarations unless that layer explicitly validates and interprets them. In pi's inspected loader, `disable-model-invocation` affects catalog visibility; that mechanism is not a command sandbox. A bundled script remains code that executes through a tool. [Pi loader and formatter][p-format].

File discovery and safe file access are also distinct. Goose's discovery contract explicitly says traversal can follow symlinks outside a search root. Its supporting-file reader separately rejects parent-directory paths and uses platform-specific confined opens; the native `load_skill` path also runs pre/post tool hooks. A successful discovery scan is therefore not evidence that every later read is safe. [Discovery boundary][g-discovery], [supporting-file access][g-files], [tool lifecycle][g-hooks].

## Budget context and define refresh behavior

Progressive disclosure reduces initial prompt size; loaded procedures and references still consume context. Codex budgets catalog metadata, can shorten descriptions or omit entries, and warns when an explicitly injected main prompt exceeds its limit. A shortened routing description may change selection quality, so record truncation as observable behavior. [Metadata allocation][c-budget], [main-prompt limit][c-injection].

Refresh metadata and content deliberately. Pi's explicit command reads the current file using the already-loaded catalog entry; `/reload` refreshes resources. Goose discovers skills when building its prompt contribution and again when handling a load. A file changed between discovery and execution can therefore differ from the version originally described. For reproducible runs, record the selected identity and content digest, and define what happens after [compaction](compaction.md) removes earlier instructions. [Pi expansion][p-expand] and [reload][p-reload], [Goose operation][g-operation].

## Builder checks

- Install two identically named skills and verify the selected source and collision report.
- Hide a skill from automatic selection and verify explicit invocation separately.
- Change or delete a file after discovery; verify refresh and missing-file behavior.
- Test oversized bodies, paginated reads, relative resources, and symlink escapes.
- Request an action explicitly forbidden by execution policy and verify the skill cannot override that denial.
- Compact after loading a procedure and verify the agent can recover required instructions without silently substituting a newer version.

## Sidekick: versioned bindings and a stable tool interface

Sidekick stores skills as drafts and published versions, with saved agent bindings resolving a version or channel. A metadata catalog enters the system prompt; `Skill` and `ReadSkillReference` load instructions and references on demand as tool results. This makes saved bindings, rather than filesystem discovery order, the runtime selection boundary. Draft access follows the saved binding rather than the conversation user’s skill-edit permissions. [Binding resolution][s-binding], [catalog assembly][s-catalog] (private source).

`UseSkillTool` checks that a skill is attached and that the requested tool is declared, then invokes the normal system dispatcher with the acting agent’s context. This preserves its applicable capability checks. Only built-in system tools are supported through that path at the pin. “Load the skill first” is model guidance; the dispatcher checks the binding on invocation rather than requiring proof of an earlier load event. A skill’s instructions, its binding, and its executable capabilities therefore remain distinct contracts. [Invocation boundary][s-use].

[c-prompt]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/ext/skills/src/catalog_prompt.rs#L3-L40
[c-injection]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/ext/skills/src/extension.rs#L403-L525
[c-host-selection]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/skills/src/selection.rs#L33-L200
[c-selection]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/ext/skills/src/selection.rs#L21-L140
[c-budget]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/ext/skills/src/render.rs#L331-L371
[p-format]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/skills.ts#L331-L390
[p-expand]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/agent-session.ts#L2172-L2200
[p-load]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/skills.ts#L414-L460
[p-reload]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/agent-session.ts#L3690-L3728
[g-operation]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/state_machine/ops_skills.rs#L47-L122
[g-command-effects]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/state_machine/ops_skills.rs#L276-L318
[g-commands]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/slash_commands/skill_slash_command.rs#L45-L65
[g-roots]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/skills/mod.rs#L317-L388
[g-discovery]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose-agent/src/skills/discovery.rs#L215-L247
[g-files]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/skills/supporting_files.rs#L181-L265
[g-hooks]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/state_machine/ops_skills.rs#L365-L425

[s-binding]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/skill_service.py#L606-L737
[s-catalog]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/context_builder_service.py#L838-L868
[s-use]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick_worker/tools/skill_executor.py#L349-L412
