Documentation Index Fetch the complete documentation index at: https://mintlify.com/openai/symphony/llms.txt
Use this file to discover all available pages before exploring further.
The WORKFLOW.md file is Symphony’s core configuration artifact. It combines YAML frontmatter for runtime settings with a Liquid-templated Markdown body that becomes the agent’s instruction prompt.
File Structure
A WORKFLOW.md file has three parts:
Opening --- delimiter
YAML frontmatter (configuration)
Closing --- delimiter
Markdown body (prompt template)
---
tracker :
kind : linear
project_slug : "symphony-0c79b11b75ea"
workspace :
root : ~/code/symphony-workspaces
hooks :
after_create : |
git clone https://github.com/openai/symphony .
agent :
max_concurrent_agents : 10
codex :
command : codex app-server
---
You are working on a Linear ticket `{{ issue.identifier }}`
Title: {{ issue.title }}
Description: {{ issue.description }}
YAML Frontmatter
The frontmatter contains all runtime configuration. See Configuration for the complete reference.
Parsing Rules
Must be valid YAML
Top-level keys: tracker, polling, workspace, agent, codex, hooks, observability, server
Empty frontmatter (---\n---) is valid and uses all defaults
Invalid YAML halts Symphony startup until fixed
Symphony validates the workflow file on startup and when it detects changes. Syntax errors will prevent the orchestrator from scheduling new work.
Markdown Body: Prompt Template
The Markdown body after the closing --- becomes the agent prompt. Symphony uses Liquid templating via the Solid library to inject issue data.
Template Rendering
From lib/symphony_elixir/prompt_builder.ex:18-24:
template
|> Solid . render! (
%{
"attempt" => Keyword . get (opts, :attempt ),
"issue" => issue |> Map . from_struct () |> to_solid_map ()
},
@render_opts
)
Symphony renders the template with:
attempt: Retry counter (nil for first run, integer for retries)
issue: Full Linear issue struct as a map
Available Variables
Retry attempt number. nil on first run, 1+ on retries. {% if attempt %}
This is retry attempt # {{ attempt }}
{% endif %}
Linear issue ID (e.g., "MT-123"). Working on {{ issue . identifier }}
Issue body/description. May be empty. {% if issue . description %}
{{ issue . description }}
{% else %}
No description provided.
{% endif %}
Current Linear state name (e.g., "In Progress", "Done"). Current status: {{ issue . state }}
Direct Linear URL to the issue.
List of label names (lowercased). Labels: {{ issue . labels }}
Git branch name associated with the issue.
Linear user ID of the assignee.
Array of blocking issues. Each blocker has id, identifier, and state. {% if issue . blocked_by %}
Blocked by:
{% for blocker in issue . blocked_by %}
- {{ blocker . identifier }} ( {{ blocker . state }} )
{% endfor %}
{% endif %}
Issue creation timestamp.
Example: Production Workflow
Symphony’s own WORKFLOW.md (from elixir/WORKFLOW.md:1-48):
Frontmatter
Prompt Template
tracker :
kind : linear
project_slug : "symphony-0c79b11b75ea"
active_states :
- Todo
- In Progress
- Merging
- Rework
terminal_states :
- Closed
- Cancelled
- Duplicate
- Done
polling :
interval_ms : 5000
workspace :
root : ~/code/symphony-workspaces
hooks :
after_create : |
git clone --depth 1 https://github.com/openai/symphony .
if command -v mise >/dev/null 2>&1; then
cd elixir && mise trust && mise exec -- mix deps.get
fi
agent :
max_concurrent_agents : 10
max_turns : 20
codex :
command : codex --model gpt-5.3-codex app-server
approval_policy : never
thread_sandbox : workspace-write
turn_sandbox_policy :
type : workspaceWrite
Template Rendering Mode
From lib/symphony_elixir/prompt_builder.ex:8:
@render_opts [ strict_variables: true , strict_filters: true ]
Symphony uses strict mode :
Undefined variables cause template rendering to fail
Typos in {{ issue.tittle }} will raise errors
Unknown Liquid filters will fail
This prevents silent failures from typos in production prompts.
Default Prompt
If the Markdown body is empty or only whitespace, Symphony uses this default (from lib/symphony_elixir/config.ex:12-24):
You are working on a Linear issue.
Identifier: {{ issue . identifier }}
Title: {{ issue . title }}
Body:
{% if issue . description %}
{{ issue . description }}
{% else %}
No description provided.
{% endif %}
Workflow File Reloading
Symphony watches WORKFLOW.md for changes:
Changes trigger validation
Invalid changes halt new agent scheduling
Running agents continue with their original configuration
Fix the file to resume scheduling
During development with Elixir hot code reloading, you can update the workflow file without restarting the service.
Common Patterns
Retry Awareness
{% if attempt %}
## Continuation (Attempt # {{ attempt }} )
This workspace has prior state. Resume from current progress.
Do not restart from scratch.
{% else %}
## Initial Run
Fresh workspace. Begin with investigation.
{% endif %}
State-Specific Instructions
{% if issue . state == "Rework" %}
Reviewer requested changes. Address all feedback before re-submitting.
{% elsif issue . state == "Merging" %}
Run the land skill. Do not call gh pr merge directly.
{% endif %}
Blocker Handling
{% if issue . blocked_by %}
## Blockers
This issue is blocked by:
{% for blocker in issue . blocked_by %}
- {{ blocker . identifier }} : {{ blocker . state }}
{% endfor %}
Do not proceed until blockers are resolved.
{% endif %}
Label-Based Routing
{% if issue . labels contains "bug" %}
This is a bug fix. Start by reproducing the issue.
{% elsif issue . labels contains "feature" %}
This is a new feature. Begin with design validation.
{% endif %}
Validation
To validate your workflow file syntax:
iex > SymphonyElixir . Workflow . load ( "./WORKFLOW.md" )
{ :ok , %{ config: %{ .. .}, prompt: "..." , prompt_template: "..." }}
Errors will be returned with details:
{ :error , { :workflow_parse_error , reason}}