The Atlas is V9's unified knowledge spine — the single
ConceptNode/ConceptEdge graph that makes "start from a wonder, pull the
thread" possible — and wonder resolution is the function that turns a
learner's free-text question ("why is the night sky dark?") into the minimal,
prerequisite-ordered set of concepts a lesson should teach right now. It is the
first thing that runs when anyone enters V9: the experience layer's front door
(buildWonderFrontDoor), the Prometheus forge (forgeLesson stage 0), and
Theia's thread continuation all stand on it. The Atlas is also the only one of
V9's three net-new structures (Atlas graph, Hephaestus explorable, Lesson
artifact) that is a graph — and the design doc calls it "the single largest
net-new build" (graph-unifier.ts header).
What keeps it buildable rather than aspirational is the same decision that runs
through all of V9: compose the real engines, reinvent nothing. The Atlas
owns no retrieval algorithm and no graph-reasoning algorithm. Free-text matching
is the real Sophia BM25 index (@sophia/semantic-search); prerequisite closure,
topological sort, and gap analysis are the real Mnemosyne KnowledgeGraph
(@mnemosyne/core). The package's own package.json declares exactly three
runtime dependencies — @mnemosyne/core, @oshun/contracts,
@sophia/semantic-search — and the six implementation modules under
libs/v9/atlas/src/ are the connective tissue that maps three source dialects
into one content-addressed store and reads the answers back out of those
engines. This page is the deep companion to the section
overview; the hub for the set is
../V9_features.md.
What ships, honestly#
The Atlas package is real and tested. All six implementation modules
(atlas-store.ts, source-adapters.ts, graph-unifier.ts,
grounded-leaves.ts, wonder-resolution.ts, grounding-validator.ts) are
implemented and exercised by three spec files holding 21 test cases
(atlas-graph.spec.ts — 11, wonder-resolution.spec.ts — 5,
grounded-leaves.spec.ts — 5). The tests run the real substrates, not mocks:
resolveWonder actually builds a Sophia BM25 index and queries the Mnemosyne
knowledge graph, and grounded-leaves.spec.ts actually imports
ageTodayGyr(PLANCK_2018_COSMOLOGY) from @kalika/cosmology and asserts the
bound value lands in 13.6 < age < 14.0 Gyr before declaring the node grounded.
Two things are honest seams, not stubs. The dense reranker
(WonderEmbedder) is the Nous model boundary: it is an optional injected
interface, and when it is absent resolveWonder runs BM25-only — it does
not fabricate embeddings. A wonder that matches nothing throws
WonderUnresolvedError rather than inventing a concept; the front-door
view-model catches that throw and reports unresolved: true so the UI asks for
a rephrase instead of hallucinating.
Three claims in the originating design doc are spec-ahead-of-code, and this page corrects them rather than restating them:
- The monolith says "Theia decides whether to answer directly, scaffold, or
open a thread" (
V9_features.md§1). The signal for that decision is computed and surfaced —buildWonderFrontDoorreturnsprerequisiteCount(described in code as "the scaffolding depth") and the fullconceptSet— but there is no single shipped three-wayanswer | scaffold | threaddecision function. The data a director would branch on exists; the explicit branch is a view-model signal, not a gate. V9KernelSchemaadmits four kernels (nyx,kalika,nisaba,demeter), butgrounded-leaves.tsships binders for only three (kalikaKernelLeaf,nyxEphemerisLeaf,nisabaSourceLeaf), and the Aletheia kernel registry (kernel-evaluators.ts) wires nodemeter:ref today. A Demeter-computed leaf is admissible by contract but unbuilt.- The Atlas is a runtime structure assembled from injected source graphs, not a shipped reality-corpus. The adapters, the unifier, the leaf binders, and the invariants are all real; the actual concept population is supplied by the caller (and exercised by the tests with small fixtures). There is no large persisted "map of reality" dataset checked into this package.
The concept graph: nodes, edges, and two invariants#
The vocabulary lives in libs/contracts/src/v9/concept-graph.ts and
primitives.ts. A V9ConceptNode is
{ id, label, aka, wonderAxis, discipline, kind, groundingPins, kernelRefs, epistemicStatus, safetyPolicyRef? },
and the whole point of the type is that two structural rules are baked into
V9ConceptNodeSchema via .superRefine, so a node that breaks them fails to
parse:
- Invariant 1 — grounding. A
factnode must carry ≥1V9SophiaPin(a real source id + locator + acredibilityin[0,1]). No ungrounded fact exists in the graph. - Invariant 2 — computation. A STEM
fact/principle/procedurenode must carry akernelRefinstead of a stored number. The STEM set is decided byv9NodeRequiresKernel(discipline, kind), which is true exactly when the discipline is inV9_STEM_DISCIPLINES(astronomy, physics, mathematics, chemistry, earth-science) and the kind is one of the three value-bearing kinds. The node holds a callable ref likekalika:cosmology#ageTodayGyr, not the literal13.8 Gyr, so the value is reproduced at gate time and can never silently drift.
The same two checks are also exposed as a non-throwing function,
checkV9ConceptNodeInvariants(node), which returns every violation as a
structured { nodeId, invariant, message }. That is the build-validator path:
the parsing form fails fast at the door, the aggregating form lets the Atlas
build report all violations across the whole graph at once.
Identity is content-addressed. V9ConceptIdSchema accepts either a stable
namespaced slug (nyx:olbers-paradox, kalika:age-of-the-universe) or a 64-hex
content hash (cn:<sha256>), enforced by the regex
^(cn:[a-f0-9]{64}|[a-z][a-z0-9-]*:[a-z0-9][a-z0-9._-]*)$. The slug form is
what a single source mints; the cn: form is what the unifier mints when it
collapses the same concept seen across multiple sources (below). Edges
(V9ConceptEdgeSchema) carry a type from the nine-value
V9ConceptEdgeTypeSchema —
prerequisite, related, part_of, generalizes, specializes, enables, conflicts, complements, bridges
— and a weight in [0,1]; bridges is the cross-axis thread V9 adds
(cosmology ↔ deep-time), and self-loops (from === to) are rejected by the edge
schema's own superRefine.
Building the Atlas: three dialects into one spine#
V9 unifies three existing graphs, each of which speaks its own dialect, so
source-adapters.ts provides one pure transform per source into the shared
V9ConceptNode/V9ConceptEdge vocabulary:
fromMetisConceptGraph(metis:namespace) maps the Metis concept-graph dataclass shape. Itstopic/skill/fact/procedure/principletypes already match V9 kinds, and — crucially — it grounds fact nodes from theirevidence_citation_ids, turning each citation into aV9SophiaPinat a caller-supplied default credibility (0.7). This is the seed of Invariant 1.fromSophiaKnowledgeGraph(sophia:namespace) maps the Sophia KG's entities (figures, texts, traditions) to V9 topics — organizational nodes, not atomic facts, so they need no grounding pin — and translates relation types (PART_OF → part_of,OPPOSITE_OF → conflicts,DERIVED_FROM → specializes, elserelated).fromMnemosynePrereqGraph(mnemosyne:namespace) maps the real@mnemosyne/coreKGNode/KGEdgetypes and supplies the prerequisite skeleton.
graph-unifier.ts's unifyAtlas then does the merge that is "the single
largest net-new build." It groups every draft node by atlasSlug(label) (a
lowercase, NFKD-normalized, hyphenated key), mints a canonical id
cn:<sha256(labelKey)>, and collapses the group into one node: it takes the
most specific kind
(KIND_PRIORITY = principle > procedure > fact > skill > topic), unions and
de-dupes the grounding pins (by pinId) and kernel refs (by ref), folds
sibling labels into aka, and carries any safetyPolicyRef forward. The
honesty rule is explicit in the header and verified by a test: a merged node
that still violates an invariant (e.g. an ungrounded fact that no source
could ground) is not coerced — it is reported in skipped with the parse
error's reason and left out of the store, "a logged truncation, not a fabricated
grounding." Edges are then remapped onto the canonical ids, de-duped by
(from, to, type), and any edge that touches a skipped or unknown endpoint — or
that became a self-loop after the merge — is counted in droppedEdgeCount. The
whole operation returns an AtlasUnificationResult with the raw vs merged node
counts so a build can see exactly how much collapsing happened.
The merged nodes land in AtlasStore (atlas-store.ts), and the store is
where the invariants become a runtime gate: addNode runs
V9ConceptNodeSchema.parse(node) before inserting, so an ungrounded fact or a
kernel-less STEM node is rejected at the door; addEdge refuses an edge whose
endpoints are not already nodes. The store deliberately does not reimplement
prerequisite reasoning — its toMnemosyneGraph(learnerMastery?) method
projects every node and edge into a fresh @mnemosyne/core KnowledgeGraph
(mapping prerequisite → prerequisite, part_of → part_of, everything else →
related_to, and annotating each node with its mastery), and prerequisitesOf
simply reads getPrerequisites back off that projection. This is the reuse
seam: closure, topo-sort, and gap analysis are the Mnemosyne engine's, run over
an Atlas projection.
Finally, grounded-leaves.ts is how the real domain kernels become Atlas
nodes. kalikaKernelLeaf, nyxEphemerisLeaf, and nisabaSourceLeaf each
build-and-validate a node whose kernelRef.ref points at an in-repo callable
(kalika:cosmology#ageTodayGyr, nyx:ephemeris#sun.position) — the node holds
the reference, never the value, which Aletheia recomputes at G2. The
build-time enforcement is grounding-validator.ts: validateConceptNodes
counts fact/STEM/grounded-fact nodes and collects violations, and
assertAtlasGrounded(atlas) throws on any violation so a build pipeline
blocks rather than warns. (Because the store already enforces at insert, this
gate is belt-and-suspenders: a validly-built Atlas always passes it.)
Resolving a wonder#
resolveWonder(wonder, atlas, options) in wonder-resolution.ts is the
function the rest of V9 calls. It runs four steps:
- Lexical retrieval (Sophia BM25). A fresh
createBM25LexicalIndex()is loaded with one document per Atlas node, where the indexed text islabel + aka + discipline + wonderAxis. The query is the raw wonder; the index returns up tomax(topK·4, 16)hits scored by the real BM25 engine (k1 = 1.2,b = 0.75, IDF with length normalization over an inverted index). - Optional hybrid rerank (the Nous boundary). If an
embedderis supplied,resolveWonderembeds[wonder, ...allNodeTexts]in one call and re-scores every node as(1 − denseWeight)·bm25 + denseWeight·cosine(denseWeightdefault0.5). This is what lets a wonder with zero lexical overlap — "the beginning of everything" — still reach the Big Bang node, which a pure BM25 path would miss; thewonder-resolution.spec.tstest proves exactly that with a toy embedder. Without an embedder, ranking is BM25 alone. (Blending a saturating BM25 score with a cosine in[0,1]is a deliberate, documented approximation, not a learned fusion.) - Mastery-filtered prerequisite frontier (Mnemosyne). The target's
transitive prerequisites are computed on the Mnemosyne projection, then
identifyKnowledgeGaps(mastery, target, masteryFloor)drops any prerequisite the learner already holds at or above the floor ('intermediate'by default; the Mnemosyne ladder isnovice → beginner → intermediate → advanced → expert → master). What remains is the real frontier — only what this learner still needs. - The minimal teaching set.
orderByPrerequisiteruns Mnemosyne'stopologicalSort(Kahn's algorithm, deterministically tie-broken) and filters it to the gap set, then appends the target:conceptSet = [...orderedGaps, target.id]. The output is prerequisite-first, target-last — cognitive-load discipline, encoded.
Worked example — "why is the night sky dark?" (Olbers' paradox). With an
Atlas of three nodes (How light travels, The universe has a finite age,
Olbers' paradox) and two prerequisite edges into Olbers, BM25 picks a:olbers
as the target. A learner who knows nothing gets the full frontier
[a:finite-age, a:light] and a conceptSet of length 3 ending in a:olbers. A
learner already advanced on a:light gets the frontier trimmed to
[a:finite-age] and a two-element teaching set [a:finite-age, a:olbers].
These are the literal assertions in wonder-resolution.spec.ts.
The front door, and the threads it opens#
buildWonderFrontDoor (libs/v9/experience/src/wonder-front-door.ts) is the
tested view-model the "ask a wonder" screen binds to. It wraps resolveWonder
in a try/catch: on success it returns the disambiguated resolved candidate,
the ranked candidates, the prerequisiteCount ("the scaffolding depth"), the
conceptSet, and a few suggestedWonders; on a thrown WonderUnresolvedError
it returns a fully-formed unresolved: true state with empty fields, so the UI
degrades to "try rephrasing" rather than crashing or faking a concept.
The two "surprise me" mechanisms are worth distinguishing, because they are
different code with different intent. The front door's suggestFrontier is a
cheap adjacency walk: a concept that some edge leads to from inside the
teaching set, but that is not itself in the set — the immediate next thing.
Theia owns the richer one: surpriseMe (in libs/v9/theia/src/theia.ts) ranks
unexplored concepts by readiness — the fraction of a concept's prerequisites
the learner has already mastered — surfacing "the frontier of what they almost
know." Theia also owns thread continuation (nextWonders walks the Atlas edges
in the priority order enables → bridges → specializes → related) and the
emotional arc; those are documented in
threads-and-mastery-loop.md.
The conceptSet does not stop at the front door. It is the seam into the lesson
forge: V9LessonArtifact (libs/contracts/src/v9/lesson.ts) carries
conceptSet: z.array(V9ConceptIdSchema).min(1).max(64) as part of its cache-key
digest, and the Prometheus forge's stage 0 is resolveWonder. So the same
resolution that decides what to show on the front door also seeds what gets
forged, gated, and cached — see
prometheus-lesson-forge.md.
Edge cases, failure modes, and configuration#
The Atlas's correctness story is mostly about what it refuses to do:
- Empty Atlas / no match ⇒ fail loud.
resolveWonderthrowsWonderUnresolvedErrorif the store has no nodes or nothing scores above zero. It never returns a "best guess" concept. - No lexical overlap ⇒ needs the Nous seam. A wonder phrased entirely
outside a concept's vocabulary will not be found by BM25 alone; bridging it is
the injected
embedder's job, and the system is honest that the seam is required rather than approximating semantics with a stand-in. - Cyclic prerequisite graph ⇒ graceful fallback.
topologicalSortthrows on a cycle (the Mnemosyne engine refuses to invent an order);orderByPrerequisitecatches that and falls back to the given order rather than crashing the resolution. - Ungrounded merges ⇒ skipped, not coerced. As above, a merged node that
cannot satisfy an invariant is dropped into
skipped; the unifier never back-fills a fake pin or kernel to force it through. - Dangling / self-loop edges ⇒ dropped, counted. Both the adapters and the
unifier drop edges with a missing endpoint or
from === to, and the unifier surfaces the count indroppedEdgeCountso truncation is visible. - The
demetergap is admitted, not hidden. The contract permits a Demeter kernel, but no binder or evaluator exists yet — a STEM leaf needing Demeter would have to wait on that wiring, which the registry will reject loudly (an unknown kernel ref is a G2 accuracy-gate failure) rather than silently passing.
Configuration is the WonderResolutionOptions bag: learnerMastery (the
ReadonlyMap<V9ConceptId, MasteryLevel> that drives gap filtering), topK
(default 5), masteryFloor (default 'intermediate'), embedder (the Nous
boundary), and denseWeight (default 0.5, clamped to [0,1]). One
implementation detail worth knowing: because V9ConceptId and Mnemosyne's
KGNodeId are both opaque string ids, the store performs a structural cast
between them when it projects into the engine. That cast is sound here precisely
because the Atlas's content-addressed ids are stable and unique across sources —
the same property the unifier exists to guarantee.
How it connects#
The Atlas is the substrate every other V9 subsystem reads from. Aletheia
recomputes the values its kernelRefs point at (G1/G2/G6); Hephaestus and
Prometheus consume the conceptSet; Theia walks its edges for threads and
"surprise me"; the mastery loop feeds learner MasteryLevels back in to
re-scope the next wonder. For the surrounding mechanics:
- overview.md — the product and the five nested primitives
- subsystem-map-and-gates.md — the ownership ledger and the seven-gate mechanics
- prometheus-lesson-forge.md — the nine forge
stages (stage 0 is
resolveWonder) - chiron-and-hephaestus.md — the teacher and the explorable workshop
- threads-and-mastery-loop.md — Theia threads, the emotional arc, and FSRS mastery feeding back into wonder-scoping
- braid-commons-and-films.md — the science↔human braid, Agora, and explainer films
- governance-and-boundaries.md — entitlements, the anti-metric, determinism, and the non-goals
- Hub: ../V9_features.md