# Web-First Delivery & Cross-Cutting Concerns

This page covers the bottom of V9's six-layer stack — the **delivery layer
(L6)** where a forged lesson actually reaches a learner — together with the
**cross-cutting concerns** that wrap every lesson regardless of which subsystem
produced it: governance, determinism, cost discipline, localization,
accessibility, identity, and entitlements. The throughline is a single scoping
decision that the rest of the version is built around: **V9's P1 experience is
web-first and deliberately does not depend on the Bellona Unreal cook path.**
That is not an accident or a shortcut — it is the choice that lets V9 ship
without inheriting the portfolio's single biggest blocker (the headless-engine
content pipeline), because the two heaviest "wow" surfaces are already
web-native and the one surface that needs the engine is the one V9 sequences to
P2/P3. The concrete code surface for this layer is three focused packages —
`@oshun/v9-experience` (the consumer view-models), `@oshun/v9-governance` (the
AgentRun envelope, the seven-gate confirmation, the entitlement graph), and
`@oshun/v9-cross-cutting` (determinism, cost, accessibility, champion-challenger
gating, the anti-metric guard, localization routing, LMS interop) — plus the
embodied-delivery fallback in `@oshun/v9-chiron`.

The reason it is shaped this way is the same reason the whole architecture is
shaped the way it is: V9 is an integration version, so the "delivery and
cross-cutting" story is overwhelmingly one of _consuming_ the shared platform
plane rather than rebuilding it. Every forge runs as an AgentRun under the V1
Agentic AI Studio envelope; entitlements come from the single Oshun account
graph; localization reuses the Metis lecture-package dubbing; accessibility
reuses the Metis captioning floor. What V9 adds is the _thin connective tissue_
that binds a `V9LessonArtifact` to those shared services — and, importantly, a
set of **runtime refusals** (an ungated lesson cannot be played, a banned
engagement metric cannot be set as an optimization target, a non-deterministic
forge cannot be promoted) that make the discipline checkable rather than
aspirational. This page is the deep companion to the brief "Delivery (L6) and
cross-cutting" section of the architecture overview; the section hub is
[../V9_ARCHITECTURE.md](../V9_ARCHITECTURE.md).

## What ships, honestly

The three packages this page documents are **real and tested**: 35 passing tests
across `libs/v9/experience/src/experience.spec.ts` (8), `cross-cutting.spec.ts`
(17), and `governance.spec.ts` (10), all green when run directly with vitest.
The **view-models** (`buildWonderFrontDoor`, `buildLessonPlayer`,
`buildPersonalAtlasMap`, `checkBillingAccess`), the **governance** registration
and confirmations (`registerV9AgentFamily`, `confirmSevenGateSuite`,
`confirmConsumesSharedLoop`, `checkLessonAccess`), and the **cross-cutting**
machinery (`compareArtifactDeterminism`, `CostLedger`, the a11y manifest, the
two-proportion z-test, the anti-metric guard, the LMS-interop emitters,
`planLocalization`) are all present, exercised, and back the claims in the
monolith's §8/§9.

Three honest caveats separate that real core from the more ambitious framing in
the monolith:

- **The rendered consumer application is not shipped.** `@oshun/v9-experience`
  ships the tested _data and logic_ the screens bind to, not the screens. The
  `apps/metis/{web,mobile}` targets exist but are "currently instructor/admin-
  shaped"; the consumer-first rebuild is, by the architecture's own admission,
  "the largest net-new build" and is open. Treat the front door, lesson player,
  and star-map as view-models with passing tests, not as a deployed product.
- **One substrate claim in the monolith is inaccurate, and one is over-stated.**
  §8 says "Nyx ships a WebGL/**Three.js** renderer." The Nyx renderer is real
  but it is **WebGPU/WebGL** (`@nyx/renderer-core`, dev-typed against
  `@webgpu/types`) — there is no Three.js dependency or `import … from 'three'`
  anywhere in `libs/nyx/`. Separately, §8 says "Kalika ships a `wasm-bindgen`
  build … computed physics runs in the browser." A `wasm-bindgen` build _does_
  exist, but it is the **CAS (computer-algebra) engine**
  (`libs/kalika/cas-engine/crates/kalika-cas-wasm`, built via `wasm-pack` for
  `nodejs` and `web` targets in `libs/kalika/cas-engine/scripts/build-wasm.sh`).
  The kernels the V9 explorables actually call — `@kalika/symplectic` and
  `@kalika/cosmology` — are pure TypeScript with **no** WASM crate, and the V9
  explorable libs run them _in-process_ (Node/TS), not through a browser-WASM
  build. The computation is real; the in-browser WASM packaging is the planned
  delivery, not what these libs exercise today.
- **Localization and LMS interop are P3 backlog surfaces.** The routing planner
  and the seven standards emitters are real, spec-conformant code with tests,
  but they are the _plan and the artifacts_; the dubbing/translation themselves
  are an injected Metis/Nous boundary, and institutional rollout is sequenced to
  P3.

Where something is provider-gated, it is a **fail-loud injected seam**, not a
fake: the Nous local-inference tier in localization, the embodied Chiron voice/
avatar runtime, and the no-eighth-loop Iris confirmation all _refuse_ rather
than fabricate (an unavailable tier is disclosed and downgraded; an incomplete
platform consolidation files a gap). Honest "planned/gated" beats fake
"shipped."

## The deliberate non-dependency on Bellona

The architecture's load-bearing delivery decision is negative: V9 P1 does
**not** take a dependency on the Bellona headless Unreal cook path. The
justification is that the surfaces a learner would most want to be "immersive"
are already computable in the browser:

- The **Nyx live cosmos** is a real WebGPU/WebGL renderer plus a TS client
  (`@nyx/client`), so the sky runs client-side.
- The **Kalika physics** the lessons teach is computed by deterministic kernels
  (the Friedmann solver in `@kalika/cosmology`, the velocity-Verlet integrator
  in `@kalika/symplectic`), which the Hephaestus explorables drive directly.

Only the **game-bridge explorables** — learn-by-playing _inside_ the V2–V8 game
substrates — need the engine cook path, and that work is shared with V8 and
sequenced as a P2/P3 motion. This is why, of the three explorable _kinds_ in the
contract, only `computed-kernel` has a shipped builder: the web-native kinds are
P1 and the engine-bridged kind is deferred. The embodied teacher (Chiron) does
not need Bellona either — it runs through Psyche's real-time avatar/voice
runtime (the `libs/psyche/avatar-*` libraries the Metis live-voice tutoring
service provisions), and degrades gracefully when that runtime is unavailable
(see _Embodied delivery_ below). The net effect is that V9's heaviest
experiences are either pure-client (Nyx), pure-deterministic-compute (Kalika),
or a managed fallback (Chiron) — none of which block on the engine pipeline. The
deeper accounting of which surface reuses which substrate is in
[./six-layer-reuse-stack.md](./six-layer-reuse-stack.md) and
[./subsystem-map-and-reuse-ledger.md](./subsystem-map-and-reuse-ledger.md).

## The experience layer (L6): tested view-models

`@oshun/v9-experience` is four pure functions, each projecting a domain object
into exactly the data one consumer screen needs. They are deliberately _not_
React/UI — they are the testable seam between the forge and the renderer.

**The ask-a-wonder front door.** `buildWonderFrontDoor`
(`libs/v9/experience/src/wonder-front-door.ts:43`) takes a free-text wonder and
an `AtlasStore`, calls the real `resolveWonder`
(`libs/v9/atlas/src/wonder-resolution.ts:86`, Sophia BM25 + optional Nous dense
rerank + Mnemosyne mastery-scoped prerequisite frontier), and returns the
disambiguated concept, the ranked candidates, the **scaffolding depth**
(`prerequisiteCount`), the prerequisite-first `conceptSet` a lesson would teach,
and a few "surprise me" frontier suggestions. The entry point is a _single
wonder_, not a course catalog — that is the whole product thesis. The frontier
suggestions come from `suggestFrontier` (line 80), which walks the Atlas edges
for concepts that build _on_ something in the teaching set
(`inSet.has(edge.from) && !inSet.has(edge.to)`) — the next thread a learner
almost knows. Crucially, an unresolvable wonder does **not** throw at the UI:
the `resolveWonder` call is wrapped in a try/catch (line 66) that converts a
`WonderUnresolvedError` into a calm `unresolved: true` state with a null
resolution, so the screen can show "we couldn't find that yet" rather than an
error boundary.

**The lesson player.** `buildLessonPlayer`
(`libs/v9/experience/src/lesson-player.ts:61`) projects a gated artifact into
the four panels the player shows: the grounded narrative (beats + Socratic
turns + addressed misconceptions), the **"show your sources" transparency
panel** (every claim with its Sophia pins, credibility, and epistemic label),
the explorable canvas (each kernel's params and measured `reachable` success
state), and the active-recall retrieval checkpoint. Its first line is a refusal:
`if (!isV9LessonPublishable(artifact)) throw new UngatedLessonError(artifact.id)`
— the player **cannot be built for a lesson whose seven gates did not all pass**
(`isV9LessonPublishable`, `libs/contracts/src/v9/lesson.ts:246`). The
transparency panel is not a UI nicety bolted on; it is a direct projection of
`groundTruth.claims`, so what the learner sees as "sources" is the same gated
skeleton the forge verified. The contract anatomy is in
[./lesson-artifact.md](./lesson-artifact.md).

**The personal Atlas star-map.** `buildPersonalAtlasMap`
(`libs/v9/experience/src/mastery-map.ts:40`) renders "what you know / what's
next" as a literal sky: a mastered concept is **bright** (brightness = the
Mnemosyne mastery ordinal over the six Dreyfus levels, so `master` = 1.0), an
unexplored one is **dark** (brightness 0), and a **frontier** star is a dark
concept whose every prerequisite is already mastered — the inviting next step
(line 54, computed from `atlas.prerequisitesOf(node.id).every(isMastered)` with
the mastered floor pinned at `intermediate`, line 32). It is the visual dual of
the front door's scaffolding depth, and it is tested against a two-node atlas
where mastering the basics lights up the dependent concept as the single
frontier star.

The honest framing again: these four functions are _tested data_, and the
screens that bind to them are the open consumer build. See the broader picture
in [./overview.md](./overview.md).

## Identity, entitlements, and billing

Access to a lesson is a **deny-by-default** decision over one Oshun account's
age/region attributes plus its subscription tier and grants. `checkLessonAccess`
(`libs/v9/governance/src/entitlements.ts:52`) evaluates a `V9LearnerProfile`
against a `LessonAccessRequest` and returns a discriminated `AccessVerdict`. The
precedence is deliberate and tested: **age-gating and region-blocks win over the
paywall**. A `requiresAdult` lesson is refused for a non-adult with a `age-gate`
CTA (line 56) _before_ any premium check; a `blockedRegions` hit returns a
`region-block` CTA (line 63); only then does the premium branch consider tier
and grants (line 70). This ordering means a minor is never offered an upgrade
path to buy their way past an age gate.

The cross-product story is real here, not hand-wavy. `CROSS_PRODUCT_GRANTS`
(`entitlements.ts:91`) is a concrete map from a portfolio achievement to the
specific premium lesson it unlocks — `v2-racing:podium-finish` →
`applied-physics-vehicle-dynamics`, `meditation:7-day-streak` →
`neuroscience-of-attention`, `v8-detective:case-solved` → `logic-and-deduction`
— and `applyCrossProductGrant` (line 98) returns a profile carrying the new
grant, which then satisfies `checkLessonAccess` with `via: 'grant'`. The
governance test walks exactly this path: a racing podium finish grants, and then
unlocks, the physics lesson.

`@oshun/v9-experience` layers one more thing on top: the **free-tier quota**.
`checkBillingAccess` (`libs/v9/experience/src/billing-gating.ts:36`) first calls
`checkLessonAccess`; if entitlement denies (age/region/premium), that denial
wins unchanged (line 43); otherwise, _only_ for a free learner on a free lesson,
it applies `DEFAULT_FREE_TIER_QUOTA` (5 lessons per period, line 21) and returns
a `cta: 'upgrade'` paywall when the quota is exhausted, or the verdict plus a
`quotaRemaining` count when it is not. The separation of concerns is clean: the
entitlement graph owns _eligibility_, the experience layer owns _metering_.

```mermaid
flowchart TD
    A[Learner opens a lesson] --> B{requiresAdult and not adult?}
    B -- yes --> AG[deny · cta=age-gate]
    B -- no --> C{region blocked?}
    C -- yes --> RB[deny · cta=region-block]
    C -- no --> D{premium lesson?}
    D -- yes --> E{tier=plus or holds grant?}
    E -- no --> UP[deny · cta=upgrade]
    E -- yes --> OK1[allow · via=tier or grant]
    D -- no --> F{free tier and free lesson?}
    F -- yes --> G{quota remaining?}
    G -- no --> UP2[deny · cta=upgrade · quotaExhausted]
    G -- yes --> OK2[allow · via=free · quotaRemaining]
    F -- no --> OK3[allow · via=free]
    OK1 --> H{embodied tier available?}
    OK2 --> H
    OK3 --> H
    H -- avatar --> LA[live_avatar]
    H -- voice only --> LV[live_voice · disclose downgrade]
    H -- neither --> TX[text floor · disclose downgrade]
```

## Governance: every forge is a governed AgentRun

V9 does not run ungoverned. `registerV9AgentFamily`
(`libs/v9/governance/src/agent-family.ts:73`) builds, deterministically, the
registration that places V9 under the V1 Agentic AI Studio envelope. Its DAG is
projected straight from the real Prometheus stage graph — stages 0, 1, 2, 3, 5,
6, 7, 8 (resolve, ground/solve-first, plan, write, build explorable, assess &
schedule, gate, compile & cache) — each node depending on the previous one so
the chain is _visible_, and each tagged with whether it requires an evidence
artifact (`STAGE_TITLES`, line 55). The envelope carries **budget inheritance**
(per-stage caps that must sum to ≤ the inherited ceiling, validated by
`validateBudgetInheritance`, line 111), **kill switches** at family and provider
scope with declared fallback behaviors (`pause`, `text_only`), and a **replay
manifest** that names the determinism fields (`cacheKey`, `skeletonHash`,
`ledgerEntryId`). The registration also asserts `consumesSharedLoop: true`.

That last flag is enforced, not decorative. `confirmConsumesSharedLoop`
(`libs/v9/governance/src/iris-loop.ts:41`) checks that V9 declares it consumes
the shared Iris loop and owns **zero** tool-call loops of its own — and, if the
platform Iris v2 is _not yet_ the single agentic substrate, it **files a
Phase-A2 gap** (finding F2, line 49) rather than papering over the platform's
incomplete consolidation. This is the fail-loud pattern at the governance layer:
V9 honestly reports "my side adds no loop, but the platform unification is still
open" instead of claiming a clean bill of health. Likewise,
`confirmSevenGateSuite` (`libs/v9/governance/src/gate-suite.ts:27`) attests that
the seven content-release gates run on the **shared** `ReleaseGateService`, not
a bespoke per-product checker — its `ok` is literally `gates.length === 7` over
the real `buildV9LessonGates`. The gate semantics themselves (and a real naming
divergence between two G1–G7 framings) are in
[./seven-gates-and-aletheia.md](./seven-gates-and-aletheia.md).

## Cross-cutting concerns

### Determinism

Same `(inputs, seed)` ⇒ same lesson and same hashes; a mismatch **blocks
promotion**. `compareArtifactDeterminism`
(`libs/v9/cross-cutting/src/determinism.ts:18`) compares two artifacts that
should be byte-identical across the fields that must be stable — `id`, `seed`,
the `skeletonHash`/`surfaceHash`/`cacheKey` provenance triple, and the whole
`groundTruth` skeleton — and returns the list of mismatched fields.
`verifyForgeDeterminism` (line 36) runs a forge twice and diffs the results;
`assertDeterministic` (line 44) throws on any mismatch. This is both a
provenance guarantee (a lesson is reproducible) and a cost lever (a
deterministic lesson is gated once per profile class and served many times),
which is why determinism and cost are documented together. The Prometheus
compile already derives the content-addressed hashes; this module is the
_verifier_ that proves reproducibility.

### Cost discipline

`CostLedger` (`libs/v9/cross-cutting/src/cost-discipline.ts:20`) enforces
per-stage spend ceilings with **no silent overspend**: `spend(stage, amount)`
(line 28) returns `false` — it does not absorb or clamp — when a charge would
exceed either the stage cap or the inherited ceiling. Critically, any coverage
truncation is logged explicitly via `logTruncation` (line 49): if the forge has
to drop candidates or shorten coverage to stay under budget, that is recorded
with a stage, reason, and dropped-unit count, so **no cap is ever silent**.
`validateStageBudget` (line 59) checks that per-stage caps sum within the
ceiling — the same budget- inheritance invariant the governance registration
enforces, applied at run time.

### Accessibility

Accessibility is structural and gated, not optional. `buildLessonA11yManifest`
(`libs/v9/cross-cutting/src/accessibility.ts:26`) constructs, from the artifact,
a caption cue per narrative beat _and_ per Socratic turn, a full text transcript
of the narrative, and alt text for every explorable surface (e.g. "Interactive
computed-kernel explorable: Expansion. lookback grows with z.").
`checkLessonAccessibility` (line 57) then **fails the a11y gate** if any surface
is uncaptioned, the transcript is empty, or any explorable is undescribed — a
lesson does not silently ship inaccessible. This reuses the Metis
lecture-package captioning as its base, specialized for the lesson artifact's
beat/turn/explorable shape.

### Champion-challenger ramp gating

New prompt/persona/explorable variants ramp only when they are **statistically**
better — no vibes-based promotion. `twoProportionZTest`
(`libs/v9/cross-cutting/src/champion-challenger.ts:32`) runs a pooled
two-proportion z-test over gold outcomes (the normal CDF via the Abramowitz &
Stegun 7.1.26 erf approximation, line 21), and `evaluateRamp` (line 59) promotes
a challenger **only** when the one-sided p-value is below α (default 0.05) _and_
the lift is positive, behind a minimum-trials guard (default 100 per variant).
The tests pin the behavior at both ends: 9/10 vs 5/10 does **not** promote
(insufficient trials), while 720/1000 vs 600/1000 does (significant lift). Human
review decisions are the gold set this gate consumes.

### The anti-metric guard

V9's north star is **durable understanding**, explicitly _not_ time-on-app, and
the code refuses at the type/runtime level to let an engagement metric become
the optimization target. `assertNorthStarTarget`
(`libs/v9/cross-cutting/src/anti-metric.ts:35`) throws a
`BannedOptimizationTargetError` for any of the banned engagement traps
(`time-on-app`, `session-length`, `daily-active-minutes`, `watch-time`, …, line
15, matched case-insensitively). The metric V9 _does_ instrument,
`computeDurableUnderstanding` (line 61), is the fraction of taught concepts a
learner still holds at ≥ the `intermediate` floor **after a retention delay** —
measured by retained recall (week-4 mastery), not by minutes spent. The test
confirms the semantics: of four concepts retained at `advanced`/`novice`/`null`/
`expert`, exactly two clear the floor, for a score of 0.5. A product that
optimizes session length has already lost the plot, and this module makes that a
compile/ runtime fact rather than a slogan.

### Localization and LMS interop (P3)

`planLocalization` (`libs/v9/cross-cutting/src/localization.ts:43`) routes each
BCP-47 target language to the **Nous local-inference tier** when Nous can serve
it on-device (lower cost, on-device privacy, offline equity) and to the **cloud
tier** otherwise, returning a per-language routing plan with local/cloud counts.
The plan and routing are real; the dubbing and translation themselves are the
injected Metis/Nous pipeline boundary. The institutional-interop module
(`libs/v9/cross-cutting/src/lms-interop.ts`) emits seven real, spec-conformant
ed-tech artifacts from a gated lesson: a SCORM 1.2 `imsmanifest.xml` (line 16),
a QTI 3.0 assessment item wrapping the retrieval checkpoint (line 37), an xAPI
(Tin Can) statement (line 49), an IMS Caliper event, a OneRoster line item, LTI
1.3 resource-link claims, and an Open Badges 3.0 verifiable credential (line
116). Both surfaces are P3 backlog — the artifacts are tested, the rollout is
sequenced.

## Embodied delivery and graceful degradation

The embodied teacher does not fail hard when its richest runtime is missing.
`resolveDelivery` (`libs/v9/chiron/src/delivery.ts:45`) walks the fallback order
`live_avatar → live_voice → text` (line 13) from the requested tier down to the
first one the session's capabilities support, with **text as the guaranteed
floor**, and — this is the honesty rule — every downgrade is **disclosed** (a
learner is never silently handed a lesser experience). The avatar/voice runtimes
are the Psyche libraries (`libs/psyche/avatar-core`, …); when they are
unavailable or over their latency/fidelity budget, the resolver records the
reason (`avatar-runtime-unavailable`, `voice-runtime-unavailable`) and the
disclosure string the UI surfaces. This is the same fail-loud-not-fake
discipline that runs through the whole layer.

## Failure modes and edge cases

The delivery and cross-cutting layer is built to _refuse_ rather than fabricate,
and each refusal is specific and located:

- **Ungated lesson reaches the player** → `UngatedLessonError`
  (`lesson-player.ts:62`); the player cannot be constructed unless all seven
  gate verdicts pass.
- **Unresolvable wonder at the front door** → the front door catches
  `WonderUnresolvedError` and returns `unresolved: true`
  (`wonder-front-door.ts:66`) — it degrades, it does not throw at the UI.
- **Age/region vs paywall conflict** → age-gate and region-block always precede
  the premium check (`entitlements.ts:56`/`:63`), so a minor is never offered an
  upgrade past an age gate.
- **Free-tier quota exhausted** → `cta: 'upgrade'` with `quotaExhausted: true`
  (`billing-gating.ts:48`); entitlement denials still win over the quota.
- **Non-deterministic forge** → `assertDeterministic` throws with the diverging
  field names (`determinism.ts:44`); promotion blocks.
- **Budget overspend** → `CostLedger.spend` returns `false`
  (`cost-discipline.ts:28`); any truncation is logged, never silent.
- **Inaccessible surface** → `checkLessonAccessibility` returns `ok: false` with
  the specific missing caption/transcript/alt-text (`accessibility.ts:57`).
- **Banned optimization target** → `BannedOptimizationTargetError`
  (`anti-metric.ts:35`).
- **Platform Iris not yet unified** → a Phase-A2 gap is filed, not hidden
  (`iris-loop.ts:49`).
- **Embodied runtime missing** → fall back down the chain to the text floor and
  _disclose_ the downgrade (`delivery.ts:55`).

## Related

- Section hub: [../V9_ARCHITECTURE.md](../V9_ARCHITECTURE.md)
- [./overview.md](./overview.md) — the orientation page (and its own short
  delivery/cross-cutting section)
- [./six-layer-reuse-stack.md](./six-layer-reuse-stack.md) and
  [./subsystem-map-and-reuse-ledger.md](./subsystem-map-and-reuse-ledger.md) —
  how L6 and the cross-cutting plane reuse the platform
- [./lesson-artifact.md](./lesson-artifact.md) — the gated artifact the player
  and the LMS emitters project
- [./seven-gates-and-aletheia.md](./seven-gates-and-aletheia.md) — the gate
  semantics the governance confirmation attests
- [./prometheus-lesson-pipeline.md](./prometheus-lesson-pipeline.md) — the forge
  whose stages become the governance DAG
- [./atlas-knowledge-graph.md](./atlas-knowledge-graph.md) — the wonder resolver
  the front door binds to
- [./hephaestus-explorables.md](./hephaestus-explorables.md) — the computed,
  web-native explorables that make the Bellona non-dependency possible
