Metadata-Version: 2.5
Name: exocortex-symphony
Version: 0.1.0
Summary: Unofficial build of OpenAI Symphony with a Plane tracker adapter, as self-contained executables for macOS 14 or later (run on Apple silicon) and Linux (built, never run).
Project-URL: Upstream Symphony, https://github.com/openai/symphony
License-Expression: AGPL-3.0-only AND Apache-2.0
License-File: LICENSE
License-File: src/exocortex_symphony/LICENSE.symphony
License-File: src/exocortex_symphony/NOTICE.symphony
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# exocortex-symphony

An unofficial build of [OpenAI Symphony](https://github.com/openai/symphony), packaged for pip. It
is not affiliated with or endorsed by OpenAI.

The build is upstream Symphony at the commit recorded in `BUILD.json` (`upstream_commit`) with
`plane-adapter.patch` applied. The patch adds a Plane tracker adapter and two agent tools, so that
Symphony can use a self-hosted Plane fork as its tracker. Both files ship inside the package, next
to the executables.

The package exists so that a machine with only a PyPI mirror can install Symphony. It carries one
self-contained executable per platform, built with [Burrito](https://github.com/burrito-elixir/burrito).
Each executable includes Erlang/OTP, so the machine does not need Erlang, Elixir or mise.

## Platforms

| platform             | executable                  | status                                               |
| -------------------- | --------------------------- | ---------------------------------------------------- |
| macOS, Apple silicon | `bin/symphony_macos_arm64`  | macOS 14 or later; built and run here, natively      |
| macOS, Intel         | `bin/symphony_macos_x86_64` | macOS 14 or later; built and run here, under Rosetta |
| Linux, x86_64        | `bin/symphony_linux_x86_64` | built, never run                                     |
| Linux, arm64         | `bin/symphony_linux_arm64`  | built, never run                                     |

All four are in one wheel (`py3-none-any`). The package picks the one that matches
`sys.platform` and `platform.machine()`. On any other platform the commands exit with an error that
names the platform.

- macOS: the executables need macOS 14 (Sonoma) or later, because the Erlang runtime they carry is
  built for macOS 14. They were run on this build machine (Apple silicon): the arm64 executable
  natively, the x86_64 executable under Rosetta. Neither has been run on an Intel Mac.
- Linux: the executables were cross-built on macOS and have never been run, on Linux or anywhere
  else. Treat them as untested.

## What Symphony needs at run time

- The Codex CLI on `PATH`, logged in (`codex login status`).
- git.
- For a WORKFLOW rendered by exocortex-project, the Plane API key in `PLANE_API_KEY`.

## Hand-off to Plane: the attached diff

The Plane adapter gives the agent a `plane_attach_diff` tool, which attaches the work item's change
to the work item as a `.patch` file. The patch is the workspace's current content against the fork
point of the base branch (`origin/main` by default): committed changes, uncommitted edits and new
files that are not ignored, whether or not the agent could commit. Real Codex runs in a sandbox that
forbids writes to `.git`, so its `git commit` fails; the patch still carries the change. Symphony
builds it in a temporary git index and leaves the repository's own index, HEAD, refs and files
untouched. The same content gives the same attachment; changed content gives a new one.

Symphony also attaches the diff itself at the hand-off, so the agent does not have to remember the
tool. When the agent's `plane_api` call moves its own work item to `Human Review` (a PATCH whose
body sets `state` to that state's id; another name can be set as `tracker.provider.review_state`),
Symphony first attaches the patch with the default arguments, then sends the state change as the
agent wrote it. Content the agent already attached is not uploaded again. The attach never blocks
the hand-off: the call's result carries `auto_attach` (`attached`, `already_attached`,
`no_changes` or `error`), and the Symphony log has one line
`Plane hand-off auto_attach outcome=<outcome> ... issue_identifier=<key>` per hand-off. Calling
`plane_attach_diff` earlier stays optional, for another base branch or file name.

## Known limits with real Codex

- Codex installed only through the editor extension is not on `PATH`, so Symphony cannot start it
  and exocortex-project's `doctor` reports it missing. The extension carries the executable at
  `~/.vscode/extensions/openai.chatgpt-*/bin/<platform>/codex` (for example `bin/macos-aarch64/`
  on Apple silicon). Put that directory on `PATH` in the shell that starts Symphony, or install
  the Codex CLI.
- The WORKFLOW rendered by exocortex-project 0.1.0 hardcodes the model `gpt-5.5` and the reasoning
  effort `xhigh` in `codex.command`. `symphony workflow` has no option for either; to choose another
  model or effort, edit the two `--config` values in `codex.command` by hand after rendering.
- Pull-request workflows (`--pr-host github`) need git writes: a branch, commits and a push. The
  default Codex sandbox (`workspace-write`) denies writes to `.git`, so those steps fail. The
  `--pr-host none` workflow does not need them, because the change reaches Plane as the attached
  patch.

## Commands

- `exocortex-symphony [args...]` replaces itself with the Symphony executable for this platform and
  passes every argument through unchanged. `python -m exocortex_symphony` does the same.
- `exocortex-symphony-home` prints `<data dir>/exocortex-symphony/<package version>/` and creates it
  if needed. The data dir is `$XDG_DATA_HOME`, or `~/.local/share` when that is unset. The directory
  holds `bin/symphony`, a symbolic link to the packaged executable (a copy where links cannot be
  made). Export it as `SYMPHONY_DIR` for exocortex-project 0.1.0.

If site-packages is read-only and the executable has lost its execute bit, it is copied into that
same directory (`bin/symphony_<target>`) and run from there.

## Use with exocortex-project 0.1.0

```sh
pip install exocortex-project exocortex-symphony
export SYMPHONY_DIR="$(exocortex-symphony-home)"
exocortex-project symphony workflow --project <IDENT> [--pr-host github|none]
```

`symphony workflow` writes `WORKFLOW.md` at the repository root. Start Symphony from there:

```sh
PLANE_API_KEY="$(cat ~/.config/exocortex-project/token)" exocortex-symphony \
  --i-understand-that-this-will-be-running-without-the-usual-guardrails --port 4100 WORKFLOW.md
```

This package replaces step 5 of exocortex-project 0.1.0's `SETUP.md` (git clone of the Symphony
fork, mise, `mix build`). The rest of that step still applies: render the WORKFLOW with
`exocortex-project symphony workflow`, then run `exocortex-project symphony check` and `doctor`.

Known false result: with this package, `exocortex-project symphony check` and `doctor` (0.1.0)
report that the Symphony executable has no Plane adapter. That check reads the executable as an
escript archive and does not understand the Burrito format. The adapter is present. The check that
matters is the one that runs `symphony --help` and expects `Usage: symphony`, and it passes.

## First run

On its first run, each executable unpacks its payload (Erlang runtime and the Symphony release)
into a per-user directory and reuses that directory on later runs:

- macOS: `~/Library/Application Support/.burrito/symphony_erts-<erts>_<version>/`
- Linux: `$XDG_DATA_HOME/.burrito/` or `~/.local/share/.burrito/`, same directory name. The Linux
  executables also write a musl runtime to `/tmp/libc-musl-<hash>.so`.

For this build the directory name is `symphony_erts-16.4_0.0.3-exo.3`. `SYMPHONY_INSTALL_DIR`
moves the unpack location to `$SYMPHONY_INSTALL_DIR/.burrito/`. Burrito's own maintenance commands
pass through: `exocortex-symphony maintenance directory` prints the directory, and
`exocortex-symphony maintenance uninstall` removes it after asking.

Symphony's version string carries the `-exo.N` suffix because Burrito keys the unpack directory on
the ERTS version and the app version only. A rebuilt executable with an unchanged version would run
the payload unpacked by the previous build.

## Build

The executables are not in git. `scripts/build-binaries.sh` builds them from a clean checkout of the
Symphony fork (`SYMPHONY_SRC`, default `~/projects/symphony`; not `SYMPHONY_DIR`, which is the
directory exocortex-project runs `bin/symphony` from) and writes the executables, `BUILD.json`,
`plane-adapter.patch` (`git diff <upstream commit>..<fork commit>`), `LICENSE.symphony` and
`NOTICE.symphony` into `src/exocortex_symphony/`. It needs mise with Erlang/Elixir as the fork's
`elixir/mise.toml` pins them and `zig@0.15.2`. It removes the fork's release directory
(`elixir/_build/prod/rel/symphony`) first, so no application directory from an earlier version is
packed, and runs the builds with `TMPDIR` set to a directory of its own that it removes afterwards,
because Burrito leaves about 750 MB of temporary files per full build.

```sh
SYMPHONY_SRC=~/projects/symphony scripts/build-binaries.sh
uv run pytest && uv run ruff check .
EXOCORTEX_SYMPHONY_REQUIRE_BINARIES=1 uv build
```

With `EXOCORTEX_SYMPHONY_REQUIRE_BINARIES=1`, the build fails when an executable or `BUILD.json` is
missing. Without it, the package builds without them (for development). `build_info()` returns
`BUILD.json`, which records the upstream repository and commit, the fork commit, the Symphony, ERTS
and OTP versions, and each executable's size and sha256.

## Licenses

- The Python launcher in this package: AGPL-3.0-only, like the repository it comes from. The text
  ships as `LICENSE`.
- The bundled Symphony executables and `plane-adapter.patch`: Apache-2.0. Symphony's license and
  notice ship as `LICENSE.symphony` and `NOTICE.symphony`. The executables also contain Erlang/OTP
  and Elixir (Apache-2.0) and Symphony's Hex dependencies under their own licenses.

All three files are the package's license files (`license-files`), so installers place them in the
`.dist-info/licenses/` directory; `LICENSE.symphony` and `NOTICE.symphony` also sit next to the
executables.
