Operation lifecycle — a Tier-2 op, dispatch to commit [Flow]
From the Meridians Wiki · Public · Maintained · joint
A Tier-2 operation is the async, streamed machinery that precedes a mutation: it does the I/O (LLM calls, Firecrawl, media render), streams progress, and ends by dispatching reducer actions. 111 ops across 35 handler modules, each a first-class Swagger-style contract in one catalog. This is what the engine-pipeline's "AI" box expands into.
flowchart TB
Caller(["caller — UI GeneratePanel · MCP run_op · Pilot · scheduler"])
Caller -->|"dispatch(op, cls, id, params)"| Control
Control["**control** — resolve spec by op id<br/>gate on cls/id + status"]
Control -->|"status: planned"| P501(["501 — declared, not yet built"])
Control -->|"status: built"| Handler
subgraph Handler["handler runs — emits an OpEvent stream"]
direction TB
Engine["engine work<br/>callGenerate · firecrawl · render"]
Engine -->|"emit"| Ev
Ev["**OpEvent**: token · reasoning ·<br/>progress · warning"]
end
Handler -->|"ctx.commit(action)"| Commit["**commit** — one or more reducer actions<br/>via applyLocalMutation"]
Commit -->|"OpEvent: commit {action · cls · id · version}"| Stream
Handler -->|"terminal"| Done["OpEvent: **done** {version · result}<br/>— or **error** {message · code}"]
Stream(["SSE echo → cache adopts"]) --> UI(["UI updates live"])
Done --> Next(["**nextMoves** — the spec suggests<br/>the next affordance(s)"])
Invariants
- The op is a caller, not a writer. Every state change still goes through
applyLocalMutation— the op's terminus isctx.commit(action), which fires the same reducer + echo as the UI.commits: [...]in the spec declares exactly which actions it may land. - The event stream is a small closed union.
token · reasoning · progress · commit · warning · done · error. A consumer branches on thecodeof an error (structured), never on the prose — the MCP keys recovery guidance off it. plannedvsbuiltis the surface contract. Specs are declared UP FRONT so the manifest, docs, and OpenAPI advertise the whole Tier-2 surface; aplannedop 501s with a clear message until its handler is bound. This keeps the headless surface honest about what exists.- One catalog, many transports. UI, MCP, Pilot, and the scheduler all dispatch the same
OPERATION_SPECS.nextMoveson each spec encode the practice (orient-first, escalate at consequential boundaries) — they teach an AI how to navigate, not a flat op→op chain.
Example — research-run commits START/FINISH_DOMAIN_PROGRAM_RUN, UPSERT_STREAM, UPDATE_PROJECTION, LOG_RESEARCH_RUN, UPSERT_ARTICLE, ADD_RESEARCH_BRIEF, ADD_DOMAIN_PROGRAM_EVENTS; engine runResearchRun + firecrawl; nextMove → run the Opinion pass.
Where it lives: src/lib/server/ops/{types,catalog,control,commit,next-move}.ts, src/lib/server/ops/handlers/ (35 modules). MCP surface: mcp-runtime · mcp-chains. Regenerate the MCP manual after any spec change (npm run gen:mcp-manual).