The Lesson artifact is the single thing V9's pipeline produces and the single
thing the rest of the system consumes: the compiled, cacheable,
provenance-stamped output of the Prometheus forge. Everything upstream — the
Atlas wonder-resolution, the solve-first grounding, the HTN plan, the
constrained writer, the Hephaestus explorable, the seven gates — exists to mint
this object, and everything downstream — the player, the personal star-map,
the Mnemosyne review loop, the C2PA ledger — reads it. It is defined as a real
Zod schema, V9LessonArtifactSchema, in libs/contracts/src/v9/lesson.ts (249
lines), and it is deliberately small and strict: a .strict() object with a
top-level .superRefine that enforces referential integrity, plus a
isV9LessonPublishable predicate that is true only when all seven gate verdicts
pass (lesson.ts:246). This page is the contract-side companion to the
architecture set; the monolith hub is
../V9_ARCHITECTURE.md, and the orientation page is
./overview.md.
The defining design fact — the reason the contract is shaped the way it is — is
the skeleton/surface split, the same discipline V8 used for fair detective
cases. groundTruth is the verified skeleton: grounded claims and
kernel-computed numbers that are authoritative and immutable once the lesson
clears its gates. narrative and media are the regenerable surface: the
prose, the Socratic turns, the visuals — re-skinnable over the same skeleton
without re-verifying anything. The contract makes the split machine-checkable by
hashing the two halves separately: skeletonHash over groundTruth and
surfaceHash over narrative + media, both content-addressed sha256
(libs/v9/prometheus/src/compile.ts:39-45). That one decision is what makes the
artifact simultaneously trustworthy (the skeleton is gated truth, never an
LLM's invention) and cheap (a popular lesson is gated once and served many
times; a different Chiron persona re-skins the surface without touching the
verified core). The doc-comment at the top of lesson.ts:1-12 states this in
the source itself.
What ships, honestly#
The contract and its compile/cache machinery are real and tested; the pipeline that fills the contract is real but has an honest model boundary; and a few of the monolith's field types are aspirational shorthand that the code collapses to opaque references. This section separates the three.
Implemented and tested (real today). V9LessonArtifactSchema is a genuine
strict Zod schema with enforced invariants, exercised by
libs/contracts/src/v9/__tests__/v9-contracts.spec.ts — it parses a complete
lesson and reports it publishable, rejects a claimRef out of range (spec
asserts /out of range/), requires ≥1 explorable (the Definition of Done),
and flips isV9LessonPublishable to false when any gate fails. The
compile/stamp path (compileLesson, cacheKeyFor, skeletonHashOf,
surfaceHashOf, previewProvenanceBundle, the in-memory ledger and cache) is
real and deterministic. The Prometheus pipeline composes Stages 0–8 end-to-end
into a publishable artifact (libs/v9/prometheus/src/pipeline.ts:111) and its
spec proves the delivered artifact, the determinism property (same seed →
identical hashes), the blocked path (a wrong number fails G2 and is never
cached), and the fail-loud path (no writer wired). The cross-cutting determinism
verifier and the lesson-accessibility manifest both operate over a real
V9LessonArtifact (libs/v9/cross-cutting/src/determinism.ts:18,
accessibility.ts:57).
Provider-gated (fail-loud seams, not stubs). The artifact's surface needs
a generation provider. The writer that realizes the skeleton into prose is an
injected boundary; with none wired the forge throws
LessonWriterNotConfiguredError rather than fabricating
(libs/v9/prometheus/src/write.ts:78). The pipeline test supplies a
deterministic test writer (no LLM), so the end-to-end run is real but the
content is test-authored. Likewise the media block is caller-supplied and
defaults to empty visuals (pipeline.ts:213): the artifact carries references
to Isis visuals, a Euterpe score, and an explainer film, but V9 does not itself
mint those assets here — they are the provider-gated remainder.
Spec-vs-code drift (named honestly). The monolith §6 sketch types the media
and persona fields as branded refs (IsisAssetRef, EuterpeRef,
ChironPersonaRef, ExplainerFilmRef). The shipped contract collapses all of
these to the single opaque V9ReferenceId string (lesson.ts:205,
MediaSchema at :100) — they are stable reference ids the artifact does not
itself resolve. Two smaller deltas: the monolith shows media.score as
required, but the contract makes it optional
(score: V9ReferenceIdSchema.optional(), lesson.ts:103); and the contract
adds a schemaVersion: 'v9.lesson.1' literal and a richer provenance bundle the
sketch omits. None of these are stubs — they are an honest, narrower realization
of an aspirational sketch.
The dual-representation caveat (important). V9 ships two lesson objects
and two G1–G7 gate schemes. The canonical one is V9LessonArtifact here,
gated by the Prometheus gate composer whose G1–G7 match the monolith §5 exactly.
The second is a simpler V9Lesson (libs/v9/lesson-gates/src/index.ts:56)
gated by the @oshun/v9-lesson-gates suite over the shared platform
release-gate service — and that suite assigns different meanings to the same
gate ids. Both are real and tested; the section
The seven-gate verdict block reconciles them
honestly so a reader is never misled about what "G4" means in a given file.
The skeleton/surface split, in the contract#
The split is not a convention — it is the top-level shape of the schema. The
verified skeleton is a single groundTruth object; the regenerable surface is
narrative + media; and the determinism guarantees ride on hashing those
halves independently.
groundTruth — authoritative, immutable post-gate#
V9GroundTruthSchema (lesson.ts:58) holds two arrays:
claims: V9GroundedClaim[](1–256). Each grounded claim (lesson.ts:31) istext(≤2000 chars) bound to ≥1 Sophia pin (pins.min(1)), anepistemicStatuslabel (established | contested | one-tradition | frontier), and an Aletheiatruthverdict (true | false | unverified). Themin(1)onpinsis the G1 invariant expressed in the type: an ungrounded claim cannot even be constructed. The doc-comment is blunt — "an ungrounded claim blocks."computedValues: V9KernelResult[](0–256). Each kernel result (lesson.ts:44) is the computed scalar a STEM claim must match: akernelRef, avalue, an optionalunit, a relativetolerance(default1e-6), and acontentHash— the sha256 of the full computed output, the reproducibility binding. This is V9's "solve-first" rule made into data: a node does not store "age of the universe = 13.8 Gyr," it carries the kernel computation that yields it (seeprimitives.ts:152,V9KernelRef).
Because the skeleton is the only authoritative half, it is the half the
determinism verifier compares field-by-field (compareArtifactDeterminism
checks groundTruth directly, plus the two hashes, determinism.ts:31).
narrative + media — the regenerable surface#
V9NarrativeSchema (lesson.ts:91) is beats (1–64 prose strings),
socraticTurns (each a mentor/learner turn with claimRefs indexing real
claims), and misconceptions (each a misconception + grounded correction). The
crucial property is that every surface assertion traces back to a skeleton
claim: a Socratic turn's claimRefs and a misconception's claimRef are
integer indices into groundTruth.claims, and the artifact's .superRefine
(lesson.ts:218-242) walks every one of them and rejects any index ≥ nClaims.
The surface can be regenerated freely, but it can never assert something the
skeleton has not grounded. V9MediaSchema (lesson.ts:100) is visuals[] +
optional score + optional film, all opaque V9ReferenceIds.
Anatomy of the artifact#
The full V9LessonArtifactSchema (lesson.ts:190) is the union of the two
halves plus the binding, assessment, scheduling, governance, and continuation
fields:
| Field | Type / shape | Notes |
|---|---|---|
schemaVersion |
literal 'v9.lesson.1' |
Version pin; not in the monolith sketch. |
id |
V9ReferenceId |
Derived lesson:<cacheKey[0:16]> at compile (compile.ts:167). |
seed |
int |
Determinism seed — same (inputs, seed) ⇒ same artifact + hashes. |
conceptSet |
V9ConceptId[] (1–64) |
The Atlas concept set the lesson covers. |
learnerProfileClass |
string (1–120) |
Cache by profile class, not learner id. |
groundTruth |
{ claims[], computedValues[] } |
The verified skeleton (immutable post-gate). |
narrative |
{ beats[], socraticTurns[], misconceptions[] } |
Regenerable surface; every ref traces to a claim. |
mentor |
V9ReferenceId (// ChironPersonaRef) |
Opaque persona ref; resolution is elsewhere. |
media |
{ visuals[], score?, film? } |
Opaque asset refs; score/film optional. |
explorables |
V9Explorable[] (1–8) |
≥1 required — the DoD (lesson.ts:209). |
assessment |
{ retrievalCheck{prompt,claimRef}, masterySignal } |
Active-recall item + Mnemosyne mastery signal. |
schedule |
{ checkpointRef, intervalDays, nextReviewAtIso } |
Mnemosyne spaced-review schedule. |
gates |
{ G1..G7: { pass, evidence[] } } |
The seven contract verdicts (lesson.ts:149). |
provenance |
V9C2PABundle |
Hashes + cache key + ledger id + signing time. |
thread |
{ nextWonders: V9ConceptId[] } |
Theia's continuation — the next, better questions. |
The embedded V9Explorable (explorable.ts:104) is itself gate-bearing: it
declares a successState whose reachable flag is set true only after the
target was demonstrably reached (by a kernel result or a headless verifier), and
a computed-kernel explorable must be grounded by a kernelRef, not a Sophia
pin (explorable.ts:115-124). isV9ExplorableShippable is just
successState.reachable. So "ships ≥1 explorable" means "ships ≥1 explorable
whose success state was actually demonstrated" — the artifact cannot carry a
blind-asserted interactive.
The seven-gate verdict block#
The gates field is a V9Gates object (lesson.ts:149): seven
{ pass, evidence[] } verdicts, one per gate id G1…G7. The composer that
fills it (libs/v9/prometheus/src/gates.ts:132, composeGates) maps each gate
to a real evaluation, and the mapping is a faithful realization of the
monolith §5 table:
| Gate | Meaning (contract / Prometheus) | Source of the verdict |
|---|---|---|
| G1 | Grounding — every claim has ≥1 Sophia pin | Aletheia evaluateGrounding (aletheia.ts:48) |
| G2 | Accuracy — STEM values recomputed & matched | Aletheia evaluateAccuracy (recompute vs assert, tol-bounded) |
| G3 | Pedagogy — prereq coverage + misconception + flow band | evaluatePedagogy (gates.ts:36) |
| G4 | Completeness — explorable reached its success state | Hephaestus evaluateExplorableDoD (gates.ts:133) |
| G5 | Quality — LLM-as-judge (structural default) | evaluateQuality over a QualityJudge boundary (gates.ts:88) |
| G6 | Safety — curriculum policy + epistemic labels | Aletheia evaluateSafety (aletheia.ts:51) |
| G7 | Provenance — C2PA hashes + ledger entry present | evaluateProvenance (gates.ts:105) |
Aletheia owns only the three truth-adjacent gates (G1/G2/G6); it is "a gate, not
a grounding engine" — a thin policy over Sophia's pins and the Nyx/Kalika
kernels — and the deep treatment lives in
./seven-gates-and-aletheia.md. The contract's
isV9LessonPublishable is the AND of all seven pass booleans
(lesson.ts:246); the pipeline computes blockedGateIds as the gates that
failed and returns without an artifact if any did (pipeline.ts:232-237),
so a blocked lesson is never compiled, never cached, never delivered.
Honest reconciliation of the two gate schemes. A reader who opens
libs/v9/lesson-gates/src/index.ts will find a different G1–G7. That suite
gates the simpler V9Lesson over the shared @oshun/content-release-gates
service (V9's "no eighth loop" rule — reuse the platform gate engine, don't fork
one), and it labels: G1 truth, G2 grounding, G3 teachability, G4
safety, G5 explorable, G6 adaptive-difficulty, G7 retrieval
(index.ts:110-172). It is equally real and tested — but its gate ids do not
line up with the contract's. The two are not contradictory implementations of
one spec; they are two parallel tracks. The V9LessonArtifact.gates block on
this page is always the §5-faithful scheme. When you see "G4" in the
lesson-gates package it means safety; when you see it in
prometheus/gates.ts or this contract it means completeness. The distinction
matters and is exactly the kind of drift this page exists to flag.
How an artifact is minted#
The mint happens in compileLesson (compile.ts:152), the last stage (Stage 8)
of the forge. It is pure and deterministic: given the verified skeleton, the
realized surface, the explorables, the assessment/schedule, and the gate
verdicts, it derives the provenance bundle, parses everything through
V9LessonArtifactSchema (so a malformed artifact throws before it can be
cached), writes an immutable ledger entry, and stores the result under the
profile-class cache key.
A subtle correctness check closes the loop: after compiling, the pipeline
re-asserts the contract's own isV9LessonPublishable against its gate sum and
throws if they disagree (pipeline.ts:257-260) — the contract and the
composer must never drift apart at runtime.
Determinism and the profile-class cache#
Determinism is both a provenance guarantee and a cost lever, and both ride on
three content-addressed digests in compile.ts:
cacheKeyFor(conceptSet, profileClass, seed)(compile.ts:31) hashes the sorted concept set, the profile class, and the seed. Sorting makes the key order-independent in concepts, so[a, b]and[b, a]collide on purpose. The id and ledger id are derived from this key (compile.ts:117,167), so the same inputs always name the same lesson.skeletonHashOf(groundTruth)andsurfaceHashOf(narrative, media)(compile.ts:39-45) bind the two halves independently — this is what lets a re-skin changesurfaceHashwhileskeletonHashstays fixed.
The cache is keyed by profile class, not learner id (lesson.ts:197): a
popular wonder asked by ten thousand learners in the same profile class is
forged, gated, and signed once, then served from LessonCache.get
(compile.ts:155-163, servedFromCache: true). The pipeline spec proves this
directly — the first forge reports servedFromCache: false, the second true,
same id.
The guarantee is enforced, not assumed. compareArtifactDeterminism
(determinism.ts:18) runs a forge twice and diffs id, seed, the two hashes,
cacheKey, and the whole groundTruth; assertDeterministic
(determinism.ts:44) throws to block a promotion on any mismatch, and its
spec confirms a divergent run lists skeletonHash among the mismatches
(cross-cutting.spec.ts:101).
One precise limitation worth stating: skeletonHash covers groundTruth and
surfaceHash covers narrative + media, but neither hash covers
explorables, assessment, schedule, gates, or thread. The explorable
carries its own contentHash inside its provenance (explorable.ts:91), and
the assessment/schedule are deterministic functions of the skeleton plus the
caller-supplied clock — so the design choice is that the two top-level hashes
bind truth and prose+media, while the interactive binds its own determinism
separately. The determinism verifier compensates by comparing groundTruth
structurally, but it does not diff the explorable trajectory; that is the
explorable's own content-hash job.
Provenance: the C2PA bundle and the immutable ledger#
provenance is a V9C2PABundle (lesson.ts:163): skeletonHash,
surfaceHash, cacheKey, a ledgerEntryId, per-asset assetManifests, and a
signedAtIso timestamp. The signing time is caller-supplied —
compileLesson and forgeLesson both take signedAtIso/nowUnixMs as inputs
rather than reading a clock (compile.ts:139, pipeline.ts:58-60), so the
forge never fabricates time. The ledgerEntryId is itself derived from the
content, not the run (ledger:<cacheKey[0:32]>, compile.ts:117), which is
what keeps the whole artifact byte-identical across reruns of the same inputs.
The generation ledger is append-only and immutable by construction:
InMemoryGenerationLedger.append silently no-ops if the entry id already exists
("immutable: never overwrite", compile.ts:67-70). It is an interface
(GenerationLedger) so a durable backend can replace the in-memory default
without touching the compile logic. The pipeline spec asserts the ledger holds
an entry under the artifact's provenance.ledgerEntryId after a successful
forge (pipeline.spec.ts:131).
The continuation thread#
thread.nextWonders (lesson.ts:178) is Theia's contribution: up to sixteen
concept ids naming the next, better questions this lesson opens. It is not
decorative — it is how V9 turns a lesson into a thread (Olbers' paradox → the
age of the universe → the CMB → the Big Bang). The values come from the real
Atlas graph: nextWonders(atlas, from) (libs/v9/theia/src/theia.ts:31) walks
the graph's enables → bridges → specializes → related edges in priority order
to surface continuations, and surpriseMe (theia.ts:92) ranks unlearned
concepts by the fraction of their prerequisites the learner has mastered — "the
frontier of what they almost know." The pipeline passes these through as
input.nextWonders (pipeline.ts:252); the full director is covered in
./delivery-and-cross-cutting.md.
Edge cases and failure modes#
The contract and the forge are uncommonly explicit about refusing to fabricate. The behaviours below are all real and (where noted) test-pinned:
- No writer wired ⇒ fail loud.
realizeNarrativethrowsLessonWriterNotConfiguredError(write.ts:78); the pipeline spec asserts the rejection. The surface is never invented. - No grounded fact ⇒ refuse.
buildGroundTruththrows "no grounded fact node in the concept set; refusing to forge an ungrounded lesson" (ground-truth.ts:86) — a lesson with nothing to ground is blocked at Stage 1. claimRefout of range ⇒ unparseable. The artifact.superRefinerejects any Socratic-turn, misconception, or retrieval-check ref that does not index a real claim (lesson.ts:218-242; spec asserts/out of range/). The writer-side check inrealizeNarrative(write.ts:87-93) catches the same class earlier with a louder error.- Zero explorables ⇒ unparseable.
explorables.min(1)(lesson.ts:209) enforces the DoD; the spec confirms[]throws. - Any gate fails ⇒ blocked, never cached. The pipeline returns without an
artifactand the spec confirms a G6 denial leaves the cache empty (pipeline.spec.ts:169-173). - A wrong number ⇒ G2 blocks. Because STEM values are recomputed and matched
within tolerance (
accuracy-gate.ts), a drifted literal fails accuracy and the lesson is blocked — the spec drives exactly this path. - Determinism mismatch ⇒ promotion blocked.
assertDeterministicthrows (determinism.ts:44).
Connections#
The artifact is the seam between every V9 subsystem, so it cross-links the whole set:
- The forge that fills it: ./prometheus-lesson-pipeline.md (Stages 0–8) — the artifact is its Stage-8 output.
- The gates that must pass first: ./seven-gates-and-aletheia.md — the deep treatment of G1–G7 and the Aletheia thin-policy layer.
- The verified skeleton's inputs: ./atlas-knowledge-graph.md (grounded fact nodes + kernel refs) and the reuse story in ./six-layer-reuse-stack.md and ./subsystem-map-and-reuse-ledger.md.
- The embedded interactives:
./hephaestus-explorables.md — what makes
explorables[*].successState.reachabletrue. - Determinism, cost discipline, accessibility, and the Theia continuation: ./delivery-and-cross-cutting.md.
- Orientation and the honest build status: ./overview.md and the hub ../V9_ARCHITECTURE.md.