Metis · Architecture

The Six-Layer Reuse Stack

A focused page within the Metis Architecture documentation. The full map and every sibling page live in the Architecture hub.

12sections16 minread1diagram1table

On this page

V9 (codename Metis) does not invent a generation platform; it inherits one. The architecture monolith opens by adopting the platform's own recommended target stack — six horizontal layers, from raw model access at the bottom to the consumer experience at the top — and then states the defining constraint of the whole version in one line: "One stack, six layers; V9 is a consumer of all six" (../V9_ARCHITECTURE.md §1). This page is the layer-by-layer accounting the overview defers to: what each layer is, which real in-repo substrate realizes it, and — the part that separates a reuse claim from a reuse fact — exactly how V9 consumes it, and whether it adds anything. The companion subsystem map audits the same code along the vertical axis (which libs/ surface each V9 subsystem builds on); this page reads it horizontally (which platform plane each of the six layers is, and where in that stack V9's ~20% of net-new code actually sits).

The reason the layering matters is that it is the structural form of V9's central bet. If a lesson must be grounded, gated, governed, and reproducible, those are not features you bolt onto a generator — they are the platform planes a generator must run inside. By declaring itself a consumer of all six layers rather than a new vertical, V9 buys grounding from the retrieval/kernel substrates, governance from the V1 Agentic AI Studio envelope, and the agent loop from Iris, and is left to write only the connective tissue (the Atlas spine, the Prometheus forge, the Aletheia gate). The clearest single proof is the one place V9 could most easily have cheated: the agent runtime. V9 ships a governance module whose entire job is to attest, in code, that it added no seventh-or-eighth agent loop of its own (libs/v9/governance/src/iris-loop.ts) — and to file a tracked platform gap when the shared substrate is not yet unified, rather than paper over it. Honest "consumes the layer / files the gap" beats fake "owns the layer."

What ships, honestly#

The stack is a mix of real platform substrate V9 consumes, real V9 code that realizes two of the six layers, and layer descriptions in the monolith that are aspirational framings of how the consumption is wired. This page keeps the three apart.

Implemented and tested (real today). The governance plane (L5) is the shipped @oshun/v9-governance package: the AgentRun-envelope registration with a visible DAG, budget inheritance, kill switches and a replay manifest (agent-family.ts); the seven-gate-suite confirmation (gate-suite.ts); the "no eighth loop" Iris attestation with gap-filing (iris-loop.ts); and the deny-by-default entitlement/age/region/cross-product grant logic (entitlements.ts) — all exercised by governance.spec.ts. The agent runtime (L2) it points at is equally real: the canonical AgenticLoop lives at libs/iris/agents/core/src/agentic/loop.ts with a hard DEFAULT_MAX_ITERATIONS = 24 guard, budget metering before every call, and explicit terminations. The production orchestration (L4) is the real forgeLesson pipeline (libs/v9/prometheus/src/pipeline.ts).

Spec-described / aspirational framings. Three of the monolith's layer captions describe a target wiring that the V9 code does not literally use. L3 is captioned as "domain tool servers on the shared catalog (MCP-shaped): sophia.ground, nyx.ephemeris/render, kalika.cas/solve…"; the MCP machinery is real platform code (@iris/mcp), but V9's forge reaches those capabilities by direct library import, not by MCP tool-server RPC (detail below). L4 is captioned "Prometheus … on Yemaya HTN-planned dynamic pipelines," yet no libs/v9 file imports @yemaya — the HTN planner V9 runs is its own self-contained planLesson (libs/v9/prometheus/src/plan.ts). L6's rendered consumer app is the version's largest open build; libs/v9/experience ships only the tested view-models. None of these are faked; they are honest seams or self-contained realizations where the monolith names a future integration.

Provider-gated seams (fail-loud, not faked). The model plane (L1)@iris/model-routing for routing/cost/fallback and @nous/* for local inference — is real platform code, but no @iris or @nous symbol is imported anywhere in libs/v9. V9 reaches L1 only through the injected writer / embedder / quality-judge boundaries in the pipeline; when one is absent the forge fails loud (LessonWriterNotConfiguredError, libs/v9/prometheus/src/write.ts:37) rather than fabricating a lesson.

The stack, top to bottom#

Layer What it is Realized by (real substrate) V9's relationship
L6 Delivery & Experience Consumer web/mobile; Nyx WebGL + Kalika WASM in-browser; Chiron embodied teacher; the personal Atlas map libs/v9/experience view-models; apps/metis/* (instructor-shaped today) Consumes + the open net-new build. No Bellona/UE dependency for P1 (§8).
L5 Governance plane AgentRun envelopes, budget inheritance, kill switches, approval gates, provenance/ledger, the seven gates @oshun/v9-governance over the V1 Agentic AI Studio + shared release gate V9 net-new code. Registers as an agent family; runs nothing ungoverned.
L4 Production orchestration The lesson forge: staged pipeline, mid-pipeline HITL, failure recovery, cost caps, determinism + caching @oshun/v9-prometheus forgeLesson (self-contained HTN planner) V9 net-new code. Monolith frames it as a Yemaya planner; the import is not present.
L3 Capability tools Domain capabilities (sophia.ground, nyx.ephemeris, kalika.cas, mnemosyne.schedule…) The substrate libs themselves; MCP machinery at @iris/mcp Consumes by direct import, not MCP RPC.
L2 Agent runtime THE agentic loop: tool-call iteration, max-iteration guard, sub-agent spawn, budget, memory @iris/agents-core AgenticLoop (agentic/loop.ts) Consumes; spawns NO new loop. Attested + gap-filed in code.
L1 Model plane Provider gateway, routing, fallback, caching, cost metering; local inference @iris/model-routing; @nous/core, @nous/llm Consumes only through injected seams (no @iris/@nous import in libs/v9).

The rest of this page works bottom-up — the way the stack actually executes, each layer standing on the one below — because that order makes the single most important claim (V9 adds code to only L4 and L5) fall out naturally.

L1 — the model plane#

The bottom plane is raw, governed model access: a provider gateway that routes a request to a model, falls back on failure, caches, and meters cost. In-repo this is @iris/model-routing — a real ModelRouter with cost/quality/latency optimizers, a TaskClassifier, and composable routing policies (createCostConsciousPolicy, createQualityFirstPolicy, …) — plus @nous/core and @nous/llm for local inference where privacy or cost favors it. All three are shipped platform packages with path mappings in tsconfig.base.json.

V9's relationship to this layer is the strictest in the stack: it does not touch it directly. A grep for @iris or @nous imports across libs/v9 (tests excluded) returns nothing. The model plane is reached only through the typed boundaries the Prometheus pipeline injects — the LessonWriter, the WonderEmbedder dense reranker, the MisconceptionAnticipator, and the QualityJudge. That is a deliberate inversion: rather than V9 reaching down to choose a model, the host wires a model-backed writer up into the forge as a dependency. The benefit is that the entire model plane — routing, fallback, caching, local-vs-remote — is the platform's concern, and V9 inherits whatever policy the envelope sets, with no per-product model code to drift.

L2 — the agent runtime (the loop V9 refuses to re-implement)#

This is the layer V9 leans on hardest and the one its honesty discipline most visibly protects, so it earns the deepest treatment.

The real loop. The canonical tool-calling loop every Oshun domain runs on is AgenticLoop in libs/iris/agents/core/src/agentic/loop.ts (package @iris/agents-core, re-exported from its index.ts:226). Its shape is the textbook one — LLM call → tool_use? → validate + execute tools → feed results → repeat — but the load-bearing properties are the guards around that shape:

  • A hard max-iteration guard, DEFAULT_MAX_ITERATIONS = 24 (loop.ts:102), checked at the top of every turn so an agent cannot spin forever.
  • Budget enforcement before every LLM call and every tool execution — a BudgetMeter runs checkBeforeLlmCall() (loop.ts:163) and checkBeforeToolCall() (loop.ts:230) and stops the run the instant a token, cost, or call ceiling is crossed.
  • Explicit, non-lying terminations. The run status is a closed union — completed | truncated | max_iterations | budget_exceeded | content_filtered | aborted (loop.ts:33) — and the loop "never dresses a cut-off run up as a finished one": a max_tokens stop becomes truncated, a content filter becomes content_filtered, a tripped AbortSignal becomes aborted.
  • Cooperative cancellation (an AbortSignal checked before each iteration and before each tool call) and parallel tool execution that still preserves transcript order.

V9 adds none of it. The whole point of L2 in V9 is the negative claim, and V9 makes that claim falsifiable in code. confirmConsumesSharedLoop (libs/v9/governance/src/iris-loop.ts:41) takes the V9 agent-family registration, asserts consumesSharedLoop === true, hard-codes ownAgentLoops = [] ("V9 libs add zero tool-call loops," iris-loop.ts:46), and returns ok only when both hold. The connection from V9's orchestration (L4) down into this loop is the single injected LessonWriter seam in Stage 3 (write.ts:24) — the writer is the place a model-backed Iris run plugs in, and the forge's tests exercise it with a deterministic in-process writer, while production wires a real loop run.

The gap it files instead of faking. The subtle, honest detail: the confirmation's sharedLoopIsSingleSubstrate option defaults to false (iris-loop.ts:47). So unless the caller positively asserts that Iris v2 is already the single agentic substrate platform-wide, the function files a gap — a typed IrisGapReport with finding: 'F2', phase: 'A2' (iris-loop.ts:49), recording that V9 itself owns no loop but the platform-wide consolidation remains open. This is the layer's integrity made executable: V9 will not claim the loop is unified when it cannot prove it; it reports the open consolidation as a tracked finding and moves on. governance.spec.ts tests both branches — ok with no gap when the substrate is declared unified, and a filed F2/A2 gap when it is not.

L3 — capability tools (consumed by import, not by MCP)#

L3 is the menu of domain capabilities the agent can call: in the monolith's words, "domain tool servers on the shared catalog (MCP-shaped): sophia.ground, nyx.ephemeris/render, kalika.cas/solve, mnemosyne.schedule/trace, isis.generate.*, psyche.speak, …". The MCP substrate that caption invokes is real platform code — @iris/mcp ships an mcp-client.ts, tool-discovery.ts, resource-access.ts, and prompt-templates.ts.

Here the page must be precise, because the monolith's framing and the code diverge in a way worth understanding. V9 does not route its capability calls through an MCP catalog. No libs/v9 file imports @iris/mcp. Instead, the forge composes those capabilities as direct TypeScript library imports: @mnemosyne/core for prerequisite reasoning and scheduling, @sophia/semantic-search for BM25 retrieval, @kalika/cosmology and @nyx/constants/@nyx/ephemeris for the STEM kernels. (The exact symbol-by-symbol import ledger is the subsystem map's job.) The difference is not cosmetic: an MCP tool call is a discovered, schema-described, RPC-shaped boundary; a direct import is a compile-time-checked function call. V9 chose the latter for the deterministic, reproducible core, because a lesson that must be seed-reproducible benefits from in-process, type-checked capability calls over network-shaped tool RPC. The MCP-shaped framing is best read as how a future multi-tenant capability catalog would expose these same engines — not as the wiring V9's forge uses today. The capabilities are real and reused; the transport is direct binding, not a tool server.

L4 — production orchestration (the lesson forge)#

L4 is the dynamic pipeline that turns a wonder into a gated artifact: staged execution, mid-pipeline human-in-the-loop, failure recovery, cost caps, and the determinism + caching that make a lesson reproducible. In V9 this layer is @oshun/v9-prometheus, and forgeLesson (libs/v9/prometheus/src/pipeline.ts:111) composes Stages 0–8 end-to-end — resolve (Atlas) → ground/solve-first → plan (HTN) → write (the injected Iris boundary) → build explorable (Hephaestus) → assess & schedule (Mnemosyne) → gate → compile & cache. A lesson that fails any gate is returned blocked: true with the failing gate ids and no artifact is compiled, delivered, or cached (pipeline.ts:235).

The honest nuance is the planner. The monolith captions L4 "on Yemaya HTN-planned dynamic pipelines," but no libs/v9 file imports @yemayaplanLesson (libs/v9/prometheus/src/plan.ts) imports only @oshun/contracts and is a self-contained, deterministic HTN-style planner over the verified skeleton. So L4 is realized by V9's own orchestration code, sharing the HTN discipline the monolith names without taking a dependency on the Yemaya planner library. This is a place V9 wrote connective tissue rather than consuming a substrate — which is exactly why L4 is one of only two layers that carry net-new V9 code. The full stage-by-stage treatment is in ./prometheus-lesson-pipeline.md.

L5 — the governance plane (V9's other net-new layer)#

L5 wraps everything below it in the V1 Agentic AI Studio envelope: every lesson generation is an AgentRun with a visible DAG, budget inheritance, kill switches, approval gates, a provenance bundle, and an immutable ledger — and the seven release gates are enforced here. This is the second layer V9 realizes in code, as @oshun/v9-governance. Because the delivery-and-cross-cutting page covers the governance and cross-cutting libraries in full, this section stays at the layer altitude — what makes L5 a real plane rather than a promise.

registerV9AgentFamily (libs/v9/governance/src/agent-family.ts:73) builds the registration deterministically from the real pipeline stage graph. The governable DAG has eight nodes for stages 0,1,2,3,5,6,7,8 — note that media realization (stage 4) is intentionally not a separately governed node — and each node (after the first) dependsOn the previous one, so the run is a visible chain, not a black box (STAGE_TITLES, agent-family.ts:55; the test pins the stage list exactly). Budget inheritance is concrete: an inherited ceiling is split into per-stage caps (agent-family.ts:86), and validateBudgetInheritance (agent-family.ts:111) verifies the caps sum to ≤ the ceiling — a child run cannot out-spend its parent envelope. Two kill switches are registered with typed fallbacks (fallbackBehaviorpause | skip_stage | text_only | manual_review): a family-scoped switch that pauses, and a provider-scoped switch that degrades to text_only. A replay manifest names the fields that make a run reproducible — cacheKey (deterministic inputs), skeletonHash (the plan), ledgerEntryId (the evidence) — with replayable: true.

L5 also owns access. checkLessonAccess (entitlements.ts:52) is a deny-by-default decision over a single account's age band, region, and tier, where adult-gating and region blocks take precedence over the paywall — a sensitive lesson cannot be unlocked by a subscription if the learner is under-age. And it encodes the portfolio's cross-product story literally: CROSS_PRODUCT_GRANTS (entitlements.ts:91) maps a V2 racing podium finish to the applied-physics lesson, a meditation 7-day streak to the neuroscience-of-attention lesson, and a V8 case solved to the logic-and-deduction lesson, and applyCrossProductGrant adds that grant to the learner's profile. The seven gates this plane enforces are two real framings of the same discipline (an architecture-§ numbering and a ledger-§J numbering) — both run on the shared @oshun/content-release-gates service, never a bespoke per-product checker, which is the concrete meaning of "no eighth loop" at the gate level. The divergence is documented in ./seven-gates-and-aletheia.md.

L6 — delivery & experience#

The top layer is the consumer surface: a web/mobile app that streams a lesson, runs the Nyx WebGL planetarium and the Kalika physics in the browser, hosts the embodied Chiron teacher, and renders the learner's personal Atlas map. The deliberate architectural decision here is the non-dependency on Bellona: V9's P1 experience is web-first and does not ride the Unreal cook path (../V9_ARCHITECTURE.md §8), because the two heaviest "wow" surfaces are already web-native and only the game-bridge explorables would need the engine bridge — so they are scoped to P2/P3 and do not block P1. In code, L6 is the layer with the most ground still ahead of it: libs/v9/experience ships the tested view-models the screens would bind to (buildWonderFrontDoor, buildLessonPlayer, the personal star-map), but the rendered consumer-first app is, by the architecture's own admission (§7), "the largest net-new build." The fuller account is in ./delivery-and-cross-cutting.md.

Where the net-new code actually lives#

Laid out as six layers, the version's "~20% net-new over ~80% composed" claim stops being a slogan and becomes a map you can point at. Of the six planes, V9 writes material new code in exactly two — L4 (the Prometheus forge) and L5 (the governance package) — plus the three net-new contracts in libs/contracts/src/v9 that the whole stack speaks. L1, L2, and L3 are consumed: the model plane through injected seams, the agent loop by attested reuse, the capability tools by direct import. L6 is the open build. That distribution is the architecture working as designed — the new code is concentrated where the product's identity lives (forge and govern a grounded lesson), and absent everywhere the platform already had a better answer.

flowchart TB subgraph L6["L6 Delivery & Experience — CONSUMES + open build"] EXP["libs/v9/experience view-models<br/>(rendered app = largest net-new)"] end subgraph L5["L5 Governance plane — V9 NET-NEW CODE"] GOV["@oshun/v9-governance<br/>AgentRun DAG · budget inheritance · kill switches<br/>replay · entitlements · seven gates"] end subgraph L4["L4 Production orchestration — V9 NET-NEW CODE"] PROM["@oshun/v9-prometheus forgeLesson<br/>self-contained HTN planner (no @yemaya import)"] end subgraph L3["L3 Capability tools — CONSUMES by direct import"] CAP["@mnemosyne/core · @sophia/semantic-search<br/>@kalika/* · @nyx/* &nbsp;(MCP substrate exists, unused by V9)"] end subgraph L2["L2 Agent runtime — CONSUMES, spawns NO new loop"] IRIS["@iris/agents-core AgenticLoop<br/>max-iter 24 · budget meter · explicit terminations"] end subgraph L1["L1 Model plane — CONSUMES via injected seams only"] MODEL["@iris/model-routing · @nous/*<br/>(no @iris/@nous import in libs/v9)"] end L6 --> L5 --> L4 --> L3 --> L2 --> L1 PROM -. "injected LessonWriter (Stage 3)" .-> IRIS GOV -. "confirmConsumesSharedLoop → files F2/A2 gap if not unified" .-> IRIS

The design rules that make this SOTA, not merely consolidated#

The monolith is explicit that stacking six layers is not, by itself, an achievement — a stack can be a pile. Four rules (inherited from the platform report and specialized for learning, §1) are what make it an architecture, and each has a real anchor in the layers above:

  • Mandatory gates before any human gate. Everything generated passes grounding + accuracy + policy + canon checks before a human is asked to review, enforced at L5. The block is hard, not advisory: forgeLesson returns blocked with no artifact when any gate fails (pipeline.ts:235).
  • Agents are products with visible DAGs, cost ledgers, and replay. Not a metaphor — the L5 registration is a DAG of the pipeline stages, the BudgetMeter in the L2 loop is the cost ledger, and the replay manifest names the reproducibility fields (agent-family.ts:100).
  • Determinism where it matters. A lesson is a reproducible artifact: same inputs + seed ⇒ same artifact and the same hashes, which is simultaneously a provenance guarantee and the cost lever behind L4's profile-class cache.
  • Champion–challenger everything. The cross-cutting package carries the champion–challenger and anti-metric machinery so a new generation strategy is measured against the incumbent before it ships.

Edge cases and failure modes specific to the layering#

The layered design has its own failure surface — distinct from the per-subsystem failures the overview catalogs — and the system is built to report rather than fabricate at each seam:

  • Substrate not yet unified. If the platform's single-agentic-substrate consolidation is incomplete, L5 does not assert it is done; it files the F2/A2 gap (iris-loop.ts:49) and the ok/gapFiled verdict reflects reality.
  • Missing model plane. With no LessonWriter injected (no L1 provider wired), Stage 3 throws LessonWriterNotConfiguredError (write.ts:37) — the forge fails loud rather than emitting an ungrounded lesson.
  • Budget exhaustion mid-run. The L2 loop terminates with the explicit budget_exceeded status and a BudgetViolation payload; it does not return a partial answer dressed as complete.
  • Over-allocated envelope. A registration whose per-stage caps exceed the inherited ceiling fails validateBudgetInheritance (agent-family.ts:111) before any run starts.
  • Capability-shape drift. Because L3 is consumed by direct import rather than a schema-described MCP boundary, a breaking change in a substrate is caught at compile time by V9's type-checker — the trade-off opposite to the runtime-only failure of the one structural adapter noted in the subsystem map.