Skip to content

broomva/symphony

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tags type status area aliases created
symphony
reference
active
project
README
2026-03-06

Symphony

A Rust implementation of the Symphony orchestration spec by OpenAI. For vault navigation see [[docs/Symphony Index|Symphony Index]]. For the canonical spec see [[SPEC]].

A Rust-based orchestration service that polls an issue tracker (Linear), creates isolated per-issue workspaces, and runs coding agent sessions automatically.

Symphony turns your issue backlog into autonomous coding work — it watches for "Todo" issues, clones your repo into a sandboxed workspace, runs a coding agent (like Claude Code), and manages retries, concurrency, and lifecycle hooks.

How It Works

Linear (Todo issues)
       │
       ▼
   ┌────────────────────────┐
   │  Symphony Scheduler    │  ← polls every N seconds
   │  ┌──────────────────┐  │
   │  │ Fetch candidates │  │
   │  │ Select & sort    │  │
   │  │ Dispatch workers │  │
   │  └──────────────────┘  │
   └────────────┬───────────┘
                │
       ┌────────┴────────┐
       ▼                 ▼
  ┌──────────┐     ┌──────────┐
  │ Worker 1 │     │ Worker N │    ← one per issue
  │ workspace│     │ workspace│
  │ + agent  │     │ + agent  │
  └──────────┘     └──────────┘
                │
                ▼
   ┌─────────────────────┐
   │  HTTP Dashboard     │  ← live state at :8080
   │  /api/v1/state      │
   │  /api/v1/refresh    │
   └─────────────────────┘

Poll loop: Fetches active issues from Linear → filters by project & state → dispatches up to max_concurrent_agents workers.

Per-issue worker: Creates workspace directory → runs lifecycle hooks (clone repo, rebase, etc.) → renders prompt template with issue data → launches coding agent → runs post-hooks (commit, etc.).

Reconciliation: On each tick, refreshes running issue states from Linear. If an issue moves to a terminal state (Done/Canceled), the worker is cleaned up.

Quick Start

Prerequisites

  • Rust 1.85+ (edition 2024)
  • A Linear API key
  • A coding agent CLI (e.g., claude)
  • gh CLI (if using GitHub hooks)

Build

make build    # release build → target/release/symphony

Configure

Create a WORKFLOW.md in your project root. This file has YAML frontmatter (config) and a Liquid template body (the prompt sent to the agent):

---
tracker:
  kind: linear
  api_key: $LINEAR_API_KEY          # env var expansion
  project_slug: 71c211385593        # Linear project slug ID
  active_states:
    - Todo
  terminal_states:
    - Done
    - Canceled
    - Duplicate
polling:
  interval_ms: 30000                # poll every 30s
workspace:
  root: ~/my-workspaces/project     # per-issue dirs created here
hooks:
  after_create: |                   # runs once when workspace is first created
    gh repo clone MyOrg/my-repo . -- --depth 50
    git checkout -b "$SYMPHONY_ISSUE_ID"
  before_run: |                     # runs before each agent session
    git fetch origin main
    git rebase origin/main || git rebase --abort
  after_run: |                      # runs after each agent session (failure ignored)
    git add -A
    git diff --cached --quiet || git commit -m "$SYMPHONY_ISSUE_ID: automated changes"
  timeout_ms: 120000
agent:
  max_concurrent_agents: 1
  max_turns: 10
codex:
  command: "claude --dangerously-skip-permissions"
server:
  port: 8080
---
You are a senior software engineer.

## Task
{{ issue.identifier }}: {{ issue.title }}

{% if issue.description %}
## Description
{{ issue.description }}
{% endif %}

{% if issue.labels %}
## Labels
{{ issue.labels | join: ", " }}
{% endif %}

## Instructions
- Read the codebase thoroughly before making changes
- Write clean, well-tested code following existing patterns
- Run existing tests to make sure nothing is broken

{% if attempt %}
## Retry
This is retry attempt {{ attempt }}. The previous attempt failed.
Review what went wrong and try a different approach.
{% endif %}

Set your API key

export LINEAR_API_KEY="lin_api_..."

Run

# Use default WORKFLOW.md in current directory
symphony

# Or specify a path
symphony /path/to/WORKFLOW.md

# Override the dashboard port
symphony --port 9090

Symphony will start polling Linear, and you'll see structured logs:

symphony starting                    workflow="WORKFLOW.md"
startup terminal cleanup complete    cleaned=2
fetched candidate issues             count=7
dispatching issue                    identifier="STI-746" title="Add auth flow"
workspace created                    path="~/my-workspaces/project/STI-746"
agent session completed              identifier="STI-746" exit_code=0

Dashboard

Open http://localhost:8080 for a live HTML dashboard showing running/retrying issues, token usage, and concurrency.

API endpoints:

Endpoint Description
GET / HTML dashboard
GET /api/v1/state Full orchestrator state JSON
GET /api/v1/issues/:id Single issue status
POST /api/v1/refresh Trigger immediate poll
GET /health Health check

Architecture

Rust workspace with 7 crates:

Crate Responsibility
symphony-core Domain types: Issue, Session, Workspace, OrchestratorState
symphony-config WORKFLOW.md loader, typed config, live file watcher
symphony-tracker Linear GraphQL client, issue fetching, state normalization
symphony-workspace Per-issue directory lifecycle, hook execution, path safety
symphony-agent Coding agent subprocess management (CLI pipe + JSON-RPC modes)
symphony-orchestrator Poll loop, dispatch, reconciliation, retry queue
symphony-observability Structured logging, HTTP dashboard + REST API

Key Features

  • Live config reload: Edit WORKFLOW.md while running — changes apply on next tick
  • Lifecycle hooks: after_create, before_run, after_run, before_remove with timeout enforcement
  • Retry with backoff: Failed sessions retry with exponential backoff; continuations retry immediately
  • Concurrency control: Configurable max_concurrent_agents with slot-based dispatch
  • Workspace isolation: Each issue gets its own directory; path traversal attacks are blocked
  • Template rendering: Liquid templates with full issue context (title, description, labels, attempt count)
  • Environment injection: $SYMPHONY_ISSUE_ID available in all hook scripts
  • Terminal cleanup: On startup, cleans workspaces for issues already in Done/Canceled state

WORKFLOW.md Reference

Frontmatter Fields

Field Required Default Description
tracker.kind yes Tracker type (linear)
tracker.api_key yes API key (supports $ENV_VAR syntax)
tracker.project_slug yes Linear project slug ID
tracker.active_states no ["Todo"] States that make an issue eligible
tracker.terminal_states no ["Done","Canceled"] States that trigger cleanup
polling.interval_ms no 30000 Poll interval in milliseconds
workspace.root no ./workspaces Root directory for per-issue workspaces
hooks.after_create no Shell script run once on new workspace
hooks.before_run no Shell script run before each agent session
hooks.after_run no Shell script run after each agent session
hooks.before_remove no Shell script run before workspace cleanup
hooks.timeout_ms no 30000 Hook execution timeout
agent.max_concurrent_agents no 1 Max parallel agent sessions
agent.max_turns no 10 Max agent turns per session
codex.command yes Agent CLI command to run
server.port no HTTP dashboard port (omit to disable)

Template Variables

The body after the --- frontmatter is a Liquid template with these variables:

Variable Type Description
issue.identifier string Issue ID (e.g., STI-746)
issue.title string Issue title
issue.description string Issue body/description
issue.labels array Issue labels
attempt number Retry attempt number (nil on first run)

Development

make smoke    # compile + clippy + test (the gate)
make check    # cargo check + clippy
make test     # cargo test --workspace
make build    # cargo build --release
make fmt      # cargo fmt --all

136 tests across all crates (131 unit + 5 integration tests requiring LINEAR_API_KEY).

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

Key extension points:

  • Tracker plugins: Implement the TrackerClient trait to add support for GitHub Issues, Jira, etc.
  • Agent runners: The agent runner supports any CLI that speaks line-delimited JSON on stdout
  • Workflow templates: Create new WORKFLOW.md examples for different use cases

Community

  • Issues — bug reports, feature requests
  • Discussions — questions, ideas, show & tell

License

Apache License 2.0 — see LICENSE for details.

This project implements the Symphony specification originally published by OpenAI under the Apache 2.0 license.

About

Rust orchestration service for coding agents — polls Linear, creates isolated workspaces, runs Codex app-server sessions

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages