Capability Authoring Reference¶
The complete grammar and authoring rules. This mirrors
<Runtime Home>/specs/capability-spec.md, which ships inside the binary and is
refreshed with every version — when the two disagree, the file in your
Runtime Home wins, because it describes the binary you actually have.
Writing your first capability?
Read Create a Capability instead — it walks the whole sequence in order, from finding operations to sharing the result. Come back here for the details.
Grammar¶
```runtime
version: v1
inputs:
<name>:
description: <plain-English description>
required: true|false
workflow:
- provider: <registered provider>
args: [<operation word>, ..., <argument>, ...]
- binary: <allowed binary>
args: [<positional arg>, ...]
```
| Key | Level | Required | Meaning |
|---|---|---|---|
version |
top | yes | Spec version this block was authored against |
inputs |
top | no | Named parameters, referenced as ${name} in any step's args |
workflow |
top | yes | Ordered list of steps, executed in order |
provider |
step | one of | A registered Provider (github, files) |
binary |
step | one of | A binary in allowed_binaries |
args |
step | yes | Positional arguments, exactly as typed on the CLI |
description |
input | no | What this input is for |
required |
input | no | Whether execution refuses to run without it |
Each step sets exactly one of provider or binary — never both, never
neither.
These are the only keys¶
The parser rejects anything it does not recognize, naming the offending key and its line number.
transport: rest ✗ not a key — the provider owns that decision
steps: ✗ it is `workflow:`
command: ✗ from the retired command registry
capability: ✗ there is no capability step
This strictness is the point. Before it, transport: rest on a step or
steps: instead of workflow: validated clean while doing nothing — a
file that looked correct and silently skipped its work.
args are the CLI, sequenced¶
A step's args are exactly the words you would type after runtime <provider>,
split into a list.
| On the command line | In a capability |
|---|---|
runtime files read ./a.txt |
provider: filesargs: [read, ./a.txt] |
runtime files write ./a.txt hello |
provider: filesargs: [write, ./a.txt, hello] |
runtime github repo view cli/cli |
provider: githubargs: [repo, view, cli/cli] |
runtime github repo list acme per_page=100 |
provider: githubargs: [repo, list, acme, per_page=100] |
runtime command run gh release upload v1 ./bin |
binary: ghargs: [release, upload, v1, ./bin] |
This is the same surface, just sequenced inside a file instead of typed one command at a time.
Discovering operations¶
Do not guess at operations. Every provider publishes its full surface, and that published list is the authority:
The help output lists every operation, its argument form, and the transport the provider chose for it. The transport column is information, not a choice.
The rules¶
1. Never hardcode auth¶
Every step already goes through the Auth Engine via the provider or binary it names. A capability never embeds a token, credential, or auth flow of its own.
2. Never assume a Runtime Context beyond what's active¶
If a workflow genuinely needs a specific org, project or namespace, declare it as an input:
inputs:
organization:
description: GitHub organization to audit
required: true
workflow:
- provider: github
args: [repo, list, "${organization}"]
Many operations already fall back to the active Runtime Context on their own — let them where the value really is where you are rather than what this run is about.
3. Ask when a required input is missing¶
Don't invent a placeholder and execute anyway. capability execute refuses to
run with a missing required input; respect that at authoring time too.
4. Only reference what the runtime can execute¶
A step's provider must be registered and its args must name a real
operation of that provider. A binary must already be in allowed_binaries.
5. Never depend on a transport¶
Write provider: github / args: [repo, list], never "the REST one". If you
need a specific wire protocol, the provider is missing an operation — add the
operation.
6. Don't finish until inputs are resolved¶
A capability referencing ${organization} isn't done until the author knows
where that value comes from.
Input substitution¶
${name} substitution is literal text replacement, performed before
execution.
inputs:
path:
description: Where to write
required: true
workflow:
- provider: files
args: [write, "${path}", "hello"]
An unsupplied optional input stays literal
An input you declare but do not supply remains in the arguments as the
literal string ${name}. That is why every shipped example marks its
inputs required: true. Prefer required: true unless you have a concrete
reason not to.
Passing a tool's own flags¶
Because a CLI-backed operation forwards arguments verbatim, a capability may pass that tool's own flags — including output flags:
This is not naming a transport. You are passing arguments the operation accepts; the provider still owns how it delivers them.
For the runtime's own output format, use the global flag at execution time rather than inside the file:
Composition: there is no capability: step¶
A capability cannot invoke another capability. There is no capability: step,
and runtime is not an allowed binary — so a binary: runtime step fails
validation too.
Large workflows inline the smaller ones they re-derive, and declare a Lineage section in the prose naming what was inlined:
## Lineage
Re-derives:
- `github/repo-health` — steps 3–5
- `files/scaffold-service-docs` — steps 9–12
The prose has no effect on execution. It exists so a reader can tell what a long workflow was assembled from, and so a change to a source capability has a findable set of dependents.
Worked example¶
Turning an ad-hoc audit into a capability:
# Repository standards audit
Answers "what do we own, and does this repo meet standard?"
## Lineage
Re-derives `github/repositories` for step 1.
```runtime
version: v1
inputs:
organization:
description: GitHub organization to audit
required: true
repository:
description: Repository to inspect, as <owner>/<repo>
required: true
workflow:
- provider: github
args: [repo, list, "${organization}", per_page=100]
- provider: github
args: [repo, summary, "${repository}"]
- provider: github
args: [api, GET, "/repos/${repository}/community/profile"]
```
runtime capability validate ./audit.md
runtime capability execute ./audit.md \
--input organization=my-org \
--input repository=my-org/payments-api
Note what the capability does not say: which transport serves each step. Step 1 happens to go over REST, step 2 over GraphQL, step 3 over REST — the provider decides, and can change its mind in a later runtime version without breaking this file.
Known limitations¶
Worth knowing before you design around them:
| Limitation | Consequence |
|---|---|
No mkdir operation |
files write fails when parent directories are missing — create the path some other way first |
files delete denied by default policy |
Use git rm for recoverable removal, or change policy deliberately |
| No encoding primitive | Base64 payloads must be written literally in the file |
| Execution stops at the first failure | Capabilities are not transactional — earlier steps have already run |
| No loops, no conditionals, no branching | A capability is an ordered list. Complexity belongs in what you compose, not in control flow |
| No capability-to-capability calls | Inline and declare Lineage |
These are properties of a deterministic execution format, not a backlog. A workflow that needs branching is usually two capabilities and a decision.
Validation¶
Reports every problem found, not just the first. See Validate & Execute.