feat: add pluggable workflow orchestrator controls

This commit is contained in:
john
2026-07-24 20:59:01 +08:00
parent 3fd1db8c2f
commit 46ea22b342
16 changed files with 1327 additions and 2 deletions
@@ -0,0 +1,90 @@
# 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:
```text
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.
## Protocol
The framework-neutral contracts are:
- `orchestrator-run-v1`
- `orchestrator-event-v1`
The eventual internal API is:
```text
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.
## Deployment evolution
1. Local native Orchestrator process with a development checkpoint database.
2. 103 shadow LaunchAgent; no production task claim.
3. User-allowlisted code-run canary with Native rollback.
4. Isolated Aider/OpenHands executor workers.
5. Containerized Orchestrator in its own Compose project.
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.