MeridiansMeridians

Engine pipeline — data · AI · persistence [Flow]

From the Meridians Wiki · Public · Maintained · joint

How a change flows from the UI through the deterministic engine and AI, and how state lands in the canonical record and mirrors to the browser cache. The load-bearing rule: the on-disk record is canonical; IndexedDB is a rebuildable projection.

flowchart TB
    UI(["UI — views · panels · hooks"])
    Store["**Store** — useStore() · AppState · ~110 actions"]

    UI <--> Store
    Store -->|"derive"| Engine["**Deterministic engine** (lib/core)<br/>forces · thread-log · streams · merges ·<br/>graphs · game-theory · curriculum · search"]
    UI -->|"context → prompt"| AI["**AI** (lib/engine)<br/>build → callGenerate → parse/repair"]
    Operator["operatorProfileStore (core)<br/>signed-in member's name + bio"] -->|"&lt;operator&gt; block prepended<br/>by EVERY context builder"| AI
    AI --> Routes["server provider boundaries<br/>modelFor(category) · named budgets"] --> Ext(["fleet-selected inference<br/>research · embeddings · media"])

    Headless(["Program · MCP · Pilot · loops"]) -->|"same queries / actions / ops"| Rec

    Store -->|"optimistic apply,<br/>then persist"| Rec
    subgraph Rec["**CANONICAL RECORD** — master daemon owns it"]
        direction TB
        Mut["/mutate — applyLocalMutation:<br/>gate → reduce → persist → attribute → echo"]
        FS["fs-store — atomic write · version bump"]
        SSE["/stream — SSE echo"]
        Mut --> FS --> SSE
    end
    SSE -->|"use-record-stream:<br/>adopt + saveProjection (echo-suppressed)"| Store
    Store -.->|"shadow-write<br/>(debounced)"| IDB[("IndexedDB<br/>rebuildable projection")]

Invariants

  • One writer. State changes only through reducer actions; server writes go through applyLocalMutation (gate → reduce → persist → attribute → echo). Components, ops, loops, and MCP tools are callers, not alternate writers.
  • Record wins. If record and cache disagree, the record wins. Boot reconcileRecord compares by updatedAt: cache ahead → adopt to fs, record ahead → hydrate to cache (cache-coherence).
  • The reducer is deterministic, clock pinned to the caller's now, so optimistic and authoritative applies are byte-identical. Headless commits (MCP, the Program) reach the UI through the same SSE echo.
  • Forces are derived from deltas, never authored. Never re-parse generated prose to recover canonical state.
  • Every LLM call funnels through callGenerate and is logged with its resolved actor/caller; model selection and token budgets come from the fleet profile rather than hardcoded call sites.
  • Every generation context is grounded in the operator's identity. An ambient operatorProfileStore (the third parallel of identityStore / directorMemberStore) carries the signed-in member's name + self-authored bio — pushed by the client, resolved per-op by the master — and a pure operatorContextBlock() prepends one <operator> block in every context builder. Zero call-site changes; an empty profile stays silent. This is what stops a first-person prompt (a health log) from inventing an author.

Where it lives: engine = lib/core/{forces,graph,program,learning,...}; AI = lib/engine/{ai,prompts}; record = src/app/api/local/* + lib/server/record/; cache = lib/client/cache/ + IndexedDB meridians-instance. Full store/action detail in PERSISTENCE.md.