Symphony watches your issue tracker and autonomously works tickets end-to-end using Claude. Each ticket gets an isolated workspace, a fresh clone of the target repository, and a Claude agent that implements, tests, opens a PR, and moves the ticket through your workflow — unattended.
- Symphony polls your tracker (Jira or Linear) for tickets in configured active states.
- For each eligible ticket it creates an isolated workspace directory.
- It clones the target repository into that workspace (
hooks.after_create). - It launches a Claude agent with the ticket context as the prompt.
- The agent works until the ticket reaches a terminal state or the turn limit is hit.
- When done, Symphony cleans up the workspace.
One Symphony process handles one workflow file (one tracker project + one repository). Run multiple processes in parallel to cover multiple repos.
Install mise to manage Elixir/Erlang versions:
brew install miseThen inside this repo:
cd elixir
mise trust
mise installInstall and authenticate Claude Code:
npm install -g @anthropic-ai/claude-code
claude loginRequired for the agent to open PRs and interact with GitHub:
brew install gh
gh auth loginClone and build the Symphony binary:
git clone https://github.com/wagnersza/symphony
cd symphony/elixir
mise exec -- mix setup
mise exec -- mix buildThis produces elixir/bin/symphony.
Create a .env file at the root of this repo (it is gitignored):
JIRA_API_TOKEN=your-jira-api-token
JIRA_EMAIL=you@your-company.com
JIRA_SITE_URL=https://your-org.atlassian.net
JIRA_PROJECT_KEY=YOUR_PROJECT_KEYGetting a Jira API token:
- Go to Atlassian API tokens
- Click Create API token, give it a name (e.g.
symphony), and copy the value
Board statuses Symphony expects by default:
| Status | Role |
|---|---|
To Do |
Queued — agent picks up and moves to In Progress |
In Progress |
Agent is actively working |
Doing |
Treated the same as In Progress |
UNDER REVIEW |
PR submitted — waiting for human review |
Done |
Terminal — not touched |
Backlog |
Terminal — not touched |
You can override active_states and terminal_states in your workflow file to match your board.
Add to your .env file:
LINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxxxxxx
LINEAR_PROJECT_SLUG=your-project-slugGetting a Linear API key:
- Go to Settings → API → Personal API keys
- Click Create key, give it a name (e.g.
symphony), and copy the value
Finding your project slug:
Open a Linear issue URL — it looks like https://linear.app/your-org/issue/PROJ-123. The slug is the segment after your org name (e.g. your-org). You can also use the team identifier shown on your Linear workspace settings.
Default Linear states Symphony uses:
| Status | Role |
|---|---|
Todo |
Queued |
In Progress |
Agent is actively working |
Done |
Terminal |
Cancelled / Canceled |
Terminal |
A workflow file is a Markdown file with a YAML front matter block that configures the tracker, workspace, agent limits, and hooks — followed by a Jinja2 prompt template that is sent to the agent for each ticket.
Available template variables: {{ issue.identifier }}, {{ issue.title }}, {{ issue.state }}, {{ issue.description }}, {{ issue.labels }}, {{ issue.url }}, {{ attempt }}.
---
tracker:
kind: jira
jira:
site_url: "$JIRA_SITE_URL"
email: "$JIRA_EMAIL"
api_token: "$JIRA_API_TOKEN"
project_key: "$JIRA_PROJECT_KEY"
active_states:
- To Do
- In Progress
terminal_states:
- Done
- Backlog
polling:
interval_ms: 30000
workspace:
root: ~/code/symphony-workspaces/myrepo
hooks:
after_create: |
git clone --depth 1 https://github.com/your-org/your-repo .
npm install
agent:
max_concurrent_agents: 3
max_turns: 20
codex:
command: claude -p --dangerously-skip-permissions
approval_policy: never
thread_sandbox: workspace-write
turn_sandbox_policy:
type: workspaceWrite
---
You are working on Jira ticket `{{ issue.identifier }}`: {{ issue.title }}.
...See elixir/WORKFLOW.jira.example.md for a complete Jira starter template.
---
tracker:
kind: linear
linear:
api_key: "$LINEAR_API_KEY"
project_slug: "$LINEAR_PROJECT_SLUG"
active_states:
- Todo
- In Progress
terminal_states:
- Done
- Cancelled
polling:
interval_ms: 30000
workspace:
root: ~/code/symphony-workspaces/myrepo
hooks:
after_create: |
git clone --depth 1 https://github.com/your-org/your-repo .
npm install
agent:
max_concurrent_agents: 3
max_turns: 20
codex:
command: claude -p --dangerously-skip-permissions
approval_policy: never
thread_sandbox: workspace-write
turn_sandbox_policy:
type: workspaceWrite
---
You are working on Linear issue `{{ issue.identifier }}`: {{ issue.title }}.
...See elixir/WORKFLOW.linear.example.md for a complete Linear starter template.
Full worked examples with hooks and agent instructions are in elixir/workflows/.
agent-skills is a collection of production-grade engineering skills for AI coding agents. Each skill encodes the workflow a senior engineer follows for a specific phase of development — speccing, planning, implementing, testing, reviewing, and shipping. Read more on Addy Osmani's blog.
Skills cover the full lifecycle:
| Phase | Skills used |
|---|---|
| Define | spec-driven-development |
| Plan | planning-and-task-breakdown |
| Build | incremental-implementation, test-driven-development, context-engineering |
| Review | code-review-and-quality, security-and-hardening |
| Ship | git-workflow-and-versioning, ci-cd-and-automation |
/plugin marketplace add addyosmani/agent-skills
/plugin install agent-skills@addy-agent-skillsIf you don't have GitHub SSH keys configured, use the HTTPS URL:
/plugin marketplace add https://github.com/addyosmani/agent-skills.git /plugin install agent-skills@addy-agent-skills
Symphony ships its own Claude Code plugin with the Jira and Linear API
recipes that the planning and development workflows depend on
(skills/trackers/jira/SKILL.md, skills/trackers/linear/SKILL.md).
/plugin marketplace add wagnersza/symphony
/plugin install symphony-trackers@symphony
If you don't have GitHub SSH keys configured, use the HTTPS URL:
/plugin marketplace add https://github.com/wagnersza/symphony.git /plugin install symphony-trackers@symphony
If your workflow uses Codex (codex.command: codex app-server) instead of
Claude, the plugin install above does not apply. Stage the skills directly
into the workspace via the workflow's after_create hook:
hooks:
after_create: |
git clone --depth 1 https://github.com/your-org/your-repo .
# ...other setup...
cp -r /path/to/symphony/skills .codex/skillsReplace /path/to/symphony/ with the absolute path to your local Symphony
checkout. The workspace will resolve .codex/skills/trackers/jira/SKILL.md
the same way Claude resolves the plugin path.
Rather than one workflow that jumps straight from ticket to PR, you can split the process into two dedicated workflows with a human review gate between them:
Backlog ──→ To Plan ──→ Review Plan ──→ Build ──→ In Progress ──→ In Review ──→ Done
▲ │ ▲
│ │ (human rejects plan) │
└─────────────┘ │
│
[planning.md] [development.md] ─────┘
spec-driven-development incremental-implementation
+ planning-and-task-breakdown + test-driven-development
+ git-workflow-and-versioning
+ code-review-and-quality
State responsibilities:
| State | Owner | What happens |
|---|---|---|
Backlog |
human | Idea queue — no agents touch it |
To Plan |
planning agent | Reads ticket + attachments, writes spec + task breakdown, updates Summary/Description |
Review Plan |
human | Reviews the plan; accept → Build, reject → comment + back to To Plan |
Build |
development agent | Creates one Jira subtask per planned task, then transitions parent to In Progress |
In Progress |
development agent | Implements subtasks (parallel where dependencies allow), each subtask flips through In Progress → Done |
In Review |
human | Reviews the PR |
Done |
— | Terminal |
Cancelled |
— | Terminal |
Why two workflows?
- The planning agent reads code but writes no code — it can run on cheaper/faster settings.
- The development agent gets a human-reviewed spec and ordered task list before it writes a single line.
- The plan review gate catches misunderstandings before any implementation effort is spent.
- Each phase is independently retryable without losing the other's work.
- You can run multiple development agents in parallel against tickets that are already
Build.
Planning behaviour highlights:
- Reads all ticket comments and image attachments (mockups, screenshots, diagrams) and treats them as first-class requirements.
- Updates the ticket Summary and Description in place when the original framing is vague or wrong. The Description is reserved for the durable problem statement; all progress notes go in comments.
- Posts the spec & plan as a comment when it fits within Jira's 32,767-character comment limit; otherwise attaches it as
spec-and-plan.mdand adds a short pointer comment. - When the human rejects the plan and moves the ticket back to
To Plan, the agent re-runs in revision mode — reads the feedback comments, edits the spec in place, and addsRevision Notesdocumenting how each feedback item was resolved.
Development behaviour highlights:
- On entering
Build, materialises every planned task as a Jira subtask of the parent before writing any code. The subtask description carries the task's acceptance criteria, verification, files, dependencies, and size. - Only after all subtasks exist does it transition the parent to
In Progressand start coding. - Honours
Depends on:markers from the plan to parallelise safely — only unblocked subtasks with non-overlapping files run concurrently. - A subtask is moved to
In Progressonly while it's actively being worked. The parent staysIn Progressfor the duration of the build phase. - All progress, decisions, and blockers are recorded as comments on the parent or subtasks — never written into Descriptions.
- When all subtasks are
Doneand self-review passes, opens a PR with thesymphonylabel and transitions the parent toIn Review.
Board setup:
Configure your tracker workflow with these statuses: Backlog, To Plan, Review Plan, Build, In Progress, In Review, Done, Cancelled. Each agent's active_states/terminal_states in the workflow file already match this scheme.
Running both workflows:
source .env
# Terminal 1 — planning agent (picks up tickets in `To Plan`)
./elixir/bin/symphony elixir/workflows/planning.md --i-understand-that-this-will-be-running-without-the-usual-guardrails
# Terminal 2 — development agent (picks up tickets in `Build`)
./elixir/bin/symphony elixir/workflows/development.md --i-understand-that-this-will-be-running-without-the-usual-guardrailsThe workflow files are in elixir/workflows/planning.md and elixir/workflows/development.md. Both use Jira by default — swap the tracker block for Linear if needed (see the Linear example above).
Load your credentials and start Symphony with a workflow file:
source .env
./elixir/bin/symphony my-workflow.md --i-understand-that-this-will-be-running-without-the-usual-guardrailsTo run multiple repos in parallel, open one terminal per workflow file:
# Terminal 1 — backend
source .env && ./elixir/bin/symphony elixir/workflows/api.md --i-understand-that-this-will-be-running-without-the-usual-guardrails
# Terminal 2 — frontend
source .env && ./elixir/bin/symphony elixir/workflows/frontend.md --i-understand-that-this-will-be-running-without-the-usual-guardrails# Enable the web dashboard at http://localhost:4000
./elixir/bin/symphony my-workflow.md --i-understand-that-this-will-be-running-without-the-usual-guardrails --port 4000
# Write logs to a custom directory
./elixir/bin/symphony my-workflow.md --i-understand-that-this-will-be-running-without-the-usual-guardrails --logs-root ~/logs/symphony| Field | Default | Description |
|---|---|---|
tracker.kind |
— | jira or linear |
tracker.active_states |
tracker-specific | Ticket states that trigger agent dispatch |
tracker.terminal_states |
tracker-specific | Ticket states that stop the agent |
tracker.assignee |
— | Optional — limit to one assignee (email or account ID) |
polling.interval_ms |
30000 |
How often to poll the tracker |
workspace.root |
system tmp | Directory where workspaces are created |
hooks.after_create |
— | Shell script run after workspace is created (clone repo here) |
hooks.before_remove |
— | Shell script run before workspace is deleted |
agent.max_concurrent_agents |
10 |
Max parallel agents for this workflow |
agent.max_turns |
20 |
Max agent turns per ticket attempt |
codex.command |
codex app-server |
Command used to launch the agent |
codex.approval_policy |
reject |
never to run fully unattended |
To use Claude instead of Codex:
- Set
agent.backend: :claudein your workflow file. - Install the Node wrapper deps:
cd elixir/priv/claude_agent && npm ci. - Ensure your
~/.claude/is populated (Claude CLI logged in).
See docs/superpowers/specs/2026-05-07-claude-agent-backend-design.md for protocol details.