Skip to content

RFC: Ourocode Desktop — terminal-first session fabric for Ouroboros MCP v2 #58

Description

@Q00

RFC 0001: Ourocode Desktop — Terminal-First Session Fabric for Ouroboros MCP v2

  • Status: Implementing — bootstrap complete, production gates open
  • Date: 2026-08-09
  • Target: macOS first
  • Discussion: GitHub issue #58
  • Supersedes: the desktop-product boundary in #25; the existing TUI remains a compatibility client and protocol fixture source
  • Completion evidence: 0001-completion-audit.md

Summary

Ourocode becomes a real desktop terminal emulator with an embedded view of the session forest created by Ouroboros MCP v2. It is not a coding harness with a terminal-shaped panel.

The terminal must remain fully useful when Ouroboros is stopped. When Ouroboros is connected, every server-advertised parent and child session is discoverable in a virtualized session rail, selected sessions can expose their transcript or attached terminal, and authorized steering is sent through Ouroboros rather than injected into a pseudo-terminal. Ouroboros 0.50.8 supports exact-attempt steering; authenticated peer session-to-session messaging requires a future server contract.

The first implementation is a thin AppKit and Metal shell over a Rust core. It does not link the existing BEAM TUI runtime into the desktop app. Existing Elixir code remains valuable as a legacy CLI, behavior reference, and source of captured protocol fixtures.

Why a new runtime

The current repository is an Elixir terminal workbench. It already contains useful lifecycle normalization, journaling, recovery, and parent/child projection semantics, but it has no desktop window, PTY manager, VT emulator, glyph pipeline, or GPU renderer. A desktop wrapper around the current TUI would preserve its per-pane projections and BEAM footprint while adding another UI runtime, which conflicts with the memory goal.

The desktop app therefore shares contracts with the existing client, not its presentation runtime.

Product boundary

Ourocode owns

  • local shell processes and PTYs;
  • terminal emulation, rendering, input, scrollback, selection, search, tabs, and splits;
  • a lightweight projection of the Ouroboros session forest;
  • focus, pinning, unread state, and the mapping from a session to a visible surface;
  • a local append-only audit of user-issued session actions;
  • bounded local recovery metadata.

Ouroboros owns

  • agent execution and fanout;
  • authoritative session identity, parentage, attempt identity, and lifecycle state;
  • delivery and audit of session signals;
  • cancellation and execution state;
  • replayable execution events and projections.

Explicit non-goals for the first release

  • an Electron, Tauri, or webview desktop shell;
  • replacing Ouroboros orchestration inside the client;
  • rendering every child session as a live GPU terminal;
  • treating an agent transcript as ANSI PTY output;
  • cloud sync, accounts, collaborative editing, notebooks, or an AI-first composer;
  • Windows or Linux UI before the macOS memory and correctness gates pass;
  • a general graph database or client-created session mesh.

Target architecture

AppKit window and native controls
  |-- SessionRailViewController (virtualized forest)
  |-- SurfaceContainerView
  |     |-- TerminalMetalView        real local or attached PTY
  |     `-- TranscriptView           semantic agent stream
  `-- Command and audit surfaces
             |
       typed C ABI (no JSON FFI)
             |
Rust AppCore
  |-- one mio/kqueue reactor for all PTY and MCP descriptors
  |-- terminal registry and bounded scrollback
  |-- session forest projection and signal audit
  |-- one bounded journal writer
  `-- MCP v2 client: snapshot/query, linked progress, signal, cancel
             |
       Ouroboros MCP v2 server

The main AppKit thread owns native views. A single I/O reactor owns PTY and MCP file descriptors. A single bounded writer persists metadata and audit records. The design does not allocate an actor or OS thread per session.

The Metal device, render pipelines, font discovery state, and glyph atlas are shared for the whole application. Only visible terminal surfaces allocate GPU instance buffers. An offscreen session has no layer, swapchain, or vertex buffer.

Current vertical slice (not the target)

AppKit + SwiftTerm       visible renderer/input adapter
AppKit MCP v2 client    Ouroboros catalog/session compatibility projection
          |
    bounded JSON-lines over a mode-0600 Unix socket
          |
Rust ouro-broker        PTYs/process groups, canonical viewport, bounded deltas

The current slice proves that a UI restart need not kill the shell and that a
new renderer can recover a versioned canonical visible viewport without
starting inside UTF-8 or a control sequence. The broker owns the bootstrap
vt100 state and bounded delta window; SwiftTerm remains the visible adapter.
Broker wire protocol v3 added a stable create nonce projected by list and
post-waitpid termination acknowledgement. Protocol v4 preserves those
lifecycle rules in a separate broker-v4.sock and adds manifest-bound 64 KiB
two-phase recovery, ordered PTY/resize state, bounded recovery pins/expiry, an
offscreen Swift import, and input authority only after attached_ready. This
lets a client reconcile a timed-out create without duplicating a PTY and keeps
the old renderer intact through corrupt, incomplete, or stale recovery. V2/v3
socket namespaces remain untouched; v3 is now a visibly labelled, explicit
compatibility choice rather than a silent downgrade.
On an ordinary Finder launch, the app connects to the shared user-scoped
Ouroboros endpoint at 127.0.0.1:8976; it does not create a duplicate Python
runtime or download Ouroboros implicitly. An explicit loopback MCP URL takes
precedence, --no-ouroboros disables the connection, and a missing source does
not disable the terminal. The shared HTTP endpoint is still a bootstrap bridge,
not a claim that launchd/XPC supervision is complete.
This is not yet the Ghostty production provider. The default v4 manifest names
its checkpoint OUROCODE-ANSI-REPLAY and fixture:vt100-0.15.2; it excludes
full canonical scrollback, Kitty graphics, sixel, and Ghostty-specific
semantics. The target diagram above becomes true only after the exact-pin
Ghostty snapshot provider, production renderer, MCP projection, and durable
journal move behind the broker/core boundary.

Terminal engine

The preferred engine is a pinned revision of Ghostty's MIT-licensed libghostty, behind an Ourocode-owned C/Rust adapter. libghostty-vt already provides the difficult VT, Unicode, reflow, selection, input encoding, and render-state machinery without dictating the app UI.

Before committing to it, a bounded spike must prove:

  1. the embedding and render-state APIs needed by a Rust static library and AppKit Metal view;
  2. IME, Kitty keyboard, resize/reflow, tmux, vim, ssh, and Unicode correctness;
  3. acceptable API pinning and upgrade isolation while libghostty has no tagged library version;
  4. measured incremental memory within the targets below.

If the spike fails, the fallback is a Rust terminal core using an established VT parser. Writing a new escape-sequence parser and grid from scratch is not an MVP.

The current pinned SwiftTerm bootstrap adapter has a known content-preservation risk matching SwiftTerm #494: narrow/widen reflow can duplicate or orphan rows. Disabling SIGWINCH, debouncing PTY resize, or changing the Metal buffering mode does not repair lost grid content. Before this adapter can ship, Ourocode must pin a tested fork with content-preserving flatten/rewrap/replace behavior and narrow/widen round-trip tests, or replace the adapter through the libghostty gate.

Warp is a design reference, not a dependency for the core. We should study its PTY manager, pane tree, glyph caching, semantic command boundaries, and agent-session lifecycle. Only its warpui crates are MIT licensed; the rest is AGPL-3.0. No AGPL source is copied unless the repository deliberately adopts a compatible license. The useful product idea is semantic command boundaries through OSC 133/633, not Warp's account, cloud, block-document, or AI-composer surface.

Warp is ahead today on real terminal maturity: race-safe PTY lifecycle, parser fairness, Alacritty-derived recordings, chunked scrollback, nested panes, native Metal rendering, OSC block semantics, and an existing multi-agent hierarchy and message protocol. Ourocode must not claim that hierarchy or inter-agent messaging is novel.

The credible product wedge is different. Ourocode projects a provider-neutral, local Ouroboros execution forest; exact attempt guards and durable receipts make steering fail closed; semantic agent sessions do not allocate hidden PTYs or GPU surfaces; and one reactor plus global byte, atlas, visible-surface, and thread caps make aggregate resource use measurable. Warp currently uses a PTY reader thread and 256 KiB read buffer per local terminal, materializes child conversations as hidden terminal panes, and grows glyph-cache textures without an eviction policy until configuration reset. These are hypotheses for a controlled benchmark, not proof that Ourocode already uses less memory.

The license boundary is strict: only crates/warpui and crates/warpui_core are MIT, while the terminal, app, pane, agent, and multi-agent implementation is AGPL-3.0. Even the MIT crates currently participate in Warp's workspace dependency graph, so they are not assumed to be drop-in permissive libraries. Ourocode may study public behavior and independently implement standards, but it does not copy Warp terminal or agent code.

Durable broker and mobile reconnect

Multiple desktop windows and a paired mobile client turn PTY lifetime into a service concern. The production owner is one launchd-managed, user-scoped broker, not an AppKit window and not one uvx process per tab. The broker owns PTY masters, child process groups, terminal state, bounded scrollback, Ouroboros MCP connectivity, session lifecycle, cursors, and audit receipts. Desktop windows are restartable thin clients over XPC or a Unix-domain socket.

Herdr is useful evidence for durable server ownership, reconnect, explicit input leases, and keeping the PTY alive while the UI disappears. Paseo is useful evidence for device pairing, mobile snapshots followed by ordered deltas, cursor-based resume, and falling back to a fresh snapshot when a slow consumer exceeds the retained delta window. These ideas are implemented independently: Herdr is Apache-2.0, while Paseo is AGPL-3.0-or-later and its source is not copied.

The broker never sends a PTY file descriptor or raw MCP capability to a mobile device. A paired device receives a bounded TerminalSnapshot and monotonic TerminalDelta stream and sends high-level commands guarded by a short-lived input lease. The handshake includes protocol version, broker-incarnation UUID, device identity, resume cursor, and advertised capabilities. Every command carries an idempotency key and session generation; stale generation or lease fails closed.

Local discovery and pairing use an explicit one-time code or QR payload. Long-lived device credentials are stored in Keychain, transport is encrypted and mutually authenticated, and revocation is local and immediate. Remote access is disabled by default. Plain streamable HTTP remains a loopback-only development bridge and is never the mobile transport.

Backpressure is global and bounded. Deltas are coalesced by terminal damage region, each client has a byte-limited egress queue, and a client that falls behind receives resync_required rather than causing broker memory growth. Only one device holds the input lease for a terminal by default; read-only observers do not affect focus or resize authority. Desktop and mobile tabs are views of stable broker sessions, so closing a view does not implicitly kill a durable PTY, while an explicit terminate command targets the broker-owned foreground process group.

The 0.50.8 compatibility bridge currently has to read ouroboros://sessions, which measured about 1.0 MiB for 14 persisted sessions on the development machine because it includes runtime and tool catalogs. The desktop bootstrap caps every response at 8 MiB and polls this snapshot no more often than every 15 seconds. This is not the production sync path. The broker requires a bounded session-list projection containing only identity, topology, lifecycle, short activity, routing receipt references, and monotonic cursors; transcript, tool-catalog, and runtime-detail payloads are fetched on demand.

The 2026-08-09 audit of Ouroboros remote main at bef43c1af confirms that
this projection still does not exist. ouroboros://sessions reconstructs all
session starts and then performs per-session reconstruction and activity
queries without limit, pagination, cursor, delta, or response-byte bounds.
The open dashboard projection work in
Q00/ouroboros#1922 is useful
transactional/gap-fence evidence, but it is not merged, not an MCP contract,
and currently exposes only a bounded recent dashboard picker. Ourocode's
production dependency remains a session_projection_v1 state table updated
in the event transaction plus a cursor-bounded MCP tool returning epoch,
watermark, reset, upserts, and removals.

Core data model

struct AppCore {
    terminals: SlotMap<TerminalId, TerminalSession>,
    sessions: HashMap<SessionId, SessionRow>,
    children: HashMap<SessionId, SmallVec<[SessionId; 4]>>,
    roots: Vec<SessionId>,
    selected: Option<SessionId>,
}

struct TerminalSession {
    pty: PtyHandle,
    terminal: TerminalState,
    scrollback: ByteBoundedScrollback,
    cwd: PathBuf,
    title: String,
}

struct SessionRow {
    id: SessionId,
    parent_id: Option<SessionId>,
    attempt_id: Option<AttemptId>,
    status: SessionStatus,
    label: String,
    last_seq: u64,
    unread: u32,
    surface: SurfaceKind,
    capabilities: SessionCapabilities,
    routing_decision_id: Option<RoutingDecisionId>,
}

enum SurfaceKind {
    Pty(TerminalId),
    Transcript(StreamId),
    None,
}

Ouroboros fanout is projected as a forest because every child has one authoritative parent. Cross-session messages are append-only MessageRecord values, not extra tree edges and not payload copies stored on every session row.

MCP v2 contract

The locally installed Ouroboros 0.50.8 server exposes the primitives needed for the first integration:

  • ouroboros_job_wait with stream=linked for job, execution, lineage, and subagent progress;
  • ouroboros_query_projection and ouroboros_query_events for read-only recovery;
  • ouroboros_session_signal_targets to resolve an exact active attempt;
  • ouroboros_session_signal for audited, guarded delivery;
  • ouroboros_cancel_job and ouroboros_cancel_execution for cancellation;
  • resources including ouroboros://sessions/current and ouroboros://events.

ouroboros_session_signal is not authenticated agent-to-agent RPC. In 0.50.8 it carries user, main-session, conductor, or worker intent to one exact active AC runtime attempt as a resumed follow-up turn. The caller supplies source; there is no authenticated source_session_id, reply address, causal chain, hop count, or loop policy. inform and after_turn are the broadly implemented delivery modes; redirect may fall back to after_turn, and replace is generally unsupported. A queued result proves durable ownership only, not application. The UI must follow applied, completed, rejected, or delivery-uncertain events and must not retry automatically after an uncertain acknowledgement.

A shared-broker smoke on 0.50.8 exposed a remaining authority race: ouroboros_session_signal_targets returned an exact live attempt, but both after_turn and inform requests reached accepted and queued before ending as target_lost_before_delivery. This is a safe failure, not successful steering. Production needs either an expiring target lease minted by discovery or one atomic resolve-and-send operation tied to the execution generation. The client continues to fail closed and refresh targets after rejection; it never retries the message automatically.

The current truthful UI is therefore Steer exact live attempt. A future SessionMessage contract is required before Ourocode exposes Send from session A to session B. That contract needs authenticated sender and recipient scopes, exact attempts, causal correlation, hop/loop policy, delivery and reply receipts, idempotency conflict behavior, expiry, and authorization.

The desktop client negotiates capabilities and records captured fixtures before implementing production actions. It must not guess a child identifier by recursively scanning arbitrary JSON, and it must not claim a queued signal was applied.

Normalized client events have a monotonic cursor. Duplicate events are ignored. A gap or stale attempt fails closed and triggers projection recovery. Authoritative lifecycle events are journaled before projection; repaint hints may be coalesced or dropped.

A session signal records at least source, exact target scope and attempt, expected execution, idempotency key, delivery mode, message digest, reason, contract effect, created time, and acknowledgement state. Client-side hop limits may protect UI workflows, but delivery authority and loop policy remain server-side.

Frugal AI routing is an Ouroboros contract

The standalone consumer boundary and upstream linkage are tracked in
RFC 0003.

Ourocode does not become a model router. Ouroboros owns route selection because it owns the execution profile, available runtimes, tool capabilities, budget, retry history, and outcome evidence. The terminal requests policy or an override, then renders the authoritative decision and its audit trail.

Ouroboros 0.50.8 already projects useful inputs such as suggested_model_tier, suggested tools, verifier capability and focus, maximum branching, tool-catalog fingerprint, runtime backend, estimated tokens, and estimated cost. Those are suggestions and observations, not yet a complete routing receipt. A tier label alone must never be presented as proof of which provider and model actually executed an attempt.

Complexity estimation also belongs to Ouroboros. Before dispatch, it classifies the bounded task shape, required capabilities, external-effect risk, expected context and tool fanout, then combines those features with aggregated historical outcomes from its memory layer: input/output/cache tokens, tool-call count, latency, retry and escalation rate, verifier pass rate, and nullable measured cost. Raw prompts, transcripts, and chain-of-thought are not routing-memory features. A routine repository operation may therefore begin on a Haiku-class candidate when policy allows it, while a failed verifier, capability miss, uncertainty threshold, or token overrun creates an explicit escalation decision.

The authoritative assessment is persisted before the route is bound:

struct ComplexityAssessmentRecord {
    schema_version: u16,
    assessment_id: ComplexityAssessmentId,
    execution_id: ExecutionId,
    session_scope_id: SessionScopeId,
    task_class: TaskClass,
    score: BoundedScore,
    confidence: BoundedScore,
    feature_schema_version: String,
    history_window_id: Option<RoutingMemoryWindowId>,
    reason_codes: SmallVec<[ComplexityReason; 8]>,
    required_capabilities: SmallVec<[CapabilityId; 8]>,
    external_effect_risk: ExternalEffectRisk,
    created_at: Timestamp,
}

The scoring and learning implementation may live in a closed Ouroboros policy module; the protocol, bounded inputs, decision receipt, and audit semantics remain stable. Ourocode does not reproduce the classifier. For agent launches initiated through Ourocode, an optional routing_required workspace policy fails closed when Ouroboros does not return a generation-bound ComplexityAssessmentRecord and RoutingDecisionRecord. This enforcement cannot and does not intercept arbitrary commands typed into the ordinary PTY. Model selection never weakens permission, confirmation, credential, or external-side-effect gates.

Candidate eligibility is capability-gated before cost or complexity ranking. In the shared-broker smoke, failed Codex attempts escalated to a Claude alternate harness even though the isolated MCP profile did not contain claude-agent-sdk; all three ACs then exhausted with alternate_harness_exhausted. A route whose executable, SDK, credentials, protocol version, or required tools are unavailable must be recorded as rejected and must never be selected. This preflight result belongs in the policy fingerprint and decision receipt so a cheap route cannot become an expensive guaranteed failure.

The existing execution.ac.model_routed event is best-effort telemetry after selection, not an effect-boundary decision record. Route-policy candidates contain model, harness, effort, capabilities, stable rejection reasons, and configuration-relative cost_units; those units are neither currency nor actual spend. Later route observations can include verifier outcome, failure, escalation, and retry. The missing authoritative event must be persisted before provider dispatch and include a decision ID, policy fingerprint, bounded eligible/rejected set, override provenance, supersession, and nullable estimates.

The proposed bounded record is:

struct RoutingDecisionRecord {
    schema_version: u16,
    decision_id: RoutingDecisionId,
    execution_id: ExecutionId,
    session_scope_id: SessionScopeId,
    attempt_id: Option<AttemptId>,
    complexity_assessment_id: ComplexityAssessmentId,
    policy_version: String,
    requested: RoutingPolicy,
    selected: SelectedRoute,
    considered: SmallVec<[RouteCandidateSummary; 4]>,
    reason_codes: SmallVec<[RoutingReason; 4]>,
    estimates: RoutingEstimates,
    evidence: RoutingEvidence,
    override_source: Option<RoutingOverrideSource>,
    supersedes: Option<RoutingDecisionId>,
    created_at: Timestamp,
}

struct RoutingPolicy {
    effort: Effort, // auto, low, medium, high, ultra
    max_cost_usd: Option<Decimal>,
    latency_slo_ms: Option<u64>,
    allowed_providers: SmallVec<[ProviderId; 4]>,
    privacy_class: Option<PrivacyClass>,
}

Candidate details are bounded summaries, not provider chain-of-thought. Cost, latency, token, and quality estimates remain nullable unless Ouroboros can attach their source and measurement window. The selected route records the provider, model, service tier, runtime backend, and capability set actually bound. reason_codes explain machine-readable causes such as capability requirement, budget cap, retry escalation, availability fallback, or explicit user override.

Routing events are append-only and generation guarded:

  • routing.snapshot replaces the full source-keyed routing state after connect or recovery;
  • routing.decided binds one decision to an exact execution, scope, and attempt;
  • routing.override_requested, routing.override_accepted, and routing.override_rejected expose the complete acknowledgement path;
  • the same idempotency key and body re-acknowledge, while the same key with a different body fails closed;
  • every stream carries generation and monotonic sequence; a gap or generation change triggers a snapshot rather than client inference;
  • completed, superseded, and rejected decisions retain bounded receipts or tombstones so reconnect cannot resurrect stale authority.

Until Ouroboros publishes these events and a guarded override tool, Ourocode exposes current profile and tier data as read-only suggested metadata. It does not silently switch a model from the client.

Fanout liveness and fairness

Each producer publishes a complete ChannelLivenessSnapshot { source, generation, active_count, channels[] }, not increment/decrement deltas. Each channel includes its stable scope, exact attempt, short description, state, and start time. This makes reconnect and extension reload deterministic and lets the rail show paused work and oldest-active elapsed time without scanning transcript files.

Within one session, event order is FIFO. There is deliberately no global ordering claim across sessions. The MCP client drains complete records fairly across active sessions so one noisy agent cannot starve the rest of the fanout. A durable session ID is never used as ephemeral action authority; signal and cancel operations still require the exact live attempt and execution generation.

Reference study decisions

  • Senpi: adopt explicit open/list/close session semantics, per-session FIFO, full liveness snapshots, and typed model/service-tier fields. Reject its duplicated xterm-plus-decoded-buffer memory shape, directory-wide transcript scanning, process-local liveness bus, and unaudited client-side tier toggle.
  • Gajae-Code: adopt separate durable correlation and ephemeral action authority, explicit resolved/rejected receipts, idempotency conflict rules, revision-bound cursors, generation/sequence resync, and authenticated process incarnation. Reject one WebSocket per session, PID-only liveness, URL bearer tokens, and its Node/Bun/tmux runtime as product architecture.
  • Amp: use its compact effort dial as interaction evidence only. Provider/model mapping is allowed to vary with workspace, availability, and policy, which reinforces that the dial is a request and the server decision is authoritative. Amp is proprietary service behavior, not a code dependency.

Senpi and Gajae-Code are MIT-licensed but carry upstream NOTICE lineage. These protocol ideas are independently expressed here; no implementation is copied. Amp code is not reused.

Memory and latency invariants

All gates are measured in an optimized build with the same font, window size, shell workload, and scrollback policy as the Ghostty comparison. macOS phys_footprint, private dirty memory, heap, and relevant VM regions are recorded; raw RSS alone is not a release claim.

  • provisional app-only idle phys_footprint: at most 50 MiB, excluding shell and Ouroboros child processes;
  • zero-scrollback 120x40 idle session: at most 0.75 MiB incremental retained memory;
  • 32 sessions with 10,000 short lines each: at most 150 MiB app footprint;
  • offscreen session GPU allocations: zero;
  • visible terminal surfaces with GPU buffers: at most four in the first release;
  • one global glyph atlas capped at 32 MiB;
  • scrollback capped by bytes: 8 MiB per session and 128 MiB globally by default, with segmented spill and LRU eviction;
  • warm launch to usable shell prompt p95 under 250 ms;
  • idle CPU under 0.5%;
  • key-to-frame p99 under 8 ms while eight PTYs produce output;
  • fixed runtime thread count independent of session count, apart from shell child processes and explicitly documented system threads;
  • bounded ingress and UI queues with visible overflow counters; no unbounded channel, vector, journal batch, or string clone path.

The provisional absolute targets may be corrected after Gate 0 measurement. The incremental per-session and offscreen-GPU invariants are merge blockers.

Workspace layout

Cargo.toml
crates/
  ouro-terminal-core/
  ouro-pty/
  ouro-session/
  ouro-mcp-v2/
  ouro-store/
  ouro-ffi/
apps/macos/Ourocode/
bench/
  rss_fanout/
  pty_flood/
  scrollback/
fixtures/mcp-v2/

The existing lib/ourocode/terminal, lib/ourocode/dashboard, and BEAM runtime are not linked into the new application. Reusable behavior is copied only as language-neutral fixtures and documented invariants from lifecycle, relationship recovery, replay, cancel, and steering tests.

First vertical slice

  1. Gate 0 — truth before code: measure Ghostty with controlled workloads; capture Ouroboros 0.50.8 tool/resource schemas and linked-stream fixtures; prove or reject the pinned libghostty adapter.
  2. Real terminal: launch one AppKit window containing one real /bin/zsh PTY rendered through Metal. It must run tmux, vim, ssh, Unicode input, copy/paste, scroll, and resize correctly.
  3. Session fabric: connect to MCP v2, recover a session snapshot/projection, follow linked progress, and show every root/child as grouped native tabs and virtualized rows.
  4. Routing visibility: display the server-issued effort/profile, actual bound route, reason codes, estimates, and override receipt without implementing client-side routing.
  5. Honest surfaces: local shells and explicitly attached raw streams use TerminalMetalView; agent output uses TranscriptView unless Ouroboros advertises a raw PTY attachment capability.
  6. Communication: entering a child tab resolves its exact live attempt; steering calls ouroboros_session_signal_targets followed by one guarded ouroboros_session_signal, then displays queued/delivered/rejected audit state. Cancel affects only the selected target.
  7. Recovery: relaunch and reconstruct topology, selected group/tab, cursor, routing receipt, and audit state without duplicating a signal.

Tabs, splits, themes, searchable semantic command blocks, and other platforms follow only after this slice passes correctness and memory gates.

Deterministic QA mode

The release build exposes an explicit, visibly labelled QA launch mode such as --demo fanout-8. It feeds captured MCP fixtures through the real projection and rendering paths; it is not a separate mock dashboard. Luna's Computer Use suite verifies cold launch, real shell input, incremental fanout, focus preservation, targeted signalling, cancel/error states, replay, 32-session virtualization, memory return after close, accessibility labels, and Retina layouts.

Initial acceptance criteria

  • A runnable .app and stable bundle identifier exist.
  • With Ouroboros unavailable, the app is still a correct local terminal.
  • One parent and at least eight children appear incrementally without raw MCP JSON in the UI.
  • Keyboard navigation switches sessions without losing terminal scrollback or input focus.
  • Signal and cancellation actions target exact server-authoritative attempts and expose acknowledgement state.
  • Relaunch restores the latest topology without duplicate sessions or duplicate signals.
  • A 32-session fixture allocates render resources only for visible panes and remains responsive.
  • Automated core tests, captured protocol contract tests, terminal conformance smoke tests, and Luna Computer Use smoke tests pass.

Open questions that block production integration

  1. What stable MCP v2 snapshot/resume cursor guarantees does Ouroboros commit to beyond current query and wait tools?
  2. Does any child session expose raw PTY bytes or an attachable PTY descriptor, or are child surfaces semantic transcripts only?
  3. What is the server-side acknowledgement and loop-prevention contract for inter-session signals?
  4. Which libghostty commit and API surface can be pinned with an acceptable upgrade policy?
  5. What license will Ourocode use before distributing the new desktop binary and third-party notices?
  6. Which Ouroboros event and guarded tool will carry authoritative RoutingDecisionRecord and routing overrides, rather than the current suggested_model_tier hint alone?

Decision

Proceed with Gate 0 and the one-real-PTY vertical slice. Reject a first PR that contains only a polished session mockup: the first runnable desktop artifact must be a terminal.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions