Workflow Authoring Guide
This guide provides everything you need to create well-structured workflows. Read this document when asked to create, define, or set up a new workflow for a skill.
What Workflows Are
Workflows are ordered multi-step processes defined within skills. They provide:
- Structured execution: steps run in sequence with clear boundaries
- State persistence: progress is saved between steps, enabling resumption after interruptions
- Recovery: lost context can be recovered by querying active workflows and resuming
Workflows are optional — skills may have zero, one, or many workflows depending on their purpose.
Directory Conventions
Workflows live in a workflows/ subdirectory within a skill:
skills/
└── my-skill/
├── SKILL.md # Mentions available workflows
├── references/ # Optional: skill-level reference docs
└── workflows/
└── my-workflow/
├── 01-first-step/
│ ├── instructions.md
│ ├── references/
│ └── scripts/
├── 02-next-step/
│ └── instructions.md
└── 03-final-step/
└── instructions.md
Naming:
- Workflow directories: lowercase with hyphens (e.g.
feature-planning,code-refactor) - Step directories: two-digit prefix for ordering (e.g.
01-analyze,02-design,03-implement)
Steps execute in lexicographic order of their directory names — the numeric prefix is what orders them. Gaps are allowed (01-*, 05-*, 10-*) to leave room for future steps.
SKILL.md Integration Pattern
Critical: workflows are discovered by reading the skill's SKILL.md body, not by automatic detection. You must document workflows in the SKILL.md for the agent to know they exist:
## Available Workflows
### feature-planning
Use when planning a new feature. Steps through requirements analysis,
design considerations, and implementation planning.
For each workflow, explain when to use it, what it does, and the expected outcome.
Step Format
Each step directory contains:
- instructions.md (required): the step's content with YAML frontmatter
- references/ (optional): detailed documentation read on demand
- scripts/ (optional): executable scripts for the step
A step directory without an instructions.md, or whose frontmatter is missing a valid title, is skipped at load time with a warning — the rest of the workflow still loads.
instructions.md
---
title: "Analyze Requirements"
---
# Step Instructions
The step's guidance goes here. Explain what the step does,
what to produce, and how to validate completion.
Frontmatter Fields
| Field | Required | Description |
|---|---|---|
title |
Yes | Human-readable step title (non-empty string) |
required |
No | If false, the step may be skipped when not applicable (default: true) |
condition |
No | Natural-language predicate; surfaced before the step starts so the agent decides start vs skip (see Composing Workflows) |
composes |
No | Run another workflow to completion when this step activates |
loop |
No | Run another workflow once per agent-supplied item |
* |
No | Custom fields are preserved as step metadata but are not interpreted by the engine |
skippable: true is a deprecated alias for required: false — use required in new workflows. composes and loop are mutually exclusive on one step.
Body Content
The body is returned to the agent when the step starts. Explain:
- What the step accomplishes
- What actions to take
- What outputs to produce
- How to validate completion
Workflow Execution Flow
The agent drives workflows through four tools:
- Start:
start_workflow(skill_name, workflow_name)creates a tracked instance with a unique ID, a scratchpad file for progress notes, and returns the step list. Only one instance per skill/workflow pair can be active at a time. - First step:
update_workflow_state(workflow_id, step, action="start")begins the first step and returns its instructions. When the first step carries acondition, the start guidance instead presents it as a start-or-skip decision — evaluate before calling anything. - Execute: the agent performs the step's actions, producing outputs and updating the scratchpad.
- Advance:
update_workflow_state(workflow_id, step, action="complete")marks the step done, auto-starts the next pending step, and returns its instructions — no separatestartcall needed.action="skip"does the same for skippable steps (required: falseor carrying acondition). - Finalize: when the last step is completed or skipped, the workflow is auto-finalized — state and scratchpad are cleaned up automatically.
To abort a workflow early, use end_workflow(workflow_id, action="abort").
Recovery
Workflow state survives context loss and restarts:
query_workflow()without arguments lists all active workflowsquery_workflow(workflow_id=...)returns the full state: per-step status (conditions marked inline), current step, and scratchpad path- Resume from the current step — all progress is preserved
Starting a workflow while an instance of it is already active is rejected, naming the existing instance's ID. When that happens, recover the existing instance rather than discarding it, and if it no longer serves the request, tell the user what the interrupted run had done and ask whether to resume or start fresh before ending it. Both end_workflow actions discard the state and scratchpad — never end an active instance without surfacing what it had done.
Instances abandoned across sessions are expired automatically after a configurable staleness window.
Composing Workflows
Beyond a flat sequence, a step can pull in another workflow. This lets you share reusable sub-sequences, iterate over a list, and branch — all driven by the single top-level workflow ID. Step ids resolve across the active chain: when a sub-workflow is running, its steps are the ones you can transition, and completing the parent's in-flight composes/loop step ends the sub-workflow early (the active child layers are discarded and the parent resumes from its next step). Responses carry a breadcrumb (parent/step > child/step) so you know where you are.
A composition reference is <workflow> for a workflow in the same skill, or <skill>/<workflow> to reach across skills. A composed step's own instructions.md body is not shown — the child's steps carry the instructions.
composes — run a sub-workflow inline
---
title: "Process the inbox note"
composes: process-inbox-note
---
When this step activates, the engine starts process-inbox-note, returns its first step, and you drive it like any workflow. When its last step completes, this step auto-completes and the parent's next step starts — one continuous run.
A composes step can also be required: false or carry a condition: skipping it (or failing its condition) advances the parent without running the sub-workflow.
loop — run a sub-workflow once per item
---
title: "Handle every pending reminder"
loop: send-one-reminder
---
A loop step does not auto-start. When the cascade reaches it, it halts and asks you to start it with an items list:
update_workflow_state(workflow_id, "03-handle", action="start", items=["r1", "r2", "r3"])
items is a list of opaque strings — IDs, filenames, whatever the sub-workflow knows how to interpret. The loop target runs once per item, in order; completing one iteration spawns the next. The current item appears in the breadcrumb (... (item: r2)) and in query_workflow. Pass items=[] to complete the loop step with zero iterations. items is required on a loop step and rejected anywhere else.
condition — branch on a natural-language predicate
---
title: "Escalate to the user"
condition: "the issue could not be resolved automatically"
---
When auto-advance reaches a condition step it halts and shows you the predicate. You evaluate it against the current context and either action="start" (condition holds) or action="skip" (it does not). A condition makes the step skippable even if it is required.
The predicate is likewise surfaced up front in start_workflow's guidance when a condition gates the workflow's first step, and as (if: ...) markers in query_workflow's step list — a condition is always visible before the step starts.
Authoring rules
composesandloopcannot both be on one step.- Composition graphs must be acyclic —
A composes B,B composes A, or a step composing its own workflow are rejected at load with a warning, as are references to missing or empty workflows. Check the logs after adding composition. - Avoid reusing a parent step's id inside a composed/loop target: ids resolve deepest-first, so a colliding child step id shadows the parent's and the parent step can no longer be completed early. The conventional
01-,02-prefixes per layer keep ids distinct. - Document composed/looped sub-workflows in SKILL.md like any other workflow so they can also be started on their own.
Step Design Patterns
Atomic Steps
Each step should address one concern. Break complex work into smaller, focused steps:
# Good: atomic steps
01-requirements/ # Gather and analyze requirements
02-design/ # Design the solution
03-implement/ # Write the code
04-test/ # Validate the implementation
# Bad: one giant step
01-do-everything/ # Too broad — hard to track progress
Clear Instructions
Each step should provide actionable guidance:
# Good: specific actions
## Your Task
1. Read the feature request in `docs/feature-specs/my-feature.md`
2. Identify the core requirements
3. List any ambiguities or missing information
## Output
Create a requirements checklist as a markdown file
Avoid vague directives like "think about the requirements and write them down".
Provide Clear Validation Criteria
Each step should define what "done" looks like:
## Validation
- [ ] All files pass linting
- [ ] All tests pass
- [ ] Type checking succeeds
Gate Conditional Steps with condition
Use condition when a step applies only sometimes. The engine surfaces the predicate
before the step starts (see the condition section),
so the run-or-skip decision is made before any work begins:
---
title: "Configure Database"
condition: "the project has no configured database (check for `config/database.toml`)"
---
# Database Configuration
Set up the database connection...
Keep the predicate self-contained: name what to check and how, not just when to skip.
required: false alone is the weaker option: it makes a step skippable but nothing
prompts evaluation, and skip guidance written in the step body is read only after the
step starts — past the point where skipping is still possible. Reserve it for steps the
agent may legitimately skip by its own judgment mid-run.
Leveraging References and Scripts
Use references/ for detailed content that doesn't fit in the main instructions, and scripts/ for executable code the step should run:
02-validate/
├── instructions.md # Main: "Run validation checks"
├── references/
│ └── lint-rules.md # Detailed: rule explanations
└── scripts/
└── lint-checks.sh # Executable: runs project linting
Single Source for Shared Information
The same information — an explanation, a rule, a procedure — appears in exactly one place across the skill: never duplicated across steps, between a step and the skill's SKILL.md, or between step instructions and a reference. When more than one step needs it, it lives once in the skill's references/ and each consumer points at it; detailed content only one step needs lives in that step's references/, while the step's own essentials stay inline in instructions.md. Pointers name which references/ they mean ("read the skill's references/notation.md", "read this step's references/lint-rules.md") rather than a bare relative path. Duplicated copies drift apart when the information changes — the stale copy misleads with no error signal.
Information a step needs only in some situations — an edge-case rule, a rarely-used procedure, a platform-specific caveat — belongs in that step's references/, read when its condition applies (name the condition at the pointer), not in the instructions.md every run of the step loads.
Keep Content Generic
Authored content — step instructions, references, and the skill's SKILL.md documentation alike — is reusable guidance, not an incident log: the skill-authoring guide's Keep It Generic section carries the rule (never justify a rule with a specific occurrence; generic examples only, never a real conversation's events).
Example Workflow
workflows/
└── feature-planning/
├── 01-requirements/
│ └── instructions.md
├── 02-design/
│ ├── instructions.md
│ └── references/
│ └── design-patterns.md
└── 03-implementation-plan/
└── instructions.md
01-requirements/instructions.md:
---
title: "Gather Requirements"
---
# Requirements Gathering
## Your Task
1. Read the feature request or user story
2. Identify functional and non-functional requirements
3. List assumptions and constraints
## Output
Create a requirements document at `docs/requirements/{feature-name}.md`
## Validation
- All requirements are testable
- Assumptions are explicit
- Constraints are documented
Testing Your Workflow
Before considering a workflow complete:
- Manual walkthrough: read through each step's instructions — do they make sense?
- Ordering check: are steps in the right order? Do dependencies flow correctly?
- Validation check: does each step have clear success criteria?
- SKILL.md check: is the workflow documented in the skill's SKILL.md?
- Script check: does every step script that carries real logic have tests (see below)?
Testing Step Scripts
A step's scripts/ directory holds executable logic, not documentation — when a script does real work, it gets tests:
- Test scripts that compute, parse, or decide — argument handling, output shaping, state decisions. A script that only chains shell commands together does not need tests.
- Colocate the tests with the workflow's skill (the skill's
tests/directory or beside the script) and run them with the language's standard runner from the skill directory — the same expectations as theskill-authoringguide's Testing Bundled Executables section (deterministic, offline, fixtures inside the skill directory). - Reference the tests in the step's Validation criteria when they exist ("script tests pass"), so completing the step includes running them.
- Change docs and tests together: when a change alters what a script does (new inputs, changed output), update the step's instructions, the workflow's SKILL.md documentation, and the script's tests in the same change.
Common Patterns
- Planning: requirements → design → implementation-plan → validation
- Refactoring: analyze-current → design-refactor → implement-changes → validate
- Onboarding: read-context → understand-architecture → setup-environment → first-task