Skip to content

Harden Symphony dispatch and merge upstream runtime fixes - #1

Merged
CryptoJym merged 16 commits into
mainfrom
codex/github-label-dispatch-gate
May 29, 2026
Merged

Harden Symphony dispatch and merge upstream runtime fixes#1
CryptoJym merged 16 commits into
mainfrom
codex/github-label-dispatch-gate

Conversation

@CryptoJym

Copy link
Copy Markdown
Owner

Summary

  • Adds a Linear issue identifier allowlist so a generated workflow can dispatch exactly one selected issue instead of all eligible board work.
  • Preserves source GitHub issue context in workspace .symphony/issue.json manifests for local and SSH worker workspaces.
  • Merges current upstream openai/symphony runtime 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 skipped
  • mise exec -- mix test test/symphony_elixir/core_test.exs test/symphony_elixir/workspace_and_config_test.exs -> 98 tests, 0 failures
  • git diff --check

NewRewards Context

This supports the NewRewards controller --only-issue launch path by making the Elixir runtime respect tracker.issue_identifiers natively. Without this runtime change, the NewRewards wrapper can generate a single-issue workflow, but the runtime can still see more board work than intended.

frantic-openai and others added 16 commits March 11, 2026 14:47
#### 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>`
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@CryptoJym
CryptoJym merged commit c93c564 into main May 29, 2026
2 checks passed
@CryptoJym
CryptoJym deleted the codex/github-label-dispatch-gate branch May 29, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants