Workflow Guide

June 28, 2026 · View on GitHub

This guide explains how to create and customize TAKT workflows.

Workflow Basics

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

File Locations

  • 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

Workflow Categories

To organize the workflow selection UI into categories, configure workflow_categories. See the Configuration Guide for details.

Authoring Workflow Files

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.

Workflow Schema

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
    session_key: shared-coder        # Optional explicit session key for this step
    persona: coder                   # Persona key (references personas map)
    persona_name: coder              # Display name (optional, does not affect provider_routing.personas)
    tags: [implementation, edit]     # Provider routing tags (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: 300000

Steps 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.

persona_name is only a display name. provider_routing.personas in config matches the raw persona key, while provider_routing.tags matches the optional tags array in the order written on the step. Later tags override earlier tags for the same provider/model/provider_options leaf.

session_key is supported on normal agent steps, parallel sub-steps, and loop_monitors.judge. It is not supported on system steps, workflow-call steps, or parallel parent steps because those entries do not own an agent session. Use it when multiple agent steps share a persona but must keep separate sessions, or when different agent steps must intentionally share one session. The effective runtime key is session_key plus the resolved provider suffix, for example shared-coder:claude. When session_key is omitted, TAKT uses the persona key, or the step name when no persona is set. Empty strings and whitespace-only values are rejected during workflow validation.

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.

Available Variables

VariableDescription
{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

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.

Rule Condition Types

TypeSyntaxDescription
Tag-based"condition text"Agent outputs [STEP:N] tag, matched by index
AI judgeai("condition text")AI evaluates the condition against step output
Aggregateall("X") / any("X")Aggregates parallel sub-step results

Special next Values

  • COMPLETE — End workflow successfully
  • ABORT — End workflow with failure

Rule Field: appendix

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.

Step Types

TAKT supports five step types. Pick by the structure your step needs.

Normal Step

A single agent executes the step. This is the default and matches all the earlier examples.

Parallel Step

Sub-steps execute concurrently, and the parent aggregates sub-step matches via all() / any():

  - name: reviewers
    parallel:
      - name: arch-review
        session_key: arch-review
        persona: architecture-reviewer
        policy: review
        knowledge: architecture
        edit: false
        rules:
          - condition: approved
          - condition: needs_fix
        instruction: review-arch
      - name: security-review
        session_key: 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: fix
  • all("X"): true if ALL sub-steps matched condition X
  • any("X"): true if ANY sub-steps matched condition X
  • Sub-step rules define possible outcomes; next is optional (parent handles routing)
  • Parallel sub-steps do not support promotion

Finding Contract parallel retry failure routing

When a workflow defines finding_contract, each parallel parent must declare a deterministic rule for a Finding Manager output that stays semantically invalid after retry. This rule prevents invalid manager output from aborting the workflow or updating the ledger.

Accepted rules, in selection order:

  1. return: need_replan (recommended)
  2. return: needs_fix
  3. Non-AI next: fix

ai("...") rules that point to fix are not selected for this failure path. If none of the accepted rules exists, workflow validation fails before execution.

Arpeggio Step (data-driven batch)

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: COMPLETE

Useful for batch-applying the same operation to many inputs (file lists, issue lists, generated test cases, etc.).

Team Leader Step (dynamic task decomposition)

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
      inspect_tools: [read, glob, grep]
      part_tags: [coding]
      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: review

Useful 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. part_tags sets provider routing tags on generated part steps. When omitted, parts inherit the parent step's tags. Empty and whitespace-only tags are invalid. part_tags is resolved through normal provider_routing.tags, so tag routing takes priority over persona routing from part_persona.

inspect_tools allows only read-only inspection tools (read, glob, grep) during the parent Team Leader task decomposition phase. Invalid tool names fail workflow loading. It does not affect generated child parts; child part tools remain controlled separately by part_allowed_tools. Inspection tools are supported by providers that expose allowedTools, including Claude-family providers and OpenCode. Providers that do not support Team Leader inspection tools fail at runtime with a clear error.

Workflow Call Step (subworkflow)

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: fix

The 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.

Output Contracts (Report Files)

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.md

Step-level Provider Promotion

A 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: true

Entries 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.

Step Options

OptionDefaultDescription
persona-Persona key (references section map) or file path
persona_name-Display name for logs and prompts. It does not affect provider_routing.personas
session_key-Explicit session key for normal agent steps and parallel sub-steps. The resolved provider is appended to the runtime key; empty and whitespace-only values are invalid
requires_user_inputfalseMarks a normal agent step as capable of waiting for user input. System steps, workflow-call steps, and parallel parent steps cannot set it. A step with requires_user_input: true requires interactive mode and a user input handler before the agent runs; otherwise the workflow aborts without executing that agent. The actual wait is triggered only by a matching rule with requires_user_input: true
tags-Ordered provider routing tags matched against provider_routing.tags in config
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_responsetruePass previous step's output to {previous_response}
provider_options.claude.allowed_tools-Claude tool allowlist for the step or workflow
provider_options.claude.base_url-Anthropic-compatible base URL for claude / claude-sdk (see configuration guide)
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.base_url-OpenAI-compatible base URL for Codex SDK constructor options (see configuration guide)
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_commitfalseAllow 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

For normal agent steps, parallel sub-steps, and loop_monitors.judge, model: null explicitly omits the model. This is different from leaving model out: absence continues fallback to applicable lower-priority sources such as routing, workflow, the triggering step for loop monitor judges, and input models, while null stops model resolution at that entry. Providers that require an explicit model still fail validation.

The effective tool list may be narrower than configured. When edit: false, or when a step has output_contracts and does not set edit: true, TAKT removes command/edit tools from provider_options.*.allowed_tools before calling the provider. For Claude-family providers, comma-separated entries are normalized into atomic tool specs first, Bash(...) is judged by the canonical tool name before (, and Bash, Edit, Write, Apply_Patch, and Patch are removed. For OpenCode, lowercase tools such as bash, edit, and write are removed. The same read-only filtering applies to team_leader.part_allowed_tools when the part's effective edit setting is false, such as part_edit: false or inherited edit: false.

Workflow-level Configuration

Top-level workflow fields that control overall execution behavior.

interactive_mode

Default interactive mode used when takt is invoked without arguments. One of assistant (default), passthrough, quiet, persona.

interactive_mode: assistant

workflow_config.provider_options

Workflow-wide provider options. For most provider option leaves, env- or CLI-resolved config values win first; otherwise priority is step provider_options > provider_routing.steps > provider_routing.tags > provider_routing.personas > deprecated persona_providers > workflow_config.provider_options > project .takt/config.yaml > global ~/.takt/config.yaml. For base_url, step and workflow routing leaves stay above TAKT env overrides, and the same step-to-global order is followed before TAKT_PROVIDER_OPTIONS_CODEX_BASE_URL or TAKT_PROVIDER_OPTIONS_CLAUDE_BASE_URL. Workflow YAML and project .takt/config.yaml may only set base_url to loopback hosts; use global config or TAKT env for non-loopback endpoints.

workflow_config:
  provider_options:
    codex:
      network_access: true
    claude:
      sandbox:
        allow_unsandboxed_commands: true

provider_options can reference a shared YAML preset by name. Names are resolved first-match from .takt/provider-options, ~/.takt/provider-options, then builtins/{lang}/provider-options. For repertoire packages, package-local provider-options is checked first, and @owner/repo/name resolves a preset from that package. The referenced file is the base, and inline values override matching leaves.

provider_options.extends fails fast as a configuration error when a preset or path cannot be resolved, a scoped ref points to an unavailable repertoire package, the target YAML is invalid or is not a provider-options object, the extends chain is circular, or the removed $ref key is used. Relative paths are resolved from the workflow file and must stay inside the workflow directory after symlink resolution; absolute paths and paths whose real target escapes that directory are rejected.

workflow_config:
  provider_options:
    extends: review-readonly

steps:
  - name: implement
    provider_options:
      extends: edit
      opencode:
        allowed_tools: [read, grep, bash]

Relative file paths from the workflow file are still supported for workflow-local shared files.

Example shared file:

claude:
  allowed_tools: [Read, Glob, Grep, Bash, WebSearch, WebFetch]
opencode:
  allowed_tools: [read, glob, grep, bash, websearch, webfetch]

workflow_config.runtime

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]

loop_monitors

Detect cyclic patterns between steps (e.g. reviewfixreview repeating indefinitely) and let an AI judge whether progress is being made:

loop_monitors:
  - cycle: [review, fix]
    threshold: 3
    judge:
      session_key: loop-supervisor
      persona: supervisor
      instruction: "Evaluate if the fix loop is making progress..."
      rules:
        - condition: "Progress is being made"
          next: fix
        - condition: "No progress"
          next: ABORT

loop_monitors.judge supports provider, model, and provider_options with the same provider/model validation as agent steps. When provider is omitted, the judge inherits the triggering step provider and model. When provider is set without model, the inherited model is cleared. Use model: null to explicitly use a provider or CLI default even when the triggering step has a resolved model.

loop_monitors.judge.session_key follows the same provider-suffixed runtime key behavior as step session_key. Set it when separate monitors use the same persona but should not resume the same judge session.

rate_limit_fallback

When 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.5

Attempts 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.

subworkflow

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: true

Examples

Simple Implementation Workflow

name: 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.

Workflow with Review

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.

Passing Data Between Steps

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}

Best Practices

  1. Keep iterations reasonable — 10-30 is typical for development workflows
  2. Use edit: false for review steps — Prevent reviewers from modifying code
  3. Use descriptive step names — Makes logs easier to read
  4. Test workflows incrementally — Start simple, add complexity
  5. Use /eject to customize — Copy a builtin workflow as a starting point rather than writing from scratch