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