opensymphony 3.0.0

A Rust implementation of the OpenAI Symphony orchestration design
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
# Architecture

## 1. Objective

Implement the Symphony orchestration model in Rust. The scheduler can use the
OpenHands agent-server, the local Codex app-server, or a configured local ACP
v1 agent for execution. FrankenTUI is an optional operator client.

The system must preserve these boundaries:

- the orchestrator is the source of truth for scheduling state
- the tracker is polled and reconciled by the orchestrator
- each repository-bound work issue executes in its own checkout
- a multi-repository parent uses a separate integration workspace after its
  children merge
- `WORKFLOW.md` remains the repo-owned policy and prompt contract
- UI is optional and must not affect correctness

The control-plane issue snapshot may carry an optional sanitized operator
projection for repository, parent, lease, repair, memory, containment,
provider, verification, and cleanup facts. Missing facts remain unknown. No
client infers completion, permission, or workspace confinement from absence.
An ACP run may also publish ephemeral, bound operator interactions. The
orchestrator actor owns pending decisions; gateway clients submit a response
command, and the active ACP worker returns it to the original RPC responder.
The actor validates and reserves a decision, then waits for the ACP input-sink
flush in an owned task. A completion message returns to the actor to settle the
pending decision and gateway receipt; other issues, callbacks, ticks, and
shutdown remain responsive during that wait. An in-flight decision excludes a
duplicate response.
Worker callback reports wake that actor for immediate application and snapshot
publication, independently of the tracker polling interval.
The run loop selects an event before mutating scheduler state, so a newly
arriving callback or operator command cannot cancel an in-progress tick.
Pending interactions are discarded on completion, cancellation, expiry, or
restart and cannot be reconstructed from stored evidence.

## 2. Layered design

OpenSymphony is split into five layers:

1. Policy layer
   - `WORKFLOW.md`
   - target-repo `AGENTS.md`
   - target-repo `.agents/skills/`
2. Configuration layer
   - typed workflow/config loader
   - env and path resolution
   - project sets, repository inventory, and harness profiles
3. Coordination layer
   - orchestrator actor
   - retry queue
   - reconciliation
   - runtime snapshot store
4. Execution layer
   - workspace manager
   - OpenHands REST client
   - OpenHands WebSocket runtime stream
   - local Codex app-server stdio adapter
   - local ACP v1 stdio session host
   - issue session runner
5. Observability layer
   - structured logs
   - control-plane API
   - FrankenTUI

Packaging distinction:

- modularity is preserved through explicit internal subsystem boundaries
- packaging is intentionally flat: crates.io publishes only `opensymphony`
- the `crates/opensymphony-*` directories are internal module trees compiled
  into that one package

Start with the [multi-repository guide](multi-repository.md) for issue binding
and parent integration, or the [ACP guide](acp.md) for local agent setup and
operator requests. The two choices are independent.

## 3. Main decisions

### 3.1 Rust owns orchestration

Rust owns:

- poll cadence
- issue eligibility
- bounded concurrency
- retry scheduling
- stall detection
- startup cleanup
- restart recovery
- operator snapshots

OpenHands conversation state is informative, not authoritative.

Parent admission derives child terminal status from scheduler-owned durable
outcomes or released executions. Provider-supplied terminal flags cannot
approve dispatch. A current execution supersedes any older success receipt;
unclaimed, claimed, running, and retry-queued children remain ineligible,
including when a subtree no longer requires merge evidence. Reopening or
recovering nonterminal work invalidates its durable success receipt before
batched launch preparation.

Repository routing is also orchestrator-owned: repository-bound child metadata carries
one alias, the central inventory resolves it to a canonical provider identity,
and the scheduler persists that identity plus its config and inventory
generations before claiming work. Project associations are validation scope,
not routing defaults; parents have no execution repository. A binding mutation
supersedes the claimed generation and fences late events from its worker. If
stop persistence or the harness abort fails, the old execution remains owned
and fenced for retry; restart recovery first reattaches the persisted binding
generation and then performs the same supersession against fresh tracker state.
Repository binding outcomes are carried into the control-plane issue snapshot;
invalid outcomes mark the issue blocked while preserving the typed diagnostic
for operator clients.

Hierarchy reconciliation is scheduler-owned as a separate generation axis from
checkout, run, and attempt generations. The scheduler persists required child
edges and owner-identified leases through the workspace manager's atomic JSON
artifact path. Parent dispatch consumes provider-backed merge evidence only
after terminal orchestrator outcomes and retained checkout-generation lease
resources are present; scope changes after freeze are recorded as
`HierarchyChanged` and cannot silently widen the parent run.

An eligible parent is materialized as a repository-neutral execution root for
the frozen hierarchy generation. The workspace backend resolves only active
ancestor leases, groups their retained generations by canonical repository ID,
and asks the workspace manager for one contained integration worktree per
repository. Those worktrees share a selected child's Git object store, refresh
the configured target through that repository's credential provider, and pin
the provider merge-result commits used for admission. An orchestrator-owned
copy of the checkout map remains outside the parent runtime root, and every
reopen compares the runtime map to that copy before rechecking target and merge
ancestry. The parent worker starts
once with this root as `cwd`, a relative checkout-handle map, and a generic
`parent_multi_checkout` envelope; neither the directory layout nor the prompt
assigns repository roles.

The scheduler persists one generation-bound parent integration controller with
the hierarchy state. Its versioned transitions cover admission, lease
acquisition, workspace preparation, repository refresh, harness integration,
final verification, and finalization. Each harness turn is one bounded attempt
on the shared parent conversation and records its input version, root or opaque
checkout handle, redacted log tail, cleanup receipt, and outcome. Recovery keeps
a reconciled running attempt attached; an unreconciled attempt becomes
indeterminate and must pass cleanup and repository refresh before rerun. Final
verification binds the worker-authored exact command to a trusted SHA-256
identity before redaction; durable evidence retains that identity and a redacted
diagnostic, while events older than the current attempt are ignored. Final
admission identity is stored separately from the compact transition tail so an
unbounded retry history cannot replay the initial admission transitions. If a
completed parent reopens, the scheduler creates a new controller lifecycle even
when its child-edge generation did not change. A bound parent conversation also
fixes the harness choice for that lifecycle; a configured harness switch fails
before the replacement session starts.
An integration defect creates an immutable repair attempt for one canonical
repository. The controller copies the verified target, instruction provenance,
and central review policy into that attempt, then records a durable intent and
receipt for each provider operation. Branch, push, pull-request, review, and
merge writes are preceded by provider reconciliation, so restart recovery finds
an existing result before repeating a side effect. Requested changes stay on
the same attempt and pull request. Provider outages, failed checks, review
rejection, external closure, force-push, and merge conflicts remain precise,
resumable states. GitHub is the first provider adapter and remains authoritative
for PR, review, check, and merge facts. Each scheduler tick checks a fresh full
tracker snapshot before advancing repair-provider writes, so any parent absent
from the current active set fences review, push, and merge operations in the
same observation.
After merge, the controller records the provider merge-result commit and the
workspace manager fetches the configured target through the central credential
path. The refreshed checkout is accepted only when that merge result and every
retained child merge result are reachable from the new target. Squash and
rebase results therefore do not depend on the replaced repair commit remaining
an ancestor. Both copies of the
generation-bound checkout map and its instruction hash are updated before final
verification can resume.
Final evidence is accepted only from the run-bound
`evidence/final-verification.json` receipt. The runtime reopens every checkout
at its exact prepared commit and uses the file only to select an actual command
observed through the Codex or OpenHands event stream. The controller maps the
observed command working directory to the parent root or an exact checkout
handle and supplies the deadline, exit result, bounded log,
foreground-process ownership, and
teardown from those runtime events before it can pass the attempt. Generic
harness success or a prompt-authored claim without matching events is
insufficient. Each accepted command or resource event is persisted with the
controller before the worker reaches a terminal outcome. The adapter records
stopped-turn evidence only after a terminal runtime state or acknowledged stop;
a backward-compatible run-manifest flag carries that fact across restart, and a
transport-level failed outcome cannot substitute for it. On timeout
or cancellation, a reconciled harness stopped state
releases the foreground-process receipt; any other named resource remains an
explicit cleanup fence. The durable final record maps every canonical repository to the
exact verified commit so a higher ancestor can consume the completed parent
without assigning repository roles.
When admission produces no repository targets, the same observed final command
can complete with an empty commit map. This represents a repository-neutral
parent and does not weaken command, deadline, cleanup, or controller gates.
Recovery treats a persisted launch intent with no attached conversation,
command, or resource as a metadata-only crash and safely returns through
cleanup and baseline refresh. A terminal harness manifest whose controller
outcome was lost becomes indeterminate and reruns after resource cleanup and
baseline refresh. Recovered parent memory grants restore the bearer
already held by the bound conversation into the reconstructed registry.
Legacy in-flight parent runs that predate controller persistence reconstruct
and persist that controller from the durable hierarchy, workspace envelope,
run identity, and existing conversation before backend reattachment. Route
preview conversations never become controller bindings.
Every recovered parent dispatch carries that expected conversation identity
into the worker launch boundary. A missing or different conversation manifest
fails before a replacement harness session can start. Reused turns receive a
small continuation prompt containing the current run, attempt, generation,
exact commit map, and receipt contract; the original workflow and repository
instructions remain in the bound conversation rather than being replayed.
Terminal success is gated by the durable controller's completed state in both
live and recovery release paths. Completed parent roots and their descendant
leases remain durable for capture retry; OSYM-893 consumes that terminal state
for ordered worktree cleanup and lease release. After automatic capture commits
all selected parent capsules, the run loop asks the scheduler to persist a
generation-bound subtree cleanup intent. Cleanup first archives the stopped
harness conversation and asks the workspace manager to receipt the
`before_remove` hook and detach every parent integration worktree through Git.
Only then does the scheduler release that parent's lease owners and remove
unleased descendant generations from deepest to shallowest, followed by the
non-Git parent root. A lease owned by another ancestor or by an unexpired
diagnostic hold continues to block its generation. A claimed, running, or
retry-queued child bound to the exact retained generation also blocks deletion
while that execution still owns the workspace.
Every pending cleanup tick refreshes the full tracker snapshot first. A newly
active descendant with an unresolved or matching workspace generation fences
deletion; an already resolved newer generation does not retain the old target.

The scheduler receipts every prepared or deleted target in the durable parent
controller. Workspace run manifests hold the hook and integration-worktree
receipts, while root-level generation tombstones bracket recursive deletion.
Lease releases and final completion transitions roll back in memory when their
atomic state writes fail, and the workspace manager persists an attempted hook
fence before `before_remove` can perform side effects. Restart repeats only an
incomplete step without rerunning an indeterminate hook attempt. Generation
cleanup revokes only the matching memory bearer, so a newer run for the same
issue keeps its authorization. A missing path is successful only when
the tombstone matches its issue, workspace key, path, terminal outcome, and
generation. Failed and canceled parents use the existing failed-workspace
cleanup semantics without changing terminal classification: `retain_failed`
applies to failed parents, while canceled parents continue cleanup. Reopened
parents and operator replans cannot replace a controller until its acknowledged
cleanup completes. Capture selects descendant leases owned by that controller's
generation even if the observed hierarchy has already advanced, and restart
recovery selects only the parent root matching the controller's durable
hierarchy generation.
Generation-bound OpenHands cleanup, including a parent root, also requires its
conversation store before workspace preparation can proceed. Missing
conversation evidence fails closed unless the run manifest proves preparation
failed without ever recording a conversation binding.
The completed cleanup intent remains in orchestrator state after the parent root
is removed and seeds automatic-capture completion after daemon restart, so a
terminal parent is not routed and captured again without first reopening.

### 3.2 OpenHands adapter

OpenHands provides:

- per-conversation workspace configuration
- persistent conversations
- background run triggering
- searchable event history
- real-time updates over WebSocket
- provider/model flexibility

OpenSymphony does not reimplement an agent loop.

### 3.3 OpenHands uses WebSocket and REST

REST is still required for:

- conversation creation
- sending messages
- triggering runs
- initial sync
- reconnect reconciliation
- restart recovery

### 3.4 One local OpenHands server, many workspaces

The local supervised topology runs one OpenHands server for the daemon while
passing a distinct `working_dir` per issue.

### 3.5 One conversation per issue by default

OpenSymphony persists a stable `conversation_id` per issue inside the issue
workspace and reuses it across retries and daemon restarts unless the workflow
reuse policy says otherwise.

### 3.6 GraphQL-only Linear writes

OpenSymphony 1.0.0 removed the old bridge layer for agent-side Linear writes.

The supported model is now:

- orchestrator reads Linear through the internal `opensymphony_linear` module
- initialized target repos read and write Linear through the checked-in
  GraphQL helper assets under `.agents/skills/linear/`

This keeps one canonical Linear API surface for the agent path.

### 3.7 Code Graph snapshots are repository-owned

The Code Graph indexer resolves the configured repository and target branch
server-side from `WORKFLOW.md`. It reads Git tree/blob objects and sends source
text through the bounded Tree-sitter provider; it does not execute target-repo
code or accept client filesystem roots. Snapshot membership is immutable by
repository and commit, while current read-model rows may be marked stale for
deletions. A single process-wide writer owner serializes index mutations so
DuckDB reads remain available during gateway jobs. Issue-workspace code remains
an overlay concern rather than being folded into the shared baseline.

### 3.8 Per-instance memory catalog

Central configuration starts one supervised memory endpoint for the
orchestrator instance, regardless of inventory size. The catalog keeps
repository-owned source registrations and normalized scope references beside
instance-private records. Source registration is keyed by canonical repository
ID, source kind, and commit generation; reads use the existing DuckDB schema
without migration side effects. Repository-local policy, public docs, OKF, and
legacy stores remain provenance-bearing sources rather than becoming a second
authoritative write target.

## 4. Component model

### Internal subsystem modules

- `opensymphony_domain`
  - domain models and scheduler transitions
- `opensymphony_workflow`
  - workflow loading, config resolution, prompt rendering, and alpha harness/model
    selection
- `opensymphony_workspace`
  - workspace management and manifests
- `opensymphony_linear`
  - Linear GraphQL read adapter and guarded archive mutation
- `opensymphony_memory`
  - issue capsules, DuckDB memory index, repository code snapshots, docs sync,
    and archive eligibility
- `opensymphony_code_intel`
  - built-in Tree-sitter parser provider skeletons, starting with Rust source
    summaries, one-based spans, symbols, and recoverable AST diagnostics
- `opensymphony_openhands`
  - OpenHands transport and session runner
- `opensymphony_codex`
  - local Codex app-server stdio adapter, JSON-RPC lifecycle requests, event
    normalization, installed-schema validation, credential reuse, and benchmark
    helpers for experimental transports
- `opensymphony_orchestrator`
  - scheduler loop, route decisions, and reconciliation
- `opensymphony_control`
  - control-plane snapshot store and compatibility API
- `opensymphony_gateway`
  - operator gateway API, dashboard snapshots, Linear-backed task graph reads,
    run detail/file/diff endpoints, event journal, and web assets
- `opensymphony_cli`
  - user-facing entrypoints
- `opensymphony_tui`
  - terminal operator UI
- `opensymphony_testkit`
  - fakes and contract fixtures

### Target-repo Linear assets

Initialized repositories receive a checked-in Linear skill tree:

- `SKILL.md`
- `scripts/linear_graphql.py`
- `queries/*.graphql`
- `references/*.md`

Those assets are part of the supported public interface of `opensymphony init`.
They include canonical query files for issue create/update flows, comments,
relations, attachments, project content/status updates, and introspection.

## 5. Process model

Local process graph when an OpenHands route is selected:

```text
opensymphony run
  ├─ orchestrator
  ├─ workspace manager
  ├─ linear adapter
  ├─ openhands REST client
  ├─ openhands WebSocket client
  ├─ optional Codex app-server stdio worker
  ├─ optional ACP v1 stdio session host
  ├─ gateway API
  ├─ control-plane compatibility API
  └─ local server supervisor
       └─ python -m openhands.agent_server
```

An ACP-only or Codex-only run does not launch the OpenHands server.

The scheduler attaches a `HarnessRouteDecision` to each worker start request.
The default route remains `openhands_agent_server`. Workflow `routing.harness`
or the `OPENSYMPHONY_HARNESS` environment override can select the local
`codex_app_server` or `acp` route when configured and available. ACP also
requires a named `routing.harness_profile`; the scheduler binds the selected
profile to each prepared run.
Route decisions are emitted as `routing.decision` runtime audit events so dry-run
previews and real dispatches show the selected harness, model, and model
profile.

Other processes:

- `opensymphony debug <issue-id>`
- `opensymphony tui`
- target-repo hooks started by the workspace manager
- OpenHands-managed tool execution inside the agent runtime

There is no separate agent-side Linear bridge process in 1.0.0.

## 5.1 Gateway and rich clients

The web and desktop clients consume the gateway contract rather than reaching
into orchestrator internals. Dashboard and run state come from the
control-plane snapshot, while the task graph read endpoint asks the
orchestrator-side Linear adapter for tracker hierarchy and dependency
relationships, then overlays live runtime details from the latest snapshot.
Runtime token usage in that snapshot carries input, output, cache-read, and
provider-reported total counters when the selected harness reports them; legacy
metadata without an explicit total falls back to input plus output.
Run Detail metadata also carries tracker-backed branch and PR fields when
Linear provides them: `branchName` becomes `branch_name`, and explicit GitHub PR
attachments become `pr_url`.
The gateway emits `root_ids` from the returned Linear parent/child graph so
clients can render the same forest without inventing hierarchy locally.
If the optional task graph reader cannot be built, `opensymphony run` still
starts the gateway and the task graph endpoint returns `503`; this does not
weaken the scheduler's separate Linear tracker requirement.

Native desktop builds may call the same operations through Tauri IPC instead
of loopback HTTP, but the data contract is identical. Tauri command arguments
use the Rust command parameter names exactly, including snake_case keys such as
`run_id`, `project_id`, `page_token`, `page_size`, and `file_path`. If a native
desktop read command fails, the desktop adapter may retry through the loopback
HTTP transport for the same gateway operation.
Run-event `page_token` values are gateway-generated sequence tokens encoded as
strings; malformed tokens are rejected with `400 Bad Request` instead of being
silently treated as the first page.

## 6. Failure boundaries

- scheduler correctness must not depend on tracker comments or transitions
- GraphQL write failures in the target repo do not corrupt orchestrator state
- a missing `LINEAR_API_KEY` blocks Linear operations but should fail clearly
- UI failures must not affect daemon execution

## 7. Interrupt diagnostics

Interrupt requests are recorded in orchestrator-owned issue execution state
before any harness-specific protocol call is attempted. The shared command
captures the run id, Linear issue id, harness kind, conversation or thread id,
optional turn id, reason, and expected next state. Current reasons are
`operator_cancel` and `tracker_merging_supersedes_human_review`.

The command is idempotent for the active run: repeated operator clicks or
tracker observations return the existing command instead of enqueueing another
harness interrupt. Harness adapters later translate that command to their
native protocol, but scheduler state does not depend on desktop-local state or
adapter-private DTOs.

`opensymphony run` consumes accepted gateway `cancel` actions from the gateway
event journal and forwards them into the scheduler-owned `operator_cancel`
interrupt path. The gateway validates and records the operator intent, but it
does not mutate scheduling state directly.

Run Detail diagnostics surface the orchestrator-owned cancel state as
requested, acknowledged, failed, timed out, and reason fields. Terminal cancel
states are sticky: late acknowledgements, failures, or timeouts do not overwrite
an already terminal interrupt status. Non-cancel worker outcomes do not infer a
harness interrupt acknowledgement or timeout; adapters must still report the
actual acknowledgement, failure, or timeout path.

## 8. Migration boundary

Central orchestration configuration is selected independently of the current
directory. The loader resolves one instance-owned config generation before
loading repository instructions, validates references and contained roots, and
keeps credential references separate from resolved secret values. Explicit
`legacy_single` routing preserves the existing single-repository dispatch;
strict `project_set` terminal dispatch additionally binds each worker to a
verified repository checkout generation and carries the config/inventory
provenance into the harness envelope.

`opensymphony migrate preflight` performs no writes. `migrate apply` stages a
central config and the repository implementation-instruction body, creates a
recoverable backup, and records activation before atomic replacement. The
activation marker makes interrupted replacement recoverable by `migrate
rollback`, which is blocked by an active strict-run marker.

OpenSymphony 1.0.0 is the compatibility boundary for the GraphQL-only Linear
rewrite and the provider-agnostic AI review configuration changes.

Notable removals:

- workflow-owned `openhands.mcp`
- the old bridge CLI command
- provider-specific AI review secret naming

## ACP executable protocol client

The `opensymphony_acp` internal module uses the official Rust ACP SDK for typed
requests, JSON-RPC correlation and ordered application dispatch. OpenSymphony owns
the child process and supplies bounded LF framing to the SDK line transport.
`SessionHost` owns a bounded registry of supervised per-issue sessions. Each
session actor accepts generation-fenced commands and allows one outstanding
prompt. Worker handles borrow a process across attempts; dropping a handle or
subscriber preserves the session. Idle expiry and explicit retirement use the
same supervised process teardown. The `run_turn` compatibility API creates one
session for one prompt. Neither API mutates scheduling state. The production
`opensymphony run` worker selects ACP explicitly, borrows the retained owner, and
reports normalized updates and outcomes through scheduler-owned worker messages.
The same launch preparation verifies checkout bindings, instruction provenance,
hooks, review context and scoped memory before adapter dispatch. ACP-only startup
requires neither an OpenHands client nor an OpenHands server.

The persisted route retains the ACP profile and model selection across daemon
recovery. Profile switches retire a quiescent owner before archiving its manifest;
active prompts, observation leases and uncertain submissions fence switching and
cleanup. A submitted prompt is never replayed on restart. Tool patches merge by
call identity with bounded state; replay frames do not contribute usage. Optional
usage remains absent when the peer does not report it. Raw redacted source frames
stay on the owner separately from the normalized scheduler event stream.
For a bound parent continuation, a changed run-scoped grant rotates the process
while retaining the authoritative session ID. The replacement must negotiate
load or resume; it cannot create a new parent session after a failed restore.

Durable ACP identity and submission/outcome markers live in the existing
conversation manifest, with additive ACP identity in its runtime envelope.
`ControlPlaneServer::with_acp_host` exposes authenticated observation commands
and ordered source events for a separate debug process. It does not grant IDE
writer control; scheduler holds and writer transfer belong to OSYM-907.

Handlers are installed before initialization. Permission callbacks receive the
protocol cancellation outcome; unknown requests receive method-not-found and
unknown notifications receive no response. Updates are processed before the
prompt response is returned. Host policy gates filesystem and terminal
advertisements. The ordered dispatch handler admits callbacks to one bounded
connection-owned actor. File operations are serialized; terminal waits use
bounded asynchronous responses so they cannot block RPC dispatch. Terminal
processes use the existing process-group or Windows Job Object supervisors.
ACP extension registrations are exact profile/version entries inside the ACP
module. The pinned Cursor `cursor/create_plan` request uses the scheduler-owned
plan response path; its ID-bearing `cursor/update_todos` request receives a
bounded response and contributes todo activity only after its correlated
accepted response. The response correlator tracks IDs across all inbound
callback methods, so a plan response cannot accept a concurrent same-ID todo.
A bounded pending-candidate queue fences the worker with a
visible diagnostic on saturation. Both bind to the connection-owned
active session without a peer-supplied session field. Unobserved Cursor question,
task, and image methods are outside the enabled registration. Outbound operation dispatch enters
through the gateway's operator action, binds the current run in the scheduler,
and resolves the registered method and session inside the retained owner. The
public run capability supplies its attempt binding; the host bounds concurrent
requests and the gateway journals each accepted operation's outcome. An outbound
RPC retains its own ID and deadline after prompt completion so a back-to-back
operation result is not discarded by the prompt callback epoch.
Negotiated peer support and profile enablement are both required. Timeouts
report an unknown outcome, while the unresolved SDK waiter retains its permit
until a peer response or connection closure. This caps pending replies at eight
even across successive timeout batches. Known-secret redaction runs on the
validated result before it can enter an operator receipt. Lifecycle state
remains orchestrator-owned.
Each retained prompt first retires the prior callback epoch through a bounded,
cancellable preparation step while the owner continues servicing commands. Only
then does it persist submission and dispatch the prompt. Session config responses
and updates are committed in SDK dispatch order before prompt completion. The
prompt response revokes its callback epoch before an adjacent request is
dispatched; automatic permission policy also requires that live epoch.
Callback closures use a bounded channel wait so a burst of open requests cannot
silently drop the expiry signal. A receiver that stops draining fails the turn;
worker completion clears its pending interactions. No
OpenHands server or client participates in this launch path.

## ACP live interoperability

The [ACP live qualification](acp-live-qualification.md) records production
`opensymphony run` paths for pinned Cursor and Devin stdio CLIs. Both use the
same scheduler-owned routing and issue workspace; vendor-specific behavior
stays inside the ACP client/registered extension boundary. Devin may announce
configuration before the `session/new` response, so the client buffers those
bounded announcements until it can bind the authoritative session ID. The
response wins for fields it supplies.

<!-- BEGIN OPENSYMPHONY MANAGED MEMORY SYNC -->

## Current model

- COE-252 contributed: PR #10: Implement foundation workflow and scheduler contracts
- COE-253 contributed: PR #19: COE-253: OpenHands Runtime Adapter (merge `911b0b4`)
- COE-254 contributed: PR #6: COE-254: bootstrap tracker, workspace, and orchestration core
- COE-255 contributed: PR #4: COE-255: add control plane and FrankenTUI slice
- COE-256 contributed: PR #1: COE-257: tighten hosted deployment guidance
- COE-258 contributed: PR #83: Add memory init and mapped docs sync

## Important invariants

- Preserve the behavior described in the recent captured changes unless current code and tests show it has changed.
- Use capsule source refs to inspect the original PR or Linear issue when context is ambiguous.

## Operational flow

- No generated diagram requested for this sync.

## Known gotchas

- No area-specific gotchas were inferred from the selected memory.

## Recent changes

- COE-252: Foundation and Contracts
- COE-253: OpenHands Runtime Adapter
- COE-254: Tracker, Workspaces, and Orchestration
- COE-255: Observability and FrankenTUI
- COE-256: Validation and Local Operations
- COE-258: Bootstrap workspace and crate boundaries
- COE-259: Workflow loader and typed config
- COE-260: Domain model and orchestrator state machine
- COE-261: Local agent-server supervisor
- COE-262: REST client and conversation contract
- COE-263: Workspace manager and lifecycle hooks
- COE-264: Linear read adapter and issue normalization
- COE-265: WebSocket event stream, reconciliation, and recovery
- COE-266: Issue session runner
- COE-267: Linear MCP write surface
- COE-268: Orchestrator scheduler, retries, and reconciliation
- COE-269: Control-plane API and snapshot store
- COE-270: Repository harness and generated context artifacts
- COE-271: FrankenTUI operator client
- COE-272: Fake OpenHands server and protocol contract suite
- COE-273: Live local end-to-end suite
- COE-274: CLI packaging, doctor, and local operations docs
- COE-277: Implement hierarchy-aware task selection
- COE-280: Support workflow-owned OpenHands auth, provider, and launcher overrides at runtime
- COE-281: Support path-bearing OpenHands base URLs and MCP config at runtime
- COE-282: Support workflow-owned OpenHands conversation reuse policy at runtime
- COE-283: Cache per-state running counts in the orchestrator scheduler
- COE-284: Add orchestrator run command to CLI and make it installable
- COE-286: Abort active CLI worker tasks on graceful orchestrator shutdown
- COE-287: Add opensymphony debug command for conversational session debugging
- COE-294: Detect LLM config changes and rehydrate conversations with updated env vars
- COE-382: Add supply-chain and security audits to CI
- COE-383: Decompose oversized session and TUI modules into focused submodules
- COE-384: Expand error-path tests for Linear client and workspace hooks
- COE-385: Resolve runtime tracking TODO in OpenHands session runner
- COE-386: Wire cargo-llvm-cov coverage reporting and regression floor into CI
- COE-387: Audit tracing spans and diagnostics for secret leakage
- COE-389: Current Gateway Inventory And Vocabulary
- COE-390: Gateway Schemas And Stream Feasibility
- COE-391: Gateway Module, Capabilities, And Dashboard Snapshot
- COE-392: Task Graph, Run Detail, File, And Diff Read APIs
- COE-393: Event Journal And Stream Broker
- COE-394: Frontend Workspace And Shared Schemas
- COE-395: Planning Artifact Schema And Session Service
- COE-396: Action Receipts And Initial Run Actions
- COE-397: Gateway API Client, Transport Adapters, And Reducers
- COE-398: Tauri Shell And Security Capabilities
- COE-399: Linear Read Coverage And Task Graph Cache
- COE-400: OpenHands Event Normalization And Runtime Mirror
- COE-401: Web App Entry And Deployment Modes
- COE-402: App Shell, Dashboard, Task Graph, And Run Views
- COE-403: Terminal And Log Renderer Prototype
- COE-404: Desktop Connection Profiles And Daemon Management
- COE-405: Linear Milestone, Issue, And Sub-Issue Mutations
- COE-406: Repository, Linear, And Research Analysis
- COE-407: Browser Transport And Remote Stream Protocols
- COE-408: Harness Adapter And Capability Model
- COE-409: Desktop Settings, Keychain, And Native Actions
- COE-410: Desktop Local Stream Optimization
- COE-411: Task Graph Editor And Runtime Overlay UI
- COE-412: Runtime Timeline And Terminal/Log Association
- COE-413: Implementation Plan Generator Stage
- COE-414: Diff, Validation, Approval, And Run Action Views
- COE-415: Milestone, Issue, And Sub-Issue Compiler
- COE-416: Dependency Graph And Plan Checks
- COE-417: Planning Workspace UI
- COE-419: Hosted Auth Placeholders And Web Parity
- COE-423: Model And Credential Settings
- COE-425: OpenHands Subscription Credential Adapter
- COE-426: Codex App-Server Prototype And Benchmarks
- COE-428: Model Configuration UI And Routing Metadata
- COE-429: Codex Approvals And Cross-Harness Routing
- COE-434: Long-running harness liveness and scheduler/runtime ownership contract
- COE-435: Long-running run observability fixtures and client-facing diagnostics
- COE-449: Desktop alpha recovery: replace stubs with functional app
- COE-452: DuckDB Prebuilt Developer Build Mode
- COE-453: Non-Interactive Init For Automation
- COE-461: Memory Graph DTOs And Gateway Endpoints
- COE-464: Graph Extraction, Metrics, And Community Pipeline
- COE-465: Shared Graph Frontend Package And Reducers
- COE-467: Three.js Graph Renderer And Worker Layouts
- COE-468: Concept Inspector, Search, Filters, And Accessibility Fallback
- COE-469: Live Memory Graph Integration And Privacy Gates
- COE-471: Graph Scale, Visual Regression, And Web/Desktop Hardening
- COE-473: Desktop task graph dependency and run detail parity
- COE-475: ChatGPT OAuth For Codex Harness
- COE-476: Codex Production Harness Enablement
- COE-478: Harden model profile storage and validation follow-ups
- COE-479: Codex Debug Session Resume
- COE-486: Harness Interrupt Contract And Run Diagnostics
- COE-487: Desktop Run Detail TUI Parity
- COE-488: Lazy Desktop Launcher Command
- COE-489: OpenHands Agent-Server Interrupt Adapter
- COE-490: Codex App-Server Turn Interrupt Adapter
- COE-491: Desktop Run Detail Action Wiring And Cleanup
- COE-492: Merging Supersedes Human Review Polling
- COE-493: Desktop Operations Integration Hardening
- COE-498: Tree-sitter Provider Skeleton And Rust Parsing
- COE-499: Memory Context AST Provider Integration
- COE-500: Query Packs For Supported Agent Languages
- COE-501: Code Intelligence Persistence And Ingestion
- COE-502: Read-Only AST MCP And CLI Tools
- COE-503: Code Intelligence Performance Docs And Hardening
- COE-504: Linear Polling And Rate-Limit Recovery
- COE-505: Add scheduler-side Codex stdio interrupt channel
- COE-506: Invert CodeIntelIndex trait ownership after AST memory integration
- COE-507: Deduplicate query-pack assets for grammar variants
- COE-508: Cache code-intel parsers and compiled query packs
- COE-520: Route desktop Knowledge Graph through native gateway commands
- COE-525: Desktop Installer Contract And Release Metadata
- COE-526: Desktop Release Bundle Pipeline
- COE-527: Source Build Fallback And Prerequisites
- COE-528: App Download Install And Launch Flow
- COE-529: Desktop Auto-Update Flow
- COE-530: Installer Docs And End-To-End Validation
- COE-531: Workspace Shell Graph Hero And Surface State
- COE-532: Symbol Identity Container Chain And Code Read Model
- COE-533: Code Graph DTOs Gateway Routes And Native Commands
- COE-534: Code Graph Frontend Surface Adapters And Inspector
- COE-535: Run Diff Symbol Navigation And Code Overlay
- COE-536: Cross Graph Code Memory And Work Chips
- COE-537: Code Graph Scale Accessibility And Parity Hardening
- COE-542: Target Branch Code Index And Revision Snapshots
- COE-543: Workspace Code Overlay And Composite Graph
- COE-544: Indexed Agent Code Context And Retrieval
- COE-545: Edge Delta And Module Topology Diff
- COE-546: Code Graph Bootstrap UX And End-To-End Validation
- COE-547: Central Multi-Repository Config And Safe Migration
- COE-548: Canonical Repository Binding And Task Propagation
- COE-549: Verified Checkouts Instructions And Harness Envelopes
- COE-550: Per-Instance Memory Catalog And Source Migration
- COE-551: Scoped Cross-Repository Memory And Leaf Overlays
- COE-556: Bottom-Up Subtree Cleanup And Recovery
- COE-609: ACP Session Ownership And Durable Recovery
- COE-610: ACP Client Callbacks And Session Configuration
- COE-611: ACP Execution Routing And Worker Integration
- COE-612: ACP Operator Requests And Response Routing
- COE-613: ACP Extensions And Harness Operations
- COE-615: ACP Runtime Conformance And Live Qualification

## Source refs

- COE-252
- COE-253
- COE-254
- COE-255
- COE-256
- COE-258
- COE-259
- COE-260
- COE-261
- COE-262
- COE-263
- COE-264
- COE-265
- COE-266
- COE-267
- COE-268
- COE-269
- COE-270
- COE-271
- COE-272
- COE-273
- COE-274
- COE-277
- COE-280
- COE-281
- COE-282
- COE-283
- COE-284
- COE-286
- COE-287
- COE-294
- COE-382
- COE-383
- COE-384
- COE-385
- COE-386
- COE-387
- COE-389
- COE-390
- COE-391
- COE-392
- COE-393
- COE-394
- COE-395
- COE-396
- COE-397
- COE-398
- COE-399
- COE-400
- COE-401
- COE-402
- COE-403
- COE-404
- COE-405
- COE-406
- COE-407
- COE-408
- COE-409
- COE-410
- COE-411
- COE-412
- COE-413
- COE-414
- COE-415
- COE-416
- COE-417
- COE-419
- COE-423
- COE-425
- COE-426
- COE-428
- COE-429
- COE-434
- COE-435
- COE-449
- COE-452
- COE-453
- COE-461
- COE-464
- COE-465
- COE-467
- COE-468
- COE-469
- COE-471
- COE-473
- COE-475
- COE-476
- COE-478
- COE-479
- COE-486
- COE-487
- COE-488
- COE-489
- COE-490
- COE-491
- COE-492
- COE-493
- COE-498
- COE-499
- COE-500
- COE-501
- COE-502
- COE-503
- COE-504
- COE-505
- COE-506
- COE-507
- COE-508
- COE-520
- COE-525
- COE-526
- COE-527
- COE-528
- COE-529
- COE-530
- COE-531
- COE-532
- COE-533
- COE-534
- COE-535
- COE-536
- COE-537
- COE-542
- COE-543
- COE-544
- COE-545
- COE-546
- COE-547
- COE-548
- COE-549
- COE-550
- COE-551
- COE-556
- COE-609
- COE-610
- COE-611
- COE-612
- COE-613
- COE-615

<!-- END OPENSYMPHONY MANAGED MEMORY SYNC -->