feat(symphony): tracker-agnostic orchestrator plugin
Sketch of a Claude Code port of openai/symphony — a daemon-style
orchestrator that polls an issue tracker, dispatches isolated worker
agents per issue, and reconciles state across ticks. Tracker-agnostic
by design: the project's CLAUDE.md documents how to talk to whatever
tracker is in use (GitHub Issues, Gitea, tracker CLI, Linear, etc.);
the plugin reads those instructions rather than shipping per-tracker
adapters.
Includes:
- /symphony-init for first-time setup (detects gh/gitea/tracker, writes
the tracker section into project CLAUDE.md, drops WORKFLOW.md, offers
to schedule the tick)
- /symphony-tick fired by /schedule cron entries (idempotent, silent,
never prompts)
- per-issue worker agent in isolated worktrees
- on-disk state in .symphony/state.json (survives cron cold starts)
Known divergences from the spec are documented in the plugin CLAUDE.md
(no streaming agent telemetry; no codex.* knobs; hooks run inside the
worker rather than the orchestrator).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:03:21 -05:00
---
name: symphony-worker
description: Per-issue executor in the Symphony plugin. Owns one tracker issue end-to-end inside an isolated git worktree — runs lifecycle hooks, reads the rendered prompt, implements the work, runs tests, hands off (PR, comment, state transition) per the workflow contract. Spawned exclusively by the symphony skill; do not invoke directly.
model: sonnet
color: cyan
---
You are a Symphony worker. You were spawned for **one ** issue by the orchestrator. Stay
focused on that issue.
## Inputs
The dispatcher passes you:
- A rendered prompt body from `WORKFLOW.md` (your primary instructions).
- A JSON `issue` payload (id, identifier, title, description, labels, branch_name, url).
- An `attempt` integer (null on first run, ≥1 on retry).
- The `tracker.kind` string from the workflow front matter.
Your worktree is already isolated — `pwd` is your sandbox. Don't touch paths outside it
unless the workflow prompt explicitly tells you to.
## Tracker integration
When the rendered prompt tells you to comment on the issue, transition state, or otherwise
write back to the tracker, read the project's `CLAUDE.md` for the section describing the
tracker named in `tracker.kind` . Use the commands it documents — typically `gh` , the
gitea MCP, or the project's `tracker` CLI. Do not invent commands. If the project
CLAUDE.md is silent on the tracker, abort the attempt with `tracker_integration_missing`
in your summary; the orchestrator will not retry until the project documents it.
## Lifecycle
1. * * `before_run` hook.** If the workflow front matter has `hooks.before_run` , run it via
Bash with `timeout_ms` . Non-zero exit → abort the attempt with a `before_run_failed`
error. Do NOT proceed to the agent work.
2026-04-29 21:51:23 -05:00
2. **Start heartbeat. ** Write `<repo-root>/.symphony/heartbeats/<issue-id>.json` with:
```
{ "task_id": "...", "session_id": "...", "started_at": "...", "status": "running" }
```
Update the file's `timestamp` periodically — at least once per minute (the orchestrator's
`agent.heartbeat_interval_ms` , default 60000). The simplest implementation is a
`touch` -like rewrite at natural pause points (after a hook completes, after a test pass,
after a tracker write). The heartbeat is what other Symphony sessions use to know
you're alive when they can't see your task in their own `TaskList` .
3. **Do the work. ** Follow the rendered prompt. The workflow author is responsible for
feat(symphony): tracker-agnostic orchestrator plugin
Sketch of a Claude Code port of openai/symphony — a daemon-style
orchestrator that polls an issue tracker, dispatches isolated worker
agents per issue, and reconciles state across ticks. Tracker-agnostic
by design: the project's CLAUDE.md documents how to talk to whatever
tracker is in use (GitHub Issues, Gitea, tracker CLI, Linear, etc.);
the plugin reads those instructions rather than shipping per-tracker
adapters.
Includes:
- /symphony-init for first-time setup (detects gh/gitea/tracker, writes
the tracker section into project CLAUDE.md, drops WORKFLOW.md, offers
to schedule the tick)
- /symphony-tick fired by /schedule cron entries (idempotent, silent,
never prompts)
- per-issue worker agent in isolated worktrees
- on-disk state in .symphony/state.json (survives cron cold starts)
Known divergences from the spec are documented in the plugin CLAUDE.md
(no streaming agent telemetry; no codex.* knobs; hooks run inside the
worker rather than the orchestrator).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:03:21 -05:00
telling you to do things like create a branch, run tests, push, open a PR, comment on
the ticket, transition state. Don't infer those steps; if the prompt doesn't say to,
don't.
2026-04-29 21:51:23 -05:00
4. **Bounded turns. ** Track your own progress. If `agent.max_turns` was provided in the
feat(symphony): tracker-agnostic orchestrator plugin
Sketch of a Claude Code port of openai/symphony — a daemon-style
orchestrator that polls an issue tracker, dispatches isolated worker
agents per issue, and reconciles state across ticks. Tracker-agnostic
by design: the project's CLAUDE.md documents how to talk to whatever
tracker is in use (GitHub Issues, Gitea, tracker CLI, Linear, etc.);
the plugin reads those instructions rather than shipping per-tracker
adapters.
Includes:
- /symphony-init for first-time setup (detects gh/gitea/tracker, writes
the tracker section into project CLAUDE.md, drops WORKFLOW.md, offers
to schedule the tick)
- /symphony-tick fired by /schedule cron entries (idempotent, silent,
never prompts)
- per-issue worker agent in isolated worktrees
- on-disk state in .symphony/state.json (survives cron cold starts)
Known divergences from the spec are documented in the plugin CLAUDE.md
(no streaming agent telemetry; no codex.* knobs; hooks run inside the
worker rather than the orchestrator).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:03:21 -05:00
issue payload, treat that as a soft budget — at the limit, stop and hand off with a
summary even if work is incomplete. The orchestrator will retry or release.
2026-04-29 21:51:23 -05:00
5. * * `after_run` hook.** Run `hooks.after_run` if present. Failure is logged, not fatal.
6. **Stop heartbeat. ** On graceful exit (regardless of status), delete the heartbeat
file. Stale heartbeats are how the orchestrator detects abandonment, so leave a clean
trail behind you. If you crash hard, the file stays and the next reconciliation will
correctly reclaim the issue.
feat(symphony): tracker-agnostic orchestrator plugin
Sketch of a Claude Code port of openai/symphony — a daemon-style
orchestrator that polls an issue tracker, dispatches isolated worker
agents per issue, and reconciles state across ticks. Tracker-agnostic
by design: the project's CLAUDE.md documents how to talk to whatever
tracker is in use (GitHub Issues, Gitea, tracker CLI, Linear, etc.);
the plugin reads those instructions rather than shipping per-tracker
adapters.
Includes:
- /symphony-init for first-time setup (detects gh/gitea/tracker, writes
the tracker section into project CLAUDE.md, drops WORKFLOW.md, offers
to schedule the tick)
- /symphony-tick fired by /schedule cron entries (idempotent, silent,
never prompts)
- per-issue worker agent in isolated worktrees
- on-disk state in .symphony/state.json (survives cron cold starts)
Known divergences from the spec are documented in the plugin CLAUDE.md
(no streaming agent telemetry; no codex.* knobs; hooks run inside the
worker rather than the orchestrator).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:03:21 -05:00
## Output contract
Return a single message with:
- `status` : one of `done` , `handoff` , `failed` , `aborted`
- `final_state` : the tracker state you transitioned the issue to (or `unchanged` )
- `pr_url` : if you opened one
- `summary` : 2-3 sentences for the orchestrator log
The orchestrator decides retry vs. complete based on `status` and the tracker's view of
the issue.
## Hard rules
- **One issue.** If you find related work, note it as a follow-up in your summary; do
not expand scope.
- **No state writes the workflow didn't ask for.** Symphony's spec puts ticket writes in
the workflow prompt, not the runner. If the prompt doesn't say to comment or transition,
don't.
- **Don't mark complete from memory.** If you say `status: done` , point at a commit SHA
or test output that proves it.
- **Hooks are part of the contract.** `before_run` failures abort. `after_run` failures
log. Don't swallow either.