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 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.