Skip to content

Architecture

Symphony is an agent orchestration service that auto-detects projects, provides a kanban board, and dispatches Claude CLI agents to work on issues in isolated git worktrees.

Three-Process Model

Symphony runs as three cooperating processes sharing a single SQLite database:

Web (Nuxt SPA)

  • Framework: Nuxt 4 (Vue 3 + Nitro), SSR disabled
  • Purpose: Kanban board UI, issue detail with diff viewer, project settings, orchestrator status
  • API: Nitro file-based API routes at server/api/
  • Styling: Tailwind CSS with dark mode (class strategy)

Orchestrator (Daemon)

  • Entry point: npm run orchestrator (separate Node.js process)
  • Purpose: Polls the database every 5 seconds, dispatches agents, manages worktrees, handles retries
  • PID lock: Only one orchestrator instance allowed (.symphony/orchestrator.pid)
  • Tick loop: processPhaseTransitions()detectPipelineMismatches() / assignMissingPhaseLabels()reconcile()processRetries()dispatchIntakeBatches()dispatch()workerRegistry.runDue() on each tick (background workers run maintenance tasks: learning consolidation, artifact pruning, prompt metrics, WAL checkpointing)

For a detailed explanation of the tick loop state machine, counter lifecycle, exit handler decision trees, and race condition defenses, see Orchestrator Lifecycle.

MCP Servers (Per-Agent)

  • Lifecycle: Ephemeral — one MCP server per agent session, communicating over stdio
  • Purpose: Gives agents tools to update issues, record findings/learnings, create PRs
  • Protocol: @modelcontextprotocol/sdk over stdio transport
  • Context: Each server is initialized with issueId and projectId for scoped access

Readiness Phase System

Issues pass through readiness phases before reaching implementation. Phases track maturity; status tracks execution.

Phase Pipeline

  • Exactly one phase:* label per ticket at any time
  • Researcher-created tickets start as backlog + phase:research
  • Manually created tickets get phase:ready immediately
  • Workers only dispatch to phase:ready tickets
  • Existing tickets without phase:* labels are treated as implicit phase:ready (migration mode)

Dispatch Table

PhaseProfileArtifact Path
phase:researchresearcherdocs/tickets/{id}/research.md
phase:architecturearchitectdocs/tickets/{id}/design.md
phase:groomingplannerdocs/tickets/{id}/grooming.md
phase:readyworkercode changes + PR

Completion Claims

Agents do not mutate workflow state directly. They produce an artifact and submit a structured completion claim via the complete_phase MCP tool. The orchestrator validates the claim (structural checks, optional judge review) and performs the atomic phase transition.

Transition Processor

On each tick, before dispatch:

  1. Precondition check — claim's completed_phase matches current label, contract_version matches contract
  2. Structural validation — artifact exists at resolved path, required sections present
  3. Mode dispatchstructural (auto-advance), judge (dispatch judge for verdict), or trust (structural only, skip qualitative review)
  4. Atomic transition — remove current phase:* label, add next phase:* label, record audit comment
  5. Failure — leave phase unchanged, add needs-revision label, attach feedback

Implementation: server/orchestrator/phaseTransition.ts

Artifact System

Every ticket gets a deterministic directory for phase artifacts:

docs/tickets/
  SYM-033/
    research.md          # phase:research output
    design.md            # phase:architecture output
    grooming.md          # phase:grooming output

Paths are resolved from the contract's artifact_path template. Agents receive the resolved path in runtime context and write to it. Artifacts use YAML frontmatter for metadata (ticket_id, phase, artifact_type, contract_version, artifact_revision, status).

Artifacts with lasting value can be promoted into the project docs tree (docs/research/, docs/architecture/, docs/guides/) via the promote_to field in completion claims. Implementation: server/orchestrator/artifactPromotion.ts.

Prompt Architecture

prompts/
  profiles/               # Identity: what kind of agent
    researcher.md         # gather facts, inspect code, identify uncertainty
    planner.md            # decompose work, produce ordered tasks
    worker.md             # implement safely, test, keep scope tight
    judge.md              # verify correctness, coverage, requirement fit
  phases/                 # Contracts: what artifact to produce
    research.md           # produce research brief
    architecture.md       # produce design doc
    grooming.md           # produce task breakdown
    ready.md              # implement, run gates, create PR
  overrides/              # Optional profile+phase deltas (empty initially)

Prompt composition order: workspace boundary, agent identity (profile), phase contract, profile-phase override (if exists), project custom instructions, CLAUDE.md/repo conventions, ticket-specific runtime context.

Key principle: Profile = capability (what kind of agent). Phase = assignment mode (what artifact to produce). Layers 1-4 define the workflow contract. Layers 5-7 provide local execution context and may not alter artifact requirements or transition semantics.

Data Flow

Issue Lifecycle

  1. Issues start in backlog or todo
  2. Orchestrator promotes backlog items to todo when no work is available
  3. Orchestrator claims todo issues by setting them to in_progress and dispatching an agent
  4. Agents work in isolated git worktrees with MCP tool access
  5. Agents create a PR (create_pr) which moves the issue to review
  6. Judge agents review the PR diff and approve or reject
  7. Approved PRs wait in review for human merge; rejected PRs return to todo

Phase agents (research, architecture, grooming) can dispatch to tickets in backlog or todo status — maturation work happens before delivery.

Dispatch Priority

The orchestrator dispatches agents in this order:

  1. Setup Wizard — runs first, before all other dispatch; auto-configures any project that has not yet completed wizard setup (wizardCompleted is false and wizardFailed is false)
  2. Judge — review completed work or phase artifacts (unblock the pipeline)
  3. Phase agents — tickets with phase:research, phase:architecture, phase:grooming
  4. Worker / Plannerphase:ready tickets; the planner handles needs-planning issues in the same candidate pass (not a separate priority slot)
  5. Scanner — idle background scan for improvements (only when no todo work exists and backlog < 10); controlled by scannerEnabled in project config

At least 1 slot is reserved for phase:ready workers when maxConcurrency >= 2 to prevent phase work from starving implementation. This behavior is tested in tests/integration/orchestrator/workerSlotStarvation.test.ts.

Agent Isolation

Each agent runs in a git worktree:

  • Worktrees are created in .symphony-workspaces/<identifier>/
  • Branch naming: symphony/<identifier> (e.g., symphony/SYM-001)
  • Workspace boundary is injected via the system prompt (not via CLAUDE.md files)
  • Project configs (.env, etc.) are copied into the worktree
  • On exit, the orchestrator auto-commits any uncommitted changes

Database

  • Engine: SQLite with WAL mode and busy_timeout=5000
  • ORM: Drizzle ORM with raw SQL migrations (CREATE TABLE IF NOT EXISTS)
  • Location: data/symphony.db (configurable via symphony.config.json)

Tables

TablePurpose
projectsDetected projects with config, identifier prefix, path
issuesKanban issues with status, priority, findings, learnings
issue_dependenciesDependency links between issues
issue_commentsComments from users, agents, and system
agent_profilesPer-type agent configurations (prompt, model)
agent_runsExecution history (status, duration, output, worktree path)
pull_requestsPR records with diff snapshots, gate results, risk scores
intake_batchesBatch intake processing jobs
research_coveragePer-file/concept coverage tracking for scanner agents
notificationsIn-app notifications for agent events and PR status
issue_eventsAudit log of issue field changes
project_labelsPer-project label definitions

JSON Columns

Several tables store structured data as JSON text columns:

  • issues.findings — structured findings (test results, bugs, patterns)
  • issues.learnings — reusable patterns for future agents
  • issues.labels — string array of labels
  • issues.artifacts — phase artifact records (path, type, status)
  • projects.config — per-project configuration (gates, thresholds, agent toggles)
  • pull_requests.file_stats — per-file addition/deletion counts
  • pull_requests.gate_results — quality gate pass/fail results
  • pull_requests.risk_score — computed risk level with breakdown

Directory Structure

symphony/
├── app/                          # Vue 3 SPA
│   ├── components/               # UI components (auto-imported)
│   ├── composables/              # Composables (auto-imported)
│   ├── pages/                    # File-based routes
│   └── utils/                    # Client utilities (auto-imported)
├── server/
│   ├── api/                      # Nitro API routes
│   ├── database/                 # Schema + migrations
│   ├── mcp/                      # MCP server + tools
│   ├── orchestrator/             # Orchestrator daemon
│   ├── services/                 # Business logic
│   └── utils/                    # Server utilities (auto-imported in server)
├── prompts/                      # Agent prompts (profiles/, phases/, overrides/)
├── cli/                          # CLI commands
├── tests/                        # Vitest tests
├── symphony.config.json          # Runtime config
└── data/                         # SQLite DB (gitignored)