Files
lore-engine/docs/how-it-works.html
Kaysser Kayyali c3fa2f7ce4 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>
2026-06-17 23:40:50 -04:00

452 lines
22 KiB
HTML

<!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>