Created
March 8, 2026 09:29
-
-
Save NgxuAnGMH/6546899a33f1ce6d07c504ad2b2122e1 to your computer and use it in GitHub Desktop.
Repository Wiki
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>symphony — Wiki</title> | |
| <script src="https://cdn.jsdelivr.net/npm/marked@11.0.0/marked.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script> | |
| <style> | |
| *{margin:0;padding:0;box-sizing:border-box} | |
| :root{ | |
| --bg:#ffffff;--sidebar-bg:#f8f9fb;--border:#e5e7eb; | |
| --text:#1e293b;--text-muted:#64748b;--primary:#2563eb; | |
| --primary-soft:#eff6ff;--hover:#f1f5f9;--code-bg:#f1f5f9; | |
| --radius:8px;--shadow:0 1px 3px rgba(0,0,0,.08); | |
| } | |
| body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; | |
| line-height:1.65;color:var(--text);background:var(--bg)} | |
| .layout{display:flex;min-height:100vh} | |
| .sidebar{width:280px;background:var(--sidebar-bg);border-right:1px solid var(--border); | |
| position:fixed;top:0;left:0;bottom:0;overflow-y:auto;padding:24px 16px; | |
| display:flex;flex-direction:column;z-index:10} | |
| .content{margin-left:280px;flex:1;padding:48px 64px;max-width:960px} | |
| .sidebar-header{margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--border)} | |
| .sidebar-title{font-size:16px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px} | |
| .sidebar-title svg{flex-shrink:0} | |
| .sidebar-meta{font-size:11px;color:var(--text-muted);margin-top:6px} | |
| .nav-section{margin-bottom:2px} | |
| .nav-item{display:block;padding:7px 12px;border-radius:var(--radius);cursor:pointer; | |
| font-size:13px;color:var(--text);text-decoration:none;transition:all .15s; | |
| white-space:nowrap;overflow:hidden;text-overflow:ellipsis} | |
| .nav-item:hover{background:var(--hover)} | |
| .nav-item.active{background:var(--primary-soft);color:var(--primary);font-weight:600} | |
| .nav-item.overview{font-weight:600;margin-bottom:4px} | |
| .nav-children{padding-left:14px;border-left:1px solid var(--border);margin-left:12px} | |
| .nav-group-label{font-size:11px;font-weight:600;color:var(--text-muted); | |
| text-transform:uppercase;letter-spacing:.5px;padding:12px 12px 4px;user-select:none} | |
| .sidebar-footer{margin-top:auto;padding-top:16px;border-top:1px solid var(--border); | |
| font-size:11px;color:var(--text-muted);text-align:center} | |
| .content h1{font-size:28px;font-weight:700;margin-bottom:8px;line-height:1.3} | |
| .content h2{font-size:22px;font-weight:600;margin:32px 0 12px;padding-bottom:6px;border-bottom:1px solid var(--border)} | |
| .content h3{font-size:17px;font-weight:600;margin:24px 0 8px} | |
| .content h4{font-size:15px;font-weight:600;margin:20px 0 6px} | |
| .content p{margin:12px 0} | |
| .content ul,.content ol{margin:12px 0 12px 24px} | |
| .content li{margin:4px 0} | |
| .content a{color:var(--primary);text-decoration:none} | |
| .content a:hover{text-decoration:underline} | |
| .content blockquote{border-left:3px solid var(--primary);padding:8px 16px;margin:16px 0; | |
| background:var(--primary-soft);border-radius:0 var(--radius) var(--radius) 0; | |
| color:var(--text-muted);font-size:14px} | |
| .content code{font-family:'SF Mono',Consolas,'Courier New',monospace;font-size:13px; | |
| background:var(--code-bg);padding:2px 6px;border-radius:4px} | |
| .content pre{background:#1e293b;color:#e2e8f0;border-radius:var(--radius);padding:16px; | |
| overflow-x:auto;margin:16px 0} | |
| .content pre code{background:none;padding:0;font-size:13px;line-height:1.6;color:inherit} | |
| .content table{border-collapse:collapse;width:100%;margin:16px 0} | |
| .content th,.content td{border:1px solid var(--border);padding:8px 12px;text-align:left;font-size:14px} | |
| .content th{background:var(--sidebar-bg);font-weight:600} | |
| .content img{max-width:100%;border-radius:var(--radius)} | |
| .content hr{border:none;border-top:1px solid var(--border);margin:32px 0} | |
| .content .mermaid{margin:20px 0;text-align:center} | |
| .menu-toggle{display:none;position:fixed;top:12px;left:12px;z-index:20; | |
| background:var(--bg);border:1px solid var(--border);border-radius:var(--radius); | |
| padding:8px 12px;cursor:pointer;font-size:18px;box-shadow:var(--shadow)} | |
| @media(max-width:768px){ | |
| .sidebar{transform:translateX(-100%);transition:transform .2s} | |
| .sidebar.open{transform:translateX(0);box-shadow:2px 0 12px rgba(0,0,0,.1)} | |
| .content{margin-left:0;padding:24px 20px;padding-top:56px} | |
| .menu-toggle{display:block} | |
| } | |
| .empty-state{text-align:center;padding:80px 20px;color:var(--text-muted)} | |
| .empty-state h2{font-size:20px;margin-bottom:8px;border:none} | |
| </style> | |
| </head> | |
| <body> | |
| <button class="menu-toggle" id="menu-toggle" aria-label="Toggle menu">☰</button> | |
| <div class="layout"> | |
| <nav class="sidebar" id="sidebar"> | |
| <div class="sidebar-header"> | |
| <div class="sidebar-title"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 014 4v14a3 3 0 00-3-3H2z"/><path d="M22 3h-6a4 4 0 00-4 4v14a3 3 0 013-3h7z"/></svg> | |
| symphony | |
| </div> | |
| <div class="sidebar-meta" id="meta-info"></div> | |
| </div> | |
| <div id="nav-tree"></div> | |
| <div class="sidebar-footer">Generated by GitNexus</div> | |
| </nav> | |
| <main class="content" id="content"> | |
| <div class="empty-state"><h2>Loading…</h2></div> | |
| </main> | |
| </div> | |
| <script> | |
| var PAGES = {"core-application-and-orchestration":"# Core Application and Orchestration\n\n# SymphonyElixir Core Application and Orchestration\n\n## Overview\n\nThe Core Application and Orchestration module is the central nervous system of SymphonyElixir, responsible for managing the complete lifecycle of automated agents working on Linear issues. This module orchestrates periodic polling of the Linear API, dispatches work to Codex-backed agents, monitors their progress, and handles retries and continuation logic.\n\nThe system follows a supervisor-based OTP architecture with configurable workflow definitions, isolated workspace management, and comprehensive observability.\n\n## Key Components\n\n### 1. OTP Application Entry Point (`SymphonyElixir.Application`)\n\nThe main application supervisor starts and links all core processes:\n\n```elixir\nchildren = [\n {Phoenix.PubSub, name: SymphonyElixir.PubSub},\n {Task.Supervisor, name: SymphonyElixir.TaskSupervisor},\n SymphonyElixir.WorkflowStore,\n SymphonyElixir.Orchestrator,\n SymphonyElixir.HttpServer,\n SymphonyElixir.StatusDashboard\n]\n```\n\n**Responsibilities:**\n- Sets up PubSub for inter-process communication\n- Creates a Task Supervisor for managing agent processes\n- Starts the WorkflowStore for configuration management\n- Launches the Orchestrator main loop\n- Optionally starts HTTP server for observability dashboard\n- Initializes the real-time status dashboard\n\n### 2. Orchestrator (`SymphonyElixir.Orchestrator`)\n\nThe core GenServer that implements the main orchestration logic.\n\n**Polling Loop:**\n- Periodically polls Linear for active issues (configurable interval)\n- Limits concurrent agents based on configuration\n- Sorts issues by priority and creation time\n- Validates issue state before dispatch\n\n**Agent Management:**\n- Spawns isolated agent processes via `SymphonyElixir.AgentRunner`\n- Monitors agent processes for crashes or stalls\n- Handles retries with exponential backoff\n- Tracks Codex token usage and rate limits\n\n**State Reconciliation:**\n- Regularly reconciles running issues against current Linear state\n- Stops agents when issues move to terminal states\n- Cleans up workspaces for completed issues\n\n```mermaid\nflowchart TD\n A[Orchestrator Tick] --> B[Poll Linear API]\n B --> C{Fetch Candidates}\n C -->|Issues| D[Sort & Validate]\n C -->|Error| Z[Log & Retry]\n D --> E{Dispatch Slots Available?}\n E -->|Yes| F[Start Agent Task]\n E -->|No| G[Wait Next Poll]\n F --> H[Monitor Agent]\n H --> I{Agent Completed?}\n I -->|Success| J[Check Continuation]\n I -->|Failure| K[Schedule Retry]\n J -->|Active| L[Schedule Continuation]\n J -->|Terminal| M[Clean Workspace]\n L --> G\n K --> G\n```\n\n### 3. Agent Runner (`SymphonyElixir.AgentRunner`)\n\nExecutes individual Linear issues in isolated workspaces with Codex.\n\n**Execution Flow:**\n1. Creates isolated workspace for the issue\n2. Runs pre-execution hooks (if configured)\n3. Starts Codex AppServer session\n4. Runs multiple turns (up to configured maximum)\n5. Continues if issue remains active after turn completion\n6. Runs post-execution hooks\n7. Cleans up workspace resources\n\n**Multi-turn Continuation:**\n```elixir\ndefp do_run_codex_turns(app_session, workspace, issue, recipient, opts, fetcher, turn_number, max_turns) do\n prompt = build_turn_prompt(issue, opts, turn_number, max_turns)\n \n # Run Codex turn\n with {:ok, turn_session} <- AppServer.run_turn(app_session, prompt, issue) do\n # Check if issue is still active\n case continue_with_issue?(issue, fetcher) do\n {:continue, refreshed_issue} when turn_number < max_turns ->\n # Run another turn\n do_run_codex_turns(app_session, workspace, refreshed_issue, ...)\n \n {:continue, _} ->\n # Reached max turns, return control\n :ok\n \n {:done, _} ->\n # Issue is terminal, complete\n :ok\n end\n end\nend\n```\n\n### 4. Workspace Management (`SymphonyElixir.Workspace`)\n\nProvides isolated filesystem environments for each agent execution:\n\n- Creates temporary directories under configured workspace root\n- Validates path security to prevent directory traversal\n- Executes configurable hooks at key lifecycle events:\n - `after_create`: After workspace creation\n - `before_run`: Before agent execution\n - `after_run`: After agent completion\n - `before_remove`: Before workspace cleanup\n- Cleans up temporary artifacts between runs\n\n### 5. Configuration System (`SymphonyElixir.Config`)\n\nLoads and validates configuration from `WORKFLOW.md` YAML frontmatter.\n\n**Key Configuration Sections:**\n- `tracker`: Linear API settings and issue state mappings\n- `polling`: Poll interval and scheduling\n- `workspace`: Workspace root directory and hooks\n- `agent`: Concurrency limits and retry policies\n- `codex`: Codex command and timeout settings\n- `observability`: Dashboard and monitoring settings\n\n**Environment Variable Support:**\n```yaml\ntracker:\n api_key: $LINEAR_API_KEY\n endpoint: https://api.linear.app/graphql\n```\n\n### 6. Workflow Management (`SymphonyElixir.Workflow` / `WorkflowStore`)\n\nManages workflow configuration with hot-reload capabilities:\n\n- Loads configuration and prompt template from `WORKFLOW.md`\n- Supports runtime configuration changes via file watching\n- Caches parsed configuration for performance\n- Provides template rendering for agent prompts\n\n## Execution Flow\n\n### Startup Sequence\n1. `SymphonyElixir.Application.start/2` initializes all supervisors\n2. `WorkflowStore` loads and caches `WORKFLOW.md` configuration\n3. `Orchestrator` begins polling loop\n4. `HttpServer` starts observability dashboard (if enabled)\n5. `StatusDashboard` begins rendering real-time status\n\n### Agent Dispatch Cycle\n1. Orchestrator polls Linear for active issues\n2. Filters and sorts candidate issues\n3. Validates issue is dispatchable (not blocked, in active state)\n4. Creates isolated workspace\n5. Spawns `AgentRunner` task\n6. Monitors task progress and handles completion\n\n### Agent Execution Cycle\n1. AgentRunner creates workspace and runs `before_run` hook\n2. Starts Codex AppServer session\n3. Builds initial prompt from issue data and workflow template\n4. Runs Codex turns until:\n - Issue reaches terminal state, OR\n - Maximum turns reached, OR\n - Error occurs\n5. Runs `after_run` hook\n6. Returns control to orchestrator\n\n## State Management\n\n### Orchestrator State (`Orchestrator.State`)\n```elixir\n%State{\n poll_interval_ms: 30000,\n max_concurrent_agents: 10,\n next_poll_due_at_ms: nil,\n poll_check_in_progress: false,\n running: %{}, # Map of issue_id -> running agent metadata\n completed: MapSet.new(), # Set of completed issue IDs\n claimed: MapSet.new(), # Set of issue IDs being processed\n retry_attempts: %{}, # Map of issue_id -> retry metadata\n codex_totals: %{}, # Aggregate Codex token usage\n codex_rate_limits: nil # Current Codex rate limits\n}\n```\n\n### Running Agent Metadata\nEach running agent tracks:\n- Process PID and monitor reference\n- Issue identifier and current state\n- Codex session ID and token usage\n- Turn count and timestamps\n- Retry attempt number\n- Last activity timestamp for stall detection\n\n## Error Handling and Retries\n\n### Retry Policies\n- **Continuation retries**: 1-second delay when agent completes but issue remains active\n- **Failure retries**: Exponential backoff up to configured maximum\n- **Stalled agents**: Restarted after `codex_stall_timeout_ms` of inactivity\n\n### Retry Scheduling\n```elixir\ndefp failure_retry_delay(attempt) do\n max_delay_power = min(attempt - 1, 10)\n min(@failure_retry_base_ms * (1 <<< max_delay_power), Config.max_retry_backoff_ms())\nend\n```\n\n### Issue State Reconciliation\nThe orchestrator regularly reconciles running agents against current Linear state:\n- Stops agents if issue moves to terminal state\n- Refreshes issue metadata if still active\n- Cleans up workspaces for terminal issues\n\n## Configuration-Driven Behavior\n\nAll orchestration behavior is driven by `WORKFLOW.md` configuration:\n\n```yaml\n# Example configuration\ntracker:\n kind: linear\n api_key: $LINEAR_API_KEY\n project_slug: PROJ\n active_states: [Todo, In Progress]\n terminal_states: [Closed, Done]\n\npolling:\n interval_ms: 30000\n\nagent:\n max_concurrent_agents: 5\n max_turns: 20\n max_retry_backoff_ms: 300000\n max_concurrent_agents_by_state:\n \"in progress\": 2\n\nworkspace:\n root: /tmp/symphony_workspaces\n hooks:\n before_run: ./scripts/setup.sh\n timeout_ms: 60000\n\ncodex:\n command: codex app-server\n turn_timeout_ms: 3600000\n stall_timeout_ms: 300000\n```\n\n## Observability\n\n### Real-time Dashboard\n- Shows running agents with token usage and runtime\n- Displays retry queue with delays and errors\n- Indicates polling status and next poll time\n- Tracks aggregate Codex token consumption\n\n### HTTP API\n- Provides snapshot endpoint for external monitoring\n- Supports manual refresh triggers\n- Returns structured orchestrator state\n\n### Logging\n- Structured logging with issue context\n- Configurable log levels and destinations\n- Workspace hook output capture and sanitization\n\n## Extension Points\n\n### Custom Hooks\nExtend agent behavior with shell scripts at workspace lifecycle events:\n- `after_create`: Initialize workspace environment\n- `before_run`: Setup dependencies or validation\n- `after_run`: Post-process results or cleanup\n- `before_remove`: Archive or backup workspace\n\n### Custom Trackers\nImplement alternative issue sources by:\n1. Creating a tracker module implementing the expected interface\n2. Configuring `tracker.kind` in workflow\n3. Ensuring `Tracker.fetch_candidate_issues/0` and `Tracker.fetch_issue_states_by_ids/1` are implemented\n\n### Custom Prompt Templates\nModify agent behavior by editing the prompt template in `WORKFLOW.md`:\n- Access issue data via Liquid template syntax\n- Include attempt number and turn context\n- Add domain-specific instructions or constraints\n\n## Performance Considerations\n\n- **Concurrency limits**: Prevent system overload with per-state agent limits\n- **Stall detection**: Automatically restart unresponsive agents\n- **Token tracking**: Monitor Codex usage and rate limits\n- **Workspace cleanup**: Regular cleanup of temporary directories\n- **Configuration caching**: Hot-reload without restart\n\nThe Core Application and Orchestration module provides a robust foundation for automated issue processing, with extensive configurability, comprehensive monitoring, and resilient error handling suitable for production use.","external-services-integration":"# External Services Integration\n\n# External Services Integration Module\n\n## Overview\n\nThe External Services Integration module provides Symphony with the ability to interact with third-party systems, primarily Linear for issue tracking and Codex for AI-powered code generation. This module implements a clean separation between external service APIs and internal Symphony orchestration logic.\n\n## Architecture\n\n```mermaid\ngraph TD\n A[Orchestrator] --> B[Linear Adapter]\n A --> C[Codex AppServer]\n \n B --> D[Linear Client]\n D --> E[Linear GraphQL API]\n \n C --> F[DynamicTool]\n F --> D\n C --> G[Codex Process via Stdio]\n \n style B fill:#e1f5fe\n style C fill:#f3e5f5\n```\n\n## Linear Integration\n\n### Adapter Pattern\n\nThe Linear integration follows the adapter pattern, implementing the `SymphonyElixir.Tracker` behavior to provide a consistent interface for issue tracking operations:\n\n```elixir\n@behaviour SymphonyElixir.Tracker\n```\n\n### Key Functions\n\n#### Issue Retrieval\n- `fetch_candidate_issues/0` - Fetches issues configured as \"active\" in the current project\n- `fetch_issues_by_states/1` - Retrieves issues by specific state names\n- `fetch_issue_states_by_ids/1` - Gets state information for specific issue IDs\n\n#### Issue Operations\n- `create_comment/2` - Adds comments to Linear issues\n- `update_issue_state/2` - Transitions issues between workflow states\n\n### State Resolution Mechanism\n\nA notable feature is the dynamic state ID resolution: when updating an issue's state, the adapter first queries Linear to find the state ID by name within the issue's team context:\n\n```elixir\ndefp resolve_state_id(issue_id, state_name) do\n # Queries Linear for the team's state by name\n # Returns the state ID for use in the update mutation\nend\n```\n\n### Configuration Flexibility\n\nThe adapter supports different deployment scenarios through configurable client modules:\n\n```elixir\ndefp client_module do\n Application.get_env(:symphony_elixir, :linear_client_module, Client)\nend\n```\n\n## Linear Client\n\nThe client module handles GraphQL communication with Linear's API, implementing pagination, error handling, and data normalization.\n\n### Pagination Strategy\n\nThe client implements cursor-based pagination for large issue result sets:\n\n```elixir\ndefp do_fetch_by_states_page(project_slug, state_names, assignee_filter, after_cursor, acc_issues) do\n # Fetch one page\n # Check pageInfo.hasNextPage\n # Recursively fetch next page if needed\n # Accumulate results\nend\n```\n\n### Issue Normalization\n\nRaw Linear issue data is transformed into a consistent internal representation:\n\n```elixir\n%Issue{\n id: issue[\"id\"],\n identifier: issue[\"identifier\"],\n title: issue[\"title\"],\n description: issue[\"description\"],\n priority: parse_priority(issue[\"priority\"]),\n state: get_in(issue, [\"state\", \"name\"]),\n # ... additional fields\n}\n```\n\n### Assignee Filtering\n\nThe client supports sophisticated assignee filtering:\n\n- **Specific user IDs**: Filter issues assigned to particular Linear users\n- **\"me\" keyword**: Automatically resolves to the authenticated viewer\n- **Unassigned issues**: When no filter is applied\n\n### Error Handling\n\nRobust error handling includes:\n- Graceful handling of missing API tokens\n- Logging with truncated error bodies to prevent log flooding\n- Retry-compatible error tuples\n\n## Codex Integration\n\n### AppServer Client\n\nThe `Codex.AppServer` module manages communication with the Codex AI assistant via JSON-RPC 2.0 over stdio.\n\n#### Session Lifecycle\n\n```elixir\n1. start_session/1 # Initialize Codex process\n2. run_turn/4 # Execute a single turn\n3. stop_session/1 # Clean up resources\n```\n\n#### Message Protocol\n\nThe client implements a full JSON-RPC 2.0 protocol handler:\n\n- **Initialization**: Establishes connection with capabilities exchange\n- **Thread management**: Creates isolated conversation threads\n- **Turn execution**: Processes AI prompts and handles responses\n- **Stream processing**: Manages real-time message streaming\n\n#### Approval Workflow\n\nThe orchestrator can operate in different approval modes:\n\n- **Non-interactive**: Auto-approves all operations (default)\n- **Interactive**: Requires human approval for certain operations\n\n```elixir\ndefp approve_or_require(port, id, decision, payload, payload_string, on_message, metadata, true) do\n # Auto-approve when auto_approve_requests is true\n send_message(port, %{\"id\" => id, \"result\" => %{\"decision\" => decision}})\nend\n```\n\n### Dynamic Tool System\n\nThe `DynamicTool` module enables Codex to execute client-side operations, currently focused on Linear GraphQL queries:\n\n```elixir\ndef execute(@linear_graphql_tool, arguments, opts \\\\ []) do\n # Executes raw GraphQL against Linear\n # Returns structured results to Codex\nend\n```\n\n#### Tool Specification\n\nTools are declared to Codex with schema definitions:\n\n```elixir\ndef tool_specs do\n [\n %{\n \"name\" => \"linear_graphql\",\n \"description\" => \"Execute a raw GraphQL query...\",\n \"inputSchema\" => %{\n \"type\" => \"object\",\n \"required\" => [\"query\"],\n # ... schema definition\n }\n }\n ]\nend\n```\n\n## Integration Points\n\n### Configuration Dependencies\n\nBoth services require external configuration:\n\n```elixir\n# Linear requires:\nConfig.linear_api_token()\nConfig.linear_project_slug()\nConfig.linear_active_states()\nConfig.linear_assignee()\n\n# Codex requires:\nConfig.codex_command() # Command to start Codex\nConfig.codex_runtime_settings() # Approval policies, sandbox settings\nConfig.workspace_root() # Workspace boundary\n```\n\n### Data Flow\n\n1. **Orchestrator triggers** → Linear Adapter fetches candidate issues\n2. **Selected issue** → Codex session created with workspace context\n3. **Codex execution** → May call DynamicTool for Linear operations\n4. **Completion** → Linear Adapter updates issue state/comments\n\n## Error Handling Strategy\n\n### Linear Error Categories\n\n1. **Configuration errors**: Missing API tokens, project slugs\n2. **API errors**: HTTP failures, GraphQL errors\n3. **Data errors**: Missing states, malformed responses\n\n### Codex Error Categories\n\n1. **Process errors**: Codex binary not found, process crashes\n2. **Protocol errors**: Malformed JSON, unexpected messages\n3. **Timeout errors**: Long-running operations exceeding limits\n4. **Workspace errors**: Invalid directory permissions or paths\n\n## Testing Support\n\nThe modules include testing utilities:\n\n- `normalize_issue_for_test/1-2` - Issue data normalization\n- `next_page_cursor_for_test/1` - Pagination cursor testing\n- `merge_issue_pages_for_test/1` - Page merging logic\n\n## Security Considerations\n\n### Workspace Isolation\n\nCodex sessions are strictly confined to workspace boundaries:\n\n```elixir\ndefp validate_workspace_cwd(workspace) do\n # Ensures workspace is within configured root\n # Prevents path traversal attacks\nend\n```\n\n### Credential Management\n\n- Linear tokens are passed via Authorization headers\n- Codex processes run with limited permissions\n- All external calls include timeouts to prevent hanging\n\n## Performance Characteristics\n\n### Linear API Usage\n\n- Batch fetching with configurable page sizes (default: 50)\n- Parallelizable state resolution for multiple issues\n- Cursor-based pagination minimizes memory usage\n\n### Codex Process Management\n\n- Single-use processes per session (clean isolation)\n- Configurable timeouts for turn execution\n- Line-based streaming with size limits\n\n## Extending the Module\n\n### Adding New External Services\n\n1. Implement a new adapter following the Tracker behavior\n2. Add client modules for API communication\n3. Integrate with configuration system\n4. Add to DynamicTool if Codex access is needed\n\n### Adding New Tools to Codex\n\n1. Define tool specification in `DynamicTool.tool_specs/0`\n2. Implement execution in `DynamicTool.execute/3`\n3. Ensure proper error handling and response formatting\n\n## Common Usage Patterns\n\n### Basic Issue Processing\n\n```elixir\n# Fetch active issues\n{:ok, issues} = Linear.Adapter.fetch_candidate_issues()\n\n# Process each with Codex\nfor issue <- issues do\n {:ok, _result} = Codex.AppServer.run(workspace, prompt, issue)\n \n # Update issue state\n :ok = Linear.Adapter.update_issue_state(issue.id, \"Done\")\nend\n```\n\n### Custom Tool Integration\n\n```elixir\n# Custom tool executor for Codex\ntool_executor = fn tool, arguments ->\n case tool do\n \"custom_tool\" -> handle_custom_tool(arguments)\n _ -> DynamicTool.execute(tool, arguments)\n end\nend\n\n# Use custom executor in Codex session\nCodex.AppServer.run(workspace, prompt, issue, tool_executor: tool_executor)\n```\n\n## Troubleshooting\n\n### Common Issues\n\n1. **Linear authentication failures**: Check `LINEAR_API_KEY` environment variable\n2. **Codex process crashes**: Verify `codex_command` configuration\n3. **Workspace permission errors**: Ensure directories exist and are accessible\n4. **GraphQL query failures**: Check Linear API compatibility and query syntax\n\n### Logging and Diagnostics\n\n- Both modules emit structured logs with context\n- Error responses include truncated bodies for debugging\n- Session metadata includes process IDs for correlation\n\nThis module forms the bridge between Symphony's orchestration logic and the external systems it coordinates, providing robust, configurable integration points while maintaining security and performance considerations.","mix-tasks":"# Mix Tasks\n\n# Mix Tasks Documentation\n\nThis module provides custom Mix tasks for development workflow automation, code quality enforcement, and repository maintenance in the Symphony project. These tasks are designed to be run during development, CI/CD pipelines, and workspace management.\n\n## Overview\n\nThe module contains three distinct Mix tasks that serve different purposes in the development workflow:\n\n1. **`mix pr_body.check`** - Validates pull request descriptions against the repository template\n2. **`mix specs.check`** - Enforces type specifications for public API functions\n3. **`mix workspace.before_remove`** - Cleans up GitHub pull requests before workspace removal\n\n## Task: `mix pr_body.check`\n\n### Purpose\n\nValidates that pull request descriptions follow the repository's PR template structure, ensuring consistent documentation and required information in all pull requests.\n\n### Usage\n\n```bash\nmix pr_body.check --file /path/to/pr_body.md\n```\n\n### How It Works\n\nThe task performs a multi-step validation process:\n\n1. **Template Discovery**: Searches for the PR template at standard locations (`.github/pull_request_template.md`)\n2. **Template Analysis**: Extracts all markdown headings (####, #####, ######) from the template\n3. **Validation**: Compares the PR body against the template using several checks\n\n#### Validation Rules\n\n```mermaid\nflowchart TD\n A[Start Validation] --> B{Template exists?}\n B -->|No| C[Error: Template not found]\n B -->|Yes| D[Read PR body]\n D --> E[Extract template headings]\n E --> F{All headings present?}\n F -->|No| G[Error: Missing headings]\n F -->|Yes| H{Headings in order?}\n H -->|No| I[Error: Headings out of order]\n H -->|Yes| J{Placeholders removed?}\n J -->|No| K[Error: Contains placeholders]\n J -->|Yes| L[Check section formatting]\n L --> M[Success: PR body valid]\n```\n\nThe task enforces these specific requirements:\n\n1. **Required Headings**: All headings from the template must be present in the PR body\n2. **Heading Order**: Headings must appear in the same order as in the template\n3. **No Placeholders**: The PR body must not contain any `<!-- ... -->` template comments\n4. **Section Formatting**: Each section must match the template's formatting requirements:\n - Bullet lists (if template uses `- `)\n - Checkboxes (if template uses `- [ ] `)\n - Non-empty content\n\n### Configuration\n\nThe task searches for templates at these locations (in order):\n- `.github/pull_request_template.md`\n- `../.github/pull_request_template.md`\n\n## Task: `mix specs.check`\n\n### Purpose\n\nEnforces that all public functions in the `lib/` directory have adjacent `@spec` type declarations, maintaining code quality and documentation consistency.\n\n### Usage\n\n```bash\n# Check default paths (lib/)\nmix specs.check\n\n# Check specific directories\nmix specs.check --paths lib/my_app lib/other\n\n# Use exemptions file\nmix specs.check --exemptions-file .specs_exemptions\n```\n\n### How It Works\n\nThe task delegates the actual checking to `SymphonyElixir.SpecsCheck` but provides the command-line interface and error reporting:\n\n1. **Path Scanning**: Scans specified directories (default: `[\"lib\"]`) for `.ex` files\n2. **Function Analysis**: Identifies public functions without adjacent `@spec` declarations\n3. **Exemption Support**: Allows specific functions to be excluded via an exemptions file\n4. **Error Reporting**: Outputs formatted errors with file locations and function identifiers\n\n### Exemptions File Format\n\nCreate a `.specs_exemptions` file with one function identifier per line:\n```\nMyModule.my_function/1\nOtherModule.other_function/0\n# Comments are allowed\n```\n\n### Integration with CI/CD\n\nThis task is designed to fail the build when violations are found, making it suitable for CI/CD pipelines that enforce code quality standards.\n\n## Task: `mix workspace.before_remove`\n\n### Purpose\n\nAutomatically closes open GitHub pull requests associated with the current branch when a development workspace is being removed, preventing stale PRs from accumulating.\n\n### Usage\n\n```bash\n# Close PRs for current branch in default repo\nmix workspace.before_remove\n\n# Specify branch and repository\nmix workspace.before_remove --branch feature/my-feature --repo openai/symphony\n```\n\n### How It Works\n\nThe task integrates with the GitHub CLI (`gh`) to manage pull requests:\n\n1. **Branch Detection**: Determines the current Git branch (or uses provided branch)\n2. **GitHub CLI Validation**: Ensures `gh` is installed and authenticated\n3. **PR Discovery**: Lists all open PRs for the specified branch and repository\n4. **PR Closure**: Closes each PR with an explanatory comment\n\n```mermaid\nflowchart TD\n A[Start] --> B{Is gh available?}\n B -->|No| C[Skip: GitHub CLI not installed]\n B -->|Yes| D{Is gh authenticated?}\n D -->|No| E[Skip: Not authenticated]\n D -->|Yes| F[List open PRs for branch]\n F --> G{Found PRs?}\n G -->|No| H[Done: No PRs to close]\n G -->|Yes| I[Close each PR with comment]\n I --> J[Done: PRs closed]\n```\n\n### Default Repository\n\nThe task defaults to the `openai/symphony` repository but can be overridden with the `--repo` flag.\n\n### Error Handling\n\nThe task gracefully handles various failure scenarios:\n- GitHub CLI not installed → skips PR operations\n- Authentication issues → skips PR operations\n- Git repository issues → falls back to no branch\n- Individual PR closure failures → continues with remaining PRs\n\n### Comment Template\n\nWhen closing PRs, the task adds this comment:\n> \"Closing because the Linear issue for branch {branch} entered a terminal state without merge.\"\n\n## Common Patterns\n\n### Command-Line Interface\n\nAll tasks follow consistent CLI patterns:\n- Use `OptionParser` for argument parsing\n- Support `--help` flags\n- Provide clear error messages for invalid options\n- Return appropriate exit codes (success/failure)\n\n### Error Reporting\n\nTasks use `Mix.raise/1` for fatal errors and `Mix.shell().error/1` for non-fatal reporting, ensuring proper integration with Mix's error handling.\n\n### Integration Points\n\nThese tasks are designed to integrate with:\n- **Development workflows**: Run manually during PR creation\n- **CI/CD pipelines**: Automated quality checks\n- **Workspace management**: Hook into workspace lifecycle events\n- **Editor/IDE integrations**: Can be triggered from development tools\n\n## Development and Extension\n\n### Adding New Tasks\n\nTo add a new Mix task:\n1. Create a module in `lib/mix/tasks/` following the naming convention `Mix.Tasks.YourTask`\n2. Use `use Mix.Task` and implement `run/1`\n3. Define `@shortdoc` and `@moduledoc` for documentation\n4. Follow the existing patterns for CLI parsing and error handling\n\n### Testing Considerations\n\nWhen modifying these tasks:\n- Test with both valid and invalid inputs\n- Verify error messages are user-friendly\n- Ensure task failures propagate correctly for CI/CD usage\n- Consider edge cases like missing files, network issues, or tool dependencies\n\n### Dependencies\n\n- `mix pr_body.check`: Only requires file system access\n- `mix specs.check`: Depends on `SymphonyElixir.SpecsCheck` module\n- `mix workspace.before_remove`: Requires GitHub CLI (`gh`) and Git\n\n## Usage in Symphony Workflow\n\nThese tasks are integrated into the Symphony development workflow:\n\n1. **PR Creation**: Developers run `mix pr_body.check` locally before creating PRs\n2. **Code Review**: CI runs `mix specs.check` to enforce type specifications\n3. **Workspace Cleanup**: The workspace manager calls `mix workspace.before_remove` automatically\n\nThe tasks help maintain consistent code quality and prevent common workflow issues, particularly in distributed teams with frequent PR creation and workspace management.","observability-and-logging":"# Observability and Logging\n\n# Observability and Logging Module\n\n## Overview\n\nThe Observability and Logging module provides real-time monitoring, structured logging, and a terminal dashboard for Symphony, an Elixir-based orchestration system. It combines disk-based log rotation with a live terminal UI that displays agent activity, rate limits, token usage, and system health.\n\n## Core Components\n\n### 1. LogFile: Rotating Disk Logs\n\nThe `SymphonyElixir.LogFile` module configures OTP's built-in rotating disk log handler (`:logger_disk_log_h`) to provide persistent, rotation-based logging.\n\n#### Key Features\n\n- **Automatic log rotation** based on file size and count\n- **Configurable limits**: Default 10MB per file, 5 files max\n- **Single-line formatting** for easier parsing\n- **Graceful fallback** to console logging if disk log fails\n\n#### Configuration\n\n```elixir\n# config/config.exs\nconfig :symphony_elixir,\n log_file: \"log/symphony.log\", # Default: \"log/symphony.log\"\n log_file_max_bytes: 10 * 1024 * 1024, # 10MB\n log_file_max_files: 5 # Keep 5 rotated files\n```\n\n#### Usage\n\n```elixir\n# Automatically called during application startup\nSymphonyElixir.LogFile.configure()\n```\n\nThe module:\n1. Expands the log file path and creates directories\n2. Removes any existing disk log handler\n3. Configures the new handler with wrap-type rotation\n4. Optionally removes the default console handler\n\n### 2. StatusDashboard: Terminal UI\n\nThe `SymphonyElixir.StatusDashboard` is a GenServer that provides a real-time terminal dashboard showing:\n\n- Active agents and their states\n- Token usage and throughput (tokens per second)\n- Rate limit status\n- Backoff queue for retries\n- Project links and refresh schedules\n\n#### Architecture Flow\n\n```mermaid\ngraph LR\n A[Dashboard Start] --> B[Schedule Tick]\n B --> C{Enabled?}\n C -- Yes --> D[Fetch Snapshot]\n C -- No --> E[Skip Render]\n D --> F[Calculate TPS]\n F --> G{Data Changed?}\n G -- Yes --> H[Format Content]\n G -- No --> I[Check Periodic Render]\n H --> J[Render Now?]\n J -- Yes --> K[Render Immediately]\n J -- No --> L[Schedule Delayed Render]\n K --> B\n L --> B\n```\n\n#### State Management\n\nThe dashboard uses several optimization strategies:\n\n1. **Throttled Updates**: Only renders when data changes or after minimum idle interval\n2. **Debounced Rendering**: Batches rapid updates to prevent terminal flicker\n3. **Smart Refresh**: Configurable refresh rates (default: 1 second)\n4. **Runtime Configuration**: Can be enabled/disabled via app config\n\n#### Display Format\n\nThe dashboard renders a border-styled terminal output with color-coded sections:\n\n```\n╭─ SYMPHONY STATUS\n│ Agents: 3/10\n│ Throughput: 142 tps\n│ Runtime: 15m 32s\n│ Tokens: in 12,345 | out 8,901 | total 21,246\n│ Rate Limits: primary 75% / 60m | secondary 25% / 60m | credits unlimited\n│ Project: https://linear.app/project/my-project/issues\n│ Dashboard: http://127.0.0.1:4000/\n│ Next refresh: 45s\n├─ Running\n│ ID STAGE PID AGE / TURN TOKENS SESSION EVENT\n│ ──────────────────────────────────────────────────────────────────────────────\n│ ● ISSUE-1 task_started #PID<0.1> 2m 15s / 3 1,234 abc...def task started\n├─ Backoff queue\n│ ↻ ISSUE-2 attempt=2 in 12.345s error=rate limit exceeded\n╰─\n```\n\n#### Event Humanization\n\nThe dashboard includes sophisticated event message parsing that converts raw Codex events into human-readable strings:\n\n- `\"codex/event/task_started\"` → `\"task started\"`\n- `\"item/commandExecution/requestApproval\"` → `\"command approval requested (ls -la)\"`\n- `\"turn/failed\"` → `\"turn failed: connection timeout\"`\n\n#### Configuration\n\n```elixir\n# config/config.exs\nconfig :symphony_elixir,\n observability_enabled?: true, # Enable/disable dashboard\n observability_refresh_ms: 1000, # Data refresh rate\n observability_render_interval_ms: 250 # Minimum render interval\n```\n\n## Integration Points\n\n### With Orchestrator\n\nThe dashboard fetches real-time data from the `SymphonyElixir.Orchestrator` module:\n\n```elixir\n# snapshot_payload/0 calls:\nOrchestrator.snapshot()\n# Returns: %{running: [...], retrying: [...], codex_totals: %{...}}\n```\n\n### With HTTP Server\n\nThe dashboard constructs dashboard URLs using the HTTP server's bound port:\n\n```elixir\nHttpServer.bound_port() # Gets actual port for localhost links\n```\n\n### With Configuration System\n\nAll settings can be overridden via `SymphonyElixir.Config`:\n\n```elixir\nConfig.observability_enabled?()\nConfig.observability_refresh_ms()\nConfig.observability_render_interval_ms()\n```\n\n## Message Processing Pipeline\n\nThe dashboard's event processing follows this pattern:\n\n```\nRaw Codex Event\n ↓\nunwrap_codex_message_payload/1\n ↓\nhumanize_codex_event/3 OR humanize_codex_payload/1\n ↓\nTruncation & Sanitization\n ↓\nFormatted Display String\n```\n\n## Performance Considerations\n\n### Throughput Calculation\n\nToken throughput is calculated using a rolling window approach:\n\n```elixir\n# samples: [{timestamp_ms, total_tokens}, ...]\n# window: @throughput_window_ms (5 seconds)\ntps = (current_tokens - oldest_tokens) / (time_elapsed / 1000)\n```\n\n### Memory Efficiency\n\n- Token samples are automatically pruned outside the graph window\n- Sparkline graphs use bucketed averages for constant memory usage\n- String operations use binary pattern matching for efficiency\n\n## Development Tools\n\n### SpecsCheck (Internal)\n\nThe `SymphonyElixir.SpecsCheck` module is a development utility that:\n\n- Scans `.ex` files for missing `@spec` annotations on public functions\n- Supports exemption lists for specific functions\n- Provides structured findings for CI/CD integration\n\n```bash\n# Example usage in mix task\nfindings = SpecsCheck.missing_public_specs([\"lib/\"], exemptions: [\"Module.func/1\"])\n```\n\n## Testing Support\n\nThe module includes test helper functions:\n\n```elixir\nSymphonyElixir.StatusDashboard.format_snapshot_content_for_test(snapshot, tps)\nSymphonyElixir.StatusDashboard.dashboard_url_for_test(host, port, bound_port)\nSymphonyElixir.StatusDashboard.format_running_summary_for_test(entry)\n```\n\n## Error Handling\n\nThe module implements defensive programming:\n\n1. **Graceful degradation**: Falls back to console logging if disk log fails\n2. **Resilient rendering**: Catches and logs rendering errors without crashing\n3. **Missing data handling**: Shows \"unavailable\" states when orchestrator isn't running\n4. **Terminal compatibility**: Adapts to terminal width, falls back to defaults\n\n## Best Practices\n\n### When to Enable Dashboard\n\n- **Development**: Always enabled for real-time feedback\n- **Production**: Consider disabling or increasing render intervals\n- **CI/CD**: Disable via `observability_enabled?: false`\n\n### Log Management\n\n- Monitor disk space for log rotation\n- Adjust `log_file_max_bytes` based on deployment scale\n- Consider log aggregation for distributed deployments\n\n### Customization\n\nOverride the render function for custom output:\n\n```elixir\nSymphonyElixir.StatusDashboard.start_link(\n render_fun: &MyCustomRenderer.render/1\n)\n```\n\n## Related Modules\n\n- `SymphonyElixir.Orchestrator`: Data source for dashboard\n- `SymphonyElixir.Config`: Configuration management\n- `SymphonyElixirWeb.ObservabilityPubSub`: Update notifications\n- `SymphonyElixir.HttpServer`: Dashboard URL construction\n\nThe Observability and Logging module provides comprehensive visibility into Symphony's operations, balancing detailed logging with performant real-time monitoring through an adaptive terminal interface.","other-config":"# Other — config\n\n# SymphonyElixir Configuration Module\n\n## Overview\n\nThe `config/config.exs` file serves as the primary runtime configuration for the SymphonyElixir Phoenix application. This configuration file sets up essential application parameters, endpoint settings, and third-party library integrations that govern how the application behaves in different environments.\n\n## Purpose\n\nThis configuration module establishes the foundation for the SymphonyElixir web application by:\n- Defining the JSON library for Phoenix serialization\n- Configuring the HTTP endpoint with specific adapter settings\n- Setting up error handling formats and templates\n- Establishing security and runtime parameters for development\n\n## Configuration Breakdown\n\n### JSON Library Configuration\n\n```elixir\nconfig :phoenix, :json_library, Jason\n```\n\nThis directive configures Phoenix to use **Jason** as its default JSON encoding/decoding library. Jason is a high-performance JSON parser and generator written in pure Elixir, offering significant performance advantages over the traditional Poison library.\n\n### Endpoint Configuration\n\nThe main application endpoint is configured with the following parameters:\n\n#### Adapter Settings\n```elixir\nadapter: Bandit.PhoenixAdapter\n```\nThe application uses **Bandit** as the HTTP adapter instead of the default Cowboy. Bandit is a pure-Elixir HTTP server optimized for Phoenix applications, offering improved performance and better integration with the Elixir ecosystem.\n\n#### Network Configuration\n```elixir\nurl: [host: \"localhost\"]\n```\nSets the base URL for the application. In development, this defaults to localhost. In production environments, this would typically be set to the application's domain name.\n\n#### Error Handling\n```elixir\nrender_errors: [\n formats: [html: SymphonyElixirWeb.ErrorHTML, json: SymphonyElixirWeb.ErrorJSON],\n layout: false\n]\n```\nConfigures error rendering with the following behavior:\n- **HTML errors**: Rendered using `SymphonyElixirWeb.ErrorHTML`\n- **JSON errors**: Rendered using `SymphonyElixirWeb.ErrorJSON`\n- **Layout disabled**: Error pages render without the application layout wrapper\n\n#### PubSub System\n```elixir\npubsub_server: SymphonyElixir.PubSub\n```\nSpecifies the PubSub server for real-time functionality. This enables WebSocket connections, LiveView updates, and channel-based communication throughout the application.\n\n#### LiveView Configuration\n```elixir\nlive_view: [signing_salt: \"symphony-live-view\"]\n```\nSets the signing salt for Phoenix LiveView, which is used to cryptographically sign messages between the client and server. In production, this should be a long, random string.\n\n#### Security Configuration\n```elixir\nsecret_key_base: String.duplicate(\"s\", 64),\ncheck_origin: false\n```\n- **secret_key_base**: Provides a development-only secret key for signing and encryption. The `String.duplicate(\"s\", 64)` creates a 64-character string of \"s\" characters - suitable for development but must be replaced with a cryptographically secure random string in production.\n- **check_origin**: Disabled (`false`) in development to simplify testing across different origins. This should be enabled in production with specific origins configured.\n\n#### Server Settings\n```elixir\nserver: false\n```\nDisables automatic HTTP server startup. This allows control over when the server starts, typically managed via mix tasks like `mix phx.server` or within IEx sessions.\n\n## Environment-Specific Configuration\n\nThis base configuration is extended by environment-specific files:\n- `config/dev.exs` - Development-specific settings\n- `config/test.exs` - Test environment configurations \n- `config/prod.exs` - Production deployment settings\n- `config/runtime.exs` - Runtime configuration (evaluated at boot time)\n\n## Key Design Decisions\n\n1. **Bandit over Cowboy**: The choice of Bandit reflects a preference for pure-Elixir solutions that integrate better with the BEAM VM's scheduling and concurrency model.\n\n2. **Development-Focused Security**: The configuration prioritizes developer convenience in development (`check_origin: false`, simple `secret_key_base`) while expecting proper security configuration in production files.\n\n3. **Modular Error Handling**: Separate modules for HTML and JSON error responses allow for clean separation of concerns and easier customization of error presentation.\n\n## Integration with the Codebase\n\nThis configuration file connects to several key application components:\n\n- **SymphonyElixirWeb.Endpoint**: The main web endpoint module that uses these settings\n- **SymphonyElixirWeb.ErrorHTML/ErrorJSON**: Custom error rendering modules\n- **SymphonyElixir.PubSub**: The application's real-time messaging system\n- **Phoenix Framework**: Provides the underlying web framework infrastructure\n- **Bandit**: The HTTP server implementation\n\n## Usage Notes\n\nWhen working with this configuration:\n\n1. **Development**: This configuration provides sensible defaults for local development\n2. **Production**: Critical values like `secret_key_base` and `signing_salt` must be overridden with secure values in `config/prod.exs` or via environment variables\n3. **Testing**: The `server: false` setting allows test suites to control server lifecycle\n4. **Customization**: Additional configuration can be added for specific application needs, such as database connections, external API keys, or feature flags\n\n## See Also\n\n- `config/dev.exs` - Development environment overrides\n- `lib/symphony_elixir_web/endpoint.ex` - Endpoint implementation\n- Phoenix Framework Configuration Guide\n- Bandit HTTP Server Documentation","other-docs":"# Other — docs\n\n# Documentation Module\n\n## Overview\n\nThe **docs** module contains technical documentation and implementation guides for the Symphony codebase. Unlike traditional code modules, this directory houses critical reference material that establishes standards, explains complex systems, and provides guidance for developers working on the platform. These documents serve as authoritative sources for understanding how Symphony should handle specific technical concerns, particularly around observability and external system integration.\n\n## Purpose and Scope\n\nThis module exists to:\n- **Establish standards** for consistent implementation across the codebase\n- **Document complex integrations** with external systems like Codex\n- **Provide implementation guidance** for avoiding common pitfalls\n- **Serve as reference material** for both new and experienced developers\n\nThe documents in this module are considered \"living documentation\" - they evolve alongside the codebase and represent the current best practices and understanding of the systems they describe.\n\n## Key Documents\n\n### Logging Best Practices (`logging.md`)\n\nThis document defines the conventions for structured logging within Symphony, specifically designed to enable Codex to diagnose failures efficiently.\n\n#### Core Principles\n- **Searchability**: Logs must be searchable by issue and session identifiers\n- **Context Capture**: Include sufficient execution context to identify root causes without requiring reproduction\n- **Stability**: Maintain consistent message formats to ensure dashboard and alert reliability\n\n#### Critical Context Fields\nThe document specifies which identifiers must be included in different scenarios:\n\n| Context Type | Required Fields | Purpose |\n|-------------|----------------|----------|\n| Issue-related work | `issue_id`, `issue_identifier` | Link logs to Linear tickets |\n| Codex execution | `session_id` | Track specific Codex thread/turn execution |\n\n#### Implementation Guidance\nThe document provides specific guidance for different components:\n- **AgentRunner**: Log start/completion/failure with issue context\n- **Orchestrator**: Log dispatch, retry, transitions, and worker exits\n- **Codex.AppServer**: Log session lifecycle events\n\n#### Quality Checklist\nEvery new log statement should be evaluated against:\n1. Is this tied to a Linear issue? → Include `issue_id` and `issue_identifier`\n2. Is this tied to a Codex session? → Include `session_id`\n3. Is the failure reason present and concise?\n4. Is the message format consistent with existing lifecycle logs?\n\n### Token Accounting (`token_accounting.md`)\n\nThis is a critical technical specification that explains how Symphony should interpret and handle token usage data from Codex. Misunderstanding these semantics can lead to incorrect token counting and billing.\n\n#### Core Semantics\nThe document explains Codex's token usage model:\n\n```mermaid\nflowchart TD\n A[Codex Core emits<br>TokenCountEvent] --> B{Contains<br>TokenUsageInfo}\n B --> C[total_token_usage<br>Cumulative thread total]\n B --> D[last_token_usage<br>Latest increment only]\n C --> E[thread/tokenUsage/updated<br>Primary source for totals]\n D --> F[Ignore for accounting<br>Not cumulative]\n```\n\n#### Key Distinctions\n- **`total_token_usage`**: Absolute cumulative total for the thread (use for accounting)\n- **`last_token_usage`**: Latest increment only (ignore for totals)\n- **`turn/completed.usage`**: Event-specific payload (not thread-total)\n\n#### Implementation Strategy\nThe document provides a concrete accounting algorithm:\n\n1. **Track per thread**: Maintain `absolute_total` and `accumulated_total` per `thread_id`\n2. **Source precedence**: Prefer `thread/tokenUsage/updated.tokenUsage.total` over other sources\n3. **Ignore deltas**: Do not use `last` values for cumulative totals\n4. **Avoid double-counting**: Turn-completed usage should not be added to already-counted thread totals\n\n#### Critical Warnings\n- Never classify a `usage` map by field name alone\n- Multiple turns can occur on one thread - do not reset accounting between turns\n- Turn-completed `usage` is not additive to thread totals unless proven otherwise\n- `model_context_window` is a limit, not spend - display separately\n\n## Relationship to Codebase\n\nThese documents serve as the specification for multiple Symphony components:\n\n### Code Dependencies\nWhile the docs module doesn't contain executable code, it influences:\n- **Logging infrastructure**: All logging calls should follow `logging.md` conventions\n- **Token tracking systems**: Token accounting logic must implement `token_accounting.md` strategy\n- **Observability tools**: Dashboards and alerts rely on consistent field naming\n- **API contracts**: External reporting of token usage follows documented semantics\n\n### Cross-Reference Points\nThe documents reference actual Codex source files and protocols, making them essential for:\n- Understanding Codex integration points\n- Implementing correct token accounting logic\n- Debugging issues with Codex communication\n- Maintaining compatibility with Codex updates\n\n## Usage Guidelines\n\n### For Developers\n1. **Read before implementing**: Consult relevant documents before working on logging or token-related features\n2. **Update when discovering new information**: Documents should evolve with the codebase\n3. **Reference in code comments**: Link to these documents when implementing complex logic\n4. **Use as QA checklist**: Verify implementations against documented standards\n\n### For Reviewers\n1. **Enforce standards**: Reject code that violates documented best practices\n2. **Check for consistency**: Ensure new code follows existing patterns\n3. **Validate token logic**: Token accounting is particularly error-prone - verify against the spec\n\n### For Onboarding\nThese documents serve as essential reading for:\n- New engineers joining the Symphony team\n- Developers needing to understand Codex integration\n- Anyone working on observability or billing features\n\n## Maintenance and Evolution\n\n### When to Update\n- When Codex protocol changes affect token semantics\n- When new logging requirements emerge\n- When bugs reveal gaps in current documentation\n- When team conventions evolve\n\n### Update Process\n1. Identify the need for change through implementation experience or bug discovery\n2. Update the relevant document with clear examples and rationale\n3. Communicate changes to the team\n4. Update dependent code to match new standards\n\n## Conclusion\n\nThe **docs** module plays a critical role in maintaining consistency and correctness across Symphony. While it contains no executable code, its influence permeates the codebase through:\n- Standardized logging patterns that enable effective debugging\n- Correct token accounting that ensures accurate billing\n- Clear integration specifications that prevent misinterpretation of external systems\n\nThese documents represent institutional knowledge that would otherwise be lost or inconsistently applied. They serve as the single source of truth for complex technical concerns and should be treated with the same care as production code.","other-elixir":"# Other — elixir\n\n# Symphony Elixir Agent Orchestration Service\n\n## Overview\n\nThe Symphony Elixir module is an Erlang/OTP-based orchestration service that automates software development workflows by integrating Linear issue tracking with OpenAI's Codex AI agent. It continuously monitors Linear projects for active issues, creates isolated development environments for each issue, and runs Codex agents to implement solutions according to a configurable workflow.\n\nThis service serves as the primary runtime engine for the Symphony system, implementing the specification defined in the repository's root `SPEC.md` while providing a production-ready execution environment with supervision, error handling, and observability features.\n\n## Architecture\n\nThe system follows an OTP application structure with several key components working together:\n\n```mermaid\ngraph TD\n A[CLI Entry Point] --> B(Application Supervisor)\n B --> C[Orchestrator GenServer]\n B --> D[Web Dashboard]\n C --> E[Poll Linear API]\n C --> F[Create Workspace]\n C --> G[AgentRunner Supervisor]\n G --> H[Codex App-Server Process]\n F --> I[Run Hooks]\n H --> J[Execute Workflow]\n```\n\n## Core Components\n\n### 1. Orchestrator (`SymphonyElixir.Orchestrator`)\n\nThe central coordinator that manages the lifecycle of all active issues. It operates as a stateful GenServer with the following responsibilities:\n\n- **Polling Loop**: Periodically queries Linear for issues in active states (configurable via `WORKFLOW.md`)\n- **Issue State Management**: Tracks which issues are being processed, their current state, and associated workspace metadata\n- **Concurrency Control**: Enforces `max_concurrent_agents` limit to prevent system overload\n- **Retry & Reconciliation**: Implements exponential backoff for API failures and ensures eventual consistency between Linear state and local agent state\n\n### 2. Agent Runner (`SymphonyElixir.AgentRunner`)\n\nSupervises individual Codex agent sessions using dynamic supervision patterns:\n\n- **Process Isolation**: Each issue runs in its own supervised process tree\n- **Resource Management**: Monitors agent resource usage and implements graceful shutdown\n- **State Recovery**: Persists agent state across turns within the same session\n- **Cleanup Handling**: Ensures workspaces are properly cleaned when agents terminate\n\n### 3. Workspace Management (`SymphonyElixir.Workspace`)\n\nProvides safe, isolated directories for agent execution:\n\n- **Path Safety**: Validates all workspace paths stay within the configured `workspace.root` directory\n- **Hook Execution**: Runs `after_create` and `before_remove` shell hooks at appropriate lifecycle points\n- **Template Expansion**: Handles Liquid template variables (`{{ issue.identifier }}`, etc.) in workflow prompts\n- **Environment Isolation**: Ensures agents cannot accidentally modify source repositories\n\n### 4. Configuration System (`SymphonyElixir.Config`)\n\nLoads and validates runtime configuration from `WORKFLOW.md` front matter:\n\n```yaml\n# WORKFLOW.md Front Matter Structure\ntracker:\n kind: linear\n project_slug: \"project-abc\"\n api_key: $LINEAR_API_KEY # Environment variable expansion\nworkspace:\n root: ~/code/workspaces # Path expansion\nhooks:\n after_create: |\n git clone git@github.com/org/repo.git .\nagent:\n max_concurrent_agents: 10\n max_turns: 20\ncodex:\n command: \"codex app-server --model gpt-5.3-codex\"\n approval_policy: never\n thread_sandbox: workspace-write\n```\n\nThe system performs runtime validation of all configuration values and provides sensible defaults where appropriate.\n\n### 5. Web Dashboard (`SymphonyElixirWeb.*`)\n\nA Phoenix LiveView-based observability interface that provides:\n\n- **Real-time Monitoring**: Live updates of agent status, issue progress, and system metrics\n- **JSON API**: Programmatic access to system state at `/api/v1/state` and per-issue endpoints\n- **Operational Control**: Manual refresh triggers and diagnostic endpoints\n- **Minimal Footprint**: Uses Bandit HTTP server with compiled Phoenix assets to minimize dependencies\n\n## Execution Flow\n\n### Startup Sequence\n1. CLI parses arguments and loads workflow file\n2. Application supervisor starts all core services\n3. Orchestrator initializes with validated configuration\n4. Web dashboard starts on configured port (if enabled)\n5. Polling loop begins immediately\n\n### Issue Processing Cycle\n1. **Poll**: Query Linear for issues in `active_states`\n2. **Filter**: Exclude issues already being processed\n3. **Claim**: Atomically claim issue to prevent duplicate processing\n4. **Prepare**: Create workspace and run `after_create` hooks\n5. **Execute**: Launch Codex app-server with workflow prompt\n6. **Monitor**: Track agent progress through multiple turns\n7. **Complete**: Clean up workspace when issue reaches terminal state\n\n### Agent Session Lifecycle\n```\nIssue in active state\n ↓\nCreate workspace + run hooks\n ↓\nLaunch Codex app-server\n ↓\nSend workflow prompt\n ↓\nLoop: [Receive turn → Process → Send next action]\n ↓\nMax turns reached OR issue becomes terminal\n ↓\nTerminate Codex process\n ↓\nRun before_remove hooks\n ↓\nRemove workspace\n```\n\n## Safety & Reliability Features\n\n### Workspace Safety\n- **Absolute Path Enforcement**: All workspace operations validate paths stay within `workspace.root`\n- **Repository Protection**: Codex never runs with current working directory in source repositories\n- **Cleanup Guarantees**: `before_remove` hooks always run, even on abnormal termination\n\n### Configuration Safety\n- **Environment Isolation**: Each configuration section (`tracker`, `workspace`, etc.) has validated schemas\n- **Path Expansion**: Supports `~` (home directory) and `$VAR` (environment variable) expansion\n- **Template Safety**: Liquid templates are sandboxed with strict variable access\n\n### Error Handling\n- **Supervision Trees**: All components are properly supervised with restart strategies\n- **Graceful Degradation**: Failed issues are retried with exponential backoff\n- **State Persistence**: Orchestrator state survives process restarts\n\n## Development Guidelines\n\n### Code Quality Requirements\n- **Type Specifications**: All public functions (`def`) must have adjacent `@spec` annotations\n- **Pattern Consistency**: Follow existing module structures in `lib/symphony_elixir/*`\n- **Documentation Updates**: When behavior changes, update relevant documentation in same PR:\n - `README.md` for implementation details\n - `WORKFLOW.md` for configuration changes\n - Repository root `README.md` for project-wide changes\n\n### Testing Strategy\n```bash\n# Run full quality gate\nmake all # → format check, lint, test coverage, dialyzer\n\n# Iterative development\nmix test --cover # Test with coverage\nmix dialyzer # Type checking\nmix specs.check # Verify @spec completeness\n```\n\n### PR Requirements\n- PR bodies must exactly follow `../.github/pull_request_template.md`\n- Validate locally with `mix pr_body.check --file /path/to/pr_body.md`\n- Include documentation updates for any behavior or configuration changes\n\n## Integration Points\n\n### Linear Integration\n- Uses Linear GraphQL API with personal access tokens\n- Supports custom active/terminal state mappings\n- Implements optimistic locking to prevent race conditions\n\n### Codex Integration\n- Launches Codex in app-server mode as subprocess\n- Injects `linear_graphql` tool for direct Linear API access\n- Configures sandbox policies based on workflow settings\n\n### Skill Ecosystem\nThe system expects certain skills to be present in the repository:\n- `commit`, `push`, `pull`, `land`: Git workflow skills\n- `linear`: Linear interaction skill (requires `linear_graphql` tool)\n- Custom skills defined in `.codex/skills/` directory\n\n## Configuration Reference\n\n### Required Environment Variables\n- `LINEAR_API_KEY`: Linear personal access token\n- Optional: `SYMPHONY_WORKSPACE_ROOT`, `CODEX_BIN`, etc. for path expansion\n\n### CLI Arguments\n```bash\n./bin/symphony [WORKFLOW_PATH] [--logs-root PATH] [--port NUMBER]\n```\n\n### Workflow Template Variables\nThe Markdown body of `WORKFLOW.md` supports Liquid template syntax:\n- `{{ issue.identifier }}`: Linear issue identifier (e.g., \"MT-32\")\n- `{{ issue.title }}`: Issue title\n- `{{ issue.description }}`: Issue description\n- `{{ issue.state }}`: Current Linear state\n- `{{ attempt }}`: Retry attempt number (for continuation sessions)\n\n## Operational Considerations\n\n### Scaling Characteristics\n- **Concurrency**: Limited by `max_concurrent_agents` (default: 10)\n- **Resource Usage**: Each agent requires memory for Codex process and workspace\n- **API Rate Limits**: Linear polling respects API quotas with adaptive backoff\n\n### Monitoring & Observability\n- **Logs**: Structured logging following `docs/logging.md` conventions\n- **Metrics**: Web dashboard shows active agents, queue length, error rates\n- **Health Checks**: JSON API provides system status endpoint\n\n### Security Model\n- **API Tokens**: Linear tokens stored in memory only\n- **Process Isolation**: Agents run in separate workspaces with restricted filesystem access\n- **Network Access**: Codex agents have outbound network access for API calls\n\n## Extension Points\n\nThe architecture supports several extension mechanisms:\n\n1. **Custom Hooks**: Add pre/post-execution scripts in `hooks` configuration\n2. **Template Variables**: Extend Liquid context with custom issue fields\n3. **Skill Integration**: Add repository-local skills in `.codex/skills/`\n4. **Dashboard Plugins**: Extend Phoenix LiveView components for custom observability\n\n## License & Attribution\n\nThis implementation is provided under the Apache License 2.0 as a reference implementation of the Symphony specification. For production use, consider building a hardened version based on the spec while using this codebase as an architectural reference.","other-notice":"# Other — NOTICE\n\n# NOTICE Module Documentation\n\n## Overview\n\nThe `NOTICE` module is not a functional code component but a **legal notice file** that contains licensing information for this codebase. It provides the Apache License 2.0 notice that governs the use, modification, and distribution of the software.\n\n## Purpose\n\nThis file serves as the **primary legal documentation** for the project, specifying:\n- The copyright holder (OpenAI, 2025)\n- The licensing terms (Apache License 2.0)\n- The conditions under which the software can be used and distributed\n- The disclaimer of warranties and limitation of liability\n\n## Key Components\n\n### License Declaration\nThe file explicitly states that the software is licensed under the **Apache License, Version 2.0**, a permissive open-source license that allows for commercial use, modification, distribution, and private use with minimal restrictions.\n\n### Required Notices\nThe notice includes:\n- Copyright statement\n- License text reference\n- Disclaimer of warranties (\"AS IS\" basis)\n- Limitation of liability clause\n- Permission notice requirements\n\n## How It Works\n\nUnlike typical code modules, the `NOTICE` file:\n1. **Does not execute** - It contains no executable code or functions\n2. **Has no call relationships** - It doesn't interact with other modules programmatically\n3. **Is a static artifact** - It's included in the codebase as a legal requirement\n\n## Integration with the Codebase\n\n### Build and Distribution\nWhile the `NOTICE` file doesn't participate in runtime execution, it must be:\n- **Preserved intact** in all source distributions\n- **Included** in binary distributions when applicable\n- **Referenced** in project documentation\n- **Maintained** alongside code changes that might affect licensing\n\n### Developer Responsibilities\nWhen contributing to or using this codebase, developers must:\n1. **Read and understand** the license terms\n2. **Comply** with the Apache 2.0 requirements when distributing modified versions\n3. **Preserve** this notice in all copies or substantial portions of the software\n4. **Include** the same license terms when redistributing the software\n\n## Practical Usage Examples\n\n### Scenario 1: Using the software in your project\n```plaintext\nWhen incorporating this code into your project, you must:\n- Keep the NOTICE file in your distribution\n- Include the Apache 2.0 license terms\n- Provide attribution as required by the license\n```\n\n### Scenario 2: Modifying and redistributing\n```plaintext\nIf you modify and redistribute this software:\n- You may change the code but must preserve this NOTICE\n- You must state any significant changes made\n- You must include the original copyright notice\n```\n\n## Best Practices\n\n1. **Do not modify** the NOTICE file without legal counsel\n2. **Keep it visible** in the project root directory\n3. **Reference it** in your project's README or documentation\n4. **Ensure compliance** when creating derivative works\n5. **Understand the implications** of the \"AS IS\" warranty disclaimer\n\n## Related Files\n\nWhile this module stands alone legally, it relates to:\n- `LICENSE` file (if present, contains full license text)\n- Source code headers (may contain abbreviated copyright notices)\n- Project documentation (should reference this licensing)\n\n## Legal Note\n\nThis documentation provides technical context but **does not constitute legal advice**. Always consult with legal professionals regarding license compliance and software distribution.","other-priv":"# Other — priv\n\n# Dashboard Styles Documentation (`priv/static/dashboard.css`)\n\n## Overview\n\nThe `dashboard.css` file provides the complete styling system for the application's dashboard interface. This CSS module implements a modern, accessibility-focused design system with consistent theming, responsive layouts, and smooth animations. The styles are specifically crafted to work with Phoenix LiveView for real-time state updates.\n\n## Design System\n\n### Color Palette\n\nThe design system uses CSS custom properties (variables) to maintain consistency:\n\n| Variable | Default Value | Purpose |\n|----------|---------------|---------|\n| `--page` | `#f7f7f8` | Primary page background |\n| `--page-soft` | `#fbfbfc` | Subtle gradient backgrounds |\n| `--page-deep` | `#ececf1` | Deeper background variations |\n| `--card` | `rgba(255, 255, 255, 0.94)` | Card component background |\n| `--card-muted` | `#f3f4f6` | Secondary card backgrounds |\n| `--ink` | `#202123` | Primary text color |\n| `--muted` | `#6e6e80` | Secondary/disabled text |\n| `--line` | `#ececf1` | Divider lines |\n| `--line-strong` | `#d9d9e3` | Stronger borders |\n| `--accent` | `#10a37f` | Primary brand/accent color |\n| `--accent-ink` | `#0f513f` | Text on accent backgrounds |\n| `--accent-soft` | `#e8faf4` | Subtle accent backgrounds |\n| `--danger` | `#b42318` | Error/danger states |\n| `--danger-soft` | `#fef3f2` | Subtle danger backgrounds |\n\n### Typography\n\nThe system uses two font families:\n- **Primary**: `\"Sohne\"`, `\"SF Pro Text\"`, `\"Helvetica Neue\"`, `\"Segoe UI\"`, sans-serif\n- **Monospace**: `\"Sohne Mono\"`, `\"SFMono-Regular\"`, `\"SF Mono\"`, Consolas, monospace\n\nFont features like `tabular-nums` and `slashed-zero` ensure consistent numeric display.\n\n## Component Architecture\n\nThe dashboard is built from several key component types, each with specific styling rules:\n\n### 1. Layout Containers\n\n```css\n/* Main application shell */\n.app-shell {\n max-width: 1280px;\n margin: 0 auto;\n padding: 2rem 1rem 3.5rem;\n}\n\n/* Dashboard grid layout */\n.dashboard-shell {\n display: grid;\n gap: 1rem;\n}\n```\n\n### 2. Card Components\n\nAll card variants share a common foundation with variations:\n\n```mermaid\ngraph TD\n A[Card Base Styles] --> B[Hero Card]\n A --> C[Section Card]\n A --> D[Metric Card]\n A --> E[Error Card]\n \n B --> F[Large radius 28px]\n C --> G[Medium radius 24px]\n D --> H[Small radius 22px]\n E --> I[Error styling]\n \n B --> J[Hero Grid Layout]\n C --> K[Table Sections]\n D --> L[Metric Grid]\n```\n\n### 3. Interactive Elements\n\n#### Primary Buttons\n- Green accent color with hover elevation\n- Smooth transitions for transforms and shadows\n- Responsive padding and typography\n\n#### Secondary Buttons\n- Neutral styling with subtle borders\n- Lighter shadow treatment\n- Consistent interaction patterns\n\n#### Subtle Buttons\n- Minimal styling for auxiliary actions\n- Transparent backgrounds that become opaque on hover\n- Smaller typography and padding\n\n### 4. Status Indicators\n\nThe system includes dynamic status badges that respond to LiveView connection state:\n\n```css\n/* Only show \"live\" badge when Phoenix LiveView is connected */\n[data-phx-main].phx-connected .status-badge-live {\n display: inline-flex;\n}\n\n[data-phx-main].phx-connected .status-badge-offline {\n display: none;\n}\n```\n\n### 5. Data Display Components\n\n#### Metric Grid\n- Responsive grid layout (`repeat(auto-fit, minmax(180px, 1fr))`)\n- Clamped font sizes for responsive typography\n- Consistent spacing and alignment\n\n#### Data Tables\n- Two variants: standard and \"running\" (fixed layout)\n- Responsive overflow handling\n- Styled headers with uppercase treatment\n- Zebra-striping via border-top approach\n\n#### State Badges\nColor-coded badges for different states:\n- **Active**: Green (accent color family)\n- **Warning**: Amber/yellow tones\n- **Danger**: Red (danger color family)\n\n## Responsive Design\n\nThe dashboard implements a mobile-first responsive approach with two main breakpoints:\n\n### 1. Tablet/Mobile (≤860px)\n- Reduces padding in app shell\n- Converts hero grid from 2-column to single column\n- Changes status stack alignment\n- Adjusts metric grid to 2 columns\n\n### 2. Small Mobile (≤560px)\n- Forces metric grid to single column\n- Reduces border radii for tighter spaces\n- Further reduces padding in cards\n\n## Integration with Phoenix LiveView\n\nThe CSS includes specific selectors that work with Phoenix LiveView's connection state:\n- `.phx-connected` class applied by LiveView when WebSocket connection is established\n- Status badges dynamically show/hide based on connection state\n- This allows for real-time status updates without JavaScript\n\n## Performance Considerations\n\n1. **Hardware Acceleration**: Uses `backdrop-filter: blur()` on cards with appropriate vendor prefixes implied\n2. **Paint Efficiency**: Minimizes layout thrashing with stable grid/flexbox layouts\n3. **Animation Performance**: Uses `transform` and `opacity` for performant animations\n4. **Font Loading**: Assumes web font loading strategy is handled elsewhere\n\n## Usage Guidelines\n\n### When Adding New Components\n\n1. **Use Design Tokens**: Always reference CSS custom properties rather than hardcoded values\n2. **Follow Spacing Scale**: Maintain consistent spacing using the established rhythm (multiples of 0.25rem)\n3. **Respect Breakpoints**: Test new components at all responsive breakpoints\n4. **Maintain Accessibility**: Ensure sufficient color contrast and focus states\n\n### Common Patterns\n\n```css\n/* Card pattern */\n.new-card {\n background: var(--card);\n border: 1px solid rgba(217, 217, 227, 0.82);\n box-shadow: var(--shadow-sm);\n backdrop-filter: blur(18px);\n border-radius: 22px; /* Choose appropriate radius */\n}\n\n/* Stack layout pattern */\n.new-stack {\n display: grid;\n gap: 0.24rem; /* Standard stack gap */\n min-width: 0; /* Prevents grid blowout */\n}\n```\n\n## Browser Support\n\nThe styles assume support for:\n- CSS Grid and Flexbox\n- CSS Custom Properties (variables)\n- `backdrop-filter` (with appropriate fallbacks in unsupported browsers)\n- CSS `clamp()` function for responsive typography\n\nVendor prefixes are not included in this file, assuming they're handled during build processing.\n\n## File Location and Build Process\n\nLocated at `priv/static/dashboard.css`, this file is served statically. In production, it should be:\n- Minified and compressed\n- Possibly concatenated with other CSS files\n- Served with appropriate cache headers\n\nThe static nature means changes require server restart or static file cache invalidation.","other-readme-md":"# Other — README.md\n\n# Symphony README.md Module Documentation\n\n## Overview\n\nThe `README.md` module serves as the primary entry point and high-level documentation for the Symphony project. This is not a code module in the traditional sense, but rather the front-facing documentation that introduces developers to Symphony's purpose, functionality, and getting started pathways.\n\n## Purpose\n\nThe README.md module has several key objectives:\n1. **Introduce Symphony's core concept** - Transforming project work into autonomous implementation runs\n2. **Provide immediate visual understanding** - Through demo video and screenshots\n3. **Guide users to appropriate implementation paths** - Either building their own or using the reference implementation\n4. **Set proper expectations** - Clearly marking the project as an engineering preview for trusted environments\n\n## Key Components\n\n### 1. Project Definition\n```\nSymphony turns project work into isolated, autonomous implementation runs, allowing teams to manage\nwork instead of supervising coding agents.\n```\nThis concise statement defines Symphony's value proposition: shifting from micro-managing coding agents to managing work at a higher abstraction level.\n\n### 2. Visual Demonstration\n- Embedded demo video poster image linking to full demo\n- Concrete example of Symphony monitoring a Linear board and spawning agents\n- Shows proof-of-work deliverables (CI status, PR reviews, complexity analysis, walkthrough videos)\n\n### 3. Critical Warning\n```\n> [!WARNING]\n> Symphony is a low-key engineering preview for testing in trusted environments.\n```\nSets appropriate expectations about project maturity and intended usage context.\n\n### 4. Prerequisites\nReferences \"[harness engineering](https://openai.com/index/harness-engineering/)\" as the foundational approach that Symphony builds upon.\n\n### 5. Implementation Pathways\n\n#### Option 1: Build Your Own\nProvides a direct prompt for coding agents to implement Symphony based on the SPEC.md, enabling language-agnostic adoption.\n\n#### Option 2: Use Reference Implementation\nDirects users to the Elixir implementation (`elixir/README.md`) with another agent-friendly prompt for setup assistance.\n\n## Architecture Context\n\nWhile the README.md itself contains no executable code, it serves as the navigation hub connecting several key project components:\n\n```mermaid\ngraph TD\n README[README.md] --> SPEC[SPEC.md<br/>Implementation Specification]\n README --> ELIXIR_README[elixir/README.md<br/>Reference Implementation]\n README --> LICENSE[LICENSE<br/>Apache 2.0 License]\n README --> GITHUB_MEDIA[.github/media/<br/>Demo Assets]\n \n SPEC --> USER_IMPL[User's Custom Implementation]\n ELIXIR_README --> ELIXIR_CODE[Elixir Codebase]\n \n style README fill:#f9f,stroke:#333,stroke-width:4px\n```\n\n## Usage Patterns\n\n### For New Users\n1. Read the high-level concept and watch the demo video\n2. Assess if their project aligns with harness engineering principles\n3. Choose between building a custom implementation or using the Elixir reference\n4. Follow the appropriate setup instructions\n\n### For Contributors\n1. Understand the project's scope and philosophy\n2. Refer to SPEC.md for implementation requirements\n3. Use the reference implementation as a working example\n\n### For Documentation Maintainers\n1. Keep the demo video and screenshots current\n2. Update warning status as project maturity evolves\n3. Maintain clear pathways to implementation options\n\n## Relationship to Other Modules\n\n- **SPEC.md**: The README references SPEC.md as the authoritative implementation specification\n- **elixir/README.md**: The README provides the primary navigation path to the reference implementation\n- **.github/media/**: Contains visual assets referenced in the README\n- **LICENSE**: Linked for legal compliance information\n\n## Best Practices for Maintenance\n\n1. **Keep it agent-friendly**: Both implementation options include prompts that coding agents can directly use\n2. **Maintain visual relevance**: Update demo materials as the project evolves\n3. **Clear warnings**: Always properly represent the project's stability and intended use cases\n4. **Pathway clarity**: Ensure users can easily navigate to their preferred implementation method\n\n## Note on Execution Flow\n\nAs a static documentation file, README.md has no execution flows, internal calls, or dependencies in the traditional programming sense. Its \"execution\" is human reading and navigation, and its \"calls\" are hyperlinks to other project resources.","other-spec-md":"# Other — SPEC.md\n\n# Symphony Service Specification\n\n## Overview\n\nSymphony is a long-running orchestration service that automates coding agent workflows by continuously reading issues from a tracker (currently Linear), creating isolated workspaces, and running coding agent sessions for each issue. It transforms issue execution from manual scripts into a repeatable daemon workflow while maintaining policy versioning through repository-controlled configuration.\n\n## Key Components\n\n### Core Architecture Layers\n\n```mermaid\nflowchart TD\n Policy[Policy Layer<br/>WORKFLOW.md] --> Config[Configuration Layer<br/>Typed getters]\n Config --> Coord[Coordination Layer<br/>Orchestrator]\n Coord --> Exec[Execution Layer<br/>Workspace + Agent]\n Exec --> Integ[Integration Layer<br/>Linear adapter]\n Coord --> Obs[Observability Layer<br/>Logs + Status]\n```\n\n### Component Breakdown\n\n1. **Workflow Loader** - Reads and parses `WORKFLOW.md` with YAML front matter\n2. **Configuration Layer** - Provides typed access to config values with environment variable resolution\n3. **Issue Tracker Client** - Fetches issues from Linear (or other trackers) and normalizes them\n4. **Orchestrator** - Main scheduler managing polling, dispatch, and state transitions\n5. **Workspace Manager** - Creates and manages per-issue isolated workspaces\n6. **Agent Runner** - Launches and manages coding agent app-server sessions\n7. **Status Surface** - Optional human-readable dashboard/API for monitoring\n\n## Workflow Configuration\n\n### WORKFLOW.md Structure\n\nThe workflow file uses a Markdown format with optional YAML front matter:\n\n```markdown\n---\ntracker:\n kind: linear\n api_key: $LINEAR_API_KEY\n project_slug: my-project\n active_states: [\"Todo\", \"In Progress\"]\n \npolling:\n interval_ms: 30000\n\nworkspace:\n root: /tmp/symphony_workspaces\n\nagent:\n max_concurrent_agents: 10\n max_retry_backoff_ms: 300000\n\ncodex:\n command: codex app-server\n approval_policy: auto_approve_all\n turn_timeout_ms: 3600000\n---\n\n# Your prompt template here\nWork on issue {{ issue.identifier }}: {{ issue.title }}\n\nDescription: {{ issue.description }}\n\nLabels: {{ issue.labels | join: \", \" }}\n```\n\n### Configuration Resolution\n\nConfiguration follows this precedence:\n1. Explicit runtime setting (CLI argument)\n2. YAML front matter values in `WORKFLOW.md`\n3. Environment variable indirection via `$VAR_NAME`\n4. Built-in defaults\n\nDynamic reloading is supported - changes to `WORKFLOW.md` are detected and applied without restarting the service.\n\n## Orchestration State Machine\n\n### Internal Issue States\n\nThe orchestrator maintains these internal states (distinct from tracker states):\n\n| State | Description |\n|-------|-------------|\n| **Unclaimed** | Not running, no retry scheduled |\n| **Claimed** | Reserved by orchestrator (running or queued) |\n| **Running** | Active agent session |\n| **RetryQueued** | Scheduled for retry after delay |\n| **Released** | Claim removed (terminal or inactive) |\n\n### Polling and Dispatch Cycle\n\n```text\n1. Reconcile running issues (check for stalls, refresh states)\n2. Validate dispatch configuration\n3. Fetch candidate issues from tracker\n4. Sort by priority (ascending), then creation date\n5. Dispatch while concurrency slots available:\n - Issue must be in active_states\n - Not already running/claimed\n - Concurrency limits satisfied\n - For Todo state: all blockers must be terminal\n6. Update observability\n```\n\n### Concurrency Control\n\nThe service supports both global and per-state concurrency limits:\n- `agent.max_concurrent_agents` (global default: 10)\n- `agent.max_concurrent_agents_by_state` (optional per-state overrides)\n\n## Workspace Management\n\n### Workspace Layout\n\n```\n<workspace.root>/<sanitized_issue_identifier>/\n├── .symphony_meta/\n└── [codebase files]\n```\n\n**Sanitization Rule**: Replace any character not in `[A-Za-z0-9._-]` with `_`\n\n### Workspace Safety Invariants\n\n1. **Path Containment**: Workspace path must be under configured workspace root\n2. **CWD Isolation**: Agent runs only in per-issue workspace directory\n3. **Key Sanitization**: Directory names use sanitized identifiers\n\n### Hooks System\n\nFour optional hooks with configurable timeouts (default: 60 seconds):\n\n| Hook | Timing | Failure Semantics |\n|------|--------|-------------------|\n| `after_create` | After workspace directory creation | Fatal - aborts workspace creation |\n| `before_run` | Before each agent attempt | Fatal - aborts current attempt |\n| `after_run` | After each agent attempt | Logged and ignored |\n| `before_remove` | Before workspace deletion | Logged and ignored |\n\n## Agent Integration\n\n### Coding Agent Protocol\n\nSymphony integrates with coding agents supporting the app-server JSON-RPC protocol over stdio:\n\n1. **Launch**: `bash -lc <codex.command>` in workspace directory\n2. **Handshake**: \n - `initialize` → `initialized` notification\n - `thread/start` with approval/sandbox policies\n - `turn/start` with rendered prompt\n3. **Streaming**: Process line-delimited JSON messages until turn completion\n\n### Session Management\n\n- **Session ID**: `<thread_id>-<turn_id>`\n- **Continuation**: Same thread reused across multiple turns up to `agent.max_turns`\n- **Timeouts**:\n - `read_timeout_ms`: Request/response timeout (default: 5s)\n - `turn_timeout_ms`: Total turn duration (default: 1 hour)\n - `stall_timeout_ms`: Event inactivity detection (default: 5 minutes)\n\n### Extension: Linear GraphQL Tool\n\nWhen `tracker.kind == \"linear\"`, Symphony can optionally expose a `linear_graphql` tool to agents:\n\n```json\n{\n \"query\": \"query { issues(filter: { state: { name: { eq: \\\"Todo\\\" } } }) { nodes { id title } } }\",\n \"variables\": {}\n}\n```\n\nThis allows agents to read/write Linear data using Symphony's configured authentication.\n\n## Tracker Integration (Linear)\n\n### Required Operations\n\n1. `fetch_candidate_issues()` - Issues in active states for configured project\n2. `fetch_issues_by_states()` - For startup terminal cleanup\n3. `fetch_issue_states_by_ids()` - For active-run reconciliation\n\n### Normalization Rules\n\n- **Labels**: Converted to lowercase\n- **Blockers**: Derived from `blocks` relations\n- **Priority**: Integer only (non-integers become null)\n- **Timestamps**: ISO-8601 parsed\n\n### Important Boundary\n\nSymphony primarily **reads** from trackers. Ticket mutations (state transitions, comments) are typically handled by coding agents using available tools, not by the orchestrator directly.\n\n## Observability\n\n### Structured Logging\n\nAll logs include contextual fields:\n- `issue_id`, `issue_identifier` for issue-related events\n- `session_id` for agent session events\n- Structured `key=value` formatting\n\n### Optional HTTP Server\n\nEnable via CLI `--port` or `server.port` in workflow front matter:\n\n```yaml\n---\nserver:\n port: 8080\n---\n```\n\n**Endpoints**:\n- `GET /` - Human-readable dashboard\n- `GET /api/v1/state` - JSON system state snapshot\n- `GET /api/v1/<issue_identifier>` - Issue-specific debug details\n- `POST /api/v1/refresh` - Trigger immediate poll+reconcile\n\n### Metrics and Token Accounting\n\nThe service tracks:\n- Aggregate token usage (input/output/total)\n- Runtime seconds (including active sessions)\n- Latest rate-limit data from agent events\n- Turn counts per session\n\n## Safety and Security\n\n### Trust Boundary Definition\n\nEach implementation must explicitly document its trust posture. Symphony supports configurations ranging from high-trust (auto-approve) to restrictive (manual approval, sandboxing).\n\n### Mandatory Safety Controls\n\n1. **Filesystem Isolation**: Agent runs only in designated workspace\n2. **Path Validation**: Workspace must be under configured root\n3. **Secret Handling**: `$VAR` indirection, no logging of tokens\n\n### Hardening Recommendations\n\n- Run under dedicated OS user\n- Restrict workspace root permissions\n- Use container/VM isolation layers\n- Filter eligible issues/projects/labels\n- Narrow `linear_graphql` tool scope\n\n## Failure Recovery\n\n### Error Classes and Recovery\n\n| Error Class | Recovery Behavior |\n|-------------|-------------------|\n| **Workflow/Config** | Skip dispatch, keep service alive |\n| **Workspace** | Fail attempt, schedule retry |\n| **Agent Session** | Fail attempt, exponential backoff retry |\n| **Tracker API** | Skip tick, retry next poll |\n| **Observability** | Log locally, don't crash orchestrator |\n\n### Retry Logic\n\n- **Normal exit**: Short continuation retry (1 second)\n- **Failure**: Exponential backoff: `min(10000 * 2^(attempt-1), max_retry_backoff_ms)`\n- **Cap**: Configurable via `agent.max_retry_backoff_ms` (default: 5 minutes)\n\n### Restart Recovery\n\nService is intentionally stateless for scheduling:\n- No persistent retry queue or session state\n- Startup performs terminal workspace cleanup\n- Fresh polling re-discovers active work\n\n## Testing Requirements\n\n### Core Conformance Tests\n\n1. **Workflow Parsing**: Path precedence, YAML front matter, dynamic reload\n2. **Workspace Safety**: Path containment, sanitization, hook execution\n3. **Tracker Integration**: Candidate fetch, state refresh, normalization\n4. **Orchestration**: Dispatch eligibility, reconciliation, retry logic\n5. **Agent Client**: Protocol handshake, timeouts, error mapping\n\n### Extension Tests (If Implemented)\n\n- HTTP server port binding and API endpoints\n- `linear_graphql` tool functionality and safety\n- Client-side tool advertising and execution\n\n### Real Integration Profile\n\nRecommended production validation:\n- Actual tracker connectivity with valid credentials\n- End-to-end agent execution in isolated environment\n- Hook execution in target OS/shell environment\n\n## Implementation Checklist\n\n### Required for Conformance\n\n- [ ] Workflow file loader with YAML front matter support\n- [ ] Dynamic config reload without restart\n- [ ] Orchestrator with polling, dispatch, reconciliation\n- [ ] Workspace manager with safety invariants\n- [ ] Coding agent app-server client integration\n- [ ] Linear tracker client with pagination\n- [ ] Structured logging with context fields\n- [ ] Exponential retry with continuation support\n\n### Recommended Extensions\n\n- [ ] Optional HTTP server with dashboard/API\n- [ ] `linear_graphql` client-side tool\n- [ ] Persistent state across restarts\n- [ ] Pluggable tracker adapters beyond Linear\n\n## Key Design Principles\n\n1. **Repository-Owned Policy**: `WORKFLOW.md` is version-controlled with code\n2. **Isolation**: Per-issue workspaces prevent cross-issue contamination\n3. **Observability First**: Structured logs enable operator debugging\n4. **Graceful Degradation**: Component failures don't cascade to system failure\n5. **Explicit Safety Posture**: Each implementation documents its trust model\n\n## Integration Points\n\nSymphony connects to:\n- **Issue Tracker API** (Linear GraphQL) for reading issue data\n- **Local Filesystem** for workspaces and logs\n- **Coding Agent Executable** via stdio JSON-RPC protocol\n- **Optional**: HTTP clients for dashboard/API access\n\nThe service is designed to be language-agnostic and portable across different runtime environments while maintaining consistent behavior through this specification.","other-test":"# Other — test\n\n# SymphonyElixir Test Suite Documentation\n\n## Overview\n\nThe SymphonyElixir test suite is a comprehensive collection of ExUnit tests that validate the core functionality, Mix tasks, and integration points of the SymphonyElixir autonomous agent system. These tests ensure that the system correctly orchestrates work on Linear tickets using Codex agents while maintaining proper isolation, security boundaries, and configuration validation.\n\n## Architecture and Design Patterns\n\n### Test Isolation and Cleanup\nAll tests follow a strict pattern of creating temporary directories and cleaning up after execution:\n\n- **Temporary workspaces**: Created using `System.tmp_dir!()` with unique identifiers\n- **Environment management**: `with_env/2` and `with_path/2` helpers for controlled execution\n- **File system cleanup**: Automatic removal of test directories in `after` blocks\n- **Process isolation**: Each test runs in its own isolated environment\n\n### External Command Simulation\nTests simulate external dependencies through script injection:\n\n```elixir\n# Example: Fake gh CLI for testing workspace cleanup\nwith_fake_gh(\"\"\"\n#!/bin/sh\nif [ \"$1\" = \"pr\" ] && [ \"$2\" = \"list\" ]; then\n printf '101\\n102\\n'\nfi\n\"\"\", fn log_path ->\n # Test logic using simulated gh\nend)\n```\n\n### Configuration Override Pattern\nTests override workflow configurations dynamically:\n\n```elixir\nwrite_workflow_file!(Workflow.workflow_file_path(),\n workspace_root: workspace_root,\n codex_command: \"#{codex_binary} app-server\",\n codex_approval_policy: \"never\"\n)\n```\n\n## Test Organization\n\n### 1. Mix Task Tests (`test/mix/tasks/`)\n\n#### `pr_body_check_test.exs` - PR Template Validation\nValidates that PR descriptions follow the required template format.\n\n**Key validations:**\n- Required headings exist in correct order\n- Sections are not empty\n- Template placeholders are replaced\n- Bullet and checkbox formatting compliance\n- File existence and readability checks\n\n**Test helper functions:**\n- `in_temp_repo/1`: Creates temporary git repository context\n- `write_template!/1`: Writes PR template to `.github/pull_request_template.md`\n\n#### `specs_check_task_test.exs` - Function Specification Validation\nEnsures public functions have `@spec` declarations or explicit exemptions.\n\n**Key features:**\n- Path-based module scanning with `--paths` option\n- Exemptions file support with comment parsing\n- Default `lib/` directory scanning\n- Clear error reporting with file and function location\n\n#### `workspace_before_remove_test.exs` - Workspace Cleanup Hook\nTests the hook that closes GitHub PRs before workspace removal.\n\n**Key behaviors:**\n- Integrates with `gh` CLI for PR management\n- Graceful handling of missing tools (git, gh)\n- Error tolerance for partial PR closure failures\n- Branch detection from git or `--branch` option\n\n### 2. Test Support Modules (`test/support/`)\n\n#### `snapshot_support.exs` - Snapshot Testing Utilities\nProvides deterministic snapshot comparison for status dashboard outputs.\n\n**Key functions:**\n- `assert_dashboard_snapshot!/2`: Compares ANSI and plain text snapshots\n- `escape_ansi/1`: Preserves escape sequences for debugging\n- `strip_ansi/1`: Removes ANSI codes for content comparison\n- `UPDATE_SNAPSHOTS=1` environment variable for snapshot updates\n\n#### `test_support.exs` - Common Test Setup\nShared test context and utilities used across multiple test modules.\n\n**Provided aliases:**\n- `SymphonyElixir.AgentRunner`, `CLI`, `Config`, `Orchestrator`, etc.\n- `SymphonyElixir.Linear` components: `Client`, `Issue`\n- `SymphonyElixir.Workflow` components: `Workflow`, `WorkflowStore`\n\n**Key helpers:**\n- `write_workflow_file!/2`: Dynamically generates workflow configurations\n- `restore_env/2`: Environment variable cleanup\n- `stop_default_http_server/0`: HTTP server management for tests\n\n### 3. Core Functionality Tests (`test/symphony_elixir/`)\n\n#### `app_server_test.exs` - Codex App Server Integration\nValidates the interaction between SymphonyElixir and the Codex app-server protocol.\n\n**Key test scenarios:**\n\n```mermaid\nsequenceDiagram\n participant Test as Test Suite\n participant AppServer as SymphonyElixir.AppServer\n participant FakeCodex as Fake Codex Binary\n participant ToolExec as Tool Executor\n \n Test->>AppServer: run(workspace, prompt, issue)\n AppServer->>FakeCodex: Start process with JSON-RPC\n FakeCodex->>AppServer: Send JSON messages\n Note over AppServer,FakeCodex: Protocol handshake<br/>(init, thread/start, turn/start)\n FakeCodex->>AppServer: tool/call request\n AppServer->>ToolExec: Execute supported tool\n ToolExec->>AppServer: Return tool result\n AppServer->>FakeCodex: Send tool response\n FakeCodex->>AppServer: turn/completed\n AppServer->>Test: Return {:ok, result}\n```\n\n**Security validations:**\n- Workspace root path guarding\n- Sandbox policy enforcement\n- Approval policy handling (`never`, `on-request`, etc.)\n- Dynamic tool execution and rejection\n\n**Protocol handling:**\n- JSON-RPC newline-delimited message parsing\n- Partial JSON line buffering\n- STDERR capture and logging\n- Input requirement detection as hard failure\n\n#### `cli_test.exs` - Command Line Interface\nTests the CLI entry point with its guardrail acknowledgment requirement.\n\n**Key validation:**\n- Mandatory `--i-understand-that-this-will-be-running-without-the-usual-guardrails` flag\n- Workflow file path resolution (default and explicit)\n- Application startup error propagation\n- `--logs-root` option handling\n\n#### `core_test.exs` - System Core Logic\nComprehensive tests of the main orchestration and business logic.\n\n**Configuration validation:**\n- Default value fallbacks\n- Environment variable resolution (`LINEAR_API_KEY`, `LINEAR_ASSIGNEE`)\n- Type coercion and validation\n- Workflow file parsing (YAML front matter and prompt)\n\n**Orchestrator state management:**\n- Issue state reconciliation\n- Active vs. terminal state handling\n- Worker assignment tracking\n- Retry scheduling with exponential backoff\n\n**Agent execution flow:**\n- Workspace creation and retention\n- Multi-turn continuation while issue remains active\n- Max turn limit enforcement\n- Codex update streaming to recipients\n\n**Prompt system:**\n- Template rendering with Liquid syntax\n- Issue context variable expansion\n- Continuation guidance for retries\n- Fallback prompt generation\n\n## Test Execution Patterns\n\n### Typical Test Flow\n1. **Setup**: Create temporary directory, write workflow configuration\n2. **Execution**: Run the component under test with controlled inputs\n3. **Verification**: Assert expected outputs, side effects, or state changes\n4. **Cleanup**: Remove temporary files and restore environment\n\n### Environment Configuration\n```elixir\n# Common test setup pattern\nsetup do\n # Re-enable Mix tasks for clean state\n Mix.Task.reenable(\"task.name\")\n \n # Create isolated workspace\n workspace_root = create_temp_dir()\n \n # Write test-specific workflow config\n write_workflow_file!(workflow_path, overrides)\n \n # Configure cleanup\n on_exit(fn -> File.rm_rf(workspace_root) end)\n \n :ok\nend\n```\n\n### External Command Simulation Pattern\n```elixir\n# Simulating gh CLI behavior\nwith_fake_gh(\"\"\"\n#!/bin/sh\nif [ \"$1\" = \"pr\" ] && [ \"$2\" = \"list\" ]; then\n echo \"101\"\n echo \"102\"\nfi\n\"\"\", fn log_path ->\n # Test executes with simulated gh\n result = Mix.Tasks.Workspace.BeforeRemove.run([\"--branch\", \"test\"])\n assert result == :ok\n assert File.read!(log_path) =~ \"pr list\"\nend)\n```\n\n## Key Test Helpers Reference\n\n### `SymphonyElixir.TestSupport`\n- **`__using__/1`**: Sets up common imports and aliases\n- **`write_workflow_file!/2`**: Generates WORKFLOW.md with config overrides\n- **`restore_env/2`**: Restores environment variables post-test\n- **`stop_default_http_server/0`**: Stops default HTTP server for clean tests\n\n### `SymphonyElixir.TestSupport.Snapshot`\n- **`assert_dashboard_snapshot!/2`**: Validates status dashboard output\n- **`assert_snapshot!/2`**: General snapshot assertion\n- **`escape_ansi/1`**: Converts escape sequences to readable form\n- **`strip_ansi/1`**: Removes ANSI codes for content comparison\n\n### Mix Task Test Helpers\n- **`in_temp_repo/1`**: Git repository context\n- **`in_temp_project/1`**: Elixir project context\n- **`in_temp_dir/1`**: Generic temporary directory\n- **`with_fake_binaries/2`**: Injects fake CLI tools into PATH\n\n## Running the Tests\n\n### Basic Execution\n```bash\n# Run all tests\nmix test\n\n# Run specific test file\nmix test test/symphony_elixir/app_server_test.exs\n\n# Run with coverage\nmix test --cover\n```\n\n### Snapshot Updates\n```bash\n# Update snapshot fixtures\nUPDATE_SNAPSHOTS=1 mix test test/symphony_elixir/status_dashboard_snapshot_test.exs\n```\n\n### Task-Specific Tests\n```bash\n# Test Mix tasks\nmix test test/mix/tasks/\n\n# Test core functionality\nmix test test/symphony_elixir/\n```\n\n## Contributing New Tests\n\n### Test Structure Guidelines\n1. **Isolation**: Use temporary directories and unique identifiers\n2. **Cleanup**: Always clean up files and processes in `on_exit`\n3. **Realism**: Simulate external dependencies accurately\n4. **Coverage**: Test both success and failure paths\n5. **Clarity**: Use descriptive test names and comments\n\n### Common Pitfalls to Avoid\n- **State leakage**: Ensure tests don't affect each other\n- **External dependencies**: Mock or fake all external commands\n- **Timing issues**: Use appropriate sleeps or message passing\n- **Resource cleanup**: Always clean up temporary files\n\n### Example Test Template\n```elixir\ndefmodule MyComponentTest do\n use ExUnit.Case\n import ExUnit.CaptureIO\n \n setup do\n # Setup code here\n :ok\n end\n \n test \"descriptive test name\" do\n # Test logic with assertions\n assert result == expected\n end\n \n defp helper_function do\n # Private helpers for test setup\n end\nend\n```\n\nThis test suite provides comprehensive validation of SymphonyElixir's autonomous agent system, ensuring reliable operation of PR validation, spec checking, workspace management, and core orchestration logic.","other":"# Other\n\n# Other Module Group\n\n## Overview\n\nThe **Other** module group contains foundational documentation, legal notices, configuration files, and supporting assets for the Symphony project. Unlike functional code modules, these components establish the project's boundaries, provide user-facing documentation, define system architecture, and configure the runtime environment.\n\n## Purpose and Organization\n\nThis collection serves as the project's scaffolding, providing:\n\n- **Legal and licensing** information ([NOTICE](notice.md))\n- **User onboarding** and high-level introduction ([README.md](readme.md))\n- **Architectural specification** ([SPEC.md](spec.md))\n- **Runtime configuration** ([config](config.md))\n- **Technical documentation** ([docs](docs.md))\n- **UI styling assets** ([priv](priv.md))\n- **Quality assurance** ([test](test.md))\n\n## Key Relationships\n\n```\ngraph TD\n SPEC[SPEC.md] -->|defines architecture for| ELIXIR[elixir service]\n README[README.md] -->|introduces users to| SPEC\n CONFIG[config] -->|provides runtime config for| ELIXIR\n PRIV[priv] -->|styles dashboard for| ELIXIR\n TESTS[test] -->|validates functionality of| ELIXIR\n DOCS[docs] -->|guides implementation of| ELIXIR\n NOTICE[NOTICE] -->|provides legal context for| all\n```\n\n## How Components Work Together\n\nThe [SPEC.md](spec.md) serves as the architectural blueprint that the [elixir](elixir.md) service implements. Users typically start with the [README.md](readme.md) to understand Symphony's purpose, then reference the SPEC for detailed architecture. The [config](config.md) module configures the Elixir application at runtime, while [priv](priv.md) styles its dashboard interface. The [test](test.md) suite validates the entire system's functionality, and [docs](docs.md) provides implementation guidance. The [NOTICE](notice.md) file establishes legal parameters for all components.\n\n## Key Workflows\n\n1. **Project Onboarding**: Users read [README.md](readme.md) → consult [SPEC.md](spec.md) → review [docs](docs.md) for implementation details\n2. **Service Setup**: [config](config.md) initializes the application → [elixir](elixir.md) service starts → [priv](priv.md) assets load for UI\n3. **Quality Assurance**: [test](test.md) suite runs against [elixir](elixir.md) implementation according to [SPEC.md](spec.md) requirements\n\n## Sub-modules\n\n- [NOTICE](notice.md) - Apache 2.0 license and legal notice\n- [README.md](readme.md) - Project introduction and getting started guide\n- [SPEC.md](spec.md) - Service specification and architectural design\n- [elixir](elixir.md) - Erlang/OTP-based orchestration service\n- [config](config.md) - Runtime configuration for Phoenix application\n- [docs](docs.md) - Technical documentation and implementation guides\n- [priv](priv.md) - Dashboard CSS styling and static assets\n- [test](test.md) - ExUnit test suite for system validation","overview":"# symphony — Wiki\n\n---\n# Welcome to Symphony\n\n**Symphony** is an Elixir-based orchestration system that transforms project work into isolated, autonomous implementation runs. It allows engineering teams to manage work at a higher level—by defining tasks in an issue tracker—instead of manually supervising coding agents.\n\nIn practice, Symphony periodically checks a configured Linear board for new or updated work. For each actionable task, it spins up a dedicated, [Codex](https://openai.com/index/codex-apps)-backed agent inside an isolated workspace. This agent works to implement the task, generate proof (like CI status, PR reviews, and walkthrough videos), and safely land the changes. Engineers are freed from micromanagement and can focus on reviewing outcomes and guiding priorities.\n\n> **⚠️ Engineering Preview**\n> Symphony is currently a low-key preview for testing in trusted environments. It works best in codebases that have adopted patterns of [harness engineering](https://openai.com/index/harness-engineering/).\n\n## High-Level Architecture\n\nAt its core, Symphony is a Phoenix application built on OTP principles. It follows a clear flow: monitor an issue tracker, orchestrate agents to handle tasks, and provide observability throughout. The main modules interact as shown below:\n\n```mermaid\ngraph LR\n Linear[Linear Issues] --> Tracker[Tracker Adapter]\n Tracker --> Orchestrator[Core Orchestrator]\n Orchestrator --> Agent[Codex Agent]\n Agent --> Workspace[Isolated Workspace]\n Workspace --> PR[Pull Request & Proof]\n Orchestrator --> Dashboard[Web Dashboard]\n Dashboard --> User[Engineer]\n```\n\n## How It Works: The End-to-End Flow\n\n1. **Poll for Work:** The system's [Core Application and Orchestration](Core-Application-and-Orchestration) module periodically queries Linear via the [External Services Integration](External-Services-Integration) layer.\n2. **Orchestrate an Agent:** When a suitable issue is found, the orchestrator spawns a supervisor to manage a dedicated Codex agent for that task, providing it with a clean workspace.\n3. **Execute the Task:** The agent runs autonomously within its workspace—writing code, running tests, and creating a pull request.\n4. **Generate Proof:** Upon completion, the agent compiles proof of work (CI status, complexity analysis, a video walkthrough) and updates the Linear issue.\n5. **Observe & Manage:** Throughout the lifecycle, all activity, state, and metrics are visible in the real-time [Web Dashboard and API](Web-Dashboard-and-API) and captured by the [Observability and Logging](Observability-and-Logging) system.\n\nThe [State Management](State-Management) module provides a consistent interface for reading and writing issue data, while development and maintenance utilities are encapsulated in dedicated [Mix Tasks](Mix-Tasks).\n\n## Key Modules to Explore\n\n* **`Core Application and Orchestration`**: The central nervous system. Manages the agent lifecycle, workflow definitions, and supervisor trees.\n* **`Web Dashboard and API`**: A Phoenix LiveView dashboard and JSON API for real-time observability of runs, agents, and system health.\n* **`External Services Integration`**: Adapters for Linear and Codex, cleanly separating external API interactions from internal logic.\n* **`Observability and Logging`**: Combines rotating disk logs with a live terminal UI to monitor agents, token usage, and rate limits.\n\n## Getting Started\n\n### Basic Configuration\n\nSymphony requires environment variables to connect to Linear and Codex. A minimal `.env` file might look like this:\n\n```bash\n# Linear API Access\nLINEAR_API_KEY=your_linear_api_key\nLINEAR_TEAM_ID=your_team_id\n\n# Codex App Server\nCODEX_APPSERVER_URL=http://localhost:8080\nCODEX_APPSERVER_API_KEY=your_codex_key\n```\n\n### Running the Application\n\n1. Set the required environment variables.\n2. Install dependencies with `mix deps.get`.\n3. Start the Phoenix server with `mix phx.server`.\n\nThe application will start polling the configured Linear team. The dashboard will be available at `http://localhost:4000`, providing a live view of the orchestration state.\n\n---\n\n**Next Steps:** Dive into the [Core Application and Orchestration](Core-Application-and-Orchestration) module to understand the main supervision tree and workflow logic, or explore the [Web Dashboard](Web-Dashboard-and-API) to see how to monitor active runs.","state-management":"# State Management\n\n# Tracker Module - Issue Tracker Adapter Boundary\n\n## Overview\n\nThe `SymphonyElixir.Tracker` module serves as a **unified adapter interface** for issue tracker operations, providing a consistent API for reading and writing issue data while supporting multiple backend implementations. This module abstracts away the specifics of different issue tracker systems, allowing the rest of the application to interact with trackers in a standardized way.\n\n## Core Architecture\n\nThe module follows a **behavior-based adapter pattern** where:\n- `SymphonyElixir.Tracker` defines the interface contract\n- Concrete adapters implement the behavior for specific trackers\n- Runtime configuration determines which adapter to use\n\n```mermaid\ngraph TD\n A[Application Code] --> B[SymphonyElixir.Tracker]\n B --> C{Config.tracker_kind}\n C -->|\"memory\"| D[Memory Adapter]\n C -->|default| E[Linear Adapter]\n D --> F[In-Memory Store]\n E --> G[Linear API]\n```\n\n## Public Interface\n\n### Issue Retrieval Functions\n\n```elixir\n@spec fetch_candidate_issues() :: {:ok, [SymphonyElixir.Linear.Issue.t()]} | {:error, term()}\n```\nFetches issues eligible for processing. The definition of \"candidate\" varies by adapter.\n\n```elixir\n@spec fetch_issues_by_states([String.t()]) :: {:ok, [SymphonyElixir.Linear.Issue.t()]} | {:error, term()}\n```\nRetrieves issues filtered by their current state names. State name normalization may differ between adapters.\n\n```elixir\n@spec fetch_issue_states_by_ids([String.t()]) :: {:ok, [SymphonyElixir.Linear.Issue.t()]} | {:error, term()}\n```\nFetches complete issue data for specific issue IDs. Useful for batch operations on known issues.\n\n### Issue Modification Functions\n\n```elixir\n@spec create_comment(String.t(), String.t()) :: :ok | {:error, term()}\n```\nAdds a comment to an issue. The `issue_id` must be valid for the current tracker.\n\n```elixir\n@spec update_issue_state(String.t(), String.t()) :: :ok | {:error, term()}\n```\nTransitions an issue to a new state. State transitions must be valid within the tracker's workflow.\n\n## Adapter Selection\n\nThe active adapter is determined at runtime via configuration:\n\n```elixir\n# config/config.exs\nconfig :symphony_elixir, tracker_kind: \"memory\" # or omit for Linear adapter\n```\n\n**Available Adapters:**\n\n1. **`SymphonyElixir.Tracker.Memory`** - Used for `tracker_kind: \"memory\"`\n - In-memory storage for testing and development\n - Issues configured via application environment\n - Sends events to configured PIDs for test verification\n\n2. **`SymphonyElixir.Linear.Adapter`** - Default adapter\n - Production adapter for Linear issue tracker\n - Makes actual API calls to Linear's GraphQL endpoint\n\n## Memory Adapter Details\n\nThe memory adapter (`SymphonyElixir.Tracker.Memory`) provides a complete in-memory implementation useful for:\n\n### Configuration\n```elixir\n# In test or dev configuration\nconfig :symphony_elixir,\n tracker_kind: \"memory\",\n memory_tracker_issues: [\n %SymphonyElixir.Linear.Issue{id: \"ISS-1\", title: \"Test Issue\", state: \"Todo\"}\n ],\n memory_tracker_recipient: self() # PID to receive events\n```\n\n### State Normalization\nThe memory adapter normalizes state names by:\n1. Trimming whitespace\n2. Converting to lowercase\nThis ensures case-insensitive matching when filtering by states.\n\n### Event System\nFor test verification, the adapter sends messages to a configured PID:\n- `{:memory_tracker_comment, issue_id, body}` for comment creation\n- `{:memory_tracker_state_update, issue_id, state_name}` for state updates\n\n## Usage Examples\n\n### Basic Usage\n```elixir\n# Fetch issues in specific states\n{:ok, issues} = SymphonyElixir.Tracker.fetch_issues_by_states([\"Todo\", \"In Progress\"])\n\n# Update an issue's state\n:ok = SymphonyElixir.Tracker.update_issue_state(\"ISS-123\", \"Done\")\n\n# Add a comment\n:ok = SymphonyElixir.Tracker.create_comment(\"ISS-123\", \"Automated update completed\")\n```\n\n### Testing with Memory Adapter\n```elixir\ndefmodule MyTest do\n use ExUnit.Case\n\n setup do\n # Configure memory adapter\n Application.put_env(:symphony_elixir, :tracker_kind, \"memory\")\n Application.put_env(:symphony_elixir, :memory_tracker_recipient, self())\n \n test_issue = %SymphonyElixir.Linear.Issue{\n id: \"TEST-1\",\n title: \"Test Issue\",\n state: \"Todo\"\n }\n \n Application.put_env(:symphony_elixir, :memory_tracker_issues, [test_issue])\n \n :ok\n end\n\n test \"updates issue state\" do\n :ok = SymphonyElixir.Tracker.update_issue_state(\"TEST-1\", \"In Progress\")\n \n # Verify event was sent\n assert_receive {:memory_tracker_state_update, \"TEST-1\", \"In Progress\"}\n end\nend\n```\n\n## Integration Points\n\nThis module is designed to be called by:\n- State machine processors that need to read current issue states\n- Automation workflows that modify issue states or add comments\n- Monitoring systems that track issue lifecycle\n\n## Implementing New Adapters\n\nTo add support for a new issue tracker:\n\n1. Implement the `SymphonyElixir.Tracker` behavior\n2. Add adapter selection logic to `SymphonyElixir.Tracker.adapter/0`\n3. Update configuration documentation for the new `tracker_kind` value\n\nAll adapters must handle the same data structures (`SymphonyElixir.Linear.Issue`) to maintain compatibility with the rest of the system.\n\n## Error Handling\n\nAll functions return either `:ok` or `{:ok, data}` on success, or `{:error, reason}` on failure. Callers should handle both cases. The specific error reasons are adapter-dependent but should follow Elixir convention (`:not_found`, `:invalid_state`, etc.).","web-dashboard-and-api":"# Web Dashboard and API\n\n# Web Dashboard and API Module\n\n## Overview\n\nThe **Web Dashboard and API** module provides a real-time observability interface for Symphony's runtime operations. Built with Phoenix, it delivers both a live-updating web dashboard and a JSON API for programmatic access to orchestration state, issue tracking, and performance metrics.\n\n## Architecture\n\nThe module follows a classic Phoenix application structure with several key components:\n\n```mermaid\ngraph TD\n Endpoint[Endpoint & Router] --> Static[Static Assets]\n Endpoint --> Live[Dashboard LiveView]\n Endpoint --> API[Observability API]\n \n Live --> Presenter[Presenter]\n API --> Presenter\n \n Presenter --> Orchestrator[Orchestrator]\n \n Live --> PubSub[ObservabilityPubSub]\n Orchestrator --> PubSub\n```\n\n## Core Components\n\n### 1. Endpoint & Router (`endpoint.ex`, `router.ex`)\n\nThe `SymphonyElixirWeb.Endpoint` serves as the entry point for all HTTP traffic and configures:\n- WebSocket connections for LiveView\n- JSON parsing with Jason\n- Session management\n- Request telemetry\n\nThe `Router` defines three main routes:\n- **Static assets** (`/dashboard.css`, `/vendor/*`) - Served directly from the `StaticAssetController`\n- **Dashboard** (`/`) - Rendered by the `DashboardLive` LiveView\n- **API v1** (`/api/v1/*`) - Handled by the `ObservabilityApiController`\n\n### 2. Real-time Dashboard (`dashboard_live.ex`)\n\nThe `DashboardLive` module provides a Phoenix LiveView that automatically updates when orchestration state changes.\n\n**Key features:**\n- **Live updates**: Subscribes to `ObservabilityPubSub` and refreshes when `:observability_updated` broadcasts occur\n- **Automatic tick**: Updates runtime calculations every second\n- **Responsive layout**: Built with semantic HTML and CSS classes from `dashboard.css`\n- **Interactive elements**: Copy-to-clipboard buttons, formatted timestamps, and color-coded state badges\n\n**Dashboard sections include:**\n- **Summary metrics**: Running sessions, retrying issues, total tokens, runtime\n- **Rate limits**: Raw snapshot of upstream API rate limits\n- **Running sessions**: Active issues with state, session IDs, runtime, and token usage\n- **Retry queue**: Issues waiting for their next retry window\n\n### 3. Observability API (`observability_api_controller.ex`)\n\nThe REST API provides JSON endpoints for programmatic access:\n\n| Endpoint | Method | Description | Responses |\n|----------|--------|-------------|-----------|\n| `/api/v1/state` | GET | Complete runtime snapshot | 200: Full state payload<br>405: Method not allowed |\n| `/api/v1/:issue_identifier` | GET | Detailed information for a specific issue | 200: Issue details<br>404: Issue not found<br>405: Method not allowed |\n| `/api/v1/refresh` | POST | Manually trigger a state refresh | 202: Refresh accepted<br>503: Orchestrator unavailable<br>405: Method not allowed |\n\n**Error responses** follow a consistent format:\n```json\n{\n \"error\": {\n \"code\": \"error_code\",\n \"message\": \"Human readable message\"\n }\n}\n```\n\n### 4. Data Presentation Layer (`presenter.ex`)\n\nThe `Presenter` module serves as the data transformation layer between the orchestrator's internal state and the external API/dashboard formats.\n\n**Main functions:**\n- `state_payload/2`: Transforms an orchestrator snapshot into the complete dashboard/API payload\n- `issue_payload/3`: Creates detailed issue-specific payloads\n- `refresh_payload/1`: Formats orchestrator refresh responses\n\n**Data transformation examples:**\n- ISO 8601 timestamp formatting\n- Human-readable message summaries via `StatusDashboard.humanize_codex_message/1`\n- Token count formatting with thousands separators\n- Runtime duration calculations\n\n### 5. Static Asset Management\n\n**Embedded assets:**\n- `dashboard.css`: Custom dashboard styles\n- `phoenix_html.js`, `phoenix.js`, `phoenix_live_view.js`: Phoenix framework JavaScript\n\nAssets are compiled into the BEAM file at build time via `StaticAssets` and served with aggressive caching headers (1-year max-age).\n\n### 6. Layout System (`layouts.ex`)\n\n- `root/1`: HTML5 document with Phoenix LiveView setup and CSRF protection\n- `app/1`: Main application container for the dashboard\n\n### 7. PubSub Integration (`observability_pubsub.ex`)\n\nA thin wrapper around Phoenix PubSub that:\n- Provides a single-topic subscription for dashboard updates (`\"observability:dashboard\"`)\n- Gracefully handles cases where the PubSub process isn't running\n- Broadcasts `:observability_updated` messages when orchestration state changes\n\n## Configuration\n\nThe module supports runtime configuration via the endpoint:\n\n```elixir\n# In your config/config.exs\nconfig :symphony_elixir, SymphonyElixirWeb.Endpoint,\n orchestrator: MyCustomOrchestrator, # defaults to SymphonyElixir.Orchestrator\n snapshot_timeout_ms: 30_000 # defaults to 15_000\n```\n\n## Integration Points\n\n### With the Orchestrator\n\nThe module communicates with the orchestrator through three main functions:\n1. `Orchestrator.snapshot/2` - Retrieves current state (with timeout)\n2. `Orchestrator.request_refresh/1` - Triggers manual refresh\n3. PubSub broadcasts - Notify dashboard of updates\n\n### With External Systems\n\n- **Dashboard clients**: Web browsers connect via WebSocket for live updates\n- **API clients**: External monitoring tools can query the JSON API\n- **Configuration systems**: Runtime configuration via environment or config files\n\n## Usage Examples\n\n### Dashboard Access\nNavigate to `http://localhost:4000/` (port depends on your Phoenix configuration) to view the live dashboard.\n\n### API Usage\n\n**Get current state:**\n```bash\ncurl -X GET http://localhost:4000/api/v1/state\n```\n\n**Get issue details:**\n```bash\ncurl -X GET http://localhost:4000/api/v1/ISSUE-123\n```\n\n**Trigger refresh:**\n```bash\ncurl -X POST http://localhost:4000/api/v1/refresh\n```\n\n## Error Handling\n\nThe module implements comprehensive error handling:\n\n1. **Orchestrator unavailable**: Returns 503 with `orchestrator_unavailable` code\n2. **Snapshot timeout**: Returns error payload with `snapshot_timeout` code\n3. **Issue not found**: Returns 404 with `issue_not_found` code\n4. **Method not allowed**: Returns 405 with `method_not_allowed` code\n5. **Route not found**: Returns 404 with `not_found` code\n\n## Performance Considerations\n\n- **Snapshot timeouts**: Default 15-second timeout protects against orchestrator stalls\n- **Asset caching**: Static assets cached for 1 year\n- **LiveView efficiency**: Only updates changed portions of the DOM\n- **PubSub optimization**: Single topic reduces subscription overhead\n\n## Extension Points\n\n1. **Custom presenters**: Extend `Presenter` module for additional data transformations\n2. **Additional API endpoints**: Add new routes to the router and corresponding controller functions\n3. **Dashboard widgets**: Extend `DashboardLive` with new LiveView components\n4. **Authentication**: Add plugs to the router pipeline for secured access\n\n## Dependencies\n\n- Phoenix 1.7+ for LiveView and endpoint infrastructure\n- Jason for JSON parsing and encoding\n- SymphonyElixir.Orchestrator for runtime data\n- SymphonyElixir.PubSub for real-time updates\n\nThis module provides a complete, production-ready observability interface that balances real-time responsiveness with API accessibility, making Symphony's internal state fully transparent to operators and external monitoring systems."}; | |
| var TREE = [{"name":"Core Application and Orchestration","slug":"core-application-and-orchestration","files":["elixir/lib/symphony_elixir.ex","elixir/lib/symphony_elixir/agent_runner.ex","elixir/lib/symphony_elixir/orchestrator.ex","elixir/lib/symphony_elixir/workflow.ex","elixir/lib/symphony_elixir/workflow_store.ex","elixir/lib/symphony_elixir/prompt_builder.ex","elixir/lib/symphony_elixir/cli.ex","elixir/lib/symphony_elixir/config.ex","elixir/lib/symphony_elixir/workspace.ex","elixir/lib/symphony_elixir/http_server.ex"]},{"name":"Web Dashboard and API","slug":"web-dashboard-and-api","files":["elixir/lib/symphony_elixir_web/components/layouts.ex","elixir/lib/symphony_elixir_web/controllers/observability_api_controller.ex","elixir/lib/symphony_elixir_web/controllers/static_asset_controller.ex","elixir/lib/symphony_elixir_web/endpoint.ex","elixir/lib/symphony_elixir_web/error_html.ex","elixir/lib/symphony_elixir_web/error_json.ex","elixir/lib/symphony_elixir_web/live/dashboard_live.ex","elixir/lib/symphony_elixir_web/observability_pubsub.ex","elixir/lib/symphony_elixir_web/presenter.ex","elixir/lib/symphony_elixir_web/router.ex","elixir/lib/symphony_elixir_web/static_assets.ex"]},{"name":"External Services Integration","slug":"external-services-integration","files":["elixir/lib/symphony_elixir/linear/adapter.ex","elixir/lib/symphony_elixir/linear/client.ex","elixir/lib/symphony_elixir/linear/issue.ex","elixir/lib/symphony_elixir/codex/app_server.ex","elixir/lib/symphony_elixir/codex/dynamic_tool.ex"]},{"name":"Mix Tasks","slug":"mix-tasks","files":["elixir/lib/mix/tasks/pr_body.check.ex","elixir/lib/mix/tasks/specs.check.ex","elixir/lib/mix/tasks/workspace.before_remove.ex"]},{"name":"Observability and Logging","slug":"observability-and-logging","files":["elixir/lib/symphony_elixir/log_file.ex","elixir/lib/symphony_elixir/status_dashboard.ex","elixir/lib/symphony_elixir/specs_check.ex"]},{"name":"State Management","slug":"state-management","files":["elixir/lib/symphony_elixir/tracker.ex","elixir/lib/symphony_elixir/tracker/memory.ex"]},{"name":"Other","slug":"other","files":[],"children":[{"name":"Other — NOTICE","slug":"other-notice","files":["NOTICE"]},{"name":"Other — README.md","slug":"other-readme-md","files":["README.md"]},{"name":"Other — SPEC.md","slug":"other-spec-md","files":["SPEC.md"]},{"name":"Other — elixir","slug":"other-elixir","files":["elixir/AGENTS.md","elixir/Makefile","elixir/README.md","elixir/WORKFLOW.md","elixir/mise.toml","elixir/mix.exs"]},{"name":"Other — config","slug":"other-config","files":["elixir/config/config.exs"]},{"name":"Other — docs","slug":"other-docs","files":["elixir/docs/logging.md","elixir/docs/token_accounting.md"]},{"name":"Other — priv","slug":"other-priv","files":["elixir/priv/static/dashboard.css"]},{"name":"Other — test","slug":"other-test","files":["elixir/test/mix/tasks/pr_body_check_test.exs","elixir/test/mix/tasks/specs_check_task_test.exs","elixir/test/mix/tasks/workspace_before_remove_test.exs","elixir/test/support/snapshot_support.exs","elixir/test/support/test_support.exs","elixir/test/symphony_elixir/app_server_test.exs","elixir/test/symphony_elixir/cli_test.exs","elixir/test/symphony_elixir/core_test.exs","elixir/test/symphony_elixir/dynamic_tool_test.exs","elixir/test/symphony_elixir/extensions_test.exs","elixir/test/symphony_elixir/log_file_test.exs","elixir/test/symphony_elixir/observability_pubsub_test.exs","elixir/test/symphony_elixir/orchestrator_status_test.exs","elixir/test/symphony_elixir/specs_check_test.exs","elixir/test/symphony_elixir/status_dashboard_snapshot_test.exs","elixir/test/symphony_elixir/workspace_and_config_test.exs","elixir/test/test_helper.exs"]}]}]; | |
| var META = {"fromCommit":"b0e0ff0082236a73c12a48483d0c6036fdd31fe1","generatedAt":"2026-03-08T08:40:11.304Z","model":"deepseek-reasoner","moduleFiles":{"Core Application and Orchestration":["elixir/lib/symphony_elixir.ex","elixir/lib/symphony_elixir/agent_runner.ex","elixir/lib/symphony_elixir/orchestrator.ex","elixir/lib/symphony_elixir/workflow.ex","elixir/lib/symphony_elixir/workflow_store.ex","elixir/lib/symphony_elixir/prompt_builder.ex","elixir/lib/symphony_elixir/cli.ex","elixir/lib/symphony_elixir/config.ex","elixir/lib/symphony_elixir/workspace.ex","elixir/lib/symphony_elixir/http_server.ex"],"Web Dashboard and API":["elixir/lib/symphony_elixir_web/components/layouts.ex","elixir/lib/symphony_elixir_web/controllers/observability_api_controller.ex","elixir/lib/symphony_elixir_web/controllers/static_asset_controller.ex","elixir/lib/symphony_elixir_web/endpoint.ex","elixir/lib/symphony_elixir_web/error_html.ex","elixir/lib/symphony_elixir_web/error_json.ex","elixir/lib/symphony_elixir_web/live/dashboard_live.ex","elixir/lib/symphony_elixir_web/observability_pubsub.ex","elixir/lib/symphony_elixir_web/presenter.ex","elixir/lib/symphony_elixir_web/router.ex","elixir/lib/symphony_elixir_web/static_assets.ex"],"External Services Integration":["elixir/lib/symphony_elixir/linear/adapter.ex","elixir/lib/symphony_elixir/linear/client.ex","elixir/lib/symphony_elixir/linear/issue.ex","elixir/lib/symphony_elixir/codex/app_server.ex","elixir/lib/symphony_elixir/codex/dynamic_tool.ex"],"Mix Tasks":["elixir/lib/mix/tasks/pr_body.check.ex","elixir/lib/mix/tasks/specs.check.ex","elixir/lib/mix/tasks/workspace.before_remove.ex"],"Observability and Logging":["elixir/lib/symphony_elixir/log_file.ex","elixir/lib/symphony_elixir/status_dashboard.ex","elixir/lib/symphony_elixir/specs_check.ex"],"State Management":["elixir/lib/symphony_elixir/tracker.ex","elixir/lib/symphony_elixir/tracker/memory.ex"],"Other":["NOTICE","README.md","SPEC.md","elixir/AGENTS.md","elixir/Makefile","elixir/README.md","elixir/WORKFLOW.md","elixir/mise.toml","elixir/mix.exs","elixir/config/config.exs","elixir/docs/logging.md","elixir/docs/token_accounting.md","elixir/priv/static/dashboard.css","elixir/test/mix/tasks/pr_body_check_test.exs","elixir/test/mix/tasks/specs_check_task_test.exs","elixir/test/mix/tasks/workspace_before_remove_test.exs","elixir/test/support/snapshot_support.exs","elixir/test/support/test_support.exs","elixir/test/symphony_elixir/app_server_test.exs","elixir/test/symphony_elixir/cli_test.exs","elixir/test/symphony_elixir/core_test.exs","elixir/test/symphony_elixir/dynamic_tool_test.exs","elixir/test/symphony_elixir/extensions_test.exs","elixir/test/symphony_elixir/log_file_test.exs","elixir/test/symphony_elixir/observability_pubsub_test.exs","elixir/test/symphony_elixir/orchestrator_status_test.exs","elixir/test/symphony_elixir/specs_check_test.exs","elixir/test/symphony_elixir/status_dashboard_snapshot_test.exs","elixir/test/symphony_elixir/workspace_and_config_test.exs","elixir/test/test_helper.exs"],"Other — NOTICE":["NOTICE"],"Other — README.md":["README.md"],"Other — SPEC.md":["SPEC.md"],"Other — elixir":["elixir/AGENTS.md","elixir/Makefile","elixir/README.md","elixir/WORKFLOW.md","elixir/mise.toml","elixir/mix.exs"],"Other — config":["elixir/config/config.exs"],"Other — docs":["elixir/docs/logging.md","elixir/docs/token_accounting.md"],"Other — priv":["elixir/priv/static/dashboard.css"],"Other — test":["elixir/test/mix/tasks/pr_body_check_test.exs","elixir/test/mix/tasks/specs_check_task_test.exs","elixir/test/mix/tasks/workspace_before_remove_test.exs","elixir/test/support/snapshot_support.exs","elixir/test/support/test_support.exs","elixir/test/symphony_elixir/app_server_test.exs","elixir/test/symphony_elixir/cli_test.exs","elixir/test/symphony_elixir/core_test.exs","elixir/test/symphony_elixir/dynamic_tool_test.exs","elixir/test/symphony_elixir/extensions_test.exs","elixir/test/symphony_elixir/log_file_test.exs","elixir/test/symphony_elixir/observability_pubsub_test.exs","elixir/test/symphony_elixir/orchestrator_status_test.exs","elixir/test/symphony_elixir/specs_check_test.exs","elixir/test/symphony_elixir/status_dashboard_snapshot_test.exs","elixir/test/symphony_elixir/workspace_and_config_test.exs","elixir/test/test_helper.exs"]},"moduleTree":[{"name":"Core Application and Orchestration","slug":"core-application-and-orchestration","files":["elixir/lib/symphony_elixir.ex","elixir/lib/symphony_elixir/agent_runner.ex","elixir/lib/symphony_elixir/orchestrator.ex","elixir/lib/symphony_elixir/workflow.ex","elixir/lib/symphony_elixir/workflow_store.ex","elixir/lib/symphony_elixir/prompt_builder.ex","elixir/lib/symphony_elixir/cli.ex","elixir/lib/symphony_elixir/config.ex","elixir/lib/symphony_elixir/workspace.ex","elixir/lib/symphony_elixir/http_server.ex"]},{"name":"Web Dashboard and API","slug":"web-dashboard-and-api","files":["elixir/lib/symphony_elixir_web/components/layouts.ex","elixir/lib/symphony_elixir_web/controllers/observability_api_controller.ex","elixir/lib/symphony_elixir_web/controllers/static_asset_controller.ex","elixir/lib/symphony_elixir_web/endpoint.ex","elixir/lib/symphony_elixir_web/error_html.ex","elixir/lib/symphony_elixir_web/error_json.ex","elixir/lib/symphony_elixir_web/live/dashboard_live.ex","elixir/lib/symphony_elixir_web/observability_pubsub.ex","elixir/lib/symphony_elixir_web/presenter.ex","elixir/lib/symphony_elixir_web/router.ex","elixir/lib/symphony_elixir_web/static_assets.ex"]},{"name":"External Services Integration","slug":"external-services-integration","files":["elixir/lib/symphony_elixir/linear/adapter.ex","elixir/lib/symphony_elixir/linear/client.ex","elixir/lib/symphony_elixir/linear/issue.ex","elixir/lib/symphony_elixir/codex/app_server.ex","elixir/lib/symphony_elixir/codex/dynamic_tool.ex"]},{"name":"Mix Tasks","slug":"mix-tasks","files":["elixir/lib/mix/tasks/pr_body.check.ex","elixir/lib/mix/tasks/specs.check.ex","elixir/lib/mix/tasks/workspace.before_remove.ex"]},{"name":"Observability and Logging","slug":"observability-and-logging","files":["elixir/lib/symphony_elixir/log_file.ex","elixir/lib/symphony_elixir/status_dashboard.ex","elixir/lib/symphony_elixir/specs_check.ex"]},{"name":"State Management","slug":"state-management","files":["elixir/lib/symphony_elixir/tracker.ex","elixir/lib/symphony_elixir/tracker/memory.ex"]},{"name":"Other","slug":"other","files":[],"children":[{"name":"Other — NOTICE","slug":"other-notice","files":["NOTICE"]},{"name":"Other — README.md","slug":"other-readme-md","files":["README.md"]},{"name":"Other — SPEC.md","slug":"other-spec-md","files":["SPEC.md"]},{"name":"Other — elixir","slug":"other-elixir","files":["elixir/AGENTS.md","elixir/Makefile","elixir/README.md","elixir/WORKFLOW.md","elixir/mise.toml","elixir/mix.exs"]},{"name":"Other — config","slug":"other-config","files":["elixir/config/config.exs"]},{"name":"Other — docs","slug":"other-docs","files":["elixir/docs/logging.md","elixir/docs/token_accounting.md"]},{"name":"Other — priv","slug":"other-priv","files":["elixir/priv/static/dashboard.css"]},{"name":"Other — test","slug":"other-test","files":["elixir/test/mix/tasks/pr_body_check_test.exs","elixir/test/mix/tasks/specs_check_task_test.exs","elixir/test/mix/tasks/workspace_before_remove_test.exs","elixir/test/support/snapshot_support.exs","elixir/test/support/test_support.exs","elixir/test/symphony_elixir/app_server_test.exs","elixir/test/symphony_elixir/cli_test.exs","elixir/test/symphony_elixir/core_test.exs","elixir/test/symphony_elixir/dynamic_tool_test.exs","elixir/test/symphony_elixir/extensions_test.exs","elixir/test/symphony_elixir/log_file_test.exs","elixir/test/symphony_elixir/observability_pubsub_test.exs","elixir/test/symphony_elixir/orchestrator_status_test.exs","elixir/test/symphony_elixir/specs_check_test.exs","elixir/test/symphony_elixir/status_dashboard_snapshot_test.exs","elixir/test/symphony_elixir/workspace_and_config_test.exs","elixir/test/test_helper.exs"]}]}]}; | |
| (function() { | |
| var activePage = 'overview'; | |
| document.addEventListener('DOMContentLoaded', function() { | |
| mermaid.initialize({ startOnLoad: false, theme: 'neutral', securityLevel: 'loose' }); | |
| renderMeta(); | |
| renderNav(); | |
| document.getElementById('menu-toggle').addEventListener('click', function() { | |
| document.getElementById('sidebar').classList.toggle('open'); | |
| }); | |
| if (location.hash && location.hash.length > 1) { | |
| activePage = decodeURIComponent(location.hash.slice(1)); | |
| } | |
| navigateTo(activePage); | |
| }); | |
| function renderMeta() { | |
| if (!META) return; | |
| var el = document.getElementById('meta-info'); | |
| var parts = []; | |
| if (META.generatedAt) { | |
| parts.push(new Date(META.generatedAt).toLocaleDateString()); | |
| } | |
| if (META.model) parts.push(META.model); | |
| if (META.fromCommit) parts.push(META.fromCommit.slice(0, 8)); | |
| el.textContent = parts.join(' \u00b7 '); | |
| } | |
| function renderNav() { | |
| var container = document.getElementById('nav-tree'); | |
| var html = '<div class="nav-section">'; | |
| html += '<a class="nav-item overview" data-page="overview" href="#overview">Overview</a>'; | |
| html += '</div>'; | |
| if (TREE.length > 0) { | |
| html += '<div class="nav-group-label">Modules</div>'; | |
| html += buildNavTree(TREE); | |
| } | |
| container.innerHTML = html; | |
| container.addEventListener('click', function(e) { | |
| var target = e.target; | |
| while (target && !target.dataset.page) { target = target.parentElement; } | |
| if (target && target.dataset.page) { | |
| e.preventDefault(); | |
| navigateTo(target.dataset.page); | |
| } | |
| }); | |
| } | |
| function buildNavTree(nodes) { | |
| var html = ''; | |
| for (var i = 0; i < nodes.length; i++) { | |
| var node = nodes[i]; | |
| html += '<div class="nav-section">'; | |
| html += '<a class="nav-item" data-page="' + escH(node.slug) + '" href="#' + encodeURIComponent(node.slug) + '">' + escH(node.name) + '</a>'; | |
| if (node.children && node.children.length > 0) { | |
| html += '<div class="nav-children">' + buildNavTree(node.children) + '</div>'; | |
| } | |
| html += '</div>'; | |
| } | |
| return html; | |
| } | |
| function escH(s) { | |
| var d = document.createElement('div'); | |
| d.textContent = s; | |
| return d.innerHTML; | |
| } | |
| function navigateTo(page) { | |
| activePage = page; | |
| location.hash = encodeURIComponent(page); | |
| var items = document.querySelectorAll('.nav-item'); | |
| for (var i = 0; i < items.length; i++) { | |
| if (items[i].dataset.page === page) { | |
| items[i].classList.add('active'); | |
| } else { | |
| items[i].classList.remove('active'); | |
| } | |
| } | |
| var contentEl = document.getElementById('content'); | |
| var md = PAGES[page]; | |
| if (!md) { | |
| contentEl.innerHTML = '<div class="empty-state"><h2>Page not found</h2><p>' + escH(page) + '.md does not exist.</p></div>'; | |
| return; | |
| } | |
| contentEl.innerHTML = marked.parse(md); | |
| // Rewrite .md links to hash navigation | |
| var links = contentEl.querySelectorAll('a[href]'); | |
| for (var i = 0; i < links.length; i++) { | |
| var href = links[i].getAttribute('href'); | |
| if (href && href.endsWith('.md') && href.indexOf('://') === -1) { | |
| var slug = href.replace(/\.md$/, ''); | |
| links[i].setAttribute('href', '#' + encodeURIComponent(slug)); | |
| (function(s) { | |
| links[i].addEventListener('click', function(e) { | |
| e.preventDefault(); | |
| navigateTo(s); | |
| }); | |
| })(slug); | |
| } | |
| } | |
| // Convert mermaid code blocks into mermaid divs | |
| var mermaidBlocks = contentEl.querySelectorAll('pre code.language-mermaid'); | |
| for (var i = 0; i < mermaidBlocks.length; i++) { | |
| var pre = mermaidBlocks[i].parentElement; | |
| var div = document.createElement('div'); | |
| div.className = 'mermaid'; | |
| div.textContent = mermaidBlocks[i].textContent; | |
| pre.parentNode.replaceChild(div, pre); | |
| } | |
| try { mermaid.run({ querySelector: '.mermaid' }); } catch(e) {} | |
| window.scrollTo(0, 0); | |
| document.getElementById('sidebar').classList.remove('open'); | |
| } | |
| })(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment