---
title: Writing Skills
description: 'Contributor guide to authoring OAT skills, including runtime contracts, governance, and where to start.'
---

# Writing Skills

Use this page when you are creating or updating OAT skills in `.agents/skills`.

Skill behavior is defined by frontmatter plus the process contract in each `SKILL.md`. The goal is to make lifecycle behavior explicit, reviewable, and reusable across sessions.

## Where Skills Live

- Canonical skills live in `.agents/skills`
- `AGENTS.md` is the session-facing registry and should stay aligned with skill frontmatter
- OAT project and review artifacts should reference skill names consistently

## Authoring Priorities

- Make the mode and purpose explicit.
- Keep prerequisites and expected artifacts concrete.
- Spell out blocked vs allowed activities for state-advancing skills.
- Define user-facing progress indicators for longer workflows.
- Define a pre-work capability and authorization gate for skills that delegate to subagents, workers, or reviewers.
- Keep output obligations explicit so downstream skills and users know what changed.

## Contract components

- Mode assertion (purpose, blocked/allowed activities)
- Preconditions and required artifacts
- User-facing progress indicator expectations
- Delegation capability detection and fallback behavior, when the skill dispatches helper agents
- Output obligations
- Escalation/guardrail behavior

## Frontmatter fields in active use

- `name`
- `description`
- `version`
- `disable-model-invocation`
- `user-invocable`
- `allowed-tools`
- `oat_gateable`

## Practical Authoring Flow

1. Decide whether you are adding a general reusable skill or an OAT-specific lifecycle skill.
2. Add or update the skill under `.agents/skills/<name>/SKILL.md`.
3. Keep the `AGENTS.md` skills registry synchronized with the new frontmatter.
4. Update related docs or lifecycle references if the skill changes user-visible behavior.

## Governance rules

- Prefer skill-first invocation language.
- Keep `AGENTS.md` skills table synchronized with `.agents/skills`.
- Require explicit user approval for destructive or state-advancing transitions.

### Artifact-writing hygiene

Every role or skill that creates or edits tracked output must include the
artifact hygiene contract at its writing boundary. The writer uses a concrete
write/fix formatting command supplied by its plan, task, or brief. If none is
usable, it discovers the repository's documented command from applicable
`AGENTS.md` / `CLAUDE.md` instructions and relevant package manifests.

Contracts must distinguish write/fix commands from check-only commands, prefer
a file-scoped invocation when supported, avoid unrelated whole-tree rewrites,
and never infer or hardcode a formatter. If no command can be discovered, the
writer warns once with
`no format command discovered in repo instructions; skipping` and continues.
Run only verification relevant to the changed files unless the role's
definition of done requires broader gates.

For planned implementation, plan-producing skills resolve this command once and
place it in every artifact-writing task. Runtime discovery remains the fallback
for direct lifecycle writers and incomplete or stale plans. When the same
contract crosses dispatch boundaries, keep each copy self-contained and protect
equivalence with contract tests.

## Recommended Starting Points

- Use `create-oat-skill` when the new skill belongs to an OAT lifecycle or maintenance flow.
- Use `create-agnostic-skill` when you want a reusable workflow skill that is not OAT-specific.
- Use existing lifecycle skills as examples for progress banners, prerequisites, and artifact updates.

## Delegation-Capable Skills

Skills that dispatch subagents, workers, reviewers, or fresh-context helper sessions need a capability model before work starts. Do not assume delegation is available, and do not silently downgrade just because the runtime needs user authorization.

At minimum, the skill contract should:

- Probe whether the host can dispatch the required helper role(s).
- Distinguish `available`, `authorization required`, and `not resolved`.
- Ask once at skill start when authorization is required, with the approval scope stated clearly.
- Lock the selected tier for the run unless the user explicitly changes execution mode.
- Stop before side effects if delegation is required for correctness and authorization remains unresolved.
- Document the fallback path and any quality or independence tradeoff.

OAT-specific skills should compose with the internal
`oat-dispatch-subagents` contract instead of copying provider selection and
recovery rules. The caller still owns decomposition, user interaction,
verification of load-bearing claims, cross-lane synthesis, and artifact
writes. The dispatch contract owns capability and authorization checks, live
catalog evidence, route/model/effort selection, launch acceptance, recovery,
and the neutral dispatch record.

Keep project lifecycle policy out of that general layer. A lifecycle caller
loads `oat-project-dispatch-subagents` to resolve project, phase/task, gate,
write-boundary, commit, and worktree context, then passes a generic request to
`oat-dispatch-subagents`. Analytical callers such as repository audits can use
the general contract directly without requiring an active project. In either
case, resolve the provider first and load exactly one provider-specific
reference rather than merging provider mechanics into a universal catalog.

Use `create-agnostic-skill` or `create-oat-skill` as the starting point; both include the current delegation guidance and optional capability-detection template.

## Gate-aware skills

A skill that supports configured final gates must declare `oat_gateable: true`
in frontmatter and include a final Gate Execution step in its process contract.
Without both pieces, a configured `workflow.gates.skills.<skill>` entry is a
validation warning rather than an executable contract.

The Gate Execution step should:

1. Run `oat gate resolve <this-skill> --json`.
2. Treat `null` as "no gate configured."
3. Export the resolved path with `export PROJECT_PATH` before launching the
   command shell.
4. For `oat gate review`, require the configured command to include
   `--project "$PROJECT_PATH"`; do not append it at runtime, because the resolved
   command must execute exactly as configured.
5. Run the resolved `command` as the skill's last step and use its exit code as
   the pass/fail signal.
6. Follow the gate's `onFailure` policy:
   - `block` - remediate and rerun up to `maxAttempts`, then escalate.
   - `prompt` - surface the failure and ask the user.
   - `warn` - record the failure and continue.

For OAT review gates, prefer putting
`oat gate review --project "$PROJECT_PATH" "<prompt>"` in the configured gate
command rather than hard-coding a provider CLI directly in the skill. Use
`oat gate cross-provider-exec "<prompt>"` for generic cross-runtime commands
that should report only the child process status, not review findings.
Reusable lifecycle gate commands must omit `--target <id>` so the
dispatcher can avoid the current runtime; reserve explicit targets for
manual/debug commands or deliberate local/user-specific overrides. See
[Workflow Gates](../cli-utilities/workflow-gates.md) for the config and command
surface.

## Reading project state

Skills that need fields from the active project's `state.md` (e.g. `phase`, `phaseStatus`, `workflowMode`, `docsUpdated`, `lastCommit`) MUST query the CLI instead of hand-parsing YAML with `grep`/`awk`.

For one field, use `--field`:

```bash
WORKFLOW_MODE=$(oat project status --field project.workflowMode 2>/dev/null || echo null)
```

If the skill is reading a resolved project path instead of the active project pointer, add `--project-path`:

```bash
WORKFLOW_MODE=$(oat project status --project-path "$PROJECT_PATH" --field project.workflowMode 2>/dev/null || echo null)
```

For multiple fields, use `--shell` so the CLI reads project state once and emits shell-safe assignments:

```bash
eval "$(oat project status --shell \
  PHASE=project.phase \
  PHASE_STATUS=project.phaseStatus \
  WORKFLOW_MODE=project.workflowMode 2>/dev/null)"
```

Skill snippets assume `oat` is available on `PATH`. Environments without a global install, including CI or cloud runners, can provide an `oat` shim backed by `npx`:

```bash
mkdir -p .oat/bin
cat > .oat/bin/oat <<'EOF'
#!/usr/bin/env bash
exec npx @open-agent-toolkit/cli "$@"
EOF
chmod +x .oat/bin/oat
export PATH="$PWD/.oat/bin:$PATH"
```

Create the shim once per checkout or CI job instead of putting `command -v oat` fallback branches in every skill. The same snippet also supports setups where `oat` is intentionally provided on `PATH` by `npx`.

The JSON output is a stable contract: the field set consumed by migrated skills is locked by `MIGRATED_FIELDS` in `packages/cli/src/commands/project/status.test.ts`, so removing or renaming any of those keys is a real test failure rather than a silent runtime break. See [CLI Reference](../reference/cli-reference.md) for the full locked field set.

## Reference artifacts

- `.agents/skills/*/SKILL.md`
- `AGENTS.md`
- `.agents/skills/oat-project-implement/SKILL.md`
- `.agents/skills/oat-project-complete/SKILL.md`
- `.agents/skills/oat-project-review-receive/SKILL.md`
