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.
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-experienceships the tested data and logic the screens bind to, not the screens. Theapps/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 orimport … from 'three'anywhere inlibs/nyx/. Separately, §8 says "Kalika ships awasm-bindgenbuild … computed physics runs in the browser." Awasm-bindgenbuild does exist, but it is the CAS (computer-algebra) engine (libs/kalika/cas-engine/crates/kalika-cas-wasm, built viawasm-packfornodejsandwebtargets inlibs/kalika/cas-engine/scripts/build-wasm.sh). The kernels the V9 explorables actually call —@kalika/symplecticand@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 and
./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.
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.
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.
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.
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
WonderUnresolvedErrorand returnsunresolved: 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'withquotaExhausted: true(billing-gating.ts:48); entitlement denials still win over the quota. - Non-deterministic forge →
assertDeterministicthrows with the diverging field names (determinism.ts:44); promotion blocks. - Budget overspend →
CostLedger.spendreturnsfalse(cost-discipline.ts:28); any truncation is logged, never silent. - Inaccessible surface →
checkLessonAccessibilityreturnsok: falsewith 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
- ./overview.md — the orientation page (and its own short delivery/cross-cutting section)
- ./six-layer-reuse-stack.md and ./subsystem-map-and-reuse-ledger.md — how L6 and the cross-cutting plane reuse the platform
- ./lesson-artifact.md — the gated artifact the player and the LMS emitters project
- ./seven-gates-and-aletheia.md — the gate semantics the governance confirmation attests
- ./prometheus-lesson-pipeline.md — the forge whose stages become the governance DAG
- ./atlas-knowledge-graph.md — the wonder resolver the front door binds to
- ./hephaestus-explorables.md — the computed, web-native explorables that make the Bellona non-dependency possible