Skip to content

Make headless runtime fleet-safe across Maestro web replicas #78

Description

@haasonsaas

Current Status

From the Maestro remote/runtime review on 2026-04-22.

Reviewed heads:

  • evalops/maestro@ae6e88a22d3a36d34d07c5c507375cfd7fdcd372
  • evalops/platform@7b003938349939052f90ae41241599993a7c06c9

Parent tracker: #76

Problem

The headless runtime service is mature inside one process, but the deployed chart defaults to two Maestro web replicas while runtime ownership, event replay, connections, subscriptions, and controller leases are process-local. A remote client can create a runtime on pod A, then send SSE/POST/heartbeat traffic to pod B and get stale session/connection/subscriber failures.

Evidence:

  • deploy/helm/maestro/values.yaml:1 sets replicaCount: 2.
  • deploy/helm/maestro/templates/deployment.yaml:1-48 has basic probes/env/resources but no sticky routing, preStop/drain, runtime-owner registry, Redis/Postgres runtime routing, or ingress/SSE settings.
  • src/server/headless-runtime-service.ts:2144-2253 stores all runtimes in an in-memory Map and cleans them up from the local process.
  • src/server/handlers/headless-sessions.ts:437-452 resolves /api/headless/sessions/:id by looking up the runtime in the local process map.
  • packages/tui-rs/src/headless/remote_transport.rs:990-1062 bootstraps and subscribes over HTTP, and 1176-1234 retries message POSTs, but there is no server-side redirect/routing contract for runtime owner changes.
  • Platform's agentruntime.v1 proto already models worker leases, checkpoints, waits, heartbeats, and stream cursors (proto/agentruntime/v1/runtime.proto:13-74, 215-327).

Scope

  • Decide the near-term deployment rule: set Maestro web/headless replicas to 1 for hosted remote mode, or require ingress sticky sessions for /api/headless/* and /api/chat/ws.
  • Add a durable runtime-owner/session-routing record keyed by scope_key + session_id, including owner instance id, lease/heartbeat, created/updated timestamps, and routing metadata.
  • Add a server response contract for runtime_owned_elsewhere so clients can reconnect to the correct owner or the ingress can route deterministically.
  • Define whether event fanout/replay stays local with sticky sessions or moves to Redis/NATS/Postgres-backed broker state.
  • Align runtime cleanup/dispose with pod shutdown and Platform AgentRun lease semantics so active runs are cancelled, drained, or re-claimed intentionally.

Acceptance Criteria

  • With more than one web replica, a remote TUI can create a session, subscribe, send prompts/messages, heartbeat, disconnect, and reconnect without random 404/403 stale runtime failures.
  • With no sticky routing configured, the chart refuses multi-replica remote mode or documents/probes the unsupported configuration clearly.
  • Graceful shutdown drains or transfers active headless sessions before pod termination.
  • Tests or smoke scripts cover simulated owner mismatch/reconnect and stale connection recovery.

Related: #76, #72, evalops/platform#671, evalops/platform#780, evalops/platform#765.


Runtime Parity / Conformance Links

Use #85 as the protocol-level acceptance bar: lifecycle, subscribe/events/messages/heartbeat/disconnect, viewer/controller roles, permission request/response, utility operations, workspace-root enforcement, cursor replay/reset, and drain/resume behavior should match across the current TypeScript web/headless bridge and the target Rust runtime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions