Files
memind/docs/architecture/memind-orchestrator-boundary.md
T

4.4 KiB

Memind Workflow Orchestrator Boundary

Decision

The Orchestrator starts in the Memind repository but is designed as an independently deployable bounded context. Source colocation does not authorize in-process execution inside Portal.

The stable boundary is:

Memind Control Plane
  -> versioned Orchestrator API / events
Workflow Orchestrator
  -> versioned Executor Gateway
Executor adapters
  -> Goosed / Aider / OpenHands

LangGraph remains an internal workflow engine implementation. Memind must not depend on LangGraph graph, checkpoint, command, or interrupt types.

Ownership

Data Owner
User, authorization, billing, product run Memind
Workflow node state, checkpoint, interrupt Orchestrator
Goosed session Goosed plus Session Broker mapping
Executor job Executor Gateway / executor adapter
Workspace and deliverable Workspace / Artifact / MindSpace services
User-visible progress and audit Memind run-event projection

The Orchestrator must not read or write Memind user, billing, capability, or provider-key tables. A shared PostgreSQL instance is acceptable during the single-host phase, but the Orchestrator must use its own database and database credentials.

Runtime modes

memindadm owns the versioned routing configuration:

Mode Behavior
off Native Agent Run only
shadow Native executes; LangGraph observes and compares decisions
canary Explicit users and deterministic percentage may use LangGraph
active Workflow-allowlisted tasks use the primary engine

All modes support an explicit Native fallback. The environment kill switch MEMIND_ORCHESTRATOR_KILL_SWITCH=1 overrides admin configuration and forces Native selection.

The initial workflow allowlist contains only code-run-v1. Ordinary chat and existing Goosed session traffic remain outside the Orchestrator path.

Phase 2 implements only the off and shadow execution semantics. Although Canary and Active can be configured and evaluated by the control plane, the Portal does not hand execution ownership to LangGraph yet. This prevents an administrative configuration mistake from creating two task executors.

Protocol

The framework-neutral contracts are:

  • orchestrator-run-v1
  • orchestrator-event-v1

The implemented internal API is:

POST /v1/runs
GET /v1/runs/:id
POST /v1/runs/:id/resume
POST /v1/runs/:id/cancel
GET /v1/runs/:id/events?after=<cursor>

External state changes must use an idempotency key derived from run, graph version, node, and attempt. Graph state stores resource references and decisions, not API keys, large logs, binary artifacts, or absolute production paths.

The Phase 2 graph has three deterministic nodes:

validate_run -> build_plan -> finalize_run

It requires policy.executionMode=observe-only and policy.sideEffectsAllowed=false. It projects the Native executor boundary but cannot call Goosed, Aider, OpenHands, Tool Gateway, or the filesystem.

Failure isolation

Portal invokes Shadow only after the product run and its queued event have been committed. The observation is scheduled without awaiting it. A timeout or failure:

  1. cannot reject or delay createRun;
  2. cannot mutate the Native run status;
  3. is projected as workflow_shadow_failed;
  4. remains removable by deleting the observer wiring and changing only the service URL boundary.

Successful observations are projected as workflow_shadow_completed; graph checkpoints stay in the Orchestrator-owned PostgreSQL database.

Both terminal projection events contain bounded observation latency. The Memind-owned observability service aggregates those events and may join them to the product run status. Only per-run detail calls cross the service boundary to read LangGraph checkpoint state and node events.

Deployment evolution

  1. Local native Orchestrator process with an explicit MemorySaver for debugging.
  2. Colima Compose with an isolated PostgreSQL checkpoint database.
  3. Approved server-side shadow service; no production task claim.
  4. User-allowlisted code-run canary with Native rollback.
  5. Isolated Aider/OpenHands executor workers.
  6. Move the same service contract to Linux or Kubernetes when horizontal scaling or independent ownership becomes necessary.

The Orchestrator must not share the Goosed Compose lifecycle or mount the Docker socket. It calls MindSpace through its service API and calls Goosed through the existing proxy boundary.