Most of the V9 feature set is about generating something — a wonder resolved, a
lesson forged, an explorable computed. This page is about the opposite: the
machinery that decides what V9 is not allowed to do, and the bar a lesson must
clear before anyone calls it done. In V9 those are not slideware values; they
are enforced in two small, real, tested packages — @oshun/v9-governance
(libs/v9/governance/src) and @oshun/v9-cross-cutting
(libs/v9/cross-cutting/src), with @oshun/v9-experience's billing seam
(libs/v9/experience/src/billing-gating.ts) on top. Together they encode the
governance envelope every lesson generation runs inside (a visible DAG, an
inherited budget, kill switches, a replay manifest), the deny-by-default
entitlement graph that decides who may open a lesson, and the cross-cutting
disciplines — determinism, cost, accessibility, the anti-engagement north star,
statistical ramp gating, localization, and institutional LMS interop — that the
spec's "cross-cutting concerns" row promises.
Why build it this way? Because V9 earns the right to compose very powerful generators (Isis, Yemaya, the Iris agent runtime, the science kernels) only by refusing, at the type and runtime level, to become an ungoverned content firehose or an engagement-maximizer. The non-goals in the originating design doc — "not an LMS at launch," "no new agent loop," "does not optimize for time-on-app" — are realized as code that throws, files a gap, or labels itself P3 rather than silently drifting. The Definition of Done is the same idea read forward: a lesson is GA-complete only when its enforceable invariants (grounded, gated, deterministic, reproducible, ≥1 explorable, never served ungated) all hold. This is the feature-side companion to the architecture catalogue; the hub for the set is ../V9_features.md.
What ships, honestly#
The governance, cross-cutting, and billing modules are real and tested, and
their suites run green here: governance.spec.ts (10/10),
cross-cutting.spec.ts (17/17), and experience.spec.ts (8/8) all pass against
the actual code. The logic is domain-specific, not CRUD: a two-proportion z-test
with an Abramowitz–Stegun normal CDF, a durable-understanding fraction computed
over a post-delay retention sample, a deny-by-default access lattice, a
no-silent-overspend cost ledger, and seven spec-conformant ed-tech artifact
emitters (SCORM 1.2, QTI 3, xAPI, Caliper, OneRoster, LTI 1.3, Open Badges 3.0).
Three honesty notes matter. First, the agent-family registration is a declared
envelope, not a live AgentRun: registerV9AgentFamily builds a real,
validated registration object whose DAG mirrors the actual Prometheus
pipeline.ts stage set (verified: stages 0–3, 5–8, with stage 4 genuinely
absent in both the registration table and the real pipeline), but the V1
Agentic AI Studio it would register into is the injected platform boundary,
not exercised in-repo. Second, the "no eighth loop" confirmation defaults to
filing a gap: confirmConsumesSharedLoop reports ownAgentLoops: [] (an
honest attestation, backed by the real fact that no V9 package implements a
tool-call loop — the writer is an injected Stage-3 seam) and, unless told the
platform Iris-v2 substrate is already unified, returns a Phase-A2 gap admitting
the consolidation is incomplete. That is fail-loud, not fake-green. Third,
localization and LMS interop are explicitly P3: the routing plan and the
standards documents they emit are real, but the dubbing/translation models and
the round-trip to a real institution's LMS are injected boundaries — the bridge
exists so the "not an LMS at launch" non-goal is a deliberate later motion
rather than a missing capability. The two-different-gate-numbering issue (the
contract's G1–G7 vs. the platform suite's g1:truth … g7:retrieval) is owned in
depth by ./subsystem-map-and-gates.md; this page
treats the suite only at the governance altitude (it is the shared gate, not a
fork).
Governance: every lesson is a governed AgentRun#
The agent-family registration (agent-family.ts)#
registerV9AgentFamily() (libs/v9/governance/src/agent-family.ts:73) returns
a V9AgentFamilyRegistration declaring family: 'v9_metis_lesson',
objective: 'grounded_lesson', and consumesSharedLoop: true. Its core is a
V9DagNode[] built from the hand-maintained STAGE_TITLES table — the visible
DAG the platform governance plane renders. Each node carries a
requiredEvidence flag (none / preferred / required) and a linear
dependsOn chain so the generation is an auditable sequence rather than an
opaque call:
| Stage | DAG node | Title | Evidence |
|---|---|---|---|
| 0 | v9.stage.0 |
resolve (Atlas wonder → concept set) | preferred |
| 1 | v9.stage.1 |
ground / solve-first (skeleton) | required |
| 2 | v9.stage.2 |
plan (HTN) | preferred |
| 3 | v9.stage.3 |
write (realize skeleton, constrained) | required |
| 5 | v9.stage.5 |
build explorable (Hephaestus) | required |
| 6 | v9.stage.6 |
assess & schedule (Mnemosyne) | preferred |
| 7 | v9.stage.7 |
gate (G1–G7) | required |
| 8 | v9.stage.8 |
compile & cache (C2PA, ledger) | required |
Budget inheritance is concrete. The registration takes an inherited ceiling
(default 100_000) and distributes a flat perStageCap of
Math.floor(ceiling / dag.length) per node (agent-family.ts:87).
validateBudgetInheritance() then asserts the per-stage caps sum to ≤ the
inherited ceiling — a run can never be authorized to spend more than its parent
envelope granted it. Kill switches are first-class: a family-scoped switch
whose fallback is pause, and a provider-scoped switch whose fallback is
text_only (agent-family.ts:95) — so a failing image provider degrades the
lesson to text rather than failing the whole run. Replay is a manifest of
the three fields that make a run reproducible and auditable: cacheKey
(deterministic inputs), skeletonHash (the plan), and ledgerEntryId (the
evidence ledger), with replayable: true. The test confirms the DAG stage list
is exactly [0, 1, 2, 3, 5, 6, 7, 8] and that budget inheritance validates.
No eighth loop (iris-loop.ts)#
The repo's standing rule is "no new agent loops" — every product consumes the
shared Iris runtime. confirmConsumesSharedLoop()
(libs/v9/governance/src/iris-loop.ts:41) is V9's attestation of that rule. It
checks registration.consumesSharedLoop === true, declares ownAgentLoops: [],
and reports ok only when both hold. The honest part is the default: the
sharedLoopIsSingleSubstrate option defaults to false (iris-loop.ts:47), so
unless a caller asserts the platform has already unified on Iris v2, the
function returns a structured IrisGapReport (finding: 'F2', phase: 'A2')
saying the V9 side adds no loop but the platform consolidation remains open. V9
does not paper over an incomplete platform — it files the gap. The test
exercises both branches: gap-filed when the substrate isn't unified, clean when
it is.
Entitlements — deny by default, plus cross-product grants (entitlements.ts)#
checkLessonAccess() (libs/v9/governance/src/entitlements.ts:52) is a
deny-by-default lattice over a V9LearnerProfile (accountId, ageBand,
region, tier, grants). Precedence is deliberate and enforced in order:
adult-gating (requiresAdult against a non-adult band → age-gate) and
region blocks (blockedRegions → region-block) both take precedence over
the paywall (premium requires tier === 'plus' or a matching grant →
upgrade); a non-premium lesson falls through to
{ allow: true, via: 'free' }. A safety or licensing constraint can therefore
never be bought past by a subscription.
The interesting V9-specific mechanism is cross-product grants: an
achievement in another Oshun product unlocks a specific premium lesson.
CROSS_PRODUCT_GRANTS (entitlements.ts:91) is the real mapping —
v2-racing:podium-finish → lesson:applied-physics-vehicle-dynamics,
meditation:7-day-streak → lesson:neuroscience-of-attention,
v8-detective:case-solved → lesson:logic-and-deduction.
applyCrossProductGrant() looks the achievement up and returns a profile with
the new grant appended (idempotently), and checkLessonAccess then admits that
lesson via: 'grant'. This is the code behind the design promise that a racing
podium earns the applied-physics lesson and a meditation streak earns the
neuroscience-of-attention thread.
The shared seven-gate suite, confirmed not forked (gate-suite.ts)#
confirmSevenGateSuite() (libs/v9/governance/src/gate-suite.ts:27) exists to
attest one architectural fact: V9's release gating is the shared platform
gate, not a bespoke per-product checker. It calls buildV9LessonGates() from
@oshun/v9-lesson-gates, counts the gates, and returns
{ usesPlatformReleaseGate: true, bespokeChecker: false, ok: gateCount === 7 }.
The suite itself (libs/v9/lesson-gates/src/index.ts) composes the platform
ReleaseGateService with gateFromEvalScore / gateFromManifestCheck /
createGroundingGate — every gate is a real GateDefinition, composed not
forked. The full gate mechanics, and the two distinct gate numberings that
exist in this codebase, are documented in
./subsystem-map-and-gates.md; the governance
module's only claim is the shared-vs-bespoke one, and its test verifies the
count is seven and the flags are set.
Cross-cutting discipline#
@oshun/v9-cross-cutting is where several non-goals and DoD clauses become
runnable guards. Every function below operates over the canonical
V9LessonArtifact (libs/contracts/src/v9/lesson.ts), re-exported flat and as
V9Contracts from @oshun/contracts (libs/contracts/src/index.ts:36).
Determinism (determinism.ts)#
The DoD requires a lesson be reproducible from a seed.
compareArtifactDeterminism() diffs two artifacts that should be
byte-identical — id, seed, the three provenance hashes (skeletonHash,
surfaceHash, cacheKey), and the whole groundTruth — and returns the list
of mismatched fields. verifyForgeDeterminism() runs a forge twice and compares
the outputs, and assertDeterministic()
(libs/v9/cross-cutting/src/determinism.ts:44) throws on any mismatch,
which is how a non-deterministic generation is blocked from promotion.
Determinism is both a provenance guarantee and a cost lever: a deterministic
lesson is gated once and served from cache many times (the learnerProfileClass
cache key, not a per-learner key).
Cost discipline (cost-discipline.ts)#
CostLedger.spend(stage, amount)
(libs/v9/cross-cutting/src/cost-discipline.ts:28) enforces the per-stage caps
from the budget envelope and returns false rather than overspending —
there is no silent overspend; a stage that would exceed its cap or the inherited
ceiling is simply refused. Crucially, when coverage is dropped to stay in
budget, logTruncation() records it explicitly, so a cap is never silent: the
ledger can always tell you what was dropped and why. validateStageBudget()
mirrors the governance check (caps sum ≤ ceiling). The test drives the refusal
path directly: a 40-cap stage accepts a 30-spend, refuses the next 20, and the
total stays at 30.
The anti-metric north star (anti-metric.ts)#
This is the literal code expression of "V9 does not optimize for time-on-app."
NORTH_STAR_METRIC is durable-understanding, and
BANNED_OPTIMIZATION_TARGETS lists the engagement traps (time-on-app,
session-length, daily-active-minutes, screen-time, watch-time, and
snake/space variants). assertNorthStarTarget()
(libs/v9/cross-cutting/src/anti-metric.ts:35) normalizes a metric key and
throws BannedOptimizationTargetError if anyone tries to set an engagement
metric as the optimization target — the non-goal is a runtime guard, not a memo.
The metric V9 does instrument is computed by computeDurableUnderstanding():
the fraction of taught concepts the learner still holds at ≥ a mastery floor
(default intermediate) after a retention delay (week-4, not immediately
post-lesson), scored over the six Dreyfus MASTERY_LEVELS from
@mnemosyne/core. The test pins the semantics: of advanced, novice, null,
expert, exactly advanced + expert clear the intermediate floor →
score === 0.5.
Accessibility (accessibility.ts)#
buildLessonA11yManifest() derives a per-lesson manifest from the artifact: a
caption cue for every narrative beat and every Socratic turn, a full
transcript, and alt text for every explorable surface.
checkLessonAccessibility() (accessibility.ts:57) then fails the manifest if
any surface is uncaptioned, the transcript is empty, or any explorable is
undescribed — a lesson does not silently ship inaccessible. Because captions are
generated per beat/turn directly from narrative.beats and
narrative.socraticTurns, accessibility is structural, derived from the same
artifact the player renders, not a separate hand-maintained track.
Champion–challenger ramp gating (champion-challenger.ts)#
New prompts, personas, and explorable variants ramp only when they are
statistically better, not on vibes. twoProportionZTest() computes a
pooled-SE z statistic with a one-sided p-value via an Abramowitz–Stegun (7.1.26)
normal CDF approximation. evaluateRamp() (champion-challenger.ts:59)
promotes the challenger only when all of: pValueOneSided < alpha (default
0.05), lift > 0, and both arms have ≥ minTrials (default 100). The tests
demonstrate the discipline: 600/1000 vs 720/1000 promotes (significant lift),
600/1000 vs 605/1000 does not (noise), and an under-trialled arm is refused with
an explicit "insufficient trials" reason.
Localization (P3) (localization.ts)#
planLocalization() routes each BCP-47 target language to the Nous local
inference tier when the on-device runtime can serve it (cost, privacy, and
offline-equity) and the cloud tier otherwise, returning per-language
routings plus localCount / cloudCount. The routing plan is real and tested
(es/fr local, sw cloud); the dubbing and translation themselves are the
injected Metis/Nous pipeline boundary. This is P3 backlog by design.
Institutional LMS interop (P3) (lms-interop.ts)#
This module is the code behind the "does not become an LMS at launch" non-goal:
the bridge exists, deliberately deferred. It emits seven real, spec-conformant
artifacts from a V9LessonArtifact — a SCORM 1.2 imsmanifest.xml (single
SCO), a QTI 3.0 assessment item carrying the lesson's actual retrieval prompt,
an xAPI (Tin Can) experienced statement, an IMS Caliper 1.2 NavigationEvent,
a OneRoster 1.2 gradebook line item, LTI 1.3 resource-link claims, and an Open
Badges 3.0 verifiable credential — with xmlEscape on all interpolated values.
The tests assert the spec markers (<schemaversion>1.2</schemaversion>,
adlcp:scormtype="sco", the QTI prompt round-trip, the mailto: actor, the
OpenBadgeCredential type). What is not here is a live institutional
integration; making schools first-class is the deliberate P3 motion.
Experience: free-tier vs. subscription (billing-gating.ts)#
@oshun/v9-experience layers consumer billing onto the entitlement graph.
checkBillingAccess() (libs/v9/experience/src/billing-gating.ts:36) first
calls the governance checkLessonAccess — so age, region, and premium denials
win first — and only then applies the free-tier quota
(DEFAULT_FREE_TIER_QUOTA = 5/period) to a free learner on a free lesson. Over
quota returns { allow: false, cta: 'upgrade', quotaExhausted: true }; within
quota returns the allow verdict annotated with quotaRemaining. The test
confirms the precedence directly: an age-gate beats the quota even when the
learner has quota left.
The experience layer also enforces the most consumer-visible DoD invariant: the
lesson player refuses to render an ungated lesson. buildLessonPlayer()
throws UngatedLessonError via isV9LessonPublishable()
(libs/contracts/src/v9/lesson.ts:246, which requires all of G1–G7 pass), so
"nothing untrue, unsafe, ungrounded, or pedagogically broken can reach a
learner" is enforced at the last boundary, not just hoped for upstream. (The
player's show-your-sources panel, explorable canvas, and the personal-Atlas
star-map — brightness scaling with mastery — are covered in
./threads-and-mastery-loop.md and
./atlas-wonder-resolution.md.)
Non-goals as enforced code#
The design doc's non-goals map onto concrete enforcement points — and where the "enforcement" is really an architectural choice rather than a guard, this table says so:
| Non-goal | How it is realized |
|---|---|
| Not an LMS at launch | lms-interop.ts is real but P3: the SCORM/QTI/xAPI/LTI/Open-Badges bridge exists, deferred by design. |
| No new agent loop / model | iris-loop.ts confirmConsumesSharedLoop (ownAgentLoops: [], files an F2/A2 gap by default). |
| No new asset providers | Orchestrates Isis's existing ones; no provider code in V9 (see ./prometheus-lesson-forge.md). |
| LLM does not freestyle facts | The groundTruth skeleton + G1/G2 gates; the LLM realizes a verified skeleton (architecture, see gates page). |
| Not time-on-app | anti-metric.ts assertNorthStarTarget throws on any banned engagement metric. |
| P1 web not blocked on the UE cook | An architectural sequencing choice (P2/P3 in-game lessons); no in-repo guard — stated honestly. |
Definition of Done (GA) — invariant vs. seam#
The seven GA conditions read as the same discipline forward. The enforceable invariants are real and tested; the live-experience clauses are injected seams documented on sibling pages:
- Wonder → complete lesson within budget — the front door + the inherited
V9Budget/CostLedgerare real; the generation itself is the Prometheus forge (./prometheus-lesson-forge.md). - Aletheia proves G1/G2, lesson passes G3–G7 — the shared gate suite is real and tested (./subsystem-map-and-gates.md).
- Chiron teaches it live, voiced, Socratic, adaptive — an injected model/runtime seam (./chiron-and-hephaestus.md).
- ≥1 explorable to manipulate the idea — enforced by the schema floor
explorables: z.array(...).min(1)(lesson.ts:209) and gate G5. - Mnemosyne schedules retrieval + sequences next — the
scheduleblock and mnemosyne-glue (./threads-and-mastery-loop.md). - Theia braids it into a thread — the
thread.nextWondersschema field; the director itself is in ./braid-commons-and-films.md. - Reproducible from a seed, fully auditable —
determinism.ts+CostLedgertruncation logs + theV9C2PABundleprovenance, all real.
The honest read: the invariants a learner's safety depends on — grounded, gated, deterministic, ≥1 explorable, never served ungated, never optimized for engagement — are implemented and tested today. The live teaching, voiced mentor, and real-institution LMS round-trip remain injected boundaries by design, labeled as such here and on their owning pages.
Related#
- ./overview.md — the product and its defining "LLM never freestyles facts" decision
- ./subsystem-map-and-gates.md — the seven gates in depth, and the two distinct gate numberings
- ./prometheus-lesson-forge.md — the governed forge stages and the budget/cost path
- ./chiron-and-hephaestus.md — the mentor and explorable seams behind DoD steps 3–4
- ./threads-and-mastery-loop.md and ./atlas-wonder-resolution.md — the mastery loop, the star-map, and entitlement-gated surfaces
- ./braid-commons-and-films.md — Theia threads and the Agora commons
- The hub: ../V9_features.md