Build a Workflow from Scratch¶
Every Attune workflow follows the same pattern: a Python class with staged execution, a skill file for discovery, and an entry point for registration. This tutorial walks through each layer using our documentation generator as the running example.
By the end you'll know how to build, register, and run your own cost-optimized workflow.
What You'll Build¶
A three-stage documentation workflow that:
- Outlines with Claude Haiku (cheap, fast)
- Writes with Claude Sonnet (balanced)
- Polishes with Claude Opus (highest quality)
Four files make it work:
| File | Purpose |
|---|---|
src/attune/workflows/doc_gen/workflow.py |
Workflow class |
plugin/skills/docs/SKILL.md |
Socratic discovery |
plugin/commands/attune-docs.md |
Quick-access command |
pyproject.toml |
Entry point registration |
Step 1: Define the Workflow Class¶
Every workflow extends BaseWorkflow and declares three
things: stages, tier_map, and run_stage().
from attune.workflows.base import BaseWorkflow, ModelTier
class DocumentGenerationWorkflow(BaseWorkflow):
name = "doc-gen"
description = "Cost-optimized documentation generation"
stages = ["outline", "write", "polish"]
tier_map = {
"outline": ModelTier.CHEAP,
"write": ModelTier.CAPABLE,
"polish": ModelTier.PREMIUM,
}
async def run_stage(self, stage_name, tier, input_data):
if stage_name == "outline":
return await self._outline(input_data, tier)
if stage_name == "write":
return await self._write(input_data, tier)
if stage_name == "polish":
return await self._polish(input_data, tier)
raise ValueError(f"Unknown stage: {stage_name}")
Three concepts to understand:
stagesis an ordered list. The execution engine runs them in sequence, passing each stage's output to the next.tier_mapassigns a Claude model to each stage.CHEAProutes to Claude Haiku,CAPABLEto Claude Sonnet, andPREMIUMto Claude Opus.run_stage()is the only method you implement. It receives the stage name, the resolved tier, and the input data from the previous stage.
Why tiers matter¶
Without tier routing, every stage runs on Claude Opus. With it, you spend Opus tokens only where quality demands it:
| Stage | Tier | Claude Model | Input $/1M | Output $/1M |
|---|---|---|---|---|
| outline | CHEAP | Claude Haiku | $0.80 | $4.00 |
| write | CAPABLE | Claude Sonnet | $3.00 | $15.00 |
| polish | PREMIUM | Claude Opus | $15.00 | $75.00 |
A 10,000-token doc generation job costs roughly $0.38 with tier routing vs $0.90 on Claude Opus alone -- a 58% reduction. Scale that across hundreds of workflow runs and the savings add up fast.
Step 2: Create a Skill Definition¶
Skills give your workflow Socratic discovery -- instead of memorizing CLI flags, users describe what they need and the skill asks clarifying questions.
Create plugin/skills/docs/SKILL.md:
---
name: documentation
description: "Generate, explain, or audit documentation"
triggers:
- docs
- documentation
- readme
- changelog
- api reference
- explain
---
Below the frontmatter, define the interaction flow:
## Socratic Scoping
Before running, ask:
1. **Type**: "What kind of docs? API reference, README,
changelog, or a general guide?"
2. **Scope**: "Which path should I document?"
3. **Audience**: "Who's reading -- developers, end users,
or both?"
## Execution
Call the `document_generation` MCP tool with the scoped
parameters:
document_generation(path="<path>", doc_type="<type>")
## Output Format
Present a summary followed by the generated content:
**Generated:** <doc_type> | **Sections:** N | **Tokens:** X
## Follow-Up
After presenting results, offer:
- "Want me to export this to a file?"
- "Should I refine a specific section?"
- "Want a changelog based on recent commits?"
The triggers array is what connects natural language
to your skill. When a user types "generate docs for
src/models", the router matches on "docs" and activates
this skill.
Pattern reference: plugin/skills/security-audit/SKILL.md
Step 3: Add a Command Shortcut¶
Commands bypass Socratic discovery for users who know
exactly what they want. Create
plugin/commands/attune-docs.md:
---
name: attune-docs
description: "Generate documentation for a path"
argument-hint: "<path>"
category: workflows
aliases: [adoc]
tags: [docs, documentation, generate]
version: "3.0.0"
---
# attune-docs
Quick-access command for documentation generation.
Bypasses Socratic discovery when you know the target.
## Execution
1. If a path argument is provided, use it. Otherwise
ask: "Which path should I document?"
2. Call the `document_generation` MCP tool with the path.
3. Present results using the format from the docs skill.
## Examples
/attune-docs src/attune/models/
/attune-docs src/attune/workflows/
/attune-docs .
Now users have two paths to the same workflow:
/attune+ "generate docs" -- guided discovery/attune-docs src/-- direct execution
Pattern reference: plugin/commands/attune-security.md
Step 4: Register the Entry Point¶
Add your workflow to pyproject.toml so the CLI can
discover it:
[project.entry-points."attune.workflows"]
doc-gen = "attune.workflows.document_gen:DocumentGenerationWorkflow"
Verify it's registered:
You should see doc-gen in the output. Run it:
The CLI loads your class, instantiates it, calls
execute(), and formats the result -- including cost
savings vs a premium-only baseline.
Step 5: Build Your Own¶
You now know every layer of the system. To build your own workflow:
- Pick your stages. What are the distinct steps?
A code reviewer might use
scan,analyze,report. - Assign tiers. Which stages need Claude Opus (PREMIUM) vs Claude Haiku speed (CHEAP)?
- Implement
run_stage(). Each stage receives the previous stage's output. - Add a SKILL.md. Define triggers and Socratic questions.
- Register in pyproject.toml. One line.
Browse existing workflows for inspiration:
Ideas to try¶
- Chain workflows: lint, then test, then docs, then commit
- Use
/batchfor 50% savings on non-interactive runs - Add custom mixins for shared logic across workflows