Skip to content

[pull] main from openai:main#4

Open
pull[bot] wants to merge 41 commits into
pick7:mainfrom
openai:main
Open

[pull] main from openai:main#4
pull[bot] wants to merge 41 commits into
pick7:mainfrom
openai:main

Conversation

@pull

@pull pull Bot commented Mar 10, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

#### Context

The config layer had grown around NimbleOptions and a large getter surface. This branch moves parsing to an Ecto schema and simplifies callers to read typed nested settings directly.

#### TL;DR

*Replace the bespoke config access layer with an Ecto-backed schema and typed settings access.*

#### Summary

- Replace the old config extraction path with an Ecto embedded schema and defaults
- Collapse `Config` down to `settings/0`, `settings!/0`, and a small runtime helper surface
- Update callers and tests to read nested config structs directly instead of one-off getter wrappers
- Keep the repo green under format, lint, coverage, and dialyzer by adding the missing schema specs and tests

#### Alternatives

- Keep NimbleOptions and shrink the wrapper layer incrementally, but that would preserve the custom parser shape
- Keep the strict refactor without extra schema tests, but that left `make -C elixir all` red on coverage and dialyzer

#### Test Plan

- [x] `make -C elixir all`
- [x] `cd elixir && mix test test/symphony_elixir/workspace_and_config_test.exs --warnings-as-errors`
@pull pull Bot locked and limited conversation to collaborators Mar 10, 2026
@pull pull Bot added ⤵️ pull merge-conflict Resolve conflicts manually labels Mar 10, 2026
frantic-openai and others added 26 commits March 10, 2026 11:20
#### Context

The schema and tests now treat tracker state lists as YAML lists and only lowercase
state-limit keys, but `SPEC.md` still documented older and stricter behavior.

#### TL;DR

*Align `SPEC.md` with the current workflow config and state normalization behavior.*

#### Summary

- document tracker `active_states` and `terminal_states` as YAML string lists
- update the cheat sheet defaults to use list examples instead of CSV strings
- remove trim-based state normalization language from the spec

#### Alternatives

- restore support for comma-separated tracker state strings in the schema
- keep trim-based normalization in the spec and change the implementation

#### Test Plan

- [x] `make -C elixir all`
- [x] Docs-only diff reviewed against current schema and tests
#### Context

Symphony Elixir had a few correctness gaps around retry scheduling, refresh coalescing,
workspace path handling, Codex turn policy forwarding, and running-issue refreshes.

#### TL;DR

*Stabilize Symphony Elixir orchestration, workspace safety, and Codex policy passthrough.*

#### Summary

- Ignore stale retry timers and coalesce superseded poll ticks in the orchestrator.
- Canonicalize workspace paths and clarify last-known-good workflow reload behavior.
- Pass explicit Codex turn sandbox policies through unchanged and add integration coverage.
- Paginate Linear by-id issue-state refreshes so running issue reconciliation sees all IDs.

#### Alternatives

- Keep local validation and rewriting of explicit turn sandbox policies, but that drifted from
  Codex semantics and broke documented policy types.
- Keep single-request by-id issue refreshes, but that truncates after 50 issues and can stop
  healthy workers during reconciliation.

#### Test Plan

- [x] `make -C elixir all`
- [x] Targeted regression coverage for AppServer turn policy passthrough and Linear by-id pagination
#### Context

Add a real end-to-end path for Symphony Elixir that exercises Linear and
Codex together, and make it easy to invoke explicitly from one Make target.

#### TL;DR

*Add a real live Linear/Codex E2E test plus a `make e2e` entrypoint.*

#### Summary

- Add an opt-in live Elixir test that creates a real Linear project and issue.
- Run a real Codex turn and verify both workspace output and Linear side effects.
- Require Codex to post a Linear comment and close the issue before completion.
- Add `make e2e` with clear failures for missing `LINEAR_API_KEY` or `codex`.
- Document the new E2E entrypoint and behavior in the Elixir README.

#### Alternatives

- Keep using ad hoc `mix test` commands and environment toggles.
- Keep shimming Linear or post-processing issue state locally.
- Those keep the happy path less reproducible and do not prove Codex can
  mutate Linear itself during the turn.

#### Test Plan

- [x] `make -C elixir all`
- [x] `env -u LINEAR_API_KEY make -C elixir e2e`
- [x] `LINEAR_API_KEY=$(tr -d '\r\n' < ~/.linear_api_key) SYMPHONY_RUN_LIVE_E2E=1 mix test test/symphony_elixir/live_e2e_test.exs`
#### 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>`
#### Context

Adds the requested SD-6 smoke-test note so the Symphony board review workflow can validate a minimal docs-only change.

#### TL;DR

*Add a short board review smoke-test markdown note.*

#### Summary

- Added docs/symphony-smoke-board-review.md with a short heading and note.
- Kept the change isolated to one new docs file.

#### Alternatives

- No code or app behavior changes were made because the ticket scope is docs-only.

#### Test Plan

- [x] `make -C elixir all` via GitHub Actions `make-all` rerun
- [x] `mix pr_body.check --file <(gh pr view 79 --repo openai/symphony --json body -q .body)`
- [x] `git status --short` shows only `?? docs/symphony-smoke-board-review.md`
#### Context

Need Symphony to keep a first-class Codex thread loop for Linear issues,
including human follow-up comments and terminal cleanup.

#### TL;DR

*Adds Codex thread links, thread archiving, and Linear comment resume
support.*

#### Summary

- Add Codex app-server proxy support with thread owner metadata and
archive calls.
- Track thread IDs, comment cursors, and blocked human-input state in
the orchestrator.
- Fetch Linear comments and resume blocked or continued runs with human
follow-up prompts.
- Document the workflow/spec updates and cover stale empty workspace
repair.

#### Alternatives

- Keep comments entirely inside Codex workpads, but Linear replies stay
invisible to Symphony.
- Leave thread handling as plain dashboard text, but `codex://` links
make app handoff direct.

#### Test Plan

- [x] `make -C elixir all`
- [x] `git diff --check`
- [x] `rg -n "lin_api_|ZtX3ZP" .`
#### Context

Revert PR #84 after it was merged so Symphony main returns to the
previous workflow and implementation.

#### TL;DR

*Reverts the Codex thread link and Linear comment resume changes from PR
#84.*

#### Summary

- Revert the squash merge commit from PR #84.
- Remove the added Linear comment resume and Codex thread
ownership/archive behavior.
- Restore the previous docs, workflow config, app-server client, and
tests.

#### Alternatives

- Patch forward selectively, but the request was to revert the merged
change.

#### Test Plan

- [x] `git diff --check HEAD~1..HEAD`
- [x] `rg -n "lin_api_|ZtX3ZP" .`
- [x] `mix pr_body.check --file /tmp/symphony_revert_pr_body.md`
#### Context

Brix oaipkg installs in Symphony-launched FSS runs need DNS/network
access, but the workflow turn sandbox did not enable it.

#### TL;DR

*Allow Symphony workflow turns to use network/DNS for package installs.*

#### Summary

- Add `networkAccess: true` to the workflow turn sandbox policy.
- Document the setting for package-manager and external-host workflows.

#### Alternatives

- Leave safer code defaults unchanged and scope the allowance to this
workflow.

#### Test Plan

- [x] `/usr/bin/env mix run -e IO.inspect(...)` before and after config
change
- [x] `/usr/bin/env mix specs.check`
- [x] `/usr/bin/env mix test
test/symphony_elixir/workspace_and_config_test.exs`
- [x] `/usr/bin/env make all MIX="/usr/bin/env mix"`

Co-authored-by: Codex <codex@openai.com>
#### Context

Projects need an explicit opt-in signal so active issues are not
dispatched only because they belong to a polled project.

#### TL;DR

*Allow workflows to require Linear labels before an issue is dispatched
or continued.*

#### Summary

- Add normalized `tracker.required_labels` configuration with all-label
matching
- Apply the label gate to dispatch, retries, reconciliation, and
continuation turns
- Document the configuration and cover opt-in and label-removal behavior

#### Alternatives

- Prompt-only filtering happens after an issue is claimed and cannot
stop active work when a label is removed.
- Linear query filtering would hide removed labels from reconciliation,
preventing clean agent shutdown.

#### Test Plan

- [x] `make -C elixir all`
- [x] `mix test test/symphony_elixir/workspace_and_config_test.exs`
#### Context

Dashboard rows show tracker issue identifiers as plain text, so
operators must navigate to active issues manually.

#### TL;DR

*Link dashboard issue identifiers to their tracker-provided URLs.*

#### Summary

- Preserve tracker issue URLs across running, blocked, and retry
snapshots
- Expose issue URLs in the observability state API
- Link identifiers only when the tracker URL uses `http` or `https`
- Add a content digest to the dashboard stylesheet URL so long-lived
browser caching does not hide updated link styling

#### Alternatives

- Construct tracker URLs from identifiers. This would couple the
dashboard to one tracker and URL format.

#### Test Plan

- [x] `make -C elixir all`
- [x] Browser preview with synthetic `tracker.example` issue URLs
- [x] Browser computed style confirms a `1px` underline
- [x] Invalid URL scheme remains plain text
#### Context

Symphony Observability currently uses the browser's generic fallback
icon, which makes its control-plane tabs hard to identify.

#### TL;DR

*Add a custom, cache-busted favicon to the Symphony Observability
dashboard.*

#### Summary

- Add a generated 128x128 transparent PNG mark for Symphony.
- Serve the favicon through the existing embedded static-asset
controller.
- Add a content digest to the favicon URL to avoid stale browser caches.
- Verify the favicon markup, route response, content type, and binary
body.

#### Alternatives

- Keep the generic browser icon. This leaves Symphony tabs difficult to
distinguish.
- Embed a data URI in the layout. A normal asset route is easier to
cache, test, and replace.

#### Test Plan

- [x] `make -C elixir all`
- [x] `mix test test/symphony_elixir/extensions_test.exs`
- [x] Browser preview at `http://127.0.0.1:4022/`
#### Context

Live E2E could fail on cold Codex startup, remote `~/` workspace expansion, or Docker nested sandboxing.

#### TL;DR

*Make local and SSH live smoke tests reliable with test-only Codex isolation.*

#### Summary

- Fix remote `~/` workspace expansion so SSH workers use the intended home path.
- Run local live tests with an auth-only temporary `CODEX_HOME` and a 60s startup timeout.
- Use full access only inside disposable Docker SSH workers and finalize projects on failures.

#### Alternatives

- Did not change production Codex timeout defaults; the 60s budget is test-only.
- Did not make Docker privileged; the container remains the isolation boundary.

#### Test Plan

- [x] `make -C elixir all`
- [x] `cd elixir && CODEX_HOME=<temporary auth-only home> make e2e`
#### Context

A YAML-valid, typed-invalid reload replaced cached config and prompt, then made `Config.settings!` raise.

#### TL;DR

*Keep the last good typed workflow when a reload cannot be parsed.*

#### Summary

- Cache parsed settings with each workflow store state.
- Reject typed-invalid reloads before config and prompt become current.
- Keep preflight errors visible while runtime getters use last known good values.

#### Alternatives

- Did not fold semantic tracker preflight into store admission; that is a separate finding.
- Did not add orchestrator rescue logic; invalid typed state no longer reaches `settings!`.

#### Test Plan

- [x] `mix test test/symphony_elixir/extensions_test.exs:104`
- [x] `mix test --cover --seed 659305`
- [x] `make -C elixir all`
#### Context

Orchestrator restarts reset in-memory claims, but sibling TaskSupervisor workers could survive and be redispatched concurrently.

#### TL;DR

*Supervise agent workers and the orchestrator as one restart unit.*

#### Summary

- Add an AgentRuntimeSupervisor with TaskSupervisor and Orchestrator under `:one_for_all`.
- Route agent task starts and stops through the runtime-owned task supervisor.
- Make public and test restart paths operate on the runtime subtree as a unit.
- Add a real memory-tracker dispatch regression for hard-kill restart recovery.

#### Alternatives

- Startup cleanup fixed the narrow case but encoded ownership in callbacks instead of supervision.
- Durable leases are broader than Symphony's in-memory restart contract.

#### Test Plan

- [x] `make -C elixir all`
- [x] `mise exec -- mix test --repeat-until-failure 10 test/symphony_elixir/core_test.exs:228`
- [x] `mise exec -- mix test --cover --seed 155984 --max-cases 8`
- [x] `mise exec -- mix test test/symphony_elixir/core_test.exs test/symphony_elixir/orchestrator_status_test.exs`
#### Context

Symphony could start scheduling with semantically invalid workflow
settings, while blank required values also passed validation.

#### TL;DR

*Validate workflow settings before they become effective or start scheduling.*

#### Summary

- Make WorkflowStore cache only semantically valid workflow settings.
- Return startup errors before Orchestrator schedules cleanup or polling.
- Reject blank Linear settings and whitespace-only Codex commands.
- Keep last-good settings across invalid reloads and runtime restarts.
- Add cold-start, reload, restart, and test-bootstrap coverage.

#### Alternatives

- Validating only in Orchestrator init was rejected because bad reloads
  could break ordinary OTP restarts.
- Keeping validation only per tick was rejected because startup still
  appeared healthy with invalid settings.

#### Test Plan

- [x] `cd elixir && mise exec -- make all`
- [x] `cd elixir && mise exec -- mix test --seed 567691 --max-failures 1`
#### Context

Agent guidance did not clearly state that simplicity is a project
constraint or that adversarial review should challenge complexity.

#### TL;DR

*Make agent guidance prefer simple designs and adversarial verification.*

#### Summary

- Add one-owner/invariant and lifecycle review guidance.
- Prefer narrow real OTP tests and stable observable proof.
- Ask agents to challenge complexity and push back early.

#### Alternatives

- A detailed Symphony-specific checklist was rejected because it would
  overfit one incident instead of providing durable guidance.

#### Test Plan

- [x] `cd elixir && mise exec -- make all`
#### Context

Using Symphony currently requires installing and configuring an Elixir runtime. Downloadable executables make first use much simpler.

#### TL;DR

*Add self-contained Burrito executables for macOS and Linux.*

#### Summary

- Add Burrito targets for macOS and Linux on arm64 and x86_64.
- Route packaged releases through the existing Symphony CLI.
- Build release assets, run native smoke tests, and publish checksums from GitHub Actions.
- Document release binaries and source-run examples.

#### Alternatives

- Escript still requires Erlang/Elixir on the user's machine.
- Native macOS Burrito builds hit a Zig linker failure, so CI cross-builds on Linux.
- The workflow keeps the existing acknowledgement gate but does not pass the flag itself.

#### Test Plan

- [x] `make -C elixir all`
- [x] `mise exec -- mix test --cover`
- [x] `mise exec -- mix lint`
- [x] `mise exec -- mix dialyzer --format short`
- [x] `actionlint .github/workflows/burrito-release.yml`
- [x] Run the packaged macOS arm64 CLI on this Mac.
frantic-openai and others added 14 commits July 17, 2026 17:22
#### Context

The v0.0.1 tag run built and smoke-tested every artifact, but release creation failed because the job had no Git checkout.

#### TL;DR

*Check out Git before verifying release tags.*

#### Summary

- Add checkout to the Burrito release job before running `gh release create --verify-tag`.

#### Alternatives

- Removing `--verify-tag` would hide tag mistakes instead of fixing the missing Git context.

#### Test Plan

- [x] `yq eval .github/workflows/burrito-release.yml`
- [x] `actionlint .github/workflows/burrito-release.yml`
- [x] `git diff --check`
#### Context

Symphony's scheduler was coupled to Linear types, reads, and mutations,
making a second ticketing system require core orchestration changes.

#### TL;DR

*Introduce a generic tracker boundary while keeping Linear as the first
adapter.*

#### Summary

- Normalize scheduler inputs through `Tracker.Issue` and two read
operations.
- Keep Linear GraphQL mutations in a session-bound provider-native tool.
- Move provider config under `tracker.provider` with legacy Linear
aliases.
- Make SPEC generic and document Linear's concrete adapter profile.
- Harden secret isolation, bound config snapshots, and provider
validation.
- Use collision-resistant workspace keys as a clean cut, without
migration code.

#### Alternatives

- A lowest-common-denominator write API was rejected because it would
leak provider semantics or discard useful capabilities.
- A plugin system and full multi-provider rollout were deferred until a
second adapter proves the smallest useful boundary.

#### Test Plan

- [x] `make -C elixir all`
- [x] `mise exec -- mix test test/symphony_elixir/dynamic_tool_test.exs
test/symphony_elixir/extensions_test.exs
test/symphony_elixir/workspace_and_config_test.exs
test/symphony_elixir/app_server_test.exs`
- [x] No-context SPEC probes: TypeScript 6/6, Python 6/6, and Go passed.
- [x] Live Linear smoke: local daemon processed FST-5 to Done, wrote one
comment, and cleaned its workspace.

---------

Co-authored-by: Codex <codex@openai.com>
#### Context

Symphony's generic tracker seam needs a real non-Linear stress test without adding generic mutation APIs.

#### TL;DR

*Add GitHub Issues reads and a host-authenticated raw GitHub tool.*

#### Summary

- Add project-scoped GitHub issue reads, normalization, and dispatchability.
- Add raw `github_api` for provider-native mutations with host-side auth.
- Add contract tests, live E2E proof, and GitHub adapter documentation.

#### Alternatives

- Generic CRUD was rejected because it would flatten GitHub-specific capabilities.
- GraphQL was rejected for v1 because REST covers the required reads and tool flow simply.

#### Test Plan

- [x] `make -C elixir all`
- [x] `SYMPHONY_RUN_GITHUB_LIVE_E2E=1 mix test test/symphony_elixir/github_live_e2e_test.exs`
- [x] `SYMPHONY_RUN_LIVE_E2E=1 SYMPHONY_LIVE_LINEAR_TEAM_KEY=FST mix test test/symphony_elixir/live_e2e_test.exs:123`
#### Context

Symphony's generic tracker seam needs a Jira Cloud implementation without adding generic mutation APIs.

#### TL;DR

*Add Jira Cloud reads and a host-authenticated raw Jira REST tool.*

#### Summary

- Add project-scoped Jira reads, ADF normalization, and status dispatchability.
- Add raw `jira_rest` for provider-native mutations with host-side auth.
- Add contract tests, live E2E proof, and Jira adapter documentation.

#### Alternatives

- Generic CRUD was rejected because it would flatten Jira transitions and ADF details.
- JQL-only tooling was rejected because a raw REST tool preserves broader Jira capabilities.

#### Test Plan

- [x] `make -C elixir all`
- [x] `SYMPHONY_RUN_JIRA_LIVE_E2E=1 mix test test/symphony_elixir/jira_live_e2e_test.exs`
- [x] `SYMPHONY_RUN_LIVE_E2E=1 SYMPHONY_LIVE_LINEAR_TEAM_KEY=FST mix test test/symphony_elixir/live_e2e_test.exs:123`
#### Context

Symphony's generic tracker seam needs an Asana implementation without adding generic mutation APIs.

#### TL;DR

*Add Asana task reads and a host-authenticated raw Asana API tool.*

#### Summary

- Add project-scoped Asana task reads, section-state mapping, and dispatchability.
- Add raw `asana_api` for provider-native mutations with host-side auth.
- Add contract tests, live E2E proof, and Asana adapter documentation.

#### Alternatives

- Generic CRUD was rejected because it would flatten sections, stories, and task types.
- Completed-only state mapping was rejected because section names are the workflow source.

#### Test Plan

- [x] `make -C elixir all`
- [x] `SYMPHONY_RUN_ASANA_LIVE_E2E=1 mix test test/symphony_elixir/asana_live_e2e_test.exs`
- [x] `SYMPHONY_RUN_LIVE_E2E=1 SYMPHONY_LIVE_LINEAR_TEAM_KEY=FST mix test test/symphony_elixir/live_e2e_test.exs:123`
#### Context

Symphony's generic tracker seam needs a GitLab Issues implementation without adding generic mutation APIs.

#### TL;DR

*Add GitLab Issues reads and a host-authenticated raw GitLab API tool.*

#### Summary

- Add project-scoped GitLab issue reads, IID normalization, and dispatchability.
- Add raw `gitlab_api` for provider-native mutations with host-side auth.
- Add contract tests, live E2E proof, and GitLab adapter documentation.

#### Alternatives

- Generic CRUD was rejected because it would flatten GitLab-specific issue capabilities.
- Header pagination machinery was rejected because simple page/per-page reads are sufficient.

#### Test Plan

- [x] `make -C elixir all`
- [x] `SYMPHONY_RUN_GITLAB_LIVE_E2E=1 mix test test/symphony_elixir/gitlab_live_e2e_test.exs`
- [x] `SYMPHONY_RUN_LIVE_E2E=1 SYMPHONY_LIVE_LINEAR_TEAM_KEY=FST mix test test/symphony_elixir/live_e2e_test.exs:123`
#### Context

Jira inward Blocks links were normalized as dispatchable, so Symphony
could
start Todo work before its blocker reached a terminal state.

#### TL;DR

*Jira Todo issues now wait for inward Blocks dependencies.*

#### Summary

- Request issuelinks in Jira candidate polling and ID refresh reads.
- Normalize inward Blocks links into blocked_by.
- Ignore outward Blocks links and unrelated Jira link types.
- Gate only Todo/To Do work until blockers reach configured terminal
states.
- Test direction, custom terminals, partial payloads, and ID refresh
gating.

#### Alternatives

- Generic scheduler blocking logic: rejected because blocker semantics
remain
  provider-specific and dispatchable already owns eligibility.
- Metadata-only links: rejected because they would preserve blockers
without
  preventing premature dispatch.

#### Test Plan

- [x] HEX_HOME=/private/tmp/symphony-hex-jira-blockers make -C elixir
all
- [x] HEX_HOME=/private/tmp/symphony-hex-jira-blockers mix test
test/symphony_elixir/jira_adapter_test.exs
- [x] Live Jira Orchestrator probe created linked issues, proved no
workspace
while blocked, transitioned the blocker, proved workspace creation, and
  deleted the disposable issues.
- [x] SYMPHONY_RUN_JIRA_LIVE_E2E=1 mix test
test/symphony_elixir/jira_live_e2e_test.exs

Co-authored-by: Codex <codex@openai.com>
#### Context

Terminal reconciliation could race a live worker and could recompute cleanup from reloaded config, deleting the wrong workspace. Recorded-path cleanup also still needs the local symlink-escape guard before hooks run.

#### TL;DR

*Stop workers first and clean the validated workspace recorded for that run.*

#### Summary

- Stop terminal workers before invoking cleanup hooks or removal.
- Prefer the recorded workspace path for running, retrying, and blocked issues.
- Validate recorded local paths against their recorded parent before hooks or removal.
- Add deterministic regressions for cleanup order, config-reload target selection, and recorded symlink escapes.

#### Alternatives

- A new cleanup coordinator was unnecessary; existing metadata and operations are enough.

#### Test Plan

- [x] `HEX_HOME=/private/tmp/symphony-hex mise exec -- make -C elixir all`
- [x] `mise exec -- mix test test/symphony_elixir/core_test.exs:541`
- [x] `mise exec -- mix test test/symphony_elixir/core_test.exs:618`
- [x] `mise exec -- mix test test/symphony_elixir/workspace_and_config_test.exs:176`
#### Context

A retry performs a fresh by-id lookup, then dispatch revalidates before starting work. If that second read missed or went stale, the popped retry could stay claimed forever; bypassing it instead could dispatch stale work.

#### TL;DR

*Keep dispatch-time freshness and let retries release or reschedule from its outcome.*

#### Summary

- Preserve dispatch-time issue revalidation for retried work.
- Release the retry claim when the second refresh is missing or stale.
- Reschedule rather than drop the claim when dispatch-time refresh errors.
- Add a regression proving a missing second read cannot dispatch stale work or strand a claim.

#### Alternatives

- Reusing the first retry lookup was smaller but reopened a stale-dispatch window.

#### Test Plan

- [x] `HEX_HOME=/private/tmp/symphony-hex mise exec -- make -C elixir all`
- [x] `mise exec -- mix test test/symphony_elixir/core_test.exs:875`
- [x] `mise exec -- mix test test/symphony_elixir/core_test.exs`
#### Context

Failed new-workspace bootstrap was skipped on retry, and relative local roots followed launcher cwd instead of the selected workflow file.

#### TL;DR

*Retry failed bootstrap cleanly and anchor local roots to WORKFLOW.md.*

#### Summary

- Remove newly created partial workspaces when `after_create` fails.
- Resolve local relative roots from the selected workflow directory.
- Keep remote roots raw and use the local root in app-server validation.
- Add regressions for bootstrap retry and explicit workflow paths.

#### Alternatives

- Marker files and global cwd changes were unnecessary; one local root helper keeps the fix small.

#### Test Plan

- [x] `make -C elixir all`
- [x] `mise exec -- mix test test/symphony_elixir/workspace_and_config_test.exs:60`
- [x] `mise exec -- mix test test/symphony_elixir/workspace_and_config_test.exs:244`
#### Context

Docs called turn timeout a total cap, while runtime uses silence. Generic tool-input prompts also received fabricated answers, and approval-looking option labels alone are not enough to identify MCP approval prompts.

#### TL;DR

*Document idle timeouts and block generic tool input.*

#### Summary

- Document `turn_timeout_ms` as a reset-on-update silence interval.
- Add coverage for active stream updates and silent turns.
- Keep auto-approval only for recognized MCP approval question IDs.
- Block freeform and generic option-based tool input, including Allow/Deny choices.

#### Alternatives

- A total-runtime deadline was rejected; healthy long turns should stay alive while updates continue.
- Matching approval-looking labels alone was rejected because generic prompts can use the same words.

#### Test Plan

- [x] `HEX_HOME=/private/tmp/symphony-hex mise exec -- make -C elixir all`
- [x] `mise exec -- mix test test/symphony_elixir/app_server_test.exs:619 test/symphony_elixir/app_server_test.exs:788`
- [x] `mise exec -- mix test test/symphony_elixir/app_server_test.exs`
#### Context

Jira blockers only gated literal Todo states, so Open or Backlog issues could dispatch through active blockers.

#### TL;DR

*Gate Jira new-category issues on active blockers.*

#### Summary

- Read Jira `statusCategory.key` while normalizing issues.
- Gate `new` category issues on inward `Blocks` links.
- Preserve the Todo fallback and keep in-progress work dispatchable.
- Document the adapter rule and add an Open-state regression.

#### Alternatives

- Shared scheduler policy was unnecessary; Jira already exposes the native category signal.

#### Test Plan

- [x] `make -C elixir all`
- [x] `mise exec -- mix test test/symphony_elixir/jira_adapter_test.exs:166`
#### Context

The v0.0.2 release tag was rejected because the committed Mix project version remained 0.0.1.

#### TL;DR

*Bump the committed Elixir project version to 0.0.2.*

#### Summary

- Set `SymphonyElixir.MixProject` version to `0.0.2`.
- Align the source version with the existing `v0.0.2` release tag.

#### Alternatives

- Retagging as `v0.0.1` would not create the intended next release.
- Removing the workflow check would allow mismatched release artifacts.

#### Test Plan

- [x] `make -C elixir all`
- [x] Confirm the staged diff changes only `elixir/mix.exs`.
#### Context

Symphony releases need a short, reusable procedure that keeps tags aligned with the committed project version.

#### TL;DR

*Add a concise repo-local skill for cutting Symphony releases.*

#### Summary

- Add `.codex/skills/release/SKILL.md`.
- Cover version bumps, PR landing, annotated tags, and release verification.

#### Alternatives

- Relying on memory risks tagging an unmerged or version-mismatched commit.
- A longer runbook would duplicate the existing commit, push, and land skills.

#### Test Plan

- [x] `make -C elixir all`
- [x] `python3 /Users/frantic/.codex/skills/.system/skill-creator/scripts/quick_validate.py /Users/frantic/.codex/worktrees/symphony-add-release-skill/.codex/skills/release`
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

⤵️ pull merge-conflict Resolve conflicts manually

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants