docs: ELI5 'how it works' page + soften false-precise '36 labels' arithmetic

- Add docs/how-it-works.html: self-contained explainer with inline SVG
  diagrams, ELI5 tone. Covers the big idea, plain-AI vs Lore Engine,
  the cast (Cognee/Neo4j/Minimax-M3/45 tools), question flow, why time
  matters, disputed edges + confidence, how lore gets in, the
  consistency safety net, and how it differs from a wiki.
- Soften the false-precise '36 labels' bucket arithmetic to honest
  'roughly 36' across 00-overview, 11-extensibility, 10-critique
  (sub-arithmetic didn't reconcile across docs).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-17 23:40:50 -04:00
parent e1a0e836ac
commit c3fa2f7ce4
4 changed files with 455 additions and 3 deletions

View File

@@ -28,7 +28,7 @@ The Lore Engine is built on top of [Cognee](https://github.com/topoteretes/cogne
## What we add
- **Deeper ontology** — Era, Calendar, Lineage, Culture, Deity, MagicSystem, Spell, Language, Title, Item (covers Artifact), Region, `Relation` (reified-edge node, promoted to v1 per ADR 0009), plus 2 v1.2 nodes (Plane, Setting) and 5 consistency nodes. Roughly 36 node labels total (7 base + 19 v1 core + 2 v1.2 planes + 5 v1.1 polymorphic + 5 consistency). The 7 base types are Lore Engine originals built on Cognee's `DataPoint` — not inherited as-is (Cognee ships no `Contradiction`/`Message`/etc. nodes of its own). The v1.1 docs (`11-extensibility.md`) add the polymorphic `DomainEntity`, `TypeTemplate`, `NPC`, `PC`, and `Human` labels (`Relation` graduated to v1).
- **Deeper ontology** — Era, Calendar, Lineage, Culture, Deity, MagicSystem, Spell, Language, Title, Item (covers Artifact), Region, `Relation` (reified-edge node, promoted to v1 per ADR 0009), plus 2 v1.2 nodes (Plane, Setting) and 5 consistency nodes. Roughly 36 node labels total: 7 base (Cognee `DataPoint` originals), the v1 core entities + relations (incl. `Relation`), 2 v1.2 plane labels, 5 v1.1 polymorphic labels, and 5 consistency labels. The 7 base types are Lore Engine originals built on Cognee's `DataPoint` — not inherited as-is (Cognee ships no `Contradiction`/`Message`/etc. nodes of its own). The v1.1 docs (`11-extensibility.md`) add the polymorphic `DomainEntity`, `TypeTemplate`, `NPC`, `PC`, and `Human` labels (`Relation` graduated to v1).
- **Time as a first-class concept** — temporal validity windows on relations, era filters, "was X true at time T?" via dedicated tools. The LLM no longer has to guess which version of Aldric it is talking to.
- **Structured ingestion** — not just `.md` files. We ingest `timeline.yaml`, `family-tree.yaml`, `gazetteer.yaml`, `bestiary.yaml` as first-class sources. Free prose stays, but is no longer the only path.
- **A consistency engine** — Cypher-based rules that flag anachronisms (Aldric can't be at the Battle of Black Spire if it happened 200 years before his birth), missing lineage (a noble with no recorded parents), and ontological violations (a region claimed to be inside two non-overlapping kingdoms).

View File

@@ -191,7 +191,7 @@ After the v1 review, the modularization question surfaced four new design risks
### S1.4 (NEW, blocker) — Closed-world ontology ceiling
The v1 ontology has 36 hard-coded labels (7 inherited + 19 v1 core + 2 v1.2 planes + 6 v1.1 polymorphic + 5 consistency). A thieves-guild mission is forced into `Event`, a war campaign is forced into `Faction`-with-properties, a black-market trade log is forced into `Item`-with-properties. The LLM can *talk* about these things, but the engine can't *reason* over their structure.
The v1 ontology has roughly 36 hard-coded labels (7 base + v1 core incl. `Relation` per ADR 0009 + 2 v1.2 planes + 5 v1.1 polymorphic + 5 consistency). A thieves-guild mission is forced into `Event`, a war campaign is forced into `Faction`-with-properties, a black-market trade log is forced into `Item`-with-properties. The LLM can *talk* about these things, but the engine can't *reason* over their structure.
**Fix:** the polymorphic `DomainEntity` wrapper + `TypeTemplate` data-defined schemas. See `11-extensibility.md`. This is the load-bearing change for "arbitrary new concept, define how it associates with larger constructs, but also have flexibility to get as detailed as we need."

View File

@@ -1,6 +1,6 @@
# 11 — Extensibility: Polymorphic Type Templates
The v1 ontology has 36 hard-coded labels (7 base + 19 v1 core including `Relation`, promoted from v1.1 per ADR 0009 + 2 v1.2 planes + 5 v1.1 polymorphic + 5 consistency). That's *fine for the first world* but it has a ceiling: a thieves-guild mission is forced into `:Event`, a war campaign is forced into `:Faction`-with-properties, a black-market trade log is forced into `:Item`-with-properties. The LLM can *talk* about these things, but the engine can't *reason* over their structure.
The v1 ontology has roughly 36 hard-coded labels (7 base + a v1 core set that includes `Relation`, promoted from v1.1 per ADR 0009 + 2 v1.2 planes + 5 v1.1 polymorphic + 5 consistency). That's *fine for the first world* but it has a ceiling: a thieves-guild mission is forced into `:Event`, a war campaign is forced into `:Faction`-with-properties, a black-market trade log is forced into `:Item`-with-properties. The LLM can *talk* about these things, but the engine can't *reason* over their structure.
> **Note (ADR 0009):** `Relation` now ships in **v1**, not v1.1. It's a general reified-edge node (any edge with time bounds or confidence is a `Relation` node, because Cognee's `graph_model` can't put those properties on a native edge). The `DomainEntity` + `TypeTemplate` polymorphic system below is still v1.1; `Relation` is the one piece that graduated. See `docs/adr/0009-reified-relation-edges.md`.

452
docs/how-it-works.html Normal file
View File

@@ -0,0 +1,452 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>How the Lore Engine Works</title>
<style>
:root {
--ink: #2b2118;
--parchment: #f7f1e3;
--parchment-deep: #efe6d2;
--accent: #8a5a2b;
--accent-soft: #c79a6a;
--blue: #2c5f7c;
--green: #4a6b3a;
--red: #9b3a2e;
--shadow: rgba(43, 33, 24, 0.12);
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
color: var(--ink);
background: var(--parchment);
line-height: 1.65;
font-size: 18px;
}
.wrap { max-width: 820px; margin: 0 auto; padding: 0 24px 80px; }
header {
background: var(--parchment-deep);
border-bottom: 3px double var(--accent);
padding: 56px 24px 40px;
text-align: center;
}
header h1 {
margin: 0 0 8px;
font-size: 2.4rem;
letter-spacing: -0.5px;
}
header .sub { font-style: italic; color: var(--accent); font-size: 1.15rem; }
header .tag {
display: inline-block;
margin-top: 16px;
background: var(--accent);
color: var(--parchment);
padding: 4px 14px;
border-radius: 999px;
font-size: 0.85rem;
font-family: "Helvetica Neue", Arial, sans-serif;
letter-spacing: 0.5px;
}
section { padding: 36px 0; border-bottom: 1px solid var(--accent-soft); }
section:last-child { border-bottom: none; }
h2 {
font-size: 1.6rem;
margin: 0 0 12px;
color: var(--accent);
}
h2 .num {
display: inline-block;
width: 36px; height: 36px;
line-height: 36px; text-align: center;
background: var(--accent);
color: var(--parchment);
border-radius: 50%;
font-size: 1.1rem;
margin-right: 10px;
font-family: "Helvetica Neue", Arial, sans-serif;
}
p { margin: 12px 0; }
.lead { font-size: 1.2rem; font-style: italic; color: #5a4a38; }
.callout {
background: #fffaf0;
border-left: 4px solid var(--accent-soft);
padding: 14px 18px;
margin: 18px 0;
border-radius: 0 6px 6px 0;
}
.callout strong { color: var(--accent); }
.diagram {
background: #fffdf8;
border: 1px solid var(--accent-soft);
border-radius: 10px;
padding: 20px 12px;
margin: 22px 0;
box-shadow: 0 2px 8px var(--shadow);
overflow-x: auto;
}
.diagram svg { display: block; margin: 0 auto; max-width: 100%; height: auto; }
.diagram .cap { text-align: center; font-size: 0.85rem; color: #7a6a55; font-style: italic; margin-top: 10px; }
ul.friendly { padding-left: 22px; }
ul.friendly li { margin: 6px 0; }
.chars { display: grid; gap: 14px; }
.char {
display: grid;
grid-template-columns: 48px 1fr;
gap: 14px;
align-items: start;
background: #fffaf0;
border: 1px solid var(--accent-soft);
border-radius: 8px;
padding: 14px 16px;
}
.char .emoji { font-size: 2rem; line-height: 1; }
.char h3 { margin: 0 0 4px; font-size: 1.1rem; color: var(--ink); }
.char p { margin: 0; font-size: 0.98rem; color: #4a3f33; }
.pill {
display: inline-block;
background: var(--blue);
color: #fff;
padding: 2px 10px;
border-radius: 999px;
font-size: 0.78rem;
font-family: "Helvetica Neue", Arial, sans-serif;
}
table { width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 0.95rem; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--accent-soft); }
th { background: var(--parchment-deep); color: var(--accent); }
footer { text-align: center; padding: 40px 24px; color: #7a6a55; font-size: 0.9rem; }
code { background: #efe6d2; padding: 1px 6px; border-radius: 4px; font-size: 0.92em; font-family: "SF Mono", Menlo, Consolas, monospace; }
.vs { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 18px 0; }
.vs > div { padding: 14px; border-radius: 8px; }
.vs .bad { background: #fbeae7; border: 1px solid #d9a89f; }
.vs .good { background: #eef4e8; border: 1px solid #b6cfa0; }
.vs h4 { margin: 0 0 6px; font-size: 1rem; }
@media (max-width: 560px) { .vs { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<header>
<h1>How the Lore Engine Works</h1>
<div class="sub">a world-building brain that never lies, never forgets, and always names its sources</div>
<div class="tag">EXPLAIN IT LIKE I'M 5</div>
</header>
<div class="wrap">
<!-- 1 -->
<section>
<h2><span class="num">1</span>The big idea, in one breath</h2>
<p class="lead">It's a super-smart librarian for a made-up fantasy world — one who read every book, remembers exactly when each thing happened, and will only answer a question by pointing at the page it came from.</p>
<p>You're writing a D&amp;D-style world. You've got hundreds of notes — who's whose kid, when a kingdom fell, who was king in year 340. An AI wants to answer questions about your world ("Did House Vyr rule the Crimson Throne in the Second Age?"). The problem: AI is a confident liar. It'll happily make up a king who never existed.</p>
<p>The Lore Engine sits between your notes and the AI. The AI asks the engine; the engine looks up the real answer with a real time-stamp and a real citation; the AI is <em>not allowed</em> to answer from its own imagination.</p>
</section>
<!-- 2 -->
<section>
<h2><span class="num">2</span>Why not just... ask the AI directly?</h2>
<div class="vs">
<div class="bad">
<h4>Plain AI 🫠</h4>
<p>"Did Aldric found House Vyr?"</p>
<p><strong>"Yes, Aldric founded House Vyr in 200 TA."</strong></p>
<p style="font-size:0.85rem;color:#9b3a2e;">…he didn't. The AI guessed. You'll never know it guessed.</p>
</div>
<div class="good">
<h4>Lore Engine ✅</h4>
<p>"Did Aldric found House Vyr?"</p>
<p><strong>"No — House Vyr was founded by <em>Theron</em> in 200 TA (chronicles-vyr.md). Aldric was his son, born 280 TA."</strong></p>
<p style="font-size:0.85rem;color:#4a6b3a;">Cited. Time-checked. True.</p>
</div>
</div>
<p>Every claim the engine returns is tagged with the document it came from. So the AI can say <em>"according to the chronicles of House Vyr..."</em> — or know to distrust a single shaky source.</p>
</section>
<!-- 3 -->
<section>
<h2><span class="num">3</span>The cast of characters</h2>
<div class="chars">
<div class="char">
<div class="emoji">📚</div>
<div>
<h3>Your Codex <span class="pill">the notes</span></h3>
<p>Markdown files + YAML — character bios, timelines, family trees, maps. The raw truth of your world, written by you.</p>
</div>
</div>
<div class="char">
<div class="emoji">🗄️</div>
<div>
<h3>Cognee <span class="pill">the filing cabinet</span></h3>
<p>The open-source engine underneath. It reads your notes, chops them up, and files them away. We don't build storage — we borrow Cognee's.</p>
</div>
</div>
<div class="char">
<div class="emoji">🕸️</div>
<div>
<h3>Neo4j <span class="pill">the web of facts</span></h3>
<p>A database that stores facts as a web (a "graph"). "Aldric —son of→ Theron —ruled→ House Vyr." Following the threads is what makes reasoning possible.</p>
</div>
</div>
<div class="char">
<div class="emoji">🧠</div>
<div>
<h3>Minimax-M3 <span class="pill">the brain</span></h3>
<p>The AI model that reads your prose and pulls facts out of it ("Aldric was born in 280 TA" → a structured fact). Also the brain that answers when you ask a question.</p>
</div>
</div>
<div class="char">
<div class="emoji">🔧</div>
<div>
<h3>The 45 Tools <span class="pill">the questions</span></h3>
<p>The Lore Engine's own toolbox the AI can pick from: <code>was_true_at</code>, <code>who_is</code>, <code>list_lineage</code>... each does one little job, like a single drawer in a workbench.</p>
</div>
</div>
<div class="char">
<div class="emoji">🛡️</div>
<div>
<h3>The Consistency Engine <span class="pill">the fact-checker</span></h3>
<p>A night-watchman that scans for impossible things: a guy at a battle 200 years before he was born, a kingdom in two places at once. It flags — it never deletes.</p>
</div>
</div>
</div>
</section>
<!-- 4 -->
<section>
<h2><span class="num">4</span>How a question gets answered</h2>
<p>You ask a question. Here's the trip it takes:</p>
<div class="diagram">
<svg viewBox="0 0 760 300" width="760" height="300" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="arr" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6 Z" fill="#8a5a2b"/>
</marker>
</defs>
<!-- step boxes -->
<g font-family="Helvetica, Arial, sans-serif" font-size="13" fill="#2b2118">
<rect x="10" y="20" width="150" height="56" rx="10" fill="#fffaf0" stroke="#c79a6a"/>
<text x="85" y="44" text-anchor="middle" font-weight="bold">You</text>
<text x="85" y="62" text-anchor="middle" font-size="11" fill="#7a6a55">"Who is Aldric?"</text>
<rect x="200" y="20" width="150" height="56" rx="10" fill="#fffaf0" stroke="#c79a6a"/>
<text x="275" y="44" text-anchor="middle" font-weight="bold">AI picks a tool</text>
<text x="275" y="62" text-anchor="middle" font-size="11" fill="#7a6a55">→ who_is(Aldric)</text>
<rect x="390" y="20" width="170" height="56" rx="10" fill="#eef4e8" stroke="#b6cfa0"/>
<text x="475" y="44" text-anchor="middle" font-weight="bold">Lore Engine</text>
<text x="475" y="62" text-anchor="middle" font-size="11" fill="#7a6a55">looks up the web of facts</text>
<rect x="600" y="20" width="150" height="56" rx="10" fill="#eef4e8" stroke="#b6cfa0"/>
<text x="675" y="44" text-anchor="middle" font-weight="bold">Neo4j graph</text>
<text x="675" y="62" text-anchor="middle" font-size="11" fill="#7a6a55">the actual data</text>
<line x1="160" y1="48" x2="200" y2="48" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr)"/>
<line x1="350" y1="48" x2="390" y2="48" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr)"/>
<line x1="560" y1="48" x2="600" y2="48" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr)"/>
<!-- return -->
<rect x="390" y="150" width="170" height="56" rx="10" fill="#fbeae7" stroke="#d9a89f"/>
<text x="475" y="174" text-anchor="middle" font-weight="bold">Answer + sources</text>
<text x="475" y="192" text-anchor="middle" font-size="11" fill="#7a6a55">facts, time-stamps, citations</text>
<rect x="200" y="150" width="150" height="56" rx="10" fill="#fbeae7" stroke="#d9a89f"/>
<text x="275" y="174" text-anchor="middle" font-weight="bold">AI rewrites nicely</text>
<text x="275" y="192" text-anchor="middle" font-size="11" fill="#7a6a55">in character voice</text>
<rect x="10" y="150" width="150" height="56" rx="10" fill="#fbeae7" stroke="#d9a89f"/>
<text x="85" y="174" text-anchor="middle" font-weight="bold">You smile</text>
<text x="85" y="192" text-anchor="middle" font-size="11" fill="#7a6a55">it's actually true</text>
<line x1="675" y1="76" x2="675" y2="120" stroke="#8a5a2b" stroke-width="2"/>
<line x1="675" y1="120" x2="475" y2="120" stroke="#8a5a2b" stroke-width="2"/>
<line x1="475" y1="120" x2="475" y2="150" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr)"/>
<line x1="390" y1="178" x2="350" y2="178" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr)"/>
<line x1="200" y1="178" x2="160" y2="178" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr)"/>
<text x="380" y="250" text-anchor="middle" font-size="12" fill="#9b3a2e" font-style="italic">
The AI never answers from memory. It must call a tool and cite a source first.
</text>
</g>
</svg>
<div class="cap">One round-trip: question → tool → graph → cited answer.</div>
</div>
</section>
<!-- 5 -->
<section>
<h2><span class="num">5</span>Why time is the whole game</h2>
<p>People change. Kingdoms fall. "Who rules Valdorn?" has a different answer in year 100 vs year 500. A normal database would give you one answer and quietly be wrong for every other year.</p>
<p>Every fact in the Lore Engine has a <strong>time window</strong> — a "valid from" and "valid until." Asking "was this true at time T?" is the engine's signature move.</p>
<div class="diagram">
<svg viewBox="0 0 700 200" width="700" height="200" xmlns="http://www.w3.org/2000/svg">
<g font-family="Helvetica, Arial, sans-serif" font-size="12" fill="#2b2118">
<!-- timeline -->
<line x1="40" y1="120" x2="660" y2="120" stroke="#8a5a2b" stroke-width="2"/>
<text x="40" y="145" font-size="11" fill="#7a6a55">year 0</text>
<text x="320" y="145" font-size="11" fill="#7a6a55">year 340</text>
<text x="600" y="145" font-size="11" fill="#7a6a55">year 600</text>
<circle cx="40" cy="120" r="4" fill="#8a5a2b"/>
<circle cx="660" cy="120" r="4" fill="#8a5a2b"/>
<!-- rule window -->
<rect x="120" y="100" width="280" height="40" rx="6" fill="#eef4e8" stroke="#4a6b3a"/>
<text x="260" y="125" text-anchor="middle" fill="#4a6b3a" font-weight="bold">House Vyr rules (200 → 380)</text>
<!-- ask markers -->
<line x1="260" y1="60" x2="260" y2="100" stroke="#2c5f7c" stroke-width="2" stroke-dasharray="4 3"/>
<circle cx="260" cy="60" r="6" fill="#2c5f7c"/>
<text x="260" y="48" text-anchor="middle" fill="#2c5f7c" font-weight="bold">"year 300?" → TRUE ✅</text>
<line x1="560" y1="170" x2="560" y2="140" stroke="#9b3a2e" stroke-width="2" stroke-dasharray="4 3"/>
<circle cx="560" cy="170" r="6" fill="#9b3a2e"/>
<text x="560" y="190" text-anchor="middle" fill="#9b3a2e" font-weight="bold">"year 500?" → FALSE ❌</text>
</g>
</svg>
<div class="cap">Same fact, different year, different answer — because the fact carries its own time window.</div>
</div>
<p class="callout"><strong>ELI5:</strong> every fact is like a job with a start date and an end date. "Who's the boss?" only makes sense if you also say <em>when</em>.</p>
</section>
<!-- 6 -->
<section>
<h2><span class="num">6</span>When the books disagree</h2>
<p>Two sources, same fact, different stories. Book A says Aldric's father is Theron. Book B says it's Maric. What now?</p>
<p>The engine does <strong>not</strong> pick a winner. It keeps <em>both</em>, marks them as <em>disputed</em>, and tells the AI: "sources disagree on this — here are both, you decide how to present it."</p>
<div class="diagram">
<svg viewBox="0 0 660 170" width="660" height="170" xmlns="http://www.w3.org/2000/svg">
<g font-family="Helvetica, Arial, sans-serif" font-size="12" fill="#2b2118">
<rect x="20" y="20" width="180" height="60" rx="8" fill="#fbeae7" stroke="#d9a89f"/>
<text x="110" y="44" text-anchor="middle" font-weight="bold">Book A</text>
<text x="110" y="64" text-anchor="middle" font-size="11">father = Theron</text>
<rect x="460" y="20" width="180" height="60" rx="8" fill="#fbeae7" stroke="#d9a89f"/>
<text x="550" y="44" text-anchor="middle" font-weight="bold">Book B</text>
<text x="550" y="64" text-anchor="middle" font-size="11">father = Maric</text>
<rect x="240" y="100" width="180" height="50" rx="8" fill="#fffaf0" stroke="#c79a6a"/>
<text x="330" y="122" text-anchor="middle" font-weight="bold" fill="#8a5a2b">⚠️ Disputed edge</text>
<text x="330" y="140" text-anchor="middle" font-size="11" fill="#7a6a55">both kept, both flagged</text>
<line x1="110" y1="80" x2="290" y2="100" stroke="#9b3a2e" stroke-width="2" marker-end="url(#arr2)"/>
<line x1="550" y1="80" x2="370" y2="100" stroke="#9b3a2e" stroke-width="2" marker-end="url(#arr2)"/>
</g>
<defs>
<marker id="arr2" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6 Z" fill="#9b3a2e"/>
</marker>
</defs>
</svg>
<div class="cap">Disagreement is preserved, not silently overwritten.</div>
</div>
<p>And every fact has a <strong>confidence score</strong> — two numbers, actually. One for "did we extract this right?" and one for "how much do we trust the book it came from?" A rumor scores low; an official chronicle scores high. The engine uses the <em>lower</em> of the two, so one weak link drags the whole fact down. Honest by default.</p>
</section>
<!-- 7 -->
<section>
<h2><span class="num">7</span>How your lore gets in</h2>
<p>Two doors into the engine:</p>
<table>
<tr><th>Door</th><th>What goes in</th><th>Who does the work</th></tr>
<tr><td>📋 <strong>Structured YAML</strong></td><td>Timelines, family trees, maps — neat typed files</td><td>Filed directly. Fast, exact, no AI needed.</td></tr>
<tr><td>📖 <strong>Prose markdown</strong></td><td>Your story chapters, dialogue, descriptions</td><td>The AI reads it and pulls facts out ("Aldric, born 280 TA, son of Theron").</td></tr>
</table>
<p class="callout"><strong>ELI5:</strong> the YAML door is you handing the librarian a neat index card. The prose door is you handing the librarian a novel and letting it write its own index cards. Both end up in the same web of facts.</p>
<p>Crucially: <strong>the AI never writes to the world.</strong> Only you do. The AI reads, asks, and answers — it does not get to change the canon. That's the world-builder's job, always.</p>
</section>
<!-- 8 -->
<section>
<h2><span class="num">8</span>The safety net (the fact-checker)</h2>
<p>Even good books contain mistakes. The Consistency Engine is a night-watchman that roams the web of facts and flags things that are impossible:</p>
<ul class="friendly">
<li>🚫 <strong>Anachronisms</strong> — a guy at a battle 200 years before he was born.</li>
<li>🚫 <strong>Contradictions</strong> — two books swearing different fathers for the same person.</li>
<li>🚫 <strong>Ontology violations</strong> — a town claimed to be inside two non-overlapping kingdoms.</li>
<li><strong>Orphans</strong> — a noble with no recorded parents, a battle with no location. Not errors, just gaps — surfaced so you know what's missing.</li>
</ul>
<p class="callout"><strong>The golden rule:</strong> the engine <em>flags</em>, it never <em>deletes</em>. When you "retcon" (retroactively fix) a fact, the old version is kept and marked "superseded," not erased. So you can still ask "what was true <em>before</em> the retcon?" History is preserved, even when it changes.</p>
</section>
<!-- 9 -->
<section>
<h2><span class="num">9</span>What makes this different from a wiki?</h2>
<div class="vs">
<div class="bad">
<h4>A wiki 📄</h4>
<ul style="margin:0; padding-left:18px; font-size:0.95rem;">
<li>Stores text.</li>
<li>You read it yourself.</li>
<li>One answer, no sense of time.</li>
<li>No idea when facts were true.</li>
</ul>
</div>
<div class="good">
<h4>Lore Engine 🧠</h4>
<ul style="margin:0; padding-left:18px; font-size:0.95rem;">
<li>Stores <em>facts as a web</em> the AI can traverse.</li>
<li>The AI reasons and answers, with citations.</li>
<li>Every fact knows <em>when</em> it was true.</li>
<li>Flags its own contradictions and gaps.</li>
</ul>
</div>
</div>
<p class="lead">A wiki is a pile of pages. The Lore Engine is a <em>thinking</em> model of your world — one an AI can actually interrogate, trust, and cite.</p>
</section>
<!-- 10 -->
<section>
<h2><span class="num">10</span>So, the whole thing in one picture</h2>
<div class="diagram">
<svg viewBox="0 0 720 240" width="720" height="240" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="arr3" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6 Z" fill="#8a5a2b"/>
</marker>
</defs>
<g font-family="Helvetica, Arial, sans-serif" font-size="12" fill="#2b2118">
<rect x="20" y="90" width="140" height="60" rx="10" fill="#fffaf0" stroke="#c79a6a"/>
<text x="90" y="115" text-anchor="middle" font-weight="bold">📖 Your Codex</text>
<text x="90" y="133" text-anchor="middle" font-size="11" fill="#7a6a55">markdown + YAML</text>
<rect x="210" y="90" width="140" height="60" rx="10" fill="#fffaf0" stroke="#c79a6a"/>
<text x="280" y="115" text-anchor="middle" font-weight="bold">📚 Cognee</text>
<text x="280" y="133" text-anchor="middle" font-size="11" fill="#7a6a55">reads &amp; files</text>
<rect x="400" y="40" width="140" height="60" rx="10" fill="#eef4e8" stroke="#b6cfa0"/>
<text x="470" y="65" text-anchor="middle" font-weight="bold">🧠 Minimax-M3</text>
<text x="470" y="83" text-anchor="middle" font-size="11" fill="#7a6a55">extracts facts</text>
<rect x="400" y="140" width="140" height="60" rx="10" fill="#eef4e8" stroke="#b6cfa0"/>
<text x="470" y="165" text-anchor="middle" font-weight="bold">🕸️ Neo4j</text>
<text x="470" y="183" text-anchor="middle" font-size="11" fill="#7a6a55">web of facts</text>
<rect x="590" y="90" width="110" height="60" rx="10" fill="#fbeae7" stroke="#d9a89f"/>
<text x="645" y="115" text-anchor="middle" font-weight="bold">🛡️ + 🔧</text>
<text x="645" y="133" text-anchor="middle" font-size="11" fill="#7a6a55">fact-checker &amp; tools</text>
<line x1="160" y1="120" x2="210" y2="120" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr3)"/>
<line x1="350" y1="110" x2="400" y2="75" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr3)"/>
<line x1="350" y1="130" x2="400" y2="165" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr3)"/>
<line x1="540" y1="70" x2="590" y2="105" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr3)"/>
<line x1="540" y1="170" x2="590" y2="135" stroke="#8a5a2b" stroke-width="2" marker-end="url(#arr3)"/>
<text x="360" y="225" text-anchor="middle" font-size="11" fill="#7a6a55" font-style="italic">
Your notes → filed by Cognee → facts pulled by the AI brain, stored in the graph, guarded by the fact-checker.
</text>
</g>
</svg>
<div class="cap">The whole machine, end to end.</div>
</div>
</section>
</div>
<footer>
Lore Engine — a reasoning substrate for high-fantasy worlds. <br>
Built on Cognee + Neo4j + Minimax-M3. See <code>docs/</code> for the full design.
</footer>
</body>
</html>