Harden Symphony dispatch and merge upstream runtime fixes - #1
Merged
Conversation
#### Context Symphony needs to run tickets on SSH workers, keep the live E2E reliable, and preserve correct remote path semantics. #### TL;DR *Add SSH workers, Docker-backed SSH live E2E coverage, per-host caps, and correct worker-side `~` resolution.* #### Summary - Add SSH worker execution, remote workspace handling, and SSH app-server launch support in Elixir. - Add live E2E coverage for both local workers and SSH workers, with Docker-backed SSH workers by default. - Add an optional shared `worker.max_concurrent_agents_per_host` cap and document the SSH extension. - Keep `workspace.root` raw so SSH workers resolve `~` on the worker, while local paths expand at local use sites. #### Alternatives - Keep expanding `workspace.root` in config and carry duplicate raw path state, but that complicates the config model. - Require only absolute remote workspace roots, but that makes real worker setup less ergonomic and less representative. #### Test Plan - [x] `make -C elixir all` - [x] `cd elixir && env -u SYMPHONY_LIVE_SSH_WORKER_HOSTS LINEAR_API_KEY="$(tr -d '\r\n' < ~/.linear_api_key)" SYMPHONY_RUN_LIVE_E2E=1 mix test test/symphony_elixir/live_e2e_test.exs:128`
#### Context The Codex app-server merges stderr into stdout (`stderr_to_stdout`). Non-JSON diagnostic lines written by Codex during healthy turns were incorrectly emitted as `:malformed` protocol events in the Symphony UI. <img width="2496" height="446" alt="Screenshot 2026-03-12 at 4 27 20 PM" src="https://github.com/user-attachments/assets/3d85fa5b-c0d9-4079-87c6-850c7ccf7ee2" /> #### TL;DR *Only emit `:malformed` events for JSON-like protocol frames (lines starting with `{`); silently log all other non-JSON stream output.* #### Summary - Gate `:malformed` event emission on a new `protocol_message_candidate?/1` check — only lines starting with `{` - Preserve full logging of all non-JSON stderr noise via `log_non_json_stream_line` - Fix existing "Capture stderr log" test to assert no `:malformed` event is emitted for stderr noise - Add regression test verifying truncated JSON-like frames still surface as `:malformed` #### Alternatives - Separate stderr from stdout with a dedicated pipe: more invasive change, unnecessary given the simple heuristic - Suppress all non-JSON output silently: would lose valuable diagnostic logging #### Test Plan - [x] `make -C elixir all` - [x] `mise exec -- mix format lib/symphony_elixir/codex/app_server.ex test/symphony_elixir/app_server_test.exs` - [x] Additional: `MIX_ENV=test mise exec -- mix run --no-start -e 'Application.put_env(:symphony_elixir, :workflow_file_path, System.fetch_env!("SYMPHONY_TEST_WORKFLOW")); Mix.Task.run("test", ["test/symphony_elixir/app_server_test.exs"])'` Co-authored-by: Codex <noreply@openai.com>
#### Context SSH failover currently happens inside `AgentRunner`, which can bypass per-host caps and rerun a ticket on a second host after the first host already started setup. #### TL;DR *Keep each worker run on one SSH host and let the orchestrator own retries.* #### Summary - Remove `AgentRunner`'s internal cross-host failover loop - Keep a worker lifetime pinned to one selected SSH host - Add a regression test proving startup failure on one host does not fall through to another #### Alternatives - Keep internal failover and classify retryable errors, but that duplicates orchestrator scheduling logic - Only patch per-host cap enforcement, but invisible cross-host reruns would still risk duplicate side effects #### Test Plan - [x] `make -C elixir all` - [x] `cd elixir && mise exec -- mix test test/symphony_elixir/core_test.exs:1167 test/symphony_elixir/core_test.exs:1237 test/symphony_elixir/core_test.exs:1337 --seed 0`
## Summary Pin floating external GitHub Actions workflow refs to immutable SHAs. ## Why See the rationale doc: https://docs.google.com/document/d/1qOURCNx2zszQ0uWx7Fj5ERu4jpiYjxLVWBWgKa2wTsA/edit?tab=t.0 ## Validation - `rg -n --pcre2 "uses:\s*(?!\./)(?!docker://)[^#\n]+@(?![0-9a-f]{40}(?:\s+#.*)?$)\S+" .github/workflows` - `git diff --check` - `git diff --stat -- .github/workflows`
#### Context SPEC.md had ambiguous conformance language and implementation details that made the service contract harder to port. #### TL;DR *Clarify SPEC.md and normalize RFC-style requirement wording.* #### Summary - Add normative language and tighten config, workspace, reload, restart, and user-input wording. - Move extension config ownership to extension sections. - Point Codex protocol details at the targeted app-server spec instead of duplicating payloads. - Run a careful RFC 2119 terminology pass after mechanical normalization. #### Alternatives - Left attempt/retry redesign and broader safety issues for follow-up spec changes. #### Test Plan - [x] `make -C elixir all` - [x] `git diff --check HEAD~1..HEAD` - [x] RFC keyword scan outside code fences --------- Co-authored-by: Codex <codex@openai.com>
Summary: - Update the default Symphony Codex command to select gpt-5.5 via --config model instead of the root --model flag. - Refresh README and tests so examples and argv assertions match the app-server-compatible command shape. Rationale: - Codex app-server ignores the root --model flag, so the previous command could silently use the default model instead of the intended one. - Passing model through --config keeps the workflow aligned with Codex app-server behavior and the verified gpt-5.5 setup. Tests: - mise exec -- mix format --check-formatted - mise exec -- mix test - mise exec -- mix lint - Isolated app-server smoke test with the updated command Co-authored-by: Codex <codex@openai.com>
#### Context Restrict the PR description lint workflow so forked PR checks run with the least token access needed. #### TL;DR *Set read-only workflow permissions and stop checkout from storing credentials.* #### Summary - Add explicit contents read permission to the PR description lint workflow. - Set checkout persist-credentials to false for the lint job. #### Alternatives - Leave defaults unchanged, but explicit permissions make the token scope easier to review. #### Test Plan - [ ] `make -C elixir all` - [x] Parsed `.github/workflows/pr-description-lint.yml` with PyYAML.
#### Context Codex app-server sessions can request operator input or MCP elicitation. These should pause visibly instead of retrying until exhausted. #### TL;DR *Show input-blocked Symphony sessions in state, API, and dashboard.* #### Summary - Treat Codex input-required and MCP elicitation events as blocked sessions. - Keep blocked issues claimed until Linear state/routing changes. - Add blocked counts and per-issue blocked details to presenter payloads. - Render blocked sessions in the dashboard. - Add regression coverage for app-server and orchestrator blocked flows. #### Alternatives - Retrying was rejected because human-input blockers are not transient failures. - Moving Linear to Done was rejected because Done is post-merge terminal state. #### Test Plan - [ ] `make -C elixir all` - [x] `mise exec -- mix format` - [x] `mise exec -- mix test test/symphony_elixir/app_server_test.exs test/symphony_elixir/extensions_test.exs test/symphony_elixir/orchestrator_status_test.exs` --------- Co-authored-by: Codex <codex@openai.com>
#### Context Smoke-test ticket SD-4 asks for an isolated doc proving Symphony picked up and handled the issue. #### TL;DR *Adds the SD-4 Symphony smoke-test note.* #### Summary - Adds `docs/symphony-smoke-test-one.md`. - Keeps the change docs-only and limited to the smoke-test note. - Mentions Jira issue key `SD-4`. #### Alternatives - Leave no repo artifact; rejected because the ticket asks for a committed smoke-test doc. #### Test Plan - [x] `make -C elixir all` via GitHub Actions `make-all` - [x] `test -f docs/symphony-smoke-test-one.md && rg "SD-4|Symphony" docs/symphony-smoke-test-one.md && git diff --check` - [x] `mix pr_body.check --file <process-substitution>`
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.symphony/issue.jsonmanifests for local and SSH worker workspaces.openai/symphonyruntime fixes, including input-blocked session surfacing, Codex model config, SSH worker support, and orchestration/policy handling updates.Validation
mise exec -- mix test-> 252 tests, 0 failures, 2 skippedmise exec -- mix test test/symphony_elixir/core_test.exs test/symphony_elixir/workspace_and_config_test.exs-> 98 tests, 0 failuresgit diff --checkNewRewards Context
This supports the NewRewards controller
--only-issuelaunch path by making the Elixir runtime respecttracker.issue_identifiersnatively. Without this runtime change, the NewRewards wrapper can generate a single-issue workflow, but the runtime can still see more board work than intended.