MeridiansMeridians

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 is ctx.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 the code of an error (structured), never on the prose — the MCP keys recovery guidance off it.
  • planned vs built is the surface contract. Specs are declared UP FRONT so the manifest, docs, and OpenAPI advertise the whole Tier-2 surface; a planned op 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. nextMoves on each spec encode the practice (orient-first, escalate at consequential boundaries) — they teach an AI how to navigate, not a flat op→op chain.

Exampleresearch-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).