# Conceptual Foundations > Why the system works the way it does, and the metaphors that keep it honest. > Package-level runtime architecture now lives in > `engine/src/tangl/story/STORY_DESIGN.md`; this page remains the higher-level > conceptual rationale. StoryTangl models interactive narrative as a **graph of interdependent possibilities** that **collapses into a specific story** through traversal. This document explains the conceptual vocabulary behind that sentence — where it comes from, why it matters, and how each idea maps to real machinery in the engine. Every metaphor here earns its place by corresponding to an implemented (or concretely planned) mechanism. If a concept doesn't connect to code, it belongs in the research agenda, not here. --- ## The Central Thesis **A story is a structured collapse of possibility into experience.** An author defines a *space* of potential narratives — characters who could meet, events that could transpire, objects that could matter. A reader (or player, or algorithm) navigates that space, and the act of navigation progressively resolves ambiguity into commitment. What was once "the villain *could* be anyone" becomes "the villain is Marta." What was once "you *could* go east" becomes "you went east, and now the west road is buried." The engine's job is to make this collapse **auditable** (every decision is logged), **deterministic** (the same choices reproduce the same story), and **extensible** (new kinds of narrative content and mechanics plug in without changing the core loop). ### Why "Untangling" The original backronym — *the Abstract Narrative Graph Library* — described the data structure. The working metaphor is better: the story space starts **tangled**, a web of interdependent requirements, roles, and consequences, and the engine's job is to **untangle** it into a single coherent thread. This is not merely poetic. The resolver literally walks the dependency graph, finds nodes that satisfy open requirements, binds them, and advances the frontier. Each step reduces the degrees of freedom in the remaining graph. The journal that emerges is the untangled thread — a linear narrative extracted from a combinatorial space. --- ## Three Models, Three Layers StoryTangl separates *what could happen* from *what does happen* from *what the reader sees*. This separation is the engine's most important architectural commitment. ### Fabula — the Possibility Space The **fabula** is the complete graph of events, characters, places, and their relationships — everything that *could* be narrated. It is the "tangled" state: a castle exists, a dragon exists, a knight exists, and there are dependency edges encoding that the dragon guards the treasure and the knight needs a sword. In the engine, authored fabula first lives as a compiled `StoryTemplateBundle`: a set of structural templates (scenes, blocks, actions), concept templates (actors, locations, assets), and dependency declarations capturing what could be instantiated. Materialization then turns that compiled possibility space into one navigable runtime `StoryGraph`. The fabula contains more stories than any single traversal will realize, just as a chessboard contains more games than any single match will play. **Implementation:** `StoryCompiler` → `StoryTemplateBundle`; `StoryMaterializer` → `StoryGraph`. ### Episodic Process — the Traversal The **episodic process** is what happens when a cursor moves through the fabula. At each step, the engine validates the proposed move, provisions any unresolved dependencies, applies state updates, and emits journal fragments. This is the resolution pipeline — the phase bus that drives the story forward. The episodic process is where possibility collapses into commitment. Before a block is visited, its role slots might be open ("this scene needs *a* villain"). During provisioning, the resolver binds a specific actor to that role. After the block is visited, that binding is frozen — the villain is Marta, permanently, for this story instance. **Implementation:** `Frame.follow_edge()` → the eight-phase pipeline (VALIDATE → PLANNING → PREREQS → UPDATE → JOURNAL → FINALIZE → POSTREQS → advance frontier). ### Syuzhet — the Realized Narrative The **syuzhet** is the journal — the linear sequence of content fragments that a reader actually experiences. It is the "untangled" thread: prose paragraphs, character descriptions, choice prompts, media references, emitted in the order the traversal produced them. The syuzhet is presentation-agnostic. The same journal can be rendered as terminal text, a rich web page, or an ebook. Fragment types carry semantic roles (narrative content, dialog, choices, media) but not rendering instructions. The service layer translates fragments into a specific presentation format based on client capabilities. **Implementation:** `Journal` / `OrderedRegistry` of `BaseFragment` subclasses (`ContentFragment`, `ChoiceFragment`, `MediaFragment`); service-layer render profiles transform fragments for specific clients. ### Why This Separation Matters The same fabula can produce radically different syuzhets depending on: - **Player choices** — different traversal paths through the structural graph - **Provisioning outcomes** — different actors cast into the same roles - **Discourse rules** — different ordering, focalization, or pacing applied to the same underlying events (planned, not yet implemented) This is not a theoretical nicety. It means you can test structural properties (reachability, completability, role satisfaction) against the fabula *before* any traversal happens, and you can replay a specific syuzhet deterministically from a snapshot plus a choice log. --- ## The Compiler Metaphor The fabula/episodic/syuzhet separation maps directly to a compiler pipeline: ``` Front-end: Scripts (YAML, Twee, Ink...) ↓ parse + compile IR: StoryTemplateBundle / compiled template graph (the fabula) ↓ traverse + resolve Back-end: Journal Fragments (the syuzhet) ↓ render Output: CLI / Web / PDF / ... ``` Like LLVM's intermediate representation, the story graph is a **universal substrate** that captures narrative intent while abstracting away both the source format and the target medium. This is why transpilers between interactive fiction formats are architecturally natural: a Twine file and a StoryTangl YAML script are different front-ends compiling to the same IR. The graph doesn't care where it came from. And like a compiler, the engine's phases are **deterministic, ordered passes** over the IR. Each phase has a defined contract (what it reads, what it may mutate, what it returns). The pipeline is auditable because every phase produces receipts. **Implementation:** `StoryCompiler.compile()` (front-end) → `StoryTemplateBundle` (IR) → `StoryMaterializer.create_story()` + `Frame.follow_edge()` phases (back-end passes) → `Journal` (output). --- ## Narrative Debt and Credit When the episodic process recruits a concept — say, casting an actor into a role — but the journal hasn't yet introduced that concept to the reader, the story carries **narrative debt**. The reader doesn't know who Marta is yet, even though the engine has already committed to her as the villain. Conversely, when the journal introduces a concept before it becomes structurally necessary, the story holds **narrative credit**. Mentioning the old sword on the mantelpiece in Act I creates credit that Act III's sword fight will draw down. (Chekhov knew this, but didn't have a dependency graph.) This maps directly to the provisioning system: - **Narrative debt** = an unsatisfied dependency edge (the role slot is open, or is filled but the corresponding concept hasn't been journaled) - **Narrative credit** = a concept published to the namespace before any structural node requires it (foreshadowing, world-building) - **Debt resolution** = the journal handler that emits an introduction fragment when a newly-bound concept first appears in rendered output - **Bankruptcy** = a structural dead end where required concepts cannot be provisioned (a softlock, which formal verification should catch) A well-structured story balances its narrative books: debts are incurred strategically (mysteries, in medias res openings) and resolved before they become confusing. The engine's dependency system makes this balance *mechanically visible* — you can query the graph for outstanding debts at any point in the traversal. --- ## Landmarks in Story Space The graph's nodes are **landmarks** — fixed points of authored narrative content. Between them lies interpolation territory: template-driven text, procedurally selected details, contextual descriptions drawn from the namespace. The density of landmarks determines the confidence of the narrative: - **Dense landmarks** (many authored blocks, specific dialog) → a tightly controlled experience, closer to a traditional novel or visual novel - **Sparse landmarks** (few key scenes, procedural connections) → a more emergent experience, closer to a tabletop RPG or roguelike narrative - **Mixed density** → the typical case, where critical plot points are densely authored and transitional content is procedurally generated This is the "authoritative nonsense" principle: between landmarks, the engine generates confident, detailed-sounding descriptions through vocabulary banks and templates. The same mechanical seed ("roll on the tavern atmosphere table") renders through different thematic vocabularies to produce genre- appropriate prose. The reader experiences *specificity*; the author only wrote *structure*. **Implementation:** Template registries with scope-based selection; vocabulary banks as namespace contributors; render handlers that interpolate authored content with contextual detail. --- ## Parametric Story Space Stories exist in a low-dimensional **parametric space** defined by structural properties: protagonist agency, dramatic tension, stakes, pacing, moral complexity. These parameters are the *controls* of the narrative — the knobs an author or algorithm can turn. The fabula graph encodes specific points in this space. Different traversals through the same fabula trace different curves through the same parameter space. A path where the player always cooperates traces a different tension curve than a path where the player always defies authority. This parametric view enables several capabilities: - **Similarity metrics** — quantify how "different" two story paths are by measuring distance in parameter space - **Interpolation** — generate a story "between" two known paths by blending their parameter trajectories - **Style transfer** — project the same parameter trajectory through different thematic vocabularies ("tell this tension curve as noir" vs. "tell it as comedy") - **Verification** — check that all traversals through a fabula stay within acceptable bounds on critical parameters (e.g., tension never drops to zero, agency never becomes illusory) The key insight: because the parametric space is defined by the graph structure (not by the prose), it provides **ground truth** for controlled experiments. You can manipulate a clean semantic model and observe how it projects into narrative — unlike LLM-based approaches where the parameters and the prose are entangled. **Implementation status:** Parametric space is conceptually defined but not yet instrumented. The graph structure *implies* these parameters; extracting them requires analytics tooling that is on the research roadmap. See the research agenda for "Narrative Shape Space" directions. --- ## The Observer Roles Three roles interact with the narrative system, each engaging a different layer: | Role | Engages | Primary Actions | |------|---------|-----------------| | **Creator** | Fabula | Define story world, write scripts, set up concept templates and dependencies | | **Navigator** | Episodic process | Make choices, trigger traversals, observe state changes | | **Presenter** | Syuzhet | Format journal output for a specific medium and audience | These roles may be filled by humans, algorithms, or AI agents — the engine doesn't care. A human author creates the fabula; a human player navigates it; a web client presents it. But equally: an LLM could author worlds, a Monte Carlo tree search could navigate for testing, and a PDF generator could present for print. The **multi-lane** extension (planned) allows multiple navigators to traverse the same fabula simultaneously from different perspectives, producing interleaved syuzhets that can be cross-referenced. The important conceptual move is that **navigation is an orthogonal role**. The navigator is not necessarily the presenter, and is not necessarily the creator. A human reader may pick choices directly, but so may a scheduler, a test harness, an AI policy, or a replay driver. Multi-lane stories extend that same idea rather than introducing a new ontology: several navigators operate on the same underlying fabula, each with its own visibility, perspective, and decision policy, while the service/presentation layer remains free to surface one lane, several lanes, or a stitched cross-reference view. **Implementation:** Creator → `StoryCompiler` + `Materializer`; Navigator → `Frame` + `Ledger` (user choices drive `follow_edge`); Presenter → `GatewayHooks` + render profiles in the service layer. --- ## Determinism and Replay Every mutation in the engine is **event-sourced**: state changes are captured as patches in the ledger, and the complete history of a story instance is reproducible from a snapshot plus the patch log. This is not just an implementation convenience — it's a **narratological commitment**. The syuzhet is a *particular* collapse of the fabula. To study narrative structure, you need to be able to replay that collapse, branch from any point, and compare alternative collapses. Without deterministic replay, the engine would be a storytelling tool. With it, the engine is a **narratological instrument**. Determinism requires discipline: - **Seeded RNG** — random choices during provisioning use a seed derived from `(story_id, step, choice_hash)`, so the same choice at the same point always produces the same outcome - **Ordered dispatch** — handler execution follows deterministic priority ordering within each phase; receipts record exactly what ran - **External IO logging** — any non-deterministic input (LLM responses, media generation) is recorded in the patch so replay doesn't need to re-invoke external services **Implementation:** `Ledger` (event-sourced patch log); `DiffReplay` (snapshot + patches → reconstructed state); seeded RNG in resolver; `JobReceipt` audit trail on every dispatch. --- ## Theoretical Heritage StoryTangl draws on established narratological theory, but treats it as **engineering specification** rather than literary criticism. The question is not "is Genette's taxonomy correct?" but "can we implement it as code, and does the implementation reveal anything the theory didn't predict?" Key correspondences: | Theorist | Concept | Engine Mapping | |----------|---------|----------------| | **Bal** | Fabula / Story / Text | Graph / Frame / Journal | | **Genette** | Order, duration, frequency, focalization | Phase bus operations (planned discourse layer) | | **Chatman** | Kernels vs. satellites | Required vs. optional dependency edges | | **Barthes** | Nuclei vs. catalyzers | Planning-phase offers (critical) vs. provisioning backfill (optional) | | **Propp** | Morphological functions | Re-entrant subgraph templates (cycle patterns) | | **Aarseth** | Scriptons / textons | Journal output / graph + ledger | | **Short** | Storylets + QBN | Offer system with dependency preconditions | The engine is designed to validate these theories empirically: if Bal's three layers genuinely capture independent concerns, then modifying the discourse layer should change how a story *reads* without changing what *happens*. If Chatman's kernel/satellite distinction is real, then removing satellite nodes should leave the story structurally complete. These are testable claims, and the engine provides the apparatus to test them. For a detailed survey, see the *StoryTangl Literature Review (2025)* and the research agenda. --- ## What This Document Does Not Cover - **How** the phase pipeline works → see `engine/src/tangl/vm/VM_DESIGN.md` - **How** the graph primitives work → see `engine/src/tangl/core/CORE_DESIGN.md` - **How** story concepts are modeled → see `engine/src/tangl/story/` - **How** the service layer transforms fragments → see `engine/src/tangl/service/` - **What** research directions follow from this → see `storytangl-research-agenda.md` This document is the **why**. The subpackage design docs are the **how**.