# Governance, Cross-Cutting Discipline, Non-Goals, and Definition of Done

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](../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](./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](./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](./threads-and-mastery-loop.md) and
[./atlas-wonder-resolution.md](./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](./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:

```mermaid
flowchart TD
  A["Learner wonder"] --> B["Entitlement + billing gate<br/>checkLessonAccess → checkBillingAccess"]
  B -->|allowed| C["AgentRun envelope<br/>budget inheritance · kill switches · replay"]
  C --> D["Forge stages 0–3,5–8<br/>under CostLedger (no silent overspend)"]
  D --> E["Seven-gate suite G1–G7<br/>shared ReleaseGateService"]
  E -->|all pass| F["Determinism verify<br/>assertDeterministic (blocks on mismatch)"]
  F --> G["C2PA provenance bundle<br/>skeletonHash · cacheKey · ledgerEntryId"]
  G --> H["Lesson player<br/>refuses ungated (UngatedLessonError)"]
  E -->|any fail| R["regenerate-with-direction"]
  R --> D
  X["assertNorthStarTarget<br/>(bans time-on-app)"] -.optimization target.-> C
```

1. **Wonder → complete lesson within budget** — the front door + the inherited
   `V9Budget` / `CostLedger` are real; the generation itself is the Prometheus
   forge ([./prometheus-lesson-forge.md](./prometheus-lesson-forge.md)).
2. **Aletheia proves G1/G2, lesson passes G3–G7** — the shared gate suite is
   real and tested
   ([./subsystem-map-and-gates.md](./subsystem-map-and-gates.md)).
3. **Chiron teaches it live, voiced, Socratic, adaptive** — an injected
   model/runtime seam
   ([./chiron-and-hephaestus.md](./chiron-and-hephaestus.md)).
4. **≥1 explorable to manipulate the idea** — enforced by the schema floor
   `explorables: z.array(...).min(1)` (`lesson.ts:209`) and gate G5.
5. **Mnemosyne schedules retrieval + sequences next** — the `schedule` block and
   mnemosyne-glue
   ([./threads-and-mastery-loop.md](./threads-and-mastery-loop.md)).
6. **Theia braids it into a thread** — the `thread.nextWonders` schema field;
   the director itself is in
   [./braid-commons-and-films.md](./braid-commons-and-films.md).
7. **Reproducible from a seed, fully auditable** — `determinism.ts` +
   `CostLedger` truncation logs + the `V9C2PABundle` provenance, 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](./overview.md) — the product and its defining "LLM never
  freestyles facts" decision
- [./subsystem-map-and-gates.md](./subsystem-map-and-gates.md) — the seven gates
  in depth, and the two distinct gate numberings
- [./prometheus-lesson-forge.md](./prometheus-lesson-forge.md) — the governed
  forge stages and the budget/cost path
- [./chiron-and-hephaestus.md](./chiron-and-hephaestus.md) — the mentor and
  explorable seams behind DoD steps 3–4
- [./threads-and-mastery-loop.md](./threads-and-mastery-loop.md) and
  [./atlas-wonder-resolution.md](./atlas-wonder-resolution.md) — the mastery
  loop, the star-map, and entitlement-gated surfaces
- [./braid-commons-and-films.md](./braid-commons-and-films.md) — Theia threads
  and the Agora commons
- The hub: [../V9_features.md](../V9_features.md)
