elixir/README.md
This directory contains the current Elixir/OTP implementation of Symphony, based on
SPEC.md at the repository root.
[!WARNING] Symphony Elixir is prototype software intended for evaluation only and is presented as-is. We recommend implementing your own hardened version based on
SPEC.md.
During app-server sessions, Symphony also serves a client-side linear_graphql tool so that repo
skills can make raw Linear GraphQL calls.
If a claimed issue moves to a terminal state (Done, Closed, Cancelled, or Duplicate),
Symphony stops the active agent for that issue and cleans up matching workspaces.
LINEAR_API_KEY environment variable.WORKFLOW.md to your repo.commit, push, pull, land, and linear skills to your repo.
linear skill expects Symphony's linear_graphql app-server tool for raw Linear GraphQL
operations such as comment editing or upload flows.WORKFLOW.md file for your project.
We recommend using mise to manage Elixir/Erlang versions.
mise install
mise exec -- elixir --version
git clone https://github.com/openai/symphony
cd symphony/elixir
mise trust
mise install
mise exec -- mix setup
mise exec -- mix build
mise exec -- ./bin/symphony ./WORKFLOW.md
Pass a custom workflow file path to ./bin/symphony when starting the service:
./bin/symphony /path/to/custom/WORKFLOW.md
If no path is passed, Symphony defaults to ./WORKFLOW.md.
Optional flags:
--logs-root tells Symphony to write logs under a different directory (default: ./log)--port also starts the Phoenix observability service (default: disabled)The WORKFLOW.md file uses YAML front matter for configuration, plus a Markdown body used as the
Codex session prompt.
Minimal example:
---
tracker:
kind: linear
project_slug: "..."
workspace:
root: ~/code/workspaces
hooks:
after_create: |
git clone [email protected]:your-org/your-repo.git .
agent:
max_concurrent_agents: 10
max_turns: 20
codex:
command: codex app-server
---
You are working on a Linear issue {{ issue.identifier }}.
Title: {{ issue.title }} Body: {{ issue.description }}
Notes:
codex.approval_policy defaults to {"reject":{"sandbox_approval":true,"rules":true,"mcp_elicitations":true}}codex.thread_sandbox defaults to workspace-writecodex.turn_sandbox_policy defaults to a workspaceWrite policy rooted at the current issue workspacecodex.approval_policy values depend on the targeted Codex app-server version. In the current local Codex schema, string values include untrusted, on-failure, on-request, and never, and object-form reject is also supported.codex.thread_sandbox values: read-only, workspace-write, danger-full-access.codex.turn_sandbox_policy is set explicitly, Symphony passes the map through to Codex
unchanged. Compatibility then depends on the targeted Codex app-server version rather than local
Symphony validation.agent.max_turns caps how many back-to-back Codex turns Symphony will run in a single agent
invocation when a turn completes normally but the issue is still in an active state. Default: 20.hooks.after_create to bootstrap a fresh workspace. For a Git-backed repo, you can run
git clone ... . there, along with any other setup commands you need.mise exec inside a freshly cloned workspace, trust the repo config and fetch
the project dependencies in hooks.after_create before invoking mise later from other hooks.tracker.api_key reads from LINEAR_API_KEY when unset or when value is $LINEAR_API_KEY.~ is expanded to the home directory.$VAR. workspace.root resolves $VAR before path handling,
while codex.command stays a shell command string and any $VAR expansion there happens in the
launched shell.tracker:
api_key: $LINEAR_API_KEY
workspace:
root: $SYMPHONY_WORKSPACE_ROOT
hooks:
after_create: |
git clone --depth 1 "$SOURCE_REPO_URL" .
codex:
command: "$CODEX_BIN app-server --model gpt-5.3-codex"
WORKFLOW.md is missing or has invalid YAML at startup, Symphony does not boot.server.port or CLI --port enables the optional Phoenix LiveView dashboard and JSON API at
/, /api/v1/state, /api/v1/<issue_identifier>, and /api/v1/refresh.The observability UI now runs on a minimal Phoenix stack:
//api/v1/*lib/: application code and Mix taskstest/: ExUnit coverage for runtime behaviorWORKFLOW.md: in-repo workflow contract used by local runs../.codex/: repository-local Codex skills and setup helpersmake all
Run the real external end-to-end test only when you want Symphony to create disposable Linear
resources and launch a real codex app-server session:
cd elixir
export LINEAR_API_KEY=...
make e2e
Optional environment variables:
SYMPHONY_LIVE_LINEAR_TEAM_KEY defaults to SYME2ESYMPHONY_LIVE_SSH_WORKER_HOSTS uses those SSH hosts when set, as a comma-separated listmake e2e runs two live scenarios:
If SYMPHONY_LIVE_SSH_WORKER_HOSTS is unset, the SSH scenario uses docker compose to start two
disposable SSH workers on localhost:<port>. The live test generates a temporary SSH keypair,
mounts the host ~/.codex/auth.json into each worker, verifies that Symphony can talk to them
over real SSH, then runs the same orchestration flow against those worker addresses. This keeps
the transport representative without depending on long-lived external machines.
Set SYMPHONY_LIVE_SSH_WORKER_HOSTS if you want make e2e to target real SSH hosts instead.
The live test creates a temporary Linear project and issue, writes a temporary WORKFLOW.md, runs
a real agent turn, verifies the workspace side effect, requires Codex to comment on and close the
Linear issue, then marks the project completed so the run remains visible in Linear.
Elixir is built on Erlang/BEAM/OTP, which is great for supervising long-running processes. It has an active ecosystem of tools and libraries. It also supports hot code reloading without stopping actively running subagents, which is very useful during development.
Launch codex in your repo, give it the URL to the Symphony repo, and ask it to set things up for
you.
This project is licensed under the Apache License 2.0.