symphony-orchestrator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +412 -0
- package/apps/frontend/dist/assets/index-ByxrBxkB.css +1 -0
- package/apps/frontend/dist/assets/index-CI3z9Z5d.js +17 -0
- package/apps/frontend/dist/index.html +13 -0
- package/bin/symphony.js +46 -0
- package/package.json +28 -0
- package/vendor/symphony-darwin-arm64 +0 -0
- package/vendor/symphony-darwin-x64 +0 -0
- package/vendor/symphony-linux-x64 +0 -0
- package/vendor/symphony-win32-x64.exe +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
# Personal Symphony
|
|
2
|
+
|
|
3
|
+
Personal Symphony is an installable `symphony` CLI for the Symphony service described in
|
|
4
|
+
[Symphony SPEC](https://github.com/openai/symphony/blob/main/SPEC.md). The product repository keeps
|
|
5
|
+
the OCaml backend, the ReScript React dashboard, and the npm launcher; each workspace repository gets
|
|
6
|
+
its own repository-owned runtime contract under `.symphony/`.
|
|
7
|
+
|
|
8
|
+
This implementation variant targets GitHub Issues plus GitHub Projects for issue tracking.
|
|
9
|
+
|
|
10
|
+
## Repository Layout
|
|
11
|
+
|
|
12
|
+
- `apps/backend`: OCaml service, workflow loader, GitHub tracker boundary, workspace manager, HTTP
|
|
13
|
+
state API, CLI, and tests.
|
|
14
|
+
- `apps/frontend`: ReScript React/Vite dashboard that consumes the backend state API.
|
|
15
|
+
- `.github/ISSUE_TEMPLATE`: issue template for work items Symphony can dispatch.
|
|
16
|
+
- `.github/project-tracking.md`: required GitHub Project setup and workflow notes.
|
|
17
|
+
- `WORKFLOW.example.md`: legacy/developer fixture for the earlier root workflow format.
|
|
18
|
+
- `bin/symphony.js`: npm `bin` launcher that runs a packaged platform binary or the local dune
|
|
19
|
+
executable in product-repository development.
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- `pnpm` 10.x
|
|
24
|
+
- OCaml toolchain with `opam`, `dune`, `cmdliner`, `yojson`, and `alcotest` for product-repository
|
|
25
|
+
development only
|
|
26
|
+
- GitHub CLI: `gh`
|
|
27
|
+
- A GitHub personal access token available as `GITHUB_TOKEN` or `GH_TOKEN`
|
|
28
|
+
- `codex` CLI available on `PATH` when running real agent sessions
|
|
29
|
+
|
|
30
|
+
The local scripts run OCaml commands through `opam exec`, so make sure the active opam switch has
|
|
31
|
+
the required packages installed.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
The npm package exposes a global `symphony` command:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npm install -g symphony-orchestrator
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Published packages should include platform-specific binaries for Linux, macOS, and Windows:
|
|
42
|
+
|
|
43
|
+
- `vendor/symphony-linux-x64`
|
|
44
|
+
- `vendor/symphony-darwin-x64`
|
|
45
|
+
- `vendor/symphony-darwin-arm64`
|
|
46
|
+
- `vendor/symphony-win32-x64.exe`
|
|
47
|
+
|
|
48
|
+
When running from this product repository, the Node launcher falls back to:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
opam exec -- dune exec symphony --
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Before publishing, run:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
pnpm npm:validate:release
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The GitHub Actions `Export npm package` workflow builds those binaries on Linux, macOS, and Windows,
|
|
61
|
+
assembles the npm tarball, uploads the tarball and binaries as a GitHub workflow artifact, and uploads
|
|
62
|
+
the same files to a GitHub Release when run from a `v*` tag. Manual runs can publish to npm when
|
|
63
|
+
`publish_npm` is enabled and the repository has an `NPM_TOKEN` secret.
|
|
64
|
+
|
|
65
|
+
## Set Up In A Workspace Repository
|
|
66
|
+
|
|
67
|
+
Run the command from the root of a Git repository:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
symphony init
|
|
71
|
+
symphony
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`symphony init` and the first `symphony` run are idempotent. They create missing runtime files under
|
|
75
|
+
`.symphony/` without overwriting user-edited files:
|
|
76
|
+
|
|
77
|
+
- `.symphony/settings.json`
|
|
78
|
+
- `.symphony/prompt.md`
|
|
79
|
+
- `.symphony/.env.example`
|
|
80
|
+
- `.symphony/.gitignore`
|
|
81
|
+
- `.symphony/.env`
|
|
82
|
+
- `.symphony/state/`
|
|
83
|
+
- `.symphony/workspaces/`
|
|
84
|
+
|
|
85
|
+
`.symphony/.gitignore` ignores local secrets and runtime state:
|
|
86
|
+
|
|
87
|
+
```gitignore
|
|
88
|
+
/.env
|
|
89
|
+
/state/
|
|
90
|
+
/workspaces/
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Edit `.symphony/settings.json` to set the GitHub owner, repository, project number, project states,
|
|
94
|
+
and runtime commands. Secrets are referenced by environment variable name, not stored in settings:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"tracker": {
|
|
99
|
+
"kind": "github",
|
|
100
|
+
"owner": "your-org",
|
|
101
|
+
"repo": "your-repo",
|
|
102
|
+
"projectNumber": 1,
|
|
103
|
+
"apiKeyEnv": "GITHUB_TOKEN"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Choose the Codex model and reasoning effort in the same file. If omitted, Symphony uses `gpt-5.5`
|
|
109
|
+
with `medium` reasoning:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"codex": {
|
|
114
|
+
"command": "codex exec",
|
|
115
|
+
"model": "gpt-5.5",
|
|
116
|
+
"reasoningEffort": "medium"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
If setup is incomplete, the Terminal Console still starts and prints Readiness Gaps with remediation
|
|
122
|
+
steps. Dispatch remains disabled until those gaps are resolved.
|
|
123
|
+
|
|
124
|
+
## Project Status Workflow
|
|
125
|
+
|
|
126
|
+
Symphony moves the configured GitHub Projects `Status` field as work progresses:
|
|
127
|
+
|
|
128
|
+
- `startStatus`: applied before launching an agent, default `In progress`.
|
|
129
|
+
- `reviewStatus`: applied after the agent exits successfully, default `In review`.
|
|
130
|
+
- `retryStatus`: applied when the agent fails or times out and Symphony schedules a retry, default
|
|
131
|
+
`To-Do`.
|
|
132
|
+
- `ensureStatuses`: when `true`, Symphony creates missing single-select status options in the
|
|
133
|
+
Project field before applying them.
|
|
134
|
+
|
|
135
|
+
Configure these in `.symphony/settings.json`:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"project": {
|
|
140
|
+
"statusField": "Status",
|
|
141
|
+
"activeStates": ["To-Do", "Todo", "In Progress"],
|
|
142
|
+
"terminalStates": ["Done", "Closed", "Cancelled"],
|
|
143
|
+
"startStatus": "In progress",
|
|
144
|
+
"reviewStatus": "In review",
|
|
145
|
+
"retryStatus": "To-Do",
|
|
146
|
+
"ensureStatuses": true
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The token needs GitHub Projects write access for status moves and status option creation. If
|
|
152
|
+
`reviewStatus` is not listed in `activeStates`, completed issues stop being picked up on later polls.
|
|
153
|
+
|
|
154
|
+
## Stage Agents
|
|
155
|
+
|
|
156
|
+
Symphony can route different Project statuses to different local agent prompts. The default runtime
|
|
157
|
+
home creates:
|
|
158
|
+
|
|
159
|
+
- `.symphony/agents/planner.md` for `Backlog`.
|
|
160
|
+
- `.symphony/agents/engineer.md` for `Todo`, `To-Do`, and `In progress`.
|
|
161
|
+
- `.symphony/agents/reviewer.md` for `In review`, then moves successful reviews to `Done`.
|
|
162
|
+
|
|
163
|
+
Configure or disable this in `.symphony/settings.json`:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"stageAgents": {
|
|
168
|
+
"enabled": true,
|
|
169
|
+
"root": ".symphony/agents",
|
|
170
|
+
"defaultAgent": "engineer",
|
|
171
|
+
"stages": [
|
|
172
|
+
{
|
|
173
|
+
"states": ["Backlog"],
|
|
174
|
+
"agent": "planner",
|
|
175
|
+
"successStatus": "To-Do",
|
|
176
|
+
"retryStatus": "Backlog",
|
|
177
|
+
"goal": {
|
|
178
|
+
"enabled": false
|
|
179
|
+
},
|
|
180
|
+
"commit": {
|
|
181
|
+
"enabled": false,
|
|
182
|
+
"type": "feature",
|
|
183
|
+
"message": "<type>: <generated_message_max_90char>",
|
|
184
|
+
"push": false
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"states": ["Todo", "To-Do", "In progress", "In Progress"],
|
|
189
|
+
"agent": "engineer",
|
|
190
|
+
"startStatus": "In progress",
|
|
191
|
+
"successStatus": "In review",
|
|
192
|
+
"retryStatus": "To-Do",
|
|
193
|
+
"goal": {
|
|
194
|
+
"enabled": false
|
|
195
|
+
},
|
|
196
|
+
"commit": {
|
|
197
|
+
"enabled": true,
|
|
198
|
+
"type": "feature",
|
|
199
|
+
"message": "<type>: <generated_message_max_90char>",
|
|
200
|
+
"push": false
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"states": ["In review", "In Review"],
|
|
205
|
+
"agent": "reviewer",
|
|
206
|
+
"successStatus": "Done",
|
|
207
|
+
"retryStatus": "In progress",
|
|
208
|
+
"goal": {
|
|
209
|
+
"enabled": false
|
|
210
|
+
},
|
|
211
|
+
"commit": {
|
|
212
|
+
"enabled": false,
|
|
213
|
+
"type": "refactor",
|
|
214
|
+
"message": "<type>: <generated_message_max_90char>",
|
|
215
|
+
"push": false
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Set `"enabled": false` to use the single base `.symphony/prompt.md` for every issue.
|
|
224
|
+
|
|
225
|
+
Set `goal.enabled` to `true` on a specific stage to enable Stage Goal Handoff for that stage only.
|
|
226
|
+
When enabled, Symphony sends `/goal` with deterministic Stage Goal Context before the normal Agent
|
|
227
|
+
Prompt. Stage Goal Context includes issue identifier, title, description, URL, current project
|
|
228
|
+
status, labels, priority when present, blocker references when present, attempt, and stage agent
|
|
229
|
+
name. It omits issue creation and update timestamps.
|
|
230
|
+
|
|
231
|
+
Stage Goal Handoff requires Codex goals in `~/.codex/config.toml`:
|
|
232
|
+
|
|
233
|
+
```toml
|
|
234
|
+
[features]
|
|
235
|
+
goals = true
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
If a stage enables goal handoff but Codex goals are not enabled, Symphony reports a Readiness Gap.
|
|
239
|
+
Goal Usage reported by Codex is stored in Runtime State for running, retrying, and attention-needed
|
|
240
|
+
task details when available; missing or unparseable Goal Usage does not fail a task.
|
|
241
|
+
|
|
242
|
+
Stage commits run after an agent exits successfully and before Symphony moves the issue to the
|
|
243
|
+
stage's `successStatus`. Set `commit.enabled` per stage to control which transitions create commits;
|
|
244
|
+
for example, keep `Backlog -> To-Do` uncommitted and commit `In progress -> In review`. The message
|
|
245
|
+
template supports `<type>`, `<generated_message_max_90char>`, `<issue_identifier>`, `<issue_title>`,
|
|
246
|
+
`<from_status>`, `<to_status>`, and `<agent>`. Set `commit.push` to `true` to push the current task
|
|
247
|
+
branch after a successful stage commit and before the status transition; omitted values default to
|
|
248
|
+
`false`.
|
|
249
|
+
|
|
250
|
+
## Batch Pull Requests
|
|
251
|
+
|
|
252
|
+
Symphony can optionally open one Batch Pull Request after Orchestration Idle, using the Loop-Start
|
|
253
|
+
Branch as the PR head. Automatic PR creation is disabled by default:
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"pullRequest": {
|
|
258
|
+
"enabled": false,
|
|
259
|
+
"baseBranch": "main",
|
|
260
|
+
"title": "Symphony batch from <head_branch>",
|
|
261
|
+
"body": "Opened automatically by Symphony after orchestration became idle."
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
When `pullRequest.enabled` is `true`, `pullRequest.baseBranch` must be set explicitly. On an idle
|
|
267
|
+
poll, Symphony first performs a non-force Batch Branch Push of the Loop-Start Branch to `origin`,
|
|
268
|
+
then checks for an existing open PR with the same head/base pair before creating one with `gh`.
|
|
269
|
+
Failed pushes or PR creation attempts are recorded in Runtime State as retryable handoff failures and
|
|
270
|
+
are retried on later idle polls. Symphony does not attempt a Batch Pull Request while any issue is in
|
|
271
|
+
the configured Merge Attention Status or has unresolved orchestration attention.
|
|
272
|
+
|
|
273
|
+
The `title` and `body` fields are deterministic templates. They support `<head_branch>` and
|
|
274
|
+
`<base_branch>`; Symphony does not generate PR prose with an agent.
|
|
275
|
+
|
|
276
|
+
## GitHub Token Permissions
|
|
277
|
+
|
|
278
|
+
Personal Symphony reads GitHub Issues and GitHub Projects. Use a **personal access token (classic)**
|
|
279
|
+
when the GitHub Project is owned by a user account, such as `@your-user's Kanban`. GitHub
|
|
280
|
+
fine-grained personal access tokens currently cannot access Projects owned by a user account.
|
|
281
|
+
|
|
282
|
+
Recommended classic PAT scopes:
|
|
283
|
+
|
|
284
|
+
- `repo`: required for private Workspace Repositories and repository Issues.
|
|
285
|
+
- `read:project`: enough for readiness checks and read-only project polling.
|
|
286
|
+
- `project`: required instead of `read:project` when Symphony will move project cards, update
|
|
287
|
+
project fields, or otherwise write GitHub Projects data.
|
|
288
|
+
|
|
289
|
+
Classic PATs do **not** ask you to select individual repositories or projects. If GitHub shows a
|
|
290
|
+
repository picker, project picker, "Resource owner", or "Repository access" section, you are creating
|
|
291
|
+
a fine-grained token. Go back to **Personal access tokens > Tokens (classic) > Generate new token
|
|
292
|
+
(classic)** for user-owned Projects.
|
|
293
|
+
|
|
294
|
+
Fine-grained PATs are only suitable when the GitHub Project is owned by an organization and GitHub
|
|
295
|
+
allows fine-grained tokens for that owner. Configure the token with:
|
|
296
|
+
|
|
297
|
+
- Resource owner: the organization that owns the Workspace Repository and GitHub Project.
|
|
298
|
+
- Repository access: select the Workspace Repository, or all repositories for that owner.
|
|
299
|
+
- Repository permissions: `Metadata: Read`, `Issues: Read and write`, and `Contents: Read`.
|
|
300
|
+
- Organization permissions: `Projects: Read and write`.
|
|
301
|
+
|
|
302
|
+
Store the token in the Workspace Repository Local Environment:
|
|
303
|
+
|
|
304
|
+
```sh
|
|
305
|
+
printf 'GITHUB_TOKEN=github_pat_...\n' > .symphony/.env
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
`GITHUB_TOKEN` takes precedence over `GH_TOKEN`. If `gh auth status` shows a working stored token but
|
|
309
|
+
Symphony still reports repository or project access gaps, remove the stale `GITHUB_TOKEN` from
|
|
310
|
+
`.symphony/.env` or replace it with a token that has the scopes above.
|
|
311
|
+
|
|
312
|
+
## Product Repository Development
|
|
313
|
+
|
|
314
|
+
1. Install dependencies:
|
|
315
|
+
|
|
316
|
+
```sh
|
|
317
|
+
pnpm install
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
2. Optionally create a legacy workflow file for product-repository fixture runs:
|
|
321
|
+
|
|
322
|
+
```sh
|
|
323
|
+
cp WORKFLOW.example.md WORKFLOW.md
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
3. Edit `WORKFLOW.md`:
|
|
327
|
+
|
|
328
|
+
```yaml
|
|
329
|
+
tracker:
|
|
330
|
+
kind: github
|
|
331
|
+
owner: your-org
|
|
332
|
+
repo: your-repo
|
|
333
|
+
project_number: 1
|
|
334
|
+
api_key: $GITHUB_TOKEN
|
|
335
|
+
active_states: [Todo, In Progress]
|
|
336
|
+
terminal_states: [Done, Closed, Cancelled]
|
|
337
|
+
project_status_field: Status
|
|
338
|
+
project_status_on_dispatch: In progress
|
|
339
|
+
project_status_on_success: In review
|
|
340
|
+
project_status_on_retry: Todo
|
|
341
|
+
codex:
|
|
342
|
+
command: codex exec
|
|
343
|
+
model: gpt-5.5
|
|
344
|
+
reasoning_effort: medium
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
4. Configure the GitHub Project:
|
|
348
|
+
|
|
349
|
+
- Add a single-select `Status` field.
|
|
350
|
+
- Add active values matching `active_states`, usually `Todo` and `In Progress`.
|
|
351
|
+
- Add terminal values matching `terminal_states`, usually `Done`, `Closed`, and `Cancelled`.
|
|
352
|
+
- Add or let Symphony create transition values such as `In progress` and `In review`.
|
|
353
|
+
- Add repository issues to the project before expecting Symphony to pick them up.
|
|
354
|
+
|
|
355
|
+
5. Authenticate GitHub access:
|
|
356
|
+
|
|
357
|
+
```sh
|
|
358
|
+
gh auth status
|
|
359
|
+
export GITHUB_TOKEN=...
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
See "GitHub Token Permissions" above for the exact PAT scopes and fine-grained permissions.
|
|
363
|
+
|
|
364
|
+
## Run Locally
|
|
365
|
+
|
|
366
|
+
Validate and build everything:
|
|
367
|
+
|
|
368
|
+
```sh
|
|
369
|
+
pnpm test
|
|
370
|
+
pnpm build
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Start the OCaml backend:
|
|
374
|
+
|
|
375
|
+
```sh
|
|
376
|
+
pnpm backend:dev
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
The backend serves:
|
|
380
|
+
|
|
381
|
+
- Dashboard placeholder/API root: `http://127.0.0.1:8080/`
|
|
382
|
+
- Runtime state JSON: `http://127.0.0.1:8080/api/v1/state`
|
|
383
|
+
- Tailscale/LAN access: `http://<machine-ip>:8080/` because the backend binds to `0.0.0.0`.
|
|
384
|
+
|
|
385
|
+
Start the ReScript React frontend in another terminal:
|
|
386
|
+
|
|
387
|
+
```sh
|
|
388
|
+
pnpm frontend:dev
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Open:
|
|
392
|
+
|
|
393
|
+
```text
|
|
394
|
+
http://127.0.0.1:5173/
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
The frontend proxies `/api/*` requests to the backend at `127.0.0.1:8080`.
|
|
398
|
+
|
|
399
|
+
## Useful Commands
|
|
400
|
+
|
|
401
|
+
```sh
|
|
402
|
+
pnpm backend:build
|
|
403
|
+
pnpm backend:test
|
|
404
|
+
pnpm frontend:build
|
|
405
|
+
opam exec -- dune exec symphony -- --once
|
|
406
|
+
opam exec -- dune exec symphony -- init
|
|
407
|
+
opam exec -- dune exec symphony -- --web --port 8080
|
|
408
|
+
opam exec -- dune exec symphony -- --once WORKFLOW.md
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
If no GitHub token is configured, the runtime still starts, but readiness gaps report the missing
|
|
412
|
+
token and live issue dispatch is disabled.
|