skill.md frontmatter¶
Every skill is a directory containing a skill.md whose YAML frontmatter declares the skill's structure.
Schema¶
---
type: skill # always "skill"
name: my_skill # unique identifier
description: One-line summary # shown in `reyn skills`
entry: <phase_name> # required; phase that runs first
final_output: <artifact_type> # required; schema for the skill's result
final_output_description: | # optional; human-readable result description
...
finish_criteria: # optional; conditions for clean termination
- All inputs validated
- Final output passes the quality bar
graph: # required; allowed transitions
outline: [expand]
expand: [end]
permissions: # optional; declares required capabilities
shell: deny
python:
- module: stats
function: compute
mode: safe
required_credentials: # optional; credential keys this skill may read
- github_token
imported_from: ... # optional; provenance, set by skill_importer
imported_at: 2026-04-29T...
imported_format: claude-skill
imported_revision: <git-sha>
---
Required fields¶
type— must beskill.name— used for resolution and event correlation.entry— name of the phase to start with. Must exist inphases/.final_output— artifact type produced when the skill finishes. Must be defined inartifacts/<name>.yamlor be a stdlib artifact.graph— adjacency list. Each key is a phase name; each value is a list of allowed next-phase names. Useendto mark terminal transitions.
Optional fields¶
description— appears inreyn skills.final_output_description— long-form description shown in skill detail.finish_criteria— used by phases to know when finishing is allowed.permissions— seepermissions:(skill-level) below.required_credentials— seerequired_credentials:below.postprocessor— seepostprocessor:below.imported_*— provenance fields written byskill_importer. Inert; the parser ignores them.search_hints— optional; list of example query strings this skill can answer. Used by the BM25/embedding pre-filter when the catalog exceeds the router context window. Set by skill authors to improve recall in large multi-skill repos. Example:search_hints: ["summarize an article", "tl;dr a document"]
permissions: (skill-level)¶
permissions: in skill.md frontmatter is the only location for permission declarations. Phase-level permissions were removed in the skill-only permissions migration. See permission-model.md for full semantics and capability hierarchy.
permissions:
shell: true # false (default) | true; enables shell ops
file.read: # paths outside CWD that the skill may read
- path: ~/notes
scope: recursive
file.write: # paths outside default write zone (.reyn/, reyn/)
- path: /tmp/output
scope: just_path
mcp: [github, jira] # list of MCP server names the skill may call
python:
- module: stats # module name (no .py extension)
function: compute
mode: safe # safe | unsafe
- module: rendering
function: to_html
mode: unsafe # requires --allow-unsafe-python flag
tool: [web_search] # list of named Control IR tool names
mcp_install: true # allow mcp_install ops (optional; default false)
index_drop: true # allow index_drop ops (optional; default false)
mcp_drop_server: true # allow mcp_drop_server ops (optional; default false)
Key fields¶
shell—trueorfalse(defaultfalse). Governs whether Control IRshellops are accepted. Also requires--allow-shellat the CLI.file.read/file.write— paths outside the default zones. Each entry:path(absolute or CWD-relative;~expanded) andscope(just_pathorrecursive).file.writealso coverseditanddeleteops. Omit to stay within defaults (read: CWD; write:.reyn/,reyn/).mcp— list of MCP server names the skill may call. Just the server keys as defined inreyn.yaml'smcp.servers.python— list of Python function entries allowed in preprocessor/postprocessorpythonsteps. Each entry must match themodule+functionpair used in a step.mode: saferuns sandboxed;mode: unsaferequires--allow-unsafe-pythonat the CLI.tool— list of named Control IR tool names (e.g.web_search,web_fetch) the skill may invoke.mcp_install—trueto allowmcp_installControl IR ops (defaultfalse).index_drop—trueto allowindex_dropControl IR ops (defaultfalse).mcp_drop_server—trueto allowmcp_drop_serverControl IR ops (defaultfalse).
The permissions block is the upper-bound gate: even if a phase's allowed_ops would permit an op, the op is rejected at dispatch if it falls outside skill.permissions. See permission-model.md for the layered enforcement model.
required_credentials: (optional)¶
- Type:
list[str] - Default:
["*"](full delegation — preserves pre-FP-0016 behaviour where sub-skills inherited every parent credential) - Purpose: declares which keys from
~/.reyn/secrets.envand~/.reyn/oauth_tokens.jsonthis skill (and any sub-skill it invokes) is permitted to read. - Enforcement: at
run_skillboundaries the OS constructs aScopedSecretStorefrom this list and intersects it with the parent skill's scope (parent-cap semantics). Reads outside the allowed set raiseCredentialScopeError.
Values¶
| Value | Meaning |
|---|---|
[] |
No credentials needed. Recommended explicit declaration for skills that don't read secrets. |
["github_token", "openai_key"] |
Explicit allowlist — only the named keys are accessible. |
["*"] |
Full delegation. Use only for trusted internal skills. |
Audit¶
Every run_skill invocation emits a sub_skill_credential_scope event with the effective allowed_keys. See events.md.
Example¶
---
name: pr-reviewer
entry: review
required_credentials:
- github_token
permissions:
mcp: [github]
file.read:
- path: .
scope: recursive
final_output: review_output
---
Cross-references¶
- permission-model.md — "Per-skill credential scoping" section: threat model and deeper coverage
- secret-handling.md — secret store overview
- events.md —
sub_skill_credential_scopeevent payload
postprocessor:¶
A skill may optionally declare a postprocessor block — a deterministic transformation that runs at skill finish, between the LLM's final output and the artifact returned to the caller.
postprocessor:
output_schema: rendered_post # artifact-name string OR inline dict
output_description: |
Fully rendered HTML post with word count.
steps:
- type: python
module: rendering
function: to_html
into: html_body
- type: validate
schema:
type: object
required: [html_body]
properties:
html_body: { type: string }
For full syntax — required fields, optional fields, step kinds, on_error policy, and permission gate — see postprocessor.md. For rationale, see Concepts: postprocessor.
Body¶
After the frontmatter, the markdown body is the skill's prose description: what it does, when to use it, examples. Shown by reyn skills <name>.
Validation¶
reyn lint <skill_name> checks:
- All phases referenced in
graphexist inphases/. entryis a key ingraph.final_outputmatches an artifact inartifacts/or stdlib.- Phase artifact references are resolvable.
- Python preprocessor steps (if any) match
permissions.pythonand have a corresponding.pyfile.
Example¶
---
type: skill
name: my_explainer
description: Generate a one-paragraph explainer from a topic.
entry: outline
final_output: explainer
graph:
outline: [expand]
expand: [end]
---
# my_explainer
Takes a `topic_input` artifact and produces a friendly, example-rich
one-paragraph explainer. Two phases: `outline` produces 3 bullets;
`expand` turns them into prose.
See also¶
- phase-md.md — Phase frontmatter
reference/dsl/artifact-yaml.md— artifact schema files (Phase 2)reference/dsl/graph.md— graph semantics in depth (Phase 2)- Concepts: P2 Skill defines structure