This guide explains how to create and customize TAKT workflows.
A workflow is a YAML file that defines a sequence of steps executed by AI agents. Each step specifies:
- Which persona to use
- What instructions to give
- Rules for routing to the next step
- Builtin workflows are embedded in the npm package (
dist/resources/) ~/.takt/workflows/— User workflows (override builtins with the same name)- Use
takt eject <workflow>to copy a builtin to~/.takt/workflows/for customization
To organize the workflow selection UI into categories, configure workflow_categories.
See the Configuration Guide for details.
Use takt workflow init <name> to create a new custom workflow scaffold in .takt/workflows/ (or ~/.takt/workflows/ with --global).
--template minimal: generates a self-contained scaffold with generic step routing--template faceted: generates a workflow plus local persona/instruction facet files
After editing the generated files, run takt workflow doctor <name or path> to validate references, routing targets, and unreachable steps before executing the workflow.
name: my-workflow
description: Optional description
max_steps: 10
initial_step: first-step # Optional, defaults to the first step
# Section maps (key → file path relative to workflow YAML directory)
personas:
planner: ../facets/personas/planner.md
coder: ../facets/personas/coder.md
reviewer: ../facets/personas/architecture-reviewer.md
policies:
coding: ../facets/policies/coding.md
review: ../facets/policies/review.md
knowledge:
architecture: ../facets/knowledge/architecture.md
instructions:
plan: ../facets/instructions/plan.md
implement: ../facets/instructions/implement.md
report_formats:
plan: ../facets/output-contracts/plan.md
steps:
- name: step-name
persona: coder # Persona key (references personas map)
persona_name: coder # Display name (optional)
policy: coding # Policy key (single or array)
knowledge: architecture # Knowledge key (single or array)
instruction: implement # Instruction key (references instructions map)
edit: true # Whether the step can edit files
required_permission_mode: edit # Minimum permission: readonly, edit, or full
provider_options:
claude:
allowed_tools: # Optional Claude tool allowlist
- Read
- Glob
- Grep
- Edit
- Write
- Bash
rules:
- condition: "Implementation complete"
next: next-step
- condition: "Cannot proceed"
next: ABORT
instruction: | # Inline instructions
Your instructions here with {variables}
output_contracts: # Report file configuration
report:
- name: 00-plan.md
format: plan # References report_formats map
quality_gates: # Agent-step quality gates for step completion
- "Review the implementation before finishing" # AI directive
- type: command # Machine-executed command gate
name: quality-check
command: "./.takt/quality-gates/check.sh"
cwd: "."
timeout_ms: 300000Steps reference section maps by key name (e.g., persona: coder), not by file path. Paths in section maps are resolved relative to the workflow YAML file's directory.
String quality_gates remain AI completion directives and are injected into agent step prompts. type: command gates run inside the worktree after an agent step completes and pass only when the command exits with code 0. Workflow YAML command gates require workflow_command_gates.custom_scripts: true in config. On failure, TAKT feeds command metadata, cwd, exit code or timeout/output-limit details, the output log path, and bounded sanitized stdout/stderr back into the same agent step. Raw stdout and stderr are also written to the local output log. system and workflow_call steps do not accept quality_gates.
| Variable | Description |
|---|---|
{task} |
Original user request (auto-injected if not in template) |
{iteration} |
Workflow-wide turn count (total steps executed) |
{max_steps} |
Maximum steps allowed |
{step_iteration} |
Per-step iteration count (how many times THIS step has run) |
{previous_response} |
Previous step's output (auto-injected if not in template) |
{user_inputs} |
Additional user inputs during workflow (auto-injected if not in template) |
{report_dir} |
Report directory path (e.g., .takt/runs/20250126-143052-task-summary/reports) |
{report:filename} |
Inline the content of {report_dir}/filename |
Note:
{task},{previous_response}, and{user_inputs}are auto-injected into instructions. You only need explicit placeholders if you want to control their position in the template.
Rules define how each step routes to the next step. The instruction builder auto-injects status output rules so agents know what tags to output.
rules:
- condition: "Implementation complete"
next: review
- condition: "Cannot proceed"
next: ABORT
appendix: |
Explain what is blocking progress.| Type | Syntax | Description |
|---|---|---|
| Tag-based | "condition text" |
Agent outputs [STEP:N] tag, matched by index |
| AI judge | ai("condition text") |
AI evaluates the condition against step output |
| Aggregate | all("X") / any("X") |
Aggregates parallel sub-step results |
COMPLETE— End workflow successfullyABORT— End workflow with failure
The optional appendix field provides a template for additional AI output when that rule is matched. Useful for structured error reporting or requesting specific information.
TAKT supports five step types. Pick by the structure your step needs.
A single agent executes the step. This is the default and matches all the earlier examples.
Sub-steps execute concurrently, and the parent aggregates sub-step matches via all() / any():
- name: reviewers
parallel:
- name: arch-review
persona: architecture-reviewer
policy: review
knowledge: architecture
edit: false
rules:
- condition: approved
- condition: needs_fix
instruction: review-arch
- name: security-review
persona: security-reviewer
policy: review
edit: false
rules:
- condition: approved
- condition: needs_fix
instruction: review-security
rules:
- condition: all("approved")
next: COMPLETE
- condition: any("needs_fix")
next: fixall("X"): true if ALL sub-steps matched condition Xany("X"): true if ANY sub-steps matched condition X- Sub-step
rulesdefine possible outcomes;nextis optional (parent handles routing) - Parallel sub-steps do not support
promotion
Iterate over a data source (CSV, JSON, etc.) and apply the same step template to each row with bounded concurrency:
- name: batch-process
persona: coder
arpeggio:
source: csv
source_path: ./data/items.csv
batch_size: 5
concurrency: 3
template: ./templates/process.txt
max_retries: 2
retry_delay_ms: 1000
merge:
strategy: concat
separator: "\n---\n"
output_path: ./output/result.txt
rules:
- condition: "Processing complete"
next: COMPLETEUseful for batch-applying the same operation to many inputs (file lists, issue lists, generated test cases, etc.).
The agent acts as a leader: it decomposes the task into independent sub-parts at runtime and dispatches each part to a worker agent:
- name: implement
team_leader:
max_concurrency: 2
max_total_parts: 8
timeout_ms: 600000
part_persona: coder
part_edit: true
part_permission_mode: edit
part_allowed_tools: [Read, Glob, Grep, Edit, Write, Bash]
instruction: |
Decompose this task into independent subtasks.
rules:
- condition: "All parts completed"
next: reviewUseful for breaking one large task into independent units that can run in parallel without you having to know the unit boundaries up-front.
max_concurrency controls how many parts run at the same time. max_total_parts controls the total number of parts the leader may plan across the workflow step, up to 20. The older max_parts key is still accepted as the compatibility name for max_concurrency.
A step invokes another workflow by name. The child workflow runs in the same run; its outcome routes back via the parent's rules:
- name: peer-review
workflow_call:
workflow: peer-review
params:
impl_knowledge: cqrs-es
rules:
- condition: approved
next: COMPLETE
- condition: needs_fix
next: fixThe called workflow can declare subworkflow.params so the parent passes values (e.g. impl_knowledge or fix_knowledge) to customize the child without duplicating step definitions. See Workflow-level Configuration for subworkflow declaration.
Steps can generate report files in the report directory:
# Single report with format specification (references report_formats map)
output_contracts:
report:
- name: 00-plan.md
format: plan
# Single report with inline format
output_contracts:
report:
- name: 00-plan.md
format: |
# Plan
...
# Multiple report files with labels
output_contracts:
report:
- Scope: 01-scope.md
- Decisions: 02-decisions.mdA step can escalate its provider, model, or provider_options based on per-step execution count or AI judgment. Each entry in promotion requires at least one of at: <count> (matches from the Nth execution of this step onward) or condition: ai("..."), plus one or more override targets:
steps:
- name: review
persona: reviewer
promotion:
- at: 3
model: opus
- condition: ai("The reviewer keeps rejecting and progress has stalled")
provider: claude
model: opus
- at: 5
provider:
type: codex
model: gpt-5.5
network_access: trueEntries are evaluated in declaration order; the last matching entry wins. Promotion is the highest-priority source in provider / model / provider_options resolution (above step-level provider / model).
Promotion is not supported on parallel sub-steps.
| Option | Default | Description |
|---|---|---|
persona |
- | Persona key (references section map) or file path |
policy |
- | Policy key or array of keys |
knowledge |
- | Knowledge key or array of keys |
instruction |
- | Instruction key (references section map) |
edit |
- | Whether the step can edit project files (true/false) |
pass_previous_response |
true |
Pass previous step's output to {previous_response} |
provider_options.claude.allowed_tools |
- | Claude tool allowlist for the step or workflow |
provider_options.claude.effort |
- | Claude reasoning effort: low, medium, high, xhigh, max (xhigh requires Opus 4.7) |
provider_options.opencode.allowed_tools |
- | OpenCode tool allowlist. Tool names are lowercase, for example read, glob, grep, bash, websearch, webfetch |
provider_options.opencode.variant |
- | OpenCode model variant, passed through as a provider/model-specific string |
provider_options.codex.network_access |
- | Allow Codex sandbox to access the network (see configuration guide) |
provider_options.claude.sandbox.allow_unsandboxed_commands |
- | Run Claude Bash outside the macOS Seatbelt sandbox (see configuration guide) |
provider_options.kiro.agent |
- | Kiro CLI custom agent name passed as kiro-cli chat --agent. Steps without it use the Kiro CLI default agent |
provider |
- | Override provider for this step (claude, claude-sdk, claude-terminal, codex, opencode, cursor, copilot, kiro, or mock) |
model |
- | Override model for this step |
promotion |
- | Per-execution provider/model/options escalation (see Step-level Provider Promotion) |
mcp_servers |
- | Per-step MCP server configuration (stdio / HTTP / SSE) |
allow_git_commit |
false |
Allow git add / commit / push in step instructions. Default prohibits these so each PR represents one task |
required_permission_mode |
- | Required minimum permission mode: readonly, edit, or full |
output_contracts |
- | Report file configuration (name, format) |
quality_gates |
- | Agent-step completion gates. String entries are AI instructions; type: command entries are executed after step completion and feed failures back into the same agent step |
Top-level workflow fields that control overall execution behavior.
Default interactive mode used when takt is invoked without arguments. One of assistant (default), passthrough, quiet, persona.
interactive_mode: assistantWorkflow-wide provider options. Merged with step / persona / project / global options. Step-level options take priority for the same leaf.
workflow_config:
provider_options:
codex:
network_access: true
claude:
sandbox:
allow_unsandboxed_commands: trueprovider_options can reference a shared YAML file relative to the workflow file. The referenced file is the base, and inline values override matching leaves.
workflow_config:
provider_options:
$ref: provider-options/review-readonly.yaml
steps:
- name: implement
provider_options:
$ref: provider-options/edit.yaml
opencode:
allowed_tools: [read, grep, bash]Example shared file:
claude:
allowed_tools: [Read, Glob, Grep, Bash, WebSearch, WebFetch]
opencode:
allowed_tools: [read, glob, grep, bash, websearch, webfetch]Runtime prepare scripts that run before workflow execution. Builtin presets node / gradle are always allowed. Custom script paths require workflow_runtime_prepare.custom_scripts: true in config.
workflow_config:
runtime:
prepare: [node, gradle, ./custom-script.sh]Detect cyclic patterns between steps (e.g. review → fix → review repeating indefinitely) and let an AI judge whether progress is being made:
loop_monitors:
- cycle: [review, fix]
threshold: 3
judge:
persona: supervisor
instruction: "Evaluate if the fix loop is making progress..."
rules:
- condition: "Progress is being made"
next: fix
- condition: "No progress"
next: ABORTWhen a Claude / Codex / OpenCode rate limit is observed during a step, continue the run by re-executing the interrupted step on the next provider in the chain. The new session receives a fallback notice instruction so the AI can rebuild context from existing reports on disk.
rate_limit_fallback:
switch_chain:
- provider: claude-sdk
model: opus
- provider: codex
model: gpt-5.5Attempts within a single fallback chain are tracked on workflow state and reset on a successful step completion. The same field is also accepted in ~/.takt/config.yaml and .takt/config.yaml for project-wide / user-wide defaults.
Declare a workflow as a subworkflow that accepts parameters from a parent's workflow_call. Subworkflows are not selectable from the workflow UI.
subworkflow:
visibility: internal
params:
- name: impl_knowledge
required: truename: simple-impl
max_steps: 5
personas:
coder: ../facets/personas/coder.md
steps:
- name: implement
persona: coder
edit: true
required_permission_mode: edit
provider_options:
claude:
allowed_tools: [Read, Glob, Grep, Edit, Write, Bash, WebSearch, WebFetch]
rules:
- condition: Implementation complete
next: COMPLETE
- condition: Cannot proceed
next: ABORT
instruction: |
Implement the requested changes.name: with-review
max_steps: 10
personas:
coder: ../facets/personas/coder.md
reviewer: ../facets/personas/architecture-reviewer.md
steps:
- name: implement
persona: coder
edit: true
required_permission_mode: edit
provider_options:
claude:
allowed_tools: [Read, Glob, Grep, Edit, Write, Bash, WebSearch, WebFetch]
rules:
- condition: Implementation complete
next: review
- condition: Cannot proceed
next: ABORT
instruction: |
Implement the requested changes.
- name: review
persona: reviewer
edit: false
provider_options:
claude:
allowed_tools: [Read, Glob, Grep, WebSearch, WebFetch]
rules:
- condition: Approved
next: COMPLETE
- condition: Needs fix
next: implement
instruction: |
Review the implementation for code quality and best practices.personas:
planner: ../facets/personas/planner.md
coder: ../facets/personas/coder.md
steps:
- name: analyze
persona: planner
edit: false
provider_options:
claude:
allowed_tools: [Read, Glob, Grep, WebSearch, WebFetch]
rules:
- condition: Analysis complete
next: implement
instruction: |
Analyze this request and create a plan.
- name: implement
persona: coder
edit: true
pass_previous_response: true
required_permission_mode: edit
provider_options:
claude:
allowed_tools: [Read, Glob, Grep, Edit, Write, Bash, WebSearch, WebFetch]
rules:
- condition: Implementation complete
next: COMPLETE
instruction: |
Implement based on this analysis:
{previous_response}- Keep iterations reasonable — 10-30 is typical for development workflows
- Use
edit: falsefor review steps — Prevent reviewers from modifying code - Use descriptive step names — Makes logs easier to read
- Test workflows incrementally — Start simple, add complexity
- Use
/ejectto customize — Copy a builtin workflow as a starting point rather than writing from scratch