Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
OpenSymphony
OpenSymphony is a Rust implementation of the OpenAI Symphony specification for orchestrating AI coding agents. It connects to Linear for issue tracking and runs issues through the managed OpenHands agent-server, the local Codex app-server, or a local ACP v1 agent.

What is OpenSymphony?
OpenSymphony automates software development workflows by:
- Polling Linear for issues in active states (Todo, In Progress, etc.)
- Creating isolated workspaces for each issue with lifecycle hooks
- Dispatching AI agents through OpenHands, Codex, or a configured ACP profile
- Managing retries, reconciliation, and cleanup based on issue state changes
- Providing a desktop operator dashboard for task graph, run detail, and memory navigation
Key Features
- Hierarchy-aware scheduling: Parent issues wait for sub-issues to complete
- Multi-repository projects: Repository-bound sub-issues run in their own checkouts; an unlabeled parent checks their merged work
- Live runtime updates: OpenHands uses WebSocket with REST reconciliation; Codex and ACP use local stdio
- Per-issue workspaces: Deterministic, isolated directories with lifecycle hooks
- GraphQL-only Linear integration: The scheduler polls Linear; agent-side writes use checked-in helper/query assets
- Dependency-aware Task Graph: Shows dispatchable work, roadmap backlog, and selected-task critical paths
- Harness selection: OpenHands, local Codex app-server, or a configured ACP v1 stdio agent
- Code Graph: Tree-sitter-backed symbols, diagnostics, and source-cited structural context for agents
- Operational Knowledge Graph: Builds agent-queriable and human-navigable memory from completed work
OpenSymphony 1.0.0 is the compatibility boundary for the GraphQL-only Linear
rewrite. See Migration Guide if you are upgrading an
older setup.
Version 3.0.0 adds explicit multi-repository project routing and production ACP worker profiles. Existing single-repository workflows remain available. See the 3.0 upgrade guide before selecting a new routing mode or harness.
Packaging note: crates.io exposes a single public package, opensymphony.
Internally, the repo still keeps clear subsystem boundaries under
crates/opensymphony-*, but those directories are now internal module trees,
not separately published crates.
Quick Start
Prerequisites
- Rust 1.97.1 or newer
- Linear API key (for tracker integration)
- For OpenHands: Python 3.13.12 with
uv, plus an LLM API key for an OpenAI-compatible/LiteLLM provider - For Codex: the Codex CLI with a working ChatGPT login
For platform-specific Rust and Python/uv setup steps, see Prerequisites.
Installation
For OpenHands runs, install the pinned local OpenHands agent-server runtime:
For Codex runs, install the Codex CLI and sign in with ChatGPT before selecting the Codex harness below.
To refresh the installed CLI later, run:
OpenSymphony 2.11.0 raises the minimum supported Rust version to 1.97.1. When upgrading an older CLI, bypass any checkout-local toolchain override with:
When you run opensymphony update from a target-repo root that already has
WORKFLOW.md and config.yaml, it also refreshes the template-managed
.agents/skills/ tree without rerunning the full init flow.
Common Environment
Before running opensymphony run, add your Linear key to your shell startup
file, such as ~/.zshrc or ~/.bashrc:
Create a personal Linear API key from
Settings -> Account -> Security & access,
then use it for LINEAR_API_KEY. For advanced deployments, a Linear OAuth app
created under Settings -> API -> OAuth applications can use OAuth tokens
instead; Linear's request limits
are higher for OAuth apps (5,000 requests/hour) than personal API keys (2,500
requests/hour). See Linear's
OAuth 2.0 authentication
docs for that path.
OpenHands Runtime Environment
OpenHands is the default harness. For the managed local OpenHands runtime, also set a local agent-server secret and provider credentials:
OH_SECRET_KEY can be any random secret string for the local OpenHands runtime.
The LLM_* variables are required for API-key OpenHands runs unless your target
repo's WORKFLOW.md has been customized to resolve the LLM configuration some
other way.
Codex Runtime Environment
For local Codex app-server runs, authenticate the Codex CLI with ChatGPT:
If ChatGPT blocks device-code login, enable Security and login -> Enable device code authorization for Codex in ChatGPT settings, then retry the login.
Then select the Codex harness for OpenSymphony:
OPENSYMPHONY_CODEX_BIN is optional when codex is already on PATH. In
Codex mode, OpenSymphony uses the operator-owned Codex CLI login; it does not
need LLM_MODEL/LLM_API_KEY/LLM_BASE_URL, and it does not launch the
managed OpenHands server for Codex-only routing.
The model configuration panel in the alpha web and desktop shells records model strings, API-compatible endpoint metadata, subscription bootstrap metadata, and stored credential references. Desktop profiles persist through the local native settings boundary. The web shell persists profiles when browser or embedding host storage is available, and reports a session-only fallback in the model panel when durable storage is unavailable. Raw API keys and OAuth refresh material remain owned by the selected keychain, OpenHands auth directory, or hosted secret store.
Bootstrap A Target Repo
Bootstrap the target repository in place:
opensymphony init guides the bootstrap flow, customizes WORKFLOW.md, and
can optionally scaffold automated code review via the OpenHands PR Review Plugin, including GitHub setup through gh when it is installed and authorized for the target repo. It also ensures .gitignore ignores local OpenSymphony runtime state.
The generated workflow records a target branch marker for feature PRs and syncs.
The default is develop; non-interactive setup can choose a repository-specific
branch after the shared target-repo template has the same marker-aware
WORKFLOW.md and pull/push/land guidance:
Fresh init fetches target-repo assets from kumanday/OpenSymphony-template.
Until that template sync lands, treat the examples above as the intended
post-sync form; otherwise fresh repos can record the marker while copied skills
still use older branch guidance.
If AGENTS.md already exists during first-time setup, init leaves it alone
and writes the starter guidance to AGENTS-example.md for review.
It also initializes .opensymphony/memory/memory.yaml, the shared policy and
learned structure file required for default-on memory auto-capture.
At the end of a successful bootstrap, init prompts whether to commit and push
the generated OpenSymphony files so shared skills and, when selected, AI PR
Review setup are in the remote repository before story work begins.
For an existing target repo, opensymphony update is the lighter-weight
maintenance path: it refreshes changed or new template-owned skill files under
.agents/skills/ without touching WORKFLOW.md, AGENTS.md, or the broader
bootstrap files. When run from an OpenSymphony target repo, update also
initializes or repairs the memory config and .gitignore policy if needed.
When --target-branch or --code-review is present, update enters workflow
settings mode instead: it patches the managed WORKFLOW.md markers, rewrites
known legacy branch-control phrases when the target branch changes, and skips
the CLI reinstall, template skill refresh, and memory bootstrap.
--code-review openhands records the marker and attempts to enable an existing
OpenHands review workflow through gh workflow when
.github/workflows/ai-pr-review.yml is already present. It does not install or
repair a missing workflow file; --code-review codex and --code-review none
record the marker and attempt to disable that workflow. If gh is unavailable,
unauthorized, or cannot access Actions, update warns and leaves the workflow
state unchanged; verify or adjust the workflow state manually.
Running the Orchestrator
Then start from the target repository:
For a central configuration in another location, pass
--config /absolute/path/to/config.yaml to opensymphony run.
The default single-repository workflow needs no multi-repository labels.
For a project spanning several repositories, follow the
multi-repository guide. To run a local ACP agent,
follow the ACP harness guide. Repository routing and harness
selection are independent: an ACP profile can run a child bound to one
repository in a multi-repository project.
For real-time monitoring while the orchestrator is running, run the TUI in a separate terminal window:
To launch the alpha desktop shell without adding Tauri or npm dependencies to the normal Cargo install path, use the lazy desktop launcher:
# or
The launcher verifies a versioned desktop bundle under
~/.opensymphony/desktop/<version>/ before starting it. Early local bundles can
be materialized with --bundle-dir <path> or
OPENSYMPHONY_DESKTOP_BUNDLE_DIR; the bundle must include
opensymphony-desktop-manifest.json with version, platform, arch,
relative executable, and sha256 fields.
When no installed or local bundle verifies, the launcher falls back to a local
source build, checks Rust/Cargo, Node/npm, source archive extraction, and
platform desktop/Tauri prerequisites first, and prints the exact manual
commands when it cannot install prerequisites automatically. --dry-run is
read-only: it verifies an existing cached or local bundle and reports when a
normal run would need to build from source.
Use --install-path <dir> or OPENSYMPHONY_DESKTOP_INSTALL_PATH to choose a
different install root; the launcher still creates the versioned bundle beneath
that root. On a cache miss, the launcher downloads the release index from the
versioned GitHub release matching the running CLI and installs the compatible
asset, so an empty desktop cache produces a real install attempt instead of a
missing-manifest dead end. Set OPENSYMPHONY_DESKTOP_RELEASE_INDEX_URL to
point at a private or test index. When a cached bundle is already installed,
the launcher checks the latest release index before launch, prompts
Update before launch? [Y/n] in an interactive terminal when a newer
compatible bundle is available, and updates by default in non-interactive runs.
Pass --no-update to launch the cached bundle without checking first.
The release-index and update prompt contract is defined in
Desktop App Installer And Auto-Update Spec.
Further Details
For generated files, environment variables, config.yaml, and the template
repo details behind init, see Configuration.
For alternate config paths, debug, rehydrate, packaging, and local operator
workflows, see Operations.
Optional troubleshooting and validation:
To inspect the command surface, run:
Project Memory
OpenSymphony can preserve completed-issue knowledge as you build. When
memory.auto_capture is enabled in config.yaml (the default),
opensymphony run captures terminal issue transitions from Linear and matching
GitHub PR narrative, writes private memory under .opensymphony/memory/, and
syncs stable learned topics into public docs. Repos initialized or updated with
this release get the required memory config automatically.


The generated issue capsules are Markdown files, so .opensymphony/memory/ can
also be opened as an Obsidian vault. That gives operators a graph view of issue,
milestone, and documentation-topic relationships while keeping private capture
artifacts out of the public docs.
Manual commands remain available for setup repair, backfill, inspection, and guarded archival:
See Project Memory for archive guards, YAML import/backfill, source schema, automation flags, and the distinction between CLI commands and template-managed agent skills.
The memory index uses DuckDB's bundled build by default so local installs do not
need a separate DuckDB system package. That choice adds compile time and binary
size, but keeps the memory database portable for local-first operator workflows.
Repository developers on macOS/Homebrew can use cargo check-system-duckdb,
cargo test-system-duckdb, and cargo clippy-system-duckdb to build against a
system DuckDB installation with --no-default-features --features duckdb-prebuilt. The expected Homebrew DuckDB version is 1.5.3, pinned after
installation. That avoids both bundled source compilation and per-workspace
download caches. The portable fallback aliases cargo check-dev,
cargo test-dev, and cargo clippy-dev set DUCKDB_DOWNLOAD_LIB=1 for the
aliased command so they reuse a downloaded prebuilt libduckdb during iterative
development. See Installer and Distribution Strategy.
Architecture
flowchart TB
operator["Operator / CLI / TUI"]
subgraph daemon["OpenSymphony Daemon"]
direction TB
orchestrator["Orchestrator Scheduler"]
workspace["Workspace Manager"]
control["Gateway + Control API<br/>GET /healthz, /api/v1/snapshot, /api/v1/capabilities"]
runtime["Harness Runtime Client"]
linear_read["Linear Read Adapter"]
routing["Repository binding and parent integration"]
orchestrator --> routing --> workspace
orchestrator --> runtime
orchestrator --> linear_read
orchestrator --> control
end
subgraph execution["Execution Environment"]
direction TB
issue_ws["Per-issue Workspace"]
agent["Agent Runtime"]
graphql["GraphQL Helper + Query Assets"]
agent --> issue_ws
agent --> graphql
end
linear["Linear"]
openhands["OpenHands Agent-Server"]
codex["Codex App-Server"]
acp["Local ACP v1 agent"]
config["Central project set and harness profiles"]
operator --> control
config --> routing
config --> runtime
workspace --> issue_ws
runtime --> openhands
runtime --> codex
runtime --> acp
openhands --> agent
codex --> agent
acp --> agent
linear_read -->|read issues| linear
graphql -->|agent-side writes| linear
Internal Boundaries
OpenSymphony keeps explicit internal subsystem boundaries while shipping as one installable crates.io package:
| Internal module tree | Responsibility |
|---|---|
opensymphony_orchestrator |
Poll loop, scheduling, retries, state machine |
opensymphony_linear |
GraphQL client for orchestrator-side Linear reads |
opensymphony_memory |
Issue capsules, DuckDB memory index, docs sync, archive eligibility |
opensymphony_openhands |
REST/WebSocket client for agent runtime |
opensymphony_workspace |
Workspace lifecycle, hooks, containment |
opensymphony_control |
Control plane API and snapshot derivation |
opensymphony_tui |
FrankenTUI operator client |
opensymphony_cli |
CLI entrypoints: init, run, debug, memory, linear archive, daemon (demo), tui, doctor, rehydrate |
Deployment Modes
Local Supervised Mode (MVP)
The default mode for individual developers:
- One OpenHands server subprocess managed by the daemon
- Host filesystem access (process-level isolation)
- Loopback-only binding
- No auth by default
openhands:
transport:
base_url: http://127.0.0.1:8000
External Local Mode
For debugging or CI with a manually managed server:
openhands:
transport:
base_url: http://127.0.0.1:8000
session_api_key_env: OPENHANDS_API_KEY
Hosted Remote Mode (Future)
For organizational deployment with stronger isolation:
openhands:
transport:
base_url: https://agent-server.example.com
session_api_key_env: OPENHANDS_API_KEY
websocket:
auth_mode: header
See docs/deployment-modes.md for full details.
Workspace Lifecycle
Each issue gets a deterministic workspace:
<workspace_root>/<issue_identifier>/
├── .opensymphony/
│ ├── issue.json # Issue metadata
│ ├── conversation.json # Conversation registry and launch profile
│ └── openhands/
│ └── create-conversation-request.json
├── .opensymphony.after_create.json # Hook receipt
├── <repo_files> # Cloned repository
└── logs/ # Execution logs
Debugging Sessions
Use opensymphony debug <issue-id> to reopen the harness conversation that OpenSymphony used for that issue:
The command resolves the issue reference to its managed workspace, reads
.opensymphony/conversation.json, and resumes the same conversation_id from the
original working directory. The conversation registry persists the issue reference,
stable harness conversation ID, timestamps, transport details, and the launch
profile that created the session so a missing-but-recoverable thread can be
rehydrated without losing continuity.
When the workflow uses the local supervised OpenHands server, opensymphony debug
targets the same configured base URL as the orchestrator. If a ready server is
already listening there, the debug command reuses it; otherwise it waits through
the configured startup window before starting a local server for the session. The
default managed-local startup window is 180 seconds so agent-server has enough
time to import the pinned environment and scan its active persistence store on
slower local machines. For the most predictable behavior, prefer the
orchestrator-managed server and avoid leaving unrelated standalone openhands
CLI sessions bound to the same port. Stop opensymphony run with Ctrl-C so the
managed OpenHands process tree can be cleaned up; Ctrl-Z only suspends the
orchestrator and can leave the port bound.
Managed local OpenHands conversations are scoped by target repository under
<tool_dir>/workspace/conversations/repos/<repo-key>/. The orchestrator starts
OpenHands with OH_CONVERSATIONS_PATH pointing at that repo's active/ store,
so older archived work is not eagerly loaded during normal runs. Before startup,
known terminal issue conversations from existing workspace manifests are moved
into archived/, and current Linear candidate issues are moved into active/
from the legacy flat store or archived/. This legacy-store migration is a
temporary compatibility shim for earlier OpenSymphony versions and can be
removed after existing installs have aged out. Linear archive operations move
matching issue conversations into archived/; opensymphony debug <issue-id>
searches both stores and starts the managed server against the store that
contains the requested conversation.
Lifecycle Hooks
after_create: Clone repository, setup environmentbefore_run: Pre-execution checksafter_run: Post-execution cleanupbefore_remove: Final cleanup before workspace deletion
Testing
# Unit tests
# Faster iterative development mode with prebuilt libduckdb
# Static validation
# Live tests for OpenHands server
OPENSYMPHONY_LIVE_OPENHANDS=1
# Smoke test
# Live E2E test
OPENSYMPHONY_LIVE_OPENHANDS=1
Documentation
- Architecture - High-level design and component interactions
- Configuration - Target repo bootstrap and runtime config
- Multi-repository projects - Repository labels, parent integration, and rollout
- ACP harness - Local agent profiles, operator requests, and recovery
- Deployment Modes - Local vs hosted deployment
- Installer and Distribution Strategy - Future signed installer shape and DuckDB packaging boundaries
- Operations - Doctor, rehydration, diagnostics, and local ops
- Testing - Test strategy and validation layers
- Migration Guide - Breaking changes and upgrade steps for 1.0.0
- 3.0 upgrade guide - Moving from single-repository workflows and selecting ACP
- AGENTS.md - Repository guidelines for coding agents
- Development Guide - Contributing and development details
Safety and Security
Local Mode: The MVP runs with process-level isolation on trusted developer machines. Agent code executes on the host filesystem. This is suitable for:
- Solo development on trusted repositories
- Local experimentation
- CI on controlled runners
Hosted Mode (future): Will provide stronger isolation with container-backed workspaces and mandatory auth.
Version Pinning
OpenSymphony pins exact versions for reproducibility:
openhands-agent-server==1.24.0openhands-sdk==1.24.0- Python
3.13.12 - Rust
1.97.1
The managed local OpenHands bundle is sourced from tools/openhands-server/
and provisioned with opensymphony install openhands.
License
Acknowledgments
- OpenAI Symphony - The specification this implements
- OpenHands - Managed local agent-server runtime
- Codex app-server - Local app-server harness runtime