From aradotso-trending-skills-37
Sets up OpenAI Symphony to monitor Linear tasks, spawn isolated autonomous coding agents for implementation, and automate GitHub PRs via Elixir reference.
npx claudepluginhub joshuarweaver/cascade-ai-ml-agents-misc-1 --plugin aradotso-trending-skills-37This skill uses the workspace's default tool permissions.
> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Skill by ara.so — Daily 2026 Skills collection.
Symphony turns project work into isolated, autonomous implementation runs, allowing teams to manage work instead of supervising coding agents. Instead of watching an agent code, you define tasks (e.g. in Linear), and Symphony spawns agents that complete them, provide proof of work (CI status, PR reviews, walkthrough videos), and land PRs autonomously.
Paste this prompt into Claude Code, Cursor, or Codex:
Implement Symphony according to the following spec:
https://github.com/openai/symphony/blob/main/SPEC.md
git clone https://github.com/openai/symphony.git
cd symphony/elixir
Follow elixir/README.md, or ask an agent:
Set up Symphony for my repository based on
https://github.com/openai/symphony/blob/main/elixir/README.md
export OPENAI_API_KEY="sk-..." # OpenAI API key for Codex
export LINEAR_API_KEY="lin_api_..." # Linear integration
export GITHUB_TOKEN="ghp_..." # GitHub PR operations
export SYMPHONY_REPO_PATH="/path/to/repo" # Target repository
cd elixir
mix deps.get
elixir/config/config.exs)import Config
config :symphony,
openai_api_key: System.get_env("OPENAI_API_KEY"),
linear_api_key: System.get_env("LINEAR_API_KEY"),
github_token: System.get_env("GITHUB_TOKEN"),
repo_path: System.get_env("SYMPHONY_REPO_PATH", "./"),
poll_interval_ms: 30_000,
max_concurrent_agents: 3
mix symphony.start
# or in IEx for development
iex -S mix
Each task gets its own isolated run:
Before a PR is accepted, Symphony collects:
# In your application.ex or directly
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
Symphony.Supervisor
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
defmodule Symphony.Task do
@type t :: %__MODULE__{
id: String.t(),
title: String.t(),
description: String.t(),
source: :linear | :manual,
status: :pending | :running | :completed | :failed,
branch: String.t() | nil,
pr_url: String.t() | nil,
proof_of_work: map() | nil
}
defstruct [:id, :title, :description, :source,
status: :pending, branch: nil,
pr_url: nil, proof_of_work: nil]
end
defmodule Symphony.AgentRunner do
@doc """
Spawns an isolated agent run for a given task.
Each run gets its own branch and Codex session.
"""
def run(task) do
branch = "symphony/#{task.id}-#{slugify(task.title)}"
with :ok <- Git.create_branch(branch),
{:ok, result} <- Codex.implement(task, branch),
{:ok, pr_url} <- GitHub.open_pr(branch, task),
{:ok, proof} <- ProofOfWork.collect(pr_url) do
{:ok, %{task | status: :completed, pr_url: pr_url, proof_of_work: proof}}
else
{:error, reason} -> {:error, reason}
end
end
defp slugify(title) do
title
|> String.downcase()
|> String.replace(~r/[^a-z0-9]+/, "-")
|> String.trim("-")
end
end
defmodule Symphony.Linear.Poller do
use GenServer
@poll_interval Application.compile_env(:symphony, :poll_interval_ms, 30_000)
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(_opts) do
schedule_poll()
{:ok, %{processed_ids: MapSet.new()}}
end
def handle_info(:poll, state) do
case Symphony.Linear.Client.fetch_todo_tasks() do
{:ok, tasks} ->
new_tasks = Enum.reject(tasks, &MapSet.member?(state.processed_ids, &1.id))
Enum.each(new_tasks, &Symphony.AgentRunner.run/1)
new_ids = Enum.reduce(new_tasks, state.processed_ids, &MapSet.put(&2, &1.id))
schedule_poll()
{:noreply, %{state | processed_ids: new_ids}}
{:error, reason} ->
Logger.error("Linear poll failed: #{inspect(reason)}")
schedule_poll()
{:noreply, state}
end
end
defp schedule_poll do
Process.send_after(self(), :poll, @poll_interval)
end
end
defmodule Symphony.Linear.Client do
@linear_api "https://api.linear.app/graphql"
def fetch_todo_tasks do
query = """
query {
issues(filter: { state: { name: { eq: "Todo" } } }) {
nodes {
id
title
description
}
}
}
"""
case HTTPoison.post(@linear_api, Jason.encode!(%{query: query}), headers()) do
{:ok, %{status_code: 200, body: body}} ->
tasks =
body
|> Jason.decode!()
|> get_in(["data", "issues", "nodes"])
|> Enum.map(&to_task/1)
{:ok, tasks}
{:error, reason} ->
{:error, reason}
end
end
defp headers do
[
{"Authorization", System.get_env("LINEAR_API_KEY")},
{"Content-Type", "application/json"}
]
end
defp to_task(%{"id" => id, "title" => title, "description" => desc}) do
%Symphony.Task{id: id, title: title, description: desc, source: :linear}
end
end
defmodule Symphony.ProofOfWork do
@doc """
Collects proof of work for a PR before it can be merged.
Returns a map with CI status, review feedback, and complexity.
"""
def collect(pr_url) do
with {:ok, ci_status} <- wait_for_ci(pr_url),
{:ok, reviews} <- fetch_reviews(pr_url),
{:ok, complexity} <- analyze_complexity(pr_url) do
{:ok, %{
ci_status: ci_status,
reviews: reviews,
complexity: complexity,
collected_at: DateTime.utc_now()
}}
end
end
defp wait_for_ci(pr_url, retries \\ 30) do
case GitHub.get_pr_ci_status(pr_url) do
{:ok, :success} -> {:ok, :success}
{:ok, :pending} when retries > 0 ->
Process.sleep(60_000)
wait_for_ci(pr_url, retries - 1)
{:ok, status} -> {:ok, status}
{:error, reason} -> {:error, reason}
end
end
defp fetch_reviews(pr_url), do: GitHub.get_pr_reviews(pr_url)
defp analyze_complexity(pr_url), do: GitHub.get_pr_diff_complexity(pr_url)
end
When building Symphony in another language, the spec defines:
Minimal implementation loop in pseudocode:
# Core symphony loop
def symphony_loop(state) do
tasks = fetch_new_tasks(state.source)
tasks
|> Enum.filter(&(&1.status == :todo))
|> Enum.each(fn task ->
Task.async(fn ->
branch = create_isolated_branch(task)
invoke_agent(task, branch) # Codex / Claude / etc.
proof = collect_proof_of_work(branch)
present_for_review(task, proof)
end)
end)
Process.sleep(state.poll_interval)
symphony_loop(state)
end
defmodule Symphony.AgentPool do
use GenServer
@max_concurrent 3
def start_link(_), do: GenServer.start_link(__MODULE__, %{running: 0, queue: []}, name: __MODULE__)
def submit(task) do
GenServer.cast(__MODULE__, {:submit, task})
end
def handle_cast({:submit, task}, %{running: n} = state) when n < @max_concurrent do
spawn_agent(task)
{:noreply, %{state | running: n + 1}}
end
def handle_cast({:submit, task}, %{queue: q} = state) do
{:noreply, %{state | queue: q ++ [task]}}
end
def handle_info({:agent_done, _result}, %{running: n, queue: [next | rest]} = state) do
spawn_agent(next)
{:noreply, %{state | running: n, queue: rest}}
end
def handle_info({:agent_done, _result}, %{running: n} = state) do
{:noreply, %{state | running: n - 1}}
end
defp spawn_agent(task) do
parent = self()
spawn(fn ->
result = Symphony.AgentRunner.run(task)
send(parent, {:agent_done, result})
end)
end
end
# In IEx or a Mix task
Symphony.AgentPool.submit(%Symphony.Task{
id: "manual-001",
title: "Add rate limiting to API",
description: "Implement token bucket rate limiting on /api/v1 endpoints",
source: :manual
})
| Problem | Likely Cause | Fix |
|---|---|---|
| Agents not spawning | Missing OPENAI_API_KEY | Check env var is exported |
| Linear tasks not detected | Wrong Linear state filter | Update query filter to match your board's state name |
| PRs not opening | Missing GITHUB_TOKEN or wrong repo | Verify token has repo scope |
| CI never completes | Timeout too short | Increase retries in wait_for_ci/2 |
| Too many concurrent runs | Default pool size | Set max_concurrent_agents in config |
| Branch conflicts | Agent reusing branch names | Ensure task IDs are unique per run |
# In config/dev.exs
config :symphony, log_level: :debug
# Or at runtime
Logger.put_module_level(Symphony.AgentRunner, :debug)
Logger.put_module_level(Symphony.Linear.Poller, :debug)