Skip to content

Symphony v2: Complete architecture redesign #1

@shivamstaq

Description

@shivamstaq

Summary

Full architecture redesign of Symphony from the ground up. The v1 architecture had cost leaks (silent continuation retries), implicit state transitions, no process visibility, a heavyweight observability stack, and GitHub-only tracker support. This redesign addresses all of these with a clean-break approach.

Motivation

Problems with v1

  • Cost leaks: No-commits exits silently retried, burning API credits with no progress
  • Implicit state: State transitions scattered across orchestrator, no formal FSM
  • No visibility: No way to see what agents are doing in real-time
  • Heavy observability: Required external Grafana/Prometheus stack for basic monitoring
  • GitHub-only: No support for Linear or other issue trackers
  • TypeScript sidecar: Claude Code required a Node.js/tsx sidecar, adding operational complexity
  • No process control: No pause/resume/kill for individual agents

v2 Design Goals

  • Declarative FSM with exhaustive transition table — every state change is explicit and testable
  • Per-repo .symphony/ configuration with symphony.yaml
  • Multi-tracker support (GitHub + Linear) via adapter pattern
  • Multi-agent support (Claude Code + OpenCode + Codex) via unified agent.Agent interface
  • Built-in TUI dashboard (replaces Grafana stack for day-to-day use)
  • PTY-based agents with symphony attach for real-time observation
  • Event-driven architecture with single-goroutine state ownership (no mutexes)
  • Property-based testing (55K+ random FSM sequences)

Architecture

Package Structure

internal/
  domain/         # Shared types (WorkItem, FSM) — zero external deps
  engine/         # Central event loop, handlers, state
  tracker/KIND/   # Tracker adapters (github/, linear/, mock/)
  codehost/KIND/  # Code host adapters (github/)
  agent/KIND/     # Agent adapters (claude/, opencode/, codex/, mock/)
  config/         # symphony.yaml parser + validator
  prompt/         # Template rendering + field-based routing
  workspace/      # Git clone/worktree management
  state/          # bbolt persistent store
  logging/        # JSONL file logger
  server/         # HTTP API (healthz, metrics, webhooks, control)
  tui/views/      # Bubble Tea TUI with 3 view modes

FSM (9 states, 16 events, 28 transitions)

open → queued → preparing → running → completed → handed_off
                              ↓
                         needs_human (stall/budget/no-commits)
                              ↓
                           failed (retries exhausted)

Key Design Decisions

  • No mutexes in engine: Single goroutine processes all events sequentially
  • Explicit handoff: No-commits exit → needs_human (NOT silent retry) — prevents cost leaks
  • FSM enforcement: All state changes go through domain.Transition() with guards
  • Event log: Append-only events.jsonl records every transition for debugging/replay
  • Adapter pattern: Tracker, Agent, CodeHost interfaces with factory registration
  • PTY agents: Agents run in Go-native PTY via creack/pty, attachable via Unix socket

CLI Commands

symphony init          # Interactive setup wizard
symphony run [--mock]  # Start orchestrator with TUI
symphony doctor        # Validate config + environment + connectivity
symphony status        # JSON state dump
symphony attach <id>   # Connect to agent PTY
symphony logs          # Tail structured logs
symphony pause/resume/kill <id>  # Control individual agents
symphony events        # Query FSM event log
symphony config validate/show    # Config management

Scope

New packages (v2)

  • internal/domain/ — WorkItem model, FSM with 28 transitions
  • internal/engine/ — Event loop, eligibility, retry, stall, budget, handoff, reconcile
  • internal/agent/ — Agent interface + Claude/OpenCode/Codex/Mock adapters
  • internal/codehost/ — CodeHost interface + GitHub adapter
  • internal/tracker/{github,linear,mock}/ — Multi-tracker with factory + contract tests
  • internal/config/ — symphony.yaml parser, validator, env var resolution
  • internal/prompt/ — Field-based template routing
  • internal/server/ — HTTP API with webhook endpoint (HMAC-SHA256 verification)
  • internal/tui/views/ — Bubble Tea dashboard (overview, detail, logs)
  • internal/logging/ — JSONL structured logging
  • cmd/symphony/ — 10+ cobra subcommands
  • test/property/ — FSM property tests (55K random sequences)
  • test/scenario/ — 7 end-to-end scenario tests with harness
  • docs/ — Architecture, configuration, testing, getting-started guides

Removed packages (v1)

  • internal/adapter/ — Replaced by internal/agent/
  • internal/orchestrator/ — Replaced by internal/engine/
  • internal/webhook/ — Merged into internal/server/api.go
  • internal/ssh/ — Deferred to future work
  • test/integration/ — Replaced by test/scenario/

Testing

  • 177 passing tests across 15 packages
  • Property tests: 55K+ random event sequences verify FSM invariants
  • Scenario tests: 7 named end-to-end paths (happy path, retry exhaustion, no-commits escalation, stall recovery, reconcile closed, budget exceeded, handoff protection)
  • Contract tests: Shared behavioral tests for all tracker implementations
  • Unit tests: Eligibility (per-status/per-repo concurrency), retry backoff, stall detection, budget enforcement, handoff evaluation, prompt routing, config parsing/validation

Acceptance Criteria

  • go build ./... succeeds
  • go test ./... -count=1 — all 177 tests pass
  • symphony init generates valid .symphony/ config
  • symphony doctor validates config, credentials, connectivity
  • symphony run --mock starts TUI with mock agent
  • Multi-tracker: GitHub and Linear adapters both functional
  • Multi-agent: Claude, OpenCode, Codex adapters all wired
  • Webhook endpoint accepts GitHub events with HMAC verification
  • Multi-turn agent sessions re-check state between turns
  • Graceful shutdown cancels all running agents

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions