Metis · Features

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

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

7sections14 minread1diagram2tables

On this page

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 (blockedRegionsregion-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-finishlesson:applied-physics-vehicle-dynamics, meditation:7-day-streaklesson:neuroscience-of-attention, v8-detective:case-solvedlesson: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:

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).
  2. Aletheia proves G1/G2, lesson passes G3–G7 — the shared gate suite is real and tested (./subsystem-map-and-gates.md).
  3. Chiron teaches it live, voiced, Socratic, adaptive — an injected model/runtime seam (./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).
  6. Theia braids it into a thread — the thread.nextWonders schema field; the director itself is in ./braid-commons-and-films.md.
  7. Reproducible from a seed, fully auditabledeterminism.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.