# The Prometheus Lesson-Generation Pipeline

Prometheus is V9's lesson forge: the function that turns a learner's free-text
wonder — "how old is the universe?", "why is the night sky dark?" — into a
grounded, interactive, provenance-stamped `V9LessonArtifact`, with no human
authoring per lesson. Its governing discipline is three words, inherited from
V8's "propose/dispose" mystery generator and retargeted from _fair mystery_ to
_true explanation_: **solve-first, grounded, gated**. The verified skeleton —
the grounded claims and the kernel-recomputed numbers — is built from the real
Atlas nodes _before any prose exists_, and the language model is constrained to
_realize_ that skeleton rather than invent it ("the LLM never invents a fact or
a number", `libs/v9/prometheus/src/ground-truth.ts:7`). The whole pipeline is
one async function, `forgeLesson` (`libs/v9/prometheus/src/pipeline.ts:111`),
and it adds **no new agent loop** — it composes the already-shipped V9 packages
(Atlas resolve, the Nyx/Kalika kernels, Hephaestus explorables, the Mnemosyne
mastery loop, Aletheia + the seven contract gates) and treats the LLM writer as
an injected boundary, not a bespoke orchestrator
(`libs/v9/prometheus/src/index.ts`).

Why this shape? Because in V9 trustworthiness and cost are the same problem. A
lesson is a **reproducible artifact** with a hard split — a _verified skeleton_
that is authoritative and immutable once gated, and a _regenerable surface_ (the
prose, the Socratic dialogue, the media) that can be re-skinned over the same
proven content. That split is encoded directly in the artifact as `skeletonHash`
vs `surfaceHash`, and it is what lets V9 gate a popular lesson once and serve it
many times, and swap a different teacher persona over identical verified truth.
This page is the deep, stage-by-stage companion to the forge summary in
[./overview.md](./overview.md); the section hub is
[../V9_ARCHITECTURE.md](../V9_ARCHITECTURE.md).

## What ships, honestly

The forge is **real and end-to-end tested**, but the monolith's nine-stage table
(`../V9_ARCHITECTURE.md` §3) is in places aspirational about _who_ does the
work, so this section separates the three honesty tiers. Honest "planned/seam"
beats fake "shipped."

**Implemented and tested (real today).** `forgeLesson` composes Stages 0, 1, 2,
3, 5, 6, 7, and 8 end-to-end into a publishable artifact, and a single
end-to-end test forges "how old is the universe?" all the way through — real
~13.8 Gyr ground truth, all seven gates, determinism, the profile-class cache,
the block path, and the fail-loud path
(`libs/v9/prometheus/src/pipeline.spec.ts`). Stage 1 recomputes STEM values from
the genuine `@kalika/cosmology` Friedmann solver and `@nyx/constants`
(`libs/v9/aletheia/src/kernel-evaluators.ts:33`); Stage 6 drives the real
`@mnemosyne/core` FSRS engine
(`libs/v9/mnemosyne-glue/src/mastery-feedback.ts:42`); Stage 8 emits a
contract-validated `V9LessonArtifact` with deterministic hashes, an immutable
ledger entry, and a profile-class cache
(`libs/v9/prometheus/src/compile.ts:152`).

**Spec-described / not wired into the forge.** Two stages from the monolith
table are _not_ executed inside `forgeLesson`. **Stage 4 "Realize media" (Isis +
Euterpe + Psyche)** is absent — the pipeline accepts `media` as a pre-built
`V9Media` input and defaults it to `{ visuals: [] }`
(`libs/v9/prometheus/src/pipeline.ts:213`); Prometheus imports nothing from
`libs/isis`, `DOMAINS/euterpe`, or `DOMAINS/psyche`. **Stage 9 "Deliver"** is a
separate module, `planLessonDelivery` in `@oshun/v9-chiron`
(`libs/v9/chiron/src/lesson-delivery.ts:59`), not called by the forge. The
monolith also credits **Stage 2 to a "Yemaya HTN planner"** (reusing
`libs/yemaya/agents/planning` + `libs/yemaya/autonomous-pipelines`) and **Stage
3 to `libs/hathor/llm-npc` + `libs/iris/conversation-*`**; in the actual code
`plan.ts` is a self-contained _deterministic_ decomposition that imports none of
Yemaya, and `write.ts` defines an _injected_ writer interface that imports none
of Hathor or Iris. Those directories exist in the repo, but Prometheus does not
depend on them (its `package.json` lists only `@oshun/contracts` and the
`v9-aletheia/atlas/hephaestus/mnemosyne-glue/lesson-gates` packages). Treat
"HTN-planned on Yemaya, written by Hathor over Iris" as architectural framing,
not a wired call graph.

**Provider-gated injected seams (fail-loud, not faked).** Several inputs are
typed boundaries the forge injects rather than fakes: the LLM lesson **writer**
(the Iris boundary, Stage 3 — absent ⇒ `LessonWriterNotConfiguredError`,
`libs/v9/prometheus/src/write.ts:37`), the optional Nous dense **embedder** in
resolution, the LLM-as-judge **quality** panel for G5 (whose default is an
honest _deterministic structural proxy_, `libs/v9/prometheus/src/gates.ts:77`),
and the Metis curriculum-policy + Sekhmet age **safety** seams for G6. Two
further honesty notes: G7 verifies that the hashes and a ledger id are _present
and well-shaped_, not that real cryptographic C2PA signing occurred (the
explorable's `c2paSigned` defaults to `false`); and the supplied ledger/cache
implementations are `InMemory*` (`libs/v9/prometheus/src/compile.ts:65`, `:86`),
durable stores being the caller's to inject.

## The forge contract: input, output, and the block rule

Everything the forge needs arrives in one `ForgeLessonInput`
(`libs/v9/prometheus/src/pipeline.ts:53`). The required fields are the wonder,
the `AtlasStore`, the `learnerProfileClass`, a `seed`, a caller-supplied clock
(`nowUnixMs`, "no fabricated time"), a `signedAtIso` provenance timestamp, an
opaque `runId` (captured as `[x]` evidence, not part of the artifact), the
`explorableBuilder`, and the `retrievalScore`. Everything else is an optional
injected boundary or tuning input:

| Field                                                           | Stage | Role                                                      |
| --------------------------------------------------------------- | ----- | --------------------------------------------------------- |
| `learnerMastery`, `embedder`                                    | 0     | mastery map to scope the frontier; optional Nous reranker |
| `claimTextByConcept`                                            | 1     | override a node's default claim sentence                  |
| `writer`, `misconceptionAnticipator`                            | 3     | the Iris writer boundary; the G3 misconception pass       |
| `explorableBuilder` (required)                                  | 5     | the Hephaestus binding for the chosen concept             |
| `retrievalCard`, `retrievalScore`, `flowHistory`                | 6     | FSRS card + recall score + IRT history for the flow band  |
| `qualityJudge`, `safetyOptions`, `humanReviewed`, `nextWonders` | 7     | the G5/G6 seams and the Theia thread                      |
| `media`, `cache`, `ledger`                                      | 8     | pre-built media (Stage 4 is not run), durable stores      |

The output is a `ForgedLessonResult` (`pipeline.ts:96`): always `runId`,
`blocked`, `blockedGateIds`, the seven `gates`, the `aletheia` report, and the
resolved `conceptSet`; and — _only when every gate passed_ — the compiled
`artifact`, its `cacheKey`, and `servedFromCache`. The block rule is hard and
central: `blockedGateIds` is the list of gate ids whose verdict did not pass,
and if it is non-empty the forge returns _early_ with `blocked: true` and **no
artifact is compiled, delivered, or cached** (`pipeline.ts:232`). A blocked
lesson never reaches Stage 8.

## The flow, stage by stage

```mermaid
flowchart TD
    W["wonder: string"] --> S0
    subgraph forge["forgeLesson (pipeline.ts:111)"]
      S0["Stage 0 — resolve<br/>resolveWonder"] -->|"WonderResolution<br/>conceptSet (prereq-first) + target"| S1["Stage 1 — ground<br/>buildGroundTruth"]
      S1 -->|"V9GroundTruth + numericClaims<br/>(the verified skeleton)"| S2["Stage 2 — plan<br/>planLesson"]
      S2 -->|"LessonPlan: beats, assessmentClaimRef,<br/>explorableConceptId, personaRef"| S3["Stage 3 — write<br/>realizeNarrative"]
      S3 -->|"V9Narrative (claim-constrained)"| S5["Stage 5 — explorable<br/>explorableBuilder"]
      S5 -->|"V9Explorable"| S6["Stage 6 — assess<br/>applyRetrievalCheckpoint"]
      S6 -->|"schedule + assessment + flowChannel"| S7{"Stage 7 — gate<br/>composeGates → G1..G7"}
      S7 -->|"any verdict fails"| BL["blocked: true<br/>no artifact, not cached"]
      S7 -->|"all seven pass"| S8["Stage 8 — compile<br/>compileLesson"]
      S8 -->|"V9LessonArtifact + cacheKey"| OUT["ForgedLessonResult"]
    end
    EMB(["WonderEmbedder — Nous (optional)"]) -.->|rerank| S0
    KER[["Nyx / Kalika kernels (real)"]] -->|recompute| S1
    WR(["LessonWriter — Iris boundary"]) -.->|injected; absent ⇒ fail loud| S3
    SAF(["Metis policy + Sekhmet"]) -.->|injected| S7
    QJ(["QualityJudge — LLM panel; default structural"]) -.->|injected| S7
    MEDIA[/"media: V9Media — passthrough (Stage 4 not run)"/] -.->|input| S8
```

Each stage takes the prior stage's typed artifact and produces the next; the
verified skeleton from Stage 1 is the spine that every later stage references.

### Stage 0 — resolve (Atlas)

`resolveWonder` (`libs/v9/atlas/src/wonder-resolution.ts:86`) runs real Sophia
**BM25** lexical retrieval over the Atlas node text, optionally blended 50/50
with an injected Nous dense embedder, picks the top candidate as the `target`,
then asks the real Mnemosyne engine for that target's prerequisite frontier
filtered by the learner's mastery (`identifyKnowledgeGaps`). The output is the
**minimal teaching set in prerequisite order, then the target** — cognitive-load
discipline, not the whole subtree. A wonder that matches nothing throws
`WonderUnresolvedError` rather than inventing a concept. The deep account is
[./atlas-knowledge-graph.md](./atlas-knowledge-graph.md).

### Stage 1 — ground / solve-first (the verified skeleton)

`buildGroundTruth` (`libs/v9/prometheus/src/ground-truth.ts:43`) walks the
resolved concept set and builds the authoritative skeleton from the _grounded
Atlas nodes_, never from the model. Each `fact` node with grounding pins
contributes a `V9GroundedClaim` carrying up to 16 of its Sophia pins, the node's
`epistemicStatus`, and `truth: 'true'` (`ground-truth.ts:54`). Each STEM
`kernelRef` on a node is **recomputed** through the kernel evaluator — _compute,
don't assert_ — yielding a `V9KernelResult` with a `contentHash` over
`[ref, params, value]` and a parallel `numericClaim` for the G2 accuracy gate
(`ground-truth.ts:64`). The default evaluator is a registry of real in-repo
computations: `kalika:cosmology#ageTodayGyr` calls
`ageTodayGyr(PLANCK_2018_COSMOLOGY)`, `nyx:constants#speedOfLight` returns the
real constant (`libs/v9/aletheia/src/kernel-evaluators.ts:33`). If no grounded
fact node is in scope, the stage **throws** "refusing to forge an ungrounded
lesson" (`ground-truth.ts:86`) — there is nothing to teach, so nothing is
fabricated.

### Stage 2 — plan (deterministic HTN decomposition)

`planLesson` (`libs/v9/prometheus/src/plan.ts:35`) decomposes the skeleton into
a `LessonPlan` deterministically — the same `(skeleton, seed)` always plans the
same lesson, which is half the determinism lever. The beat outline is
`Hook → Establish[i] for each grounded claim → "Why it matters" → "The next question"`
(`plan.ts:39`). The retrieval checkpoint is pinned to the **most-credible**
claim — the claim whose highest pin credibility is greatest
(`assessmentClaimRef`, `plan.ts:47`) — so the recall item probes the
best-supported fact. The explorable defaults to the last concept in the set (the
target), and the persona defaults to `chiron:warm-generalist`
(`libs/v9/chiron/src/persona.ts:35`). Despite the monolith's "Yemaya planner"
framing, this is a self-contained function; it is HTN in spirit (decompose a
goal into ordered beats), not a call into the Yemaya planning library.

### Stage 3 — write (the injected Iris boundary)

`realizeNarrative` (`libs/v9/prometheus/src/write.ts:77`) is where the LLM
enters — and where the forge refuses to fabricate. With no `writer` wired it
throws `LessonWriterNotConfiguredError` (`write.ts:78`) rather than emit a
hand-built lesson. The `LessonWriter` is a typed boundary:
`write({ wonder, plan, groundTruth }) → LessonDraft`. The realization is then
**constrained to the grounded skeleton**: every Socratic turn's `claimRefs` must
index a real claim, and an out-of-range ref throws (`write.ts:87`); a draft with
no beats is rejected. A **misconception-anticipation pass** (the G3 input)
merges the writer's flagged misconceptions with a default anticipator that flags
every non-`established` claim as misread-prone and pairs it with its grounded
correction (`defaultMisconceptionAnticipator`, `write.ts:49`), deduped by text
with every `claimRef` validated. The result is a `V9Narrative` whose every
assertion traces back to a verified claim.

### Stage 4 — realize media (not run by the forge)

The monolith assigns visuals (Isis), score (Euterpe), and the Chiron face/voice
(Psyche) to a Stage 4. In code the forge skips it: `media` is a pre-built input
defaulting to `{ visuals: [] }` (`pipeline.ts:213`). Media generation is
therefore out-of-band today — the forge will _carry_ and _hash_ whatever media
you hand it (it feeds the `surfaceHash`), but it does not generate it.

### Stage 5 — build explorable (Hephaestus)

The forge calls the injected `explorableBuilder(plan.explorableConceptId)`
(`pipeline.ts:147`) to bind a manipulable surface to the chosen concept. The P1
builders are real computation: a Kalika orbit wrapping the velocity-Verlet
symplectic integrator, or a Nyx sky integrating the real Sun position. The load
-bearing field is the explorable's **success state**, whose `reachable` flag is
set true _only_ when a kernel result or headless verifier actually demonstrated
it — never asserted blind. Stage 7's G4 verdict is computed from exactly these
explorables; see [./hephaestus-explorables.md](./hephaestus-explorables.md).

### Stage 6 — assess & schedule (the Mnemosyne loop)

`applyRetrievalCheckpoint` (`libs/v9/mnemosyne-glue/src/mastery-feedback.ts:42`)
turns the `retrievalScore` into an FSRS review grade
(`≥0.95 easy, ≥0.75 good, ≥0.5 hard, else again`, `mastery-feedback.ts:21`),
runs the real `@mnemosyne/core` `fsrsReview` over the concept's trace card, and
schedules the next review **at the forgetting frontier** — the moment recall is
hardest-but-possible (the testing effect, `knowledge-trace.ts:62`). The forge
records the schedule (`mnemo:<concept>:int<days>d`, `intervalDays`,
`nextReviewAtIso`) and an `assessment` whose retrieval prompt is
`Recall: <the central claim text>` keyed to `plan.assessmentClaimRef`. If
`flowHistory` is supplied, `tuneDifficultyToFlow`
(`libs/v9/mnemosyne-glue/src/flow-channel.ts:70`) uses the IRT ability MLE to
recommend the next item's difficulty and classify it into Csíkszentmihályi's
channel (success probability in `[0.6, 0.85]` is _flow_); that channel is handed
to G3. The recall result also projects to a `MasteryLevel` that closes the loop
back to Atlas (`buildAtlasMasteryMap`, `mastery-feedback.ts:73`), so a mastered
concept drops out of the next wonder's frontier.

### Stage 7 — gate (the seven contract verdicts)

`composeGates` (`libs/v9/prometheus/src/gates.ts:132`) assembles the canonical
`V9Gates` from four real evaluations. **G1 grounding, G2 accuracy, and G6
safety** come from `runAletheia`, which runs the three in parallel
(`libs/v9/aletheia/src/aletheia.ts:47`): G1 blocks any claim without a pin of
credibility ≥ 0.5 (`grounding-gate.ts:43`); G2 recomputes every numeric claim
and **blocks on an unknown kernel ref or a tolerance miss** — "you cannot ship a
number you cannot reproduce" (`accuracy-gate.ts:66`); G6 requires an
epistemic-status label on every claim, applies the injected Metis/Sekhmet seams,
and forces a human-review checkpoint for the high-stakes disciplines `religion`,
`psychology`, and `neuroscience` (`safety-gate.ts:19`, `:137`). **G3 pedagogy**
(`gates.ts:36`) fails on a prerequisite-coverage gap, on _zero_ anticipated
misconceptions, or on a difficulty out of the flow band. **G4 completeness** is
the Hephaestus DoD — ≥1 computed-kernel explorable that reached its success
state (`evaluateExplorableDoD`, `explorable-runtime.ts:24`). **G5 quality**
(`gates.ts:88`) thresholds a judge score at 0.7; the default judge is an honest
structural proxy that rewards ≥3 beats, ≥1 Socratic turn, ≥1 misconception, and
a "next question" beat (`gates.ts:77`) — a real LLM panel is the injected
`qualityJudge`. **G7 provenance** (`gates.ts:105`) checks the skeleton/surface/
cacheKey hashes are 64-hex and the ledger id is present.

On failure, Aletheia attaches a **regenerate-with-direction** routing label to
each failing gate — `reground`, `regenerate-with-corrected-ground-truth`,
`re-scope`, or `block-or-escalate` (`libs/v9/aletheia/src/verdict.ts:11`) — and
the forge returns those in the `aletheia` report. Note the honest boundary: the
forge _computes and returns_ the routing, but does **not** itself run an
automated regenerate loop or HITL round; the caller acts on the actions. The
two-framing detail (this canonical G1–G7 vs the separate
`@oshun/v9-lesson-gates` suite, which Prometheus does not call) is in
[./seven-gates-and-aletheia.md](./seven-gates-and-aletheia.md).

### Stage 8 — compile & cache (only a passing lesson)

`compileLesson` (`libs/v9/prometheus/src/compile.ts:152`) runs only when every
gate passed. It first checks the **profile-class cache** by
`cacheKeyFor([...conceptSet].sort(), learnerProfileClass, seed)` — a sha256 over
the order-independent concept set, the profile _class_, and the seed
(`compile.ts:31`); a hit returns the cached artifact with
`servedFromCache: true` and no recompilation. On a miss it stamps the C2PA
bundle — `skeletonHash = sha256(groundTruth)`,
`surfaceHash = sha256([narrative, media])`, and a content-derived
`ledgerEntryId` (`ledger:<cacheKey…>`, not run-derived, so the same inputs
reproduce the same id) — then parses the result through `V9LessonArtifactSchema`
(`lesson.ts:190`), so the contract's own `superRefine` re-validates that every
`claimRef` indexes a real claim. The entry is appended to the immutable ledger
(which never overwrites, `compile.ts:67`) and cached. Back in the forge, a
defensive check asserts the contract's `isV9LessonPublishable` agrees with the
gate sum, throwing if they ever disagree (`pipeline.ts:257`). Because
`previewProvenanceBundle` is deterministic and called both for the G7 gate and
inside compile, G7 verifies exactly the bundle Stage 8 stamps. The full artifact
anatomy is in [./lesson-artifact.md](./lesson-artifact.md).

### Stage 9 — deliver (a separate Chiron module)

Delivery is _not_ part of `forgeLesson`. `planLessonDelivery`
(`libs/v9/chiron/src/lesson-delivery.ts:59`) turns a gated artifact into a
Chiron-fronted delivery plan under an integrity mode and a resolved delivery
tier, and it **refuses to deliver an ungated lesson** (`UngatedLessonError`,
`:48`) — nothing un-passed reaches a learner. The live voice/avatar runtimes are
themselves injected boundaries. See
[./delivery-and-cross-cutting.md](./delivery-and-cross-cutting.md).

## A worked forge: "how old is the universe?"

The end-to-end test (`libs/v9/prometheus/src/pipeline.spec.ts`) is the honest
worked example, because it exercises the real composition rather than a
narration. An `AtlasStore` is seeded with one grounded STEM fact bound to the
real Kalika cosmology kernel (`kalika:cosmology#ageTodayGyr`, pinned to
Planck-2018) and a `cosmology-basics` prerequisite. The forge runs with
`seed: 1234`, a fixed clock, profile class `curious-adult`, a deterministic
stand-in writer (no live LLM in the test), the real
`buildKalikaOrbitExplorable`, a `retrievalScore` of 0.9, and a three-item flow
history. The assertions establish the contract end-to-end:

- **A published, gated lesson.** `blocked` is `false`, all seven gates pass
  (`isV9LessonPublishable` is true), and the skeleton holds the _recomputed_
  value — `groundTruth.computedValues` contains the age between 13.6 and 14.0
  Gyr, produced by the Friedmann solver, never stored as the literal "13.8".
- **A real, reached explorable.** The shipped explorable's `kind` is
  `computed-kernel` and its `successState.reachable` is `true` (the symplectic
  integrator's energy drift was actually measured under tolerance).
- **Determinism.** Two forges of the same inputs produce identical
  `skeletonHash`, `surfaceHash`, `cacheKey`, and artifact `id`.
- **The cost lever.** A second forge against the same cache returns
  `servedFromCache: true` with the same lesson id — gated once per profile
  class, served many times.
- **The block path.** When the injected Sekhmet seam denies the content, G6
  fails, `blocked` is `true`, `blockedGateIds` contains `G6`, the `artifact` is
  undefined, and the cache holds nothing — a blocked lesson is never cached.
- **Fail-loud.** With `writer: undefined`, the forge rejects with
  `LessonWriterNotConfiguredError` instead of fabricating prose.

## Failure modes, configuration, and determinism

The forge is built to **refuse rather than fabricate**, and the refusals are
specific and code-located: an unresolvable wonder throws `WonderUnresolvedError`
(`wonder-resolution.ts:56`); nothing to ground throws "refusing to forge an
ungrounded lesson" (`ground-truth.ts:86`); a writer that cites a claim outside
range throws (`write.ts:87`); an unreproducible number blocks G2
(`accuracy-gate.ts:66`); an unreachable explorable blocks G4
(`explorable-runtime.ts:33`); any gate failure blocks the whole lesson with no
compile and no cache (`pipeline.ts:232`); and a contract/gate disagreement at
the finish line throws defensively (`pipeline.ts:257`).

Determinism is both a provenance and a cost lever, and it rests on three
explicit choices in `ForgeLessonInput`: a `seed`, a caller-supplied `nowUnixMs`
and `signedAtIso` (no clock is read inside the forge), and a profile-_class_
cache key rather than a learner id. Numeric tolerances are explicit rather than
implied — G2 defaults to a 1e-3 relative match while each `V9KernelResult`
carries its own 1e-6 tolerance (`lesson.ts:51`) — which is what makes "computed,
not asserted" a checkable property and not a slogan. Everything that could vary
by environment — the writer, the embedder, the quality judge, the safety policy,
the ledger, and the cache — is an injected boundary, so a forge is reproducible
exactly to the extent its caller wires deterministic seams.

## Related

- Section hub: [../V9_ARCHITECTURE.md](../V9_ARCHITECTURE.md); orientation in
  [./overview.md](./overview.md)
- [./atlas-knowledge-graph.md](./atlas-knowledge-graph.md) — Stage 0 resolution
  and the knowledge spine
- [./hephaestus-explorables.md](./hephaestus-explorables.md) — Stage 5 and the
  G4 computed-kernel DoD
- [./seven-gates-and-aletheia.md](./seven-gates-and-aletheia.md) — Stage 7 gate
  semantics and the two gate framings
- [./lesson-artifact.md](./lesson-artifact.md) — the Stage 8 delivery contract
- [./delivery-and-cross-cutting.md](./delivery-and-cross-cutting.md) — Stage 9
  delivery, governance, determinism, cost
- [./subsystem-map-and-reuse-ledger.md](./subsystem-map-and-reuse-ledger.md) and
  [./six-layer-reuse-stack.md](./six-layer-reuse-stack.md) — the reuse
  accounting
