diff --git a/README.md b/README.md
index 736cd3d..5dac1c6 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
Unlike traditional choice-based games, Kleene responds to free-form actions and improvisation. The temperature system lets you control how much the AI adapts - from traditional branching (temp 0) to fully emergent storytelling (temp 10).
-๐ฎ [**Get Started in 5 Minutes**](GETTING_STARTED.md) | ๐ [Documentation](docs/) | ๐ค [Contributing](CONTRIBUTING.md)
+๐ฎ [**Get Started in 5 Minutes**](docs/GETTING_STARTED.md) | ๐ [Documentation](docs/) | ๐ค [Contributing](CONTRIBUTING.md)
## Quick Start
@@ -26,7 +26,7 @@ Unlike traditional choice-based games, Kleene responds to free-form actions and
/kleene analyze
```
-๐ **New to Kleene?** Check out the [Getting Started Guide](GETTING_STARTED.md)
+๐ **New to Kleene?** Check out the [Getting Started Guide](docs/GETTING_STARTED.md)
---
diff --git a/commands/kleene.md b/commands/kleene.md
index a7c3516..60c2943 100644
--- a/commands/kleene.md
+++ b/commands/kleene.md
@@ -24,6 +24,21 @@ command -v yq >/dev/null 2>&1 && yq --version 2>&1 | head -1
Set `yaml_tool: yq` if output contains "mikefarah/yq" and version >= 4, otherwise `yaml_tool: grep`.
+## Server Detection (at session start)
+
+Check if a kleene-server local proxy is running:
+
+```bash
+curl -s --connect-timeout 1 http://localhost:8420/health 2>/dev/null
+```
+
+If the response contains `"status":"ok"`:
+- Set `server_url: http://localhost:8420`
+- Set `server_mode: true`
+- Remote loading becomes available for all scenarios on the server
+
+The server URL can also be set explicitly via `/kleene server [url]`.
+
## If no action provided, show menu FIRST
Use AskUserQuestion to present options:
@@ -533,6 +548,43 @@ Keywords: "export", "transcript", "save story", "save journey", "summary", "stat
If no active game:
"No active game to export. Start a game with /kleene play first."
+### Server Actions
+Keywords: "server", "connect", "proxy", "remote", "mmo"
+
+**Set Server URL** (`/kleene server [url]`):
+1. If URL provided: set `server_url` and test connection
+2. If no URL: show current server status
+
+```bash
+curl -s --connect-timeout 2 [url]/health 2>/dev/null
+```
+
+If response contains `"status":"ok"`:
+- Store `server_url` in context
+- Set `server_mode: true`
+- Report: "Connected to kleene-server at [url] ([mode] mode)"
+
+If connection fails:
+- Report: "Cannot reach kleene-server at [url]. Is it running?"
+
+**Show Server Status** (`/kleene server`):
+```
+Kleene Server Status
+ URL: http://localhost:8420
+ Mode: local
+ Connected: yes
+ Scenarios: 3 available
+
+Use: /kleene server [url] Set server URL
+ /kleene server off Disconnect from server
+```
+
+**Disconnect** (`/kleene server off`):
+1. Clear `server_url` and `server_mode`
+2. Confirm: "Disconnected from server. Using local scenarios only."
+
+When `server_mode: true`, the Play menu shows both local and remote scenarios. Remote scenarios are loaded via the server API instead of local files. The game loop uses remote loading mode (see `lib/framework/scenario-file-loading/remote-loading.md`).
+
### Help Actions
Keywords: "help", "how", "what", "?"
@@ -599,6 +651,11 @@ SETTINGS
off = Show choice menu (default)
/kleene choice [on|off] Toggle choice mode (inverse of parser)
+SERVER
+ /kleene server Show server connection status
+ /kleene server [url] Connect to kleene-server
+ /kleene server off Disconnect from server
+
DURING GAMEPLAY
Select "Other" or type freely Improvise beyond scripted choices
Your actions shape the story Explore, interact, experiment!
diff --git a/docs/ANNOUNCEMENTS.md b/docs/ANNOUNCEMENTS.md
deleted file mode 100644
index 0c53635..0000000
--- a/docs/ANNOUNCEMENTS.md
+++ /dev/null
@@ -1,474 +0,0 @@
-# Kleene Launch Announcements
-
-Ready-to-use announcement templates for launching Kleene across various platforms.
-
----
-
-## Plugin Marketplace Listing
-
-### Short Description (200 chars max)
-
-```
-Adaptive AI text adventure engine for Claude Code. Play branching stories OR improvise freely - temperature controls how much the AI adapts. 4 scenarios included. Create your own!
-```
-
-**Character count:** 196/200 โ
-
-### Long Description
-
-```
-Kleene - Text Adventures That Actually Adapt
-
-Unlike traditional choice-based games, Kleene responds to free-form actions and improvisation. The temperature system lets you control how much the AI adapts - from traditional branching (temp 0) to fully emergent storytelling (temp 10).
-
-FEATURES:
-โข True Improvisation - Type anything, not just pick from options
-โข Temperature System - Same scenario, infinite variations
-โข 4 Scenarios Included - Fantasy, thriller, surreal mystery, corporate drama
-โข Easy Creation - Simple YAML format, no coding required
-โข Built-in Tools - Generator, validator, analyzer
-
-QUICK START:
-/kleene play # Start playing
-/kleene generate # Create your own
-/kleene temperature 5 # Set adaptation level
-
-Perfect for players who want agency and creators who want to share stories.
-
-Made specifically for Claude Code. Open source (AGPL-3.0).
-```
-
----
-
-## GitHub README Badge/Header
-
-### Social Preview Text
-
-```
-๐ฎ The AI text adventure engine for Claude Code | โก Adaptive storytelling with temperature control | ๐ฒ Four scenarios included | ๐ ๏ธ Create your own adventures
-```
-
-### GitHub Release Notes (v0.2.0)
-
-```markdown
-# Kleene v0.2.0 - Marketplace Launch
-
-Kleene is now ready for public use! An adaptive AI text adventure engine built specifically for Claude Code.
-
-## What's New in v0.2.0
-
-### Decision Grid Framework
-- Expanded from four quadrants to 3ร3 grid for richer narrative possibilities
-- Player Unknown state captures improvisation and exploration
-- World Indeterminate state for outcomes not yet resolved
-
-### Temperature System
-- Improvisation temperature (0-10) controls adaptive storytelling depth
-- Temp 0 = traditional branching
-- Temp 10 = fully emergent gameplay
-
-### New Scenarios
-- **The Yabba** - Psychological thriller inspired by Wake in Fright (1971)
-- Updated existing scenarios with enhanced features
-
-### Performance Improvements
-- Removed agent layer for 60-70% faster turn response
-- Lazy loading for large scenarios (200KB+)
-- Smart caching reduces token costs
-
-### Complete Documentation
-- Getting Started guide
-- Scenario Authoring guide
-- Templates (minimal, basic, intermediate)
-- FAQ and Troubleshooting
-
-## Quick Start
-
-\`\`\`bash
-# Install the plugin in Claude Code
-# Then start playing:
-/kleene play
-
-# Or create your own:
-/kleene generate a space station mystery
-\`\`\`
-
-## Four Scenarios Included
-
-- **Dragon Quest** (beginner) - Classic fantasy
-- **The Yabba** (advanced) - Psychological thriller
-- **Altered State Nightclub** (experimental) - Surreal mystery
-- **Corporate Banking** (intermediate) - Career drama
-
-## Links
-
-- [Documentation](README.md)
-- [Getting Started](GETTING_STARTED.md)
-- [Create Your Own](docs/SCENARIO_AUTHORING.md)
-- [FAQ](docs/FAQ.md)
-
-**Full Changelog:** [CHANGELOG.md](CHANGELOG.md)
-```
-
----
-
-## Claude Code Community Post
-
-### Launch Announcement
-
-```markdown
-# ๐ฎ Introducing Kleene - Adaptive Text Adventures for Claude Code
-
-I'm excited to share Kleene, an adaptive AI text adventure engine built specifically for Claude Code!
-
-## What Makes It Different?
-
-**True Improvisation**
-Not just multiple choice - type anything and the AI responds:
-- "I examine the dragon's scales for weak points"
-- "I try to befriend the guard"
-- "I search for hidden passages"
-
-**The Temperature System (The Magic Sauce)**
-Same scenario, completely different experience:
-- Temp 0: Traditional branching (like a classic choose-your-own-adventure)
-- Temp 10: Fully emergent storytelling (the AI adapts everything)
-
-Try Dragon Quest at temp 0, then replay at temp 10. It's the same scenario file, but feels like a completely different game.
-
-## Quick Demo
-
-Here's what happened when I played The Yabba at temp 10:
-
-[Insert gameplay snippet showing improvised action and AI response]
-
-The scenario didn't explicitly script this moment - the AI adapted based on my exploration. That's Kleene's magic.
-
-## Four Scenarios Included
-
-- **Dragon Quest** (beginner) - 15-20 min
-- **The Yabba** (advanced) - 30-60 min - Psychological thriller
-- **Altered State Nightclub** (experimental) - 20-40 min
-- **Corporate Banking** (intermediate) - 25-35 min
-
-## Create Your Own
-
-Simple YAML format, no coding required:
-
-\`\`\`bash
-/kleene generate a haunted mansion mystery
-\`\`\`
-
-Or use the templates and guides in the repo.
-
-## Try It Now
-
-\`\`\`bash
-/kleene play
-\`\`\`
-
-Start with Dragon Quest at temp 0, then experiment!
-
-**Links:**
-- [GitHub](https://github.com/hiivmind/kleene-plugin)
-- [Documentation](link)
-- [Getting Started](link)
-
-Open source (AGPL-3.0). Free to play, free to create.
-
-Would love to hear what scenarios you create! ๐ญโจ
-```
-
----
-
-## Social Media Posts
-
-### Twitter/X Launch Thread
-
-**Tweet 1 (Hook):**
-```
-Ever played the SAME game and had it turn out completely different?
-
-I built Kleene - an adaptive AI text adventure engine for @ClaudeCode
-
-Same scenario file. Temp 0 = traditional branching. Temp 10 = emergent storytelling.
-
-Let me show you: ๐งต
-```
-
-**Tweet 2 (Demo):**
-```
-Here's Dragon Quest at temperature 0:
-
-[Screenshot of traditional branching]
-
-Pick from 3 options. Clean, predictable paths.
-
-Now watch what happens at temp 10...
-```
-
-**Tweet 3 (Comparison):**
-```
-Same scenario, temp 10:
-
-[Screenshot showing adapted narrative + improvisation]
-
-- Free-text actions work
-- Story adapts to YOUR discoveries
-- Bonus options appear
-- No two playthroughs are the same
-
-Same YAML file. Different experience.
-```
-
-**Tweet 4 (Features):**
-```
-What you get:
-
-โ
4 scenarios (fantasy, thriller, surreal, drama)
-โ
Temperature control (0-10)
-โ
True improvisation (type anything)
-โ
Easy creation (YAML, no code)
-โ
Built-in tools (generator, validator)
-โ
Open source (AGPL-3.0)
-
-Try it: /kleene play
-```
-
-**Tweet 5 (Call to Action):**
-```
-Built specifically for @ClaudeCode
-
-GitHub: [link]
-Docs: [link]
-
-Open source, free forever.
-
-What scenarios will you create? ๐ฎโจ
-
-#KleeneEngine #TextAdventure #InteractiveFiction
-```
-
-### Short Social Posts
-
-**Discord/Slack:**
-```
-Just launched Kleene - adaptive text adventures for Claude Code! ๐ฎ
-
-Unlike traditional IF, it responds to free-text actions. The temperature system (0-10) controls how much the AI adapts. Same scenario, infinite variations.
-
-Try it: /kleene play
-
-Repo: [link]
-```
-
-**Reddit (r/interactivefiction):**
-```
-Title: [Project] Kleene - Adaptive AI Text Adventures with Temperature Control
-
-I've been working on an adaptive text adventure engine for Claude Code that uses a "temperature" system to control narrative adaptation.
-
-The concept: same scenario file, but at temp 0 it plays like traditional branching IF, while at temp 10 it becomes fully emergent with AI-adapted storytelling.
-
-Key features:
-- Free-text improvisation (not just multiple choice)
-- Temperature system (0-10 controls adaptation)
-- Simple YAML authoring (no coding)
-- 4 bundled scenarios
-- Open source (AGPL-3.0)
-
-Example: You type "I examine the dragon's scales for weak points" and the AI generates a contextual response. At higher temps, this discovery gets woven into the narrative.
-
-Would love feedback from the IF community! Especially interested in hearing from creators.
-
-[Link to repo]
-[Link to getting started]
-```
-
----
-
-## Email/Newsletter Announcement
-
-### Subject Lines (A/B Test These)
-
-Option A: **"Introducing Kleene - Text Adventures That Actually Adapt"**
-Option B: **"Play the same game twice, get two completely different stories"**
-Option C: **"Your choices finally matter (for real) - Kleene launch"**
-
-### Email Body
-
-```
-Hi [Name],
-
-I'm excited to share Kleene, an adaptive AI text adventure engine I built for Claude Code.
-
-THE PROBLEM WITH TRADITIONAL TEXT ADVENTURES:
-
-Your "choices" often feel fake. Pick option A or B, but they lead to the same outcome with different flavor text. You can't improvise. You can't explore. You're stuck on rails.
-
-KLEENE'S SOLUTION:
-
-Temperature control. Same scenario, infinite variations:
-
-โข Temp 0: Traditional branching (for first playthrough)
-โข Temp 5: Balanced adaptation
-โข Temp 10: Fully emergent storytelling
-
-PLUS: True improvisation. Type anything - "I examine the dragon's scales" or "I try to befriend the guard" - and the AI responds.
-
-TRY IT NOW:
-
-Four scenarios included:
-- Dragon Quest (15 min, beginner)
-- The Yabba (60 min, psychological thriller)
-- Altered State Nightclub (40 min, surreal)
-- Corporate Banking (35 min, career drama)
-
-Just run: /kleene play
-
-CREATE YOUR OWN:
-
-Simple YAML format. No coding. Built-in generator and validator.
-
-Templates and guides included.
-
-IT'S FREE AND OPEN SOURCE:
-
-AGPL-3.0. Use it, share it, create with it - all derivatives stay open.
-
-[Get Started] [Read Docs] [See Examples]
-
-Would love to hear what you create!
-
-Best,
-[Your Name]
-
-P.S. - The temperature system is wild. Play Dragon Quest at temp 0, then replay at temp 10. Same scenario file, feels like two different games.
-```
-
----
-
-## Video Script (If Creating Demo Video)
-
-### 60-Second Demo
-
-```
-[0:00-0:10] Opening
-"Text adventures. We love them. But they're stuck in the past."
-
-[Show traditional text adventure screenshot]
-
-[0:10-0:20] The Problem
-"Pick A, B, or C. No improvisation. No adaptation. Same story every time."
-
-[0:20-0:35] Introducing Kleene
-"I built Kleene - an adaptive engine for Claude Code.
-
-Temperature 0: Traditional branching
-Temperature 10: AI-adapted storytelling
-
-Same scenario file. Infinite variations."
-
-[Show temp 0 vs temp 10 comparison]
-
-[0:35-0:50] Demo
-"Watch this. I type: 'I examine the dragon's scales closely'
-
-[Show AI response]
-
-The scenario didn't script this. The AI adapted based on my action."
-
-[0:50-1:00] Call to Action
-"Four scenarios included. Create your own in minutes.
-
-Free. Open source.
-
-/kleene play
-
-Link in description. Happy adventuring."
-```
-
----
-
-## Press Release (If Seeking Coverage)
-
-```
-FOR IMMEDIATE RELEASE
-
-Kleene: Adaptive AI Text Adventure Engine Launches for Claude Code
-
-[CITY, DATE] - Nathaniel Ramm today announced the launch of Kleene, an open-source adaptive text adventure engine built specifically for Claude Code. Unlike traditional interactive fiction, Kleene features a unique "temperature" system that allows players to control how much AI adaptation occurs during gameplay.
-
-"Traditional text adventures offer branching paths, but they're static," said Ramm. "With Kleene, the same scenario can play out in infinite ways depending on temperature settings and player improvisation. It's the difference between reading a choose-your-own-adventure book and having a conversation with an AI Game Master."
-
-Key innovations include:
-
-- Temperature System (0-10): Controls narrative adaptation from traditional branching to fully emergent storytelling
-- Free-Form Improvisation: Players can type any action, not just select from predetermined options
-- Hybrid Approach: Combines structured scenario design with AI-powered adaptation
-
-The plugin launches with four diverse scenarios ranging from fantasy adventures to psychological thrillers, with playtimes from 15 to 60 minutes. Creators can author new scenarios using simple YAML format with built-in generation and validation tools.
-
-Kleene is open source (AGPL-3.0) and available now in the Claude Code plugin marketplace.
-
-For more information, visit: [GitHub URL]
-
-Contact: [Email]
-```
-
----
-
-## Community Engagement Posts
-
-### "Show Us Your Scenarios" Post
-
-```
-๐ข Calling all Kleene creators!
-
-Have you made a scenario? We want to see it!
-
-Share your creations here:
-- Link to your scenario
-- Brief description
-- Estimated playtime
-- What inspired it?
-
-We'll feature the best ones in our weekly spotlight!
-
-#KleeneScenarios #InteractiveFiction
-```
-
-### Temperature Experiment Post
-
-```
-๐ฎ Temperature Experiment Challenge
-
-1. Play any Kleene scenario at temp 0
-2. Note 3 key moments
-3. Replay at temp 10
-4. Compare outcomes
-
-Share your most surprising difference in the replies!
-
-The temperature system is wild - same file, completely different experience.
-
-Who's in?
-```
-
----
-
-## Templates for User Testimonials
-
-When users share feedback, respond with:
-
-```
-Thank you for trying Kleene! ๐ฎ
-
-Would you mind if we featured this as a testimonial? We'd love to share your experience with other players/creators!
-
-Either way, thrilled you're enjoying it. Let us know if you create any scenarios - we'd love to spotlight them!
-```
-
----
-
-These templates provide consistent messaging while allowing customization for each platform's tone and format. Use them as starting points and adjust based on response and engagement.
diff --git a/docs/comparisons/comprehensive-literature-review.md b/docs/comparisons/comprehensive-literature-review.md
deleted file mode 100644
index 093ddf5..0000000
--- a/docs/comparisons/comprehensive-literature-review.md
+++ /dev/null
@@ -1,396 +0,0 @@
-# Kleene vs. The Literature: A Comprehensive Comparative Study
-
-## Executive Summary
-
-This document synthesizes comparative analyses between the Kleene narrative engine and the current landscape of LLM-powered interactive fiction research and practice. Drawing from academic papers, practitioner experiments, and community discourse, it positions Kleene within the field and identifies its distinctive contributions.
-
-**Core Finding:** Kleene's architectureโformal semantic frameworks, bounded AI generation, and external authoritative stateโaddresses problems that other systems discover through iteration. Where the literature identifies challenges, Kleene encodes solutions.
-
----
-
-## Sources Analyzed
-
-### Academic Research
-
-| Paper | Institution | Focus |
-|-------|-------------|-------|
-| **Story2Game** (Zhou et al., 2025) | Georgia Tech | LLM pipeline for generating complete IF games |
-| **GENEVA** (Leandro et al., 2024) | Microsoft Research | Graph-based branching narrative visualization |
-| **Narrative Quality Evaluation** (Valdivia & Burelli, 2025) | IT University Copenhagen | Story Quality Dimensions and Kano classification |
-| **The Language of Digital Air** (2025) | University of Macerata | AI-generated literature and authorship performance |
-| **Hipertext.net Special Issue** (2025) | UCL / UPF Barcelona | AI in narrative media (5 papers) |
-
-### Practitioner Work
-
-| Project | Creator | Focus |
-|---------|---------|-------|
-| **Intra** | Ian Bicking | LLM-driven text adventure with formal state |
-
-### Community Discourse
-
-| Source | Platform | Focus |
-|--------|----------|-------|
-| **"Why can't the parser just be an LLM?"** | intfiction.org | IF community concerns about LLM parsers |
-
----
-
-## The Central Problem: Ground Truth
-
-Every sourceโacademic, practitioner, and communityโidentifies the same fundamental challenge:
-
-> **LLMs are unreliable state managers.**
-
-### How Each Source Articulates This
-
-**Ian Bicking (Intra):**
-> "I wanted to create a game with real state, with a sense of 'ground truth': facts determined outside of narrative demands."
-
-**intfiction.org community:**
-> "LLMs lose track of conversation context over time, creating divergence between [user] understanding and [LLM] outputs."
-
-**Ferreira (AI Dungeon study):**
-> Genre conventions "amplify algorithmic biases" and narratives suffer "drift... deviating from recognized genre standards."
-
-**Kleene's response:**
-> "State is not stored in LLM memory. State is stored in validated YAML structures."
-
-This architectural decisionโexternalizing state from LLM contextโis Kleene's foundational insight, independently validated by Bicking's parallel development of Intra.
-
----
-
-## Architectural Comparisons
-
-### Generation Paradigms
-
-| System | Generation Target | Validation | Completeness Metric |
-|--------|-------------------|------------|---------------------|
-| **Story2Game** | Executable Python code | Compilation (~80% success) | Code compiles |
-| **GENEVA** | Visual DAG of narrative beats | Human inspection | Structural constraints met |
-| **AI Dungeon** | Freeform prose | None | N/A |
-| **Intra** | Narrative + state tags | Code-level checks | Author responsibility |
-| **Kleene** | YAML scenarios | 15 analysis types + JSON Schema | Decision Grid coverage |
-
-**Key insight:** Only Kleene defines formal completeness criteria for narrative coverage. A scenario with 10 endings could still be "incomplete" if all endings are victories (no Rebuff, Escape, or Fate cells covered).
-
-### State Management
-
-| System | State Location | Modification Method | Drift Prevention |
-|--------|----------------|---------------------|------------------|
-| **Story2Game** | Generated code objects | Code execution | Compilation |
-| **GENEVA** | N/A (design tool) | N/A | N/A |
-| **AI Dungeon** | LLM context | Freeform generation | None |
-| **Intra** | JavaScript objects | XML tags in LLM output | Developer discipline |
-| **Kleene** | YAML files | Typed consequence objects | Schema + soft limits |
-
-**Key insight:** Kleene and Intra independently converge on external state, but Kleene adds formal validation (JSON Schema, 15 analysis types) while Intra relies on careful prompting.
-
-### Improvisation Handling
-
-| System | Unanticipated Input | State Impact | Return Behavior |
-|--------|---------------------|--------------|-----------------|
-| **Story2Game** | Generate new code (~60% semantic success) | Full state changes possible | Continues from new state |
-| **AI Dungeon** | Freeform generation | Unlimited | Continues from new state |
-| **Intra** | Intent rewriting + resolution | Tag-based state changes | Continues from new state |
-| **Kleene** | Intent classification + feasibility check | Soft consequences only | Returns to same choices |
-
-**Key insight:** Kleene's "bounded improvisation" is uniqueโplayers can explore freely, but exploration enriches without derailing. The soft/hard consequence boundary prevents the "6-ton elephant in pocket" problem.
-
----
-
-## The Decision Grid: Kleene's Unique Contribution
-
-No other system in the literature provides a formal framework for narrative possibility space. Kleene's 3ร3 Decision Grid defines what "complete" means:
-
-```
- World Permits World Indeterminate World Blocks
- โโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
-Player Chooses โ Triumph โ Commitment โ Rebuff โ
- โโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโค
-Player Unknown โ Discovery โ Limbo โ Constraint โ
- โโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโค
-Player Avoids โ Escape โ Deferral โ Fate โ
- โโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโ
-```
-
-### Completeness Tiers
-
-| Tier | Coverage | Requirements |
-|------|----------|--------------|
-| **Bronze** | 4/9 cells | Triumph, Rebuff, Escape, Fate + death path + victory path |
-| **Silver** | 6+/9 cells | Bronze + middle cells (uncertainty, exploration) |
-| **Gold** | 9/9 cells | Full possibility space |
-
-### Why This Matters
-
-**Story2Game** can generate games with many pathsโall leading to victory. No measurement of whether failure, avoidance, or uncertainty are represented.
-
-**GENEVA** measures structural metrics (number of paths, endings) without considering whether the narrative covers different player strategies.
-
-**The Kano model** (Valdivia & Burelli) classifies quality dimensions by player satisfaction impact but doesn't define what dimensions constitute completeness.
-
-**Kleene's Decision Grid** provides the missing formal framework: a scenario isn't complete unless players can succeed, fail, explore, and avoidโand the world can permit, block, or leave outcomes hanging.
-
----
-
-## Validation Approaches
-
-### Academic Framework: Story Quality Dimensions
-
-Valdivia & Burelli identify 23 dimensions affecting narrative quality, classified via Kano model:
-- **Must-have (26%)**: Basic expectations; absence causes dissatisfaction
-- **One-dimensional (57%)**: Satisfaction proportional to performance
-- **Attractive (13%)**: Delighters when present
-- **Indifferent (4%)**: Little impact
-
-This requires **human expert panels** for evaluationโslow, expensive, post-hoc.
-
-### Kleene's Automated Validation
-
-| Analysis Type | What It Catches |
-|---------------|-----------------|
-| Grid Coverage | Missing player intent/world response combinations |
-| Null Cases | No death path, no transcendence path |
-| Structural | Unreachable nodes, dead ends, railroads |
-| Path Enumeration | Full path listing for manual review |
-| Cycle Detection | Infinite loops |
-| Item Obtainability | Required item never granted |
-| Trait Balance | Impossible trait requirements |
-| Flag Dependencies | Flag checked but never set |
-| Relationship Network | NPC relationship issues |
-| Consequence Magnitude | Over/undersized trait changes |
-| Scene Pacing | Rhythm issues |
-| Path Diversity | False choices (multiple options โ same destination) |
-| Ending Reachability | Endings with no path to them |
-| Travel Consistency | Time config issues |
-| Schema Validation | Type errors, missing fields, broken references |
-
-**Plus** JSON Schema validation against 1100-line schema with 23+ precondition types and 22+ consequence types.
-
-### Complementary Approaches
-
-| Aspect | SQD Framework | Kleene |
-|--------|---------------|--------|
-| **Focus** | Subjective quality | Structural completeness |
-| **Method** | Expert consensus | Automated analysis |
-| **Speed** | Slow (human panels) | Fast (immediate) |
-| **Coverage** | 23 quality dimensions | 15 structural checks |
-| **Iteration** | Post-generation | Pre-play |
-
-**Synthesis:** Kleene's automated analysis catches structural issues quickly; SQD-based expert evaluation catches subjective quality issues that automation misses. An ideal workflow combines both.
-
----
-
-## The Authorship Question
-
-### The Literature's Concern
-
-The "Language of Digital Air" paper identifies a crisis:
-> "The standard expectation of unknown texts rests on the minimal assumption that the text was written by a human who wants to say something."
-
-AI-generated texts lack intentionality. Paratextual apparatus (prefaces, afterwords, expert validations) exists to inject meaning that the text "may not intrinsically possess."
-
-### Kleene's Response: Formal Semantics as Meaning Infrastructure
-
-Kleene doesn't generate free-form prose that pretends to intentionality. Instead, meaning emerges from **formal structure**:
-
-| Source of Meaning | Kleene's Implementation |
-|-------------------|-------------------------|
-| Intentionality | Decision Grid cells (Triumph, Rebuff, etc.) have structural meaning |
-| Author voice | Temperature control (0 = verbatim, 10 = adaptive) |
-| Validation | Formal analysis, not rhetorical persuasion |
-| Quality metric | Completeness tier coverage, not aesthetic judgment |
-
-### Distributed Authorship Model
-
-| Role | Contribution |
-|------|--------------|
-| **Framework Author** | Decision Grid, Option types, completeness semantics |
-| **Scenario Author** | YAML structure, node graph, preconditions/consequences |
-| **Runtime LLM** | Improvisation responses, temperature-based adaptation |
-| **Player** | Intent (Chooses/Unknown/Avoids), free-text input |
-
-This explicit distribution avoids the "resurrection of the author through paratextual framing" that the literature critiques. The framework itself serves as the meaning-guarantor.
-
----
-
-## Addressing Community Concerns
-
-The intfiction.org thread "Why can't the parser just be an LLM?" represents comprehensive practitioner critique. Kleene's architecture directly addresses each concern:
-
-| Concern | Kleene's Solution | Status |
-|---------|-------------------|--------|
-| State consistency | Authored YAML + JSON Schema validation | โ Solved |
-| World model enforcement | 23+ precondition types, typed consequences | โ Solved |
-| Code generation quality | No code generation; YAML scenarios | โ Avoided |
-| Black box unpredictability | Authored structure + bounded improvisation | โ Mitigated |
-| Hallucination risk | Soft consequence limits | โ Bounded |
-| Reproducibility | Deterministic structure + save system | โ Solved |
-| "6-ton elephant" problem | Preconditions + feasibility checks | โ Solved |
-| Feedback loop degradation | State from YAML, not LLM memory | โ Solved |
-| "10,000 bowls of oatmeal" | Authored scenarios + grid completeness | โ Addressed |
-| Accessibility (offline) | Local YAML files, no server required | โ Solved |
-| Creative authorship role | Author provides structure; LLM provides texture | โ Preserved |
-
----
-
-## What Kleene Could Learn
-
-### From Story2Game
-- **Object creation in improvisation**: Limited emergent objects (flavor items, not key items)
-- **Automatic world population**: Generate location graphs from narrative
-
-### From GENEVA
-- **Visual graph output**: DAG visualization for scenario design
-- **Rapid prototyping**: Quick visual exploration of branching structures
-
-### From Intra
-- **Guided thinking pattern**: Explicit question sequences for complex action resolution
-- **NPC perspective filtering**: NPCs aware only of events in their location
-- **Streaming responses**: Token-by-token display for perceived responsiveness
-
-### From the Kano Model
-- **Severity weighting**: Classify analysis findings by satisfaction impact (Must-have violations vs. Attractive opportunities)
-
-### From Hipertext.net Research
-- **Voice consistency validation**: Check narrative text for vocabulary/tone drift
-- **Genre alignment validation**: Validate ending types match declared tone
-
----
-
-## Remaining Challenges
-
-### Acknowledged Across All Comparisons
-
-1. **Aesthetic quality**: Structural completeness โ compelling prose. The framework validates structure but cannot guarantee the generated text is actually good writing.
-
-2. **Full generation mode**: When `kleene-generate` creates complete scenarios, the generated content faces the same challenges Ferreira identifies in AI Dungeonโpotential for bias, drift, and coherence loss within generated portions.
-
-3. **High-temperature adaptation**: At temperature 10, narrative adaptation becomes substantial, approaching the unconstrained generation that produces drift in other systems.
-
-4. **The "alienness" of AI writing**: Even enthusiastic AI collaborators acknowledge LLM prose can feel "competent but somehow hollow." Narrative Purity rules attempt to mask this but may not fully succeed.
-
-5. **No visual tooling**: Unlike GENEVA, Kleene has no graph visualization for scenario designโauthors work directly with YAML.
-
----
-
-## Unique Capabilities Not Found Elsewhere
-
-### Compound Command Resolution
-
-Kleene can process multi-step natural language commands that span multiple nodes:
-
-> "go to the tree, climb it, get the egg, then go to the window, open it and climb in"
-
-This batch-resolves valid multi-node traversals in a single interactionโsomething AI Dungeon, Intra, and traditional IF parsers cannot do.
-
-### Three-Valued Logic Foundation
-
-Named for Stephen Cole Kleene's 1938 formalization, the framework uses Option types:
-- **Some(value)**: Protagonist exists and can act
-- **None(reason)**: Protagonist has ceased (death, departure, transcendence)
-- **Unknown**: Narrative hasn't resolved yet
-
-This enables the "World Indeterminate" columnโoutcomes that remain pending, creating suspense that other systems must resolve immediately.
-
-### Gallery Mode
-
-Meta-commentary system that separates analytical insight from narrative immersion:
-> "Like Frodo at Mount Doomโwhen the choice finally comes, will matters more than strength."
-
-The AI becomes a companion interpreter, not just a story generator.
-
-### Temperature as Agency Dial
-
-Explicit control over AI influence (0-10):
-- **0**: Verbatim scenario text, pure authorial voice
-- **5**: Balanced integration
-- **10**: Fully adaptive narrative
-
-This operationalizes the Vygotskian balance the academic literature theorizes about.
-
----
-
-## Synthesis: Kleene's Position in the Field
-
-### What Kleene Is
-
-A **principled architecture** for LLM-powered interactive fiction that:
-- Defines formal completeness criteria (Decision Grid)
-- Separates deterministic structure from probabilistic texture
-- Bounds AI influence through constraint architecture
-- Provides automated validation (15 analysis types + schema)
-- Gives explicit roles to authors, LLMs, and players
-
-### What Kleene Is Not
-
-- A visual design tool (cf. GENEVA)
-- A code generator (cf. Story2Game)
-- An unconstrained AI narrator (cf. AI Dungeon)
-- A solution to aesthetic quality
-
-### Where Kleene Fits
-
-```
- Authored โโโโโโโโโโโโโโโโโโโโโ Generated
- โ โ
- โโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโ
- โ โ โ โ
- Constrained โ KLEENE โ โ
- โ โ โโโโโโโ โ โ
- โ โ Authored structure โ โ
- โ โ + Generated texture โ โ
- โ โ + Formal validation โ โ
- โ โ โ โ
- โ โ โ AI โ
- โ Trad. โ โ Dungeon โ
- โ IF โ Intra โ โ
- โ โ โ โ
- Unconstrained โ โ โ
- โ โ โ โ
- โโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโ
- โ โ
- Authored โโโโโโโโโโโโโโโโโโโโโ Generated
-```
-
-Kleene occupies a unique position: **high constraint + hybrid origin**. It's more constrained than Intra (formal validation vs. developer discipline) while being more generative than traditional IF (LLM texture vs. pure authoring).
-
----
-
-## Conclusion
-
-The literature on LLM-powered interactive fiction reveals a consistent pattern: systems discover problems (state drift, hallucination, narrative incoherence) through iteration, then develop ad-hoc solutions.
-
-Kleene inverts this pattern by encoding solutions into its foundation:
-- **Decision Grid** defines completeness before generation
-- **Soft consequence limits** prevent hallucination architecturally
-- **External YAML state** eliminates context drift by design
-- **15 analysis types** catch problems before play
-
-The academic frameworks (Story Quality Dimensions, Kano model, two-dimensional agency space) provide theoretical vocabulary for problems Kleene addresses practically. The practitioner work (Intra) validates Kleene's core architectural decisions through independent convergence. The community discourse (intfiction.org) articulates concerns that Kleene's constraint architecture directly solves.
-
-**Kleene's contribution to the genre is not a better prompt or a smarter modelโit's a formal semantic framework that makes structure explicit, constrains generation to texture, and gives all participants clear roles within a defined possibility space.**
-
----
-
-## References
-
-### Academic Papers
-- Zhou, E., et al. (2025). Story2Game: Generating (Almost) Everything in an Interactive Fiction Game. arXiv:2505.03547v1
-- Leandro, J., et al. (2024). GENEVA: GENErating and Visualizing branching narratives using LLMs. IEEE CoG 2024. arXiv:2311.09213
-- Valdivia, A. & Burelli, P. (2025). Evaluating Quality of Gaming Narratives Co-created with AI. IEEE CoG 2025. arXiv:2509.04239
-- "The Language of the Digital Air: AI-Generated Literature and the Performance of Authorship" (2025). University of Macerata. doi:10.3390/h14080164
-- Hipertext.net Issue 31 (2025). Artificial Intelligence in Narrative Media. raco.cat/index.php/Hipertext
-
-### Practitioner Sources
-- Bicking, I. (2025). Intra: LLM-Driven Text Adventure. ianbicking.org/blog/2025/07/intra-llm-text-adventure
-
-### Community Discourse
-- "Why can't the parser just be an LLM?" intfiction.org/t/why-cant-the-parser-just-be-an-llm/64001
-
-### Kleene Framework
-- `lib/framework/core/core.md` โ Decision Grid, Option types, completeness tiers
-- `lib/framework/gameplay/improvisation.md` โ Soft consequences, intent classification, temperature
-- `lib/schema/scenario-schema.json` โ 1100-line JSON Schema
-- `skills/kleene-analyze/SKILL.md` โ 15 analysis types, validation pipeline
-- `skills/kleene-play/SKILL.md` โ Play engine, compound command resolution
-- `docs/design/theoretical_background.md` โ Three-valued logic foundations
diff --git a/docs/comparisons/geneva-comparison.md b/docs/comparisons/geneva-comparison.md
deleted file mode 100644
index b6094e3..0000000
--- a/docs/comparisons/geneva-comparison.md
+++ /dev/null
@@ -1,411 +0,0 @@
-# Analysis: Kleene Framework vs. Microsoft Research GENEVA
-
-## Overview
-
-This document compares the **Kleene narrative engine** against **GENEVA** (Leandro et al., Microsoft Research, IEEE CoG 2024), a graph-based tool for generating and visualizing branching narratives using GPT-4.
-
-Both systems use LLMs for narrative generation, but serve different purposes in the game development pipeline.
-
-**Paper:** [GENEVA: GENErating and Visualizing branching narratives using LLMs](https://arxiv.org/abs/2311.09213)
-**Interactive Demo:** https://narrative.msr-emergence.com/
-
----
-
-## The Two Systems at a Glance
-
-| Aspect | **GENEVA** | **Kleene** |
-|--------|------------|------------|
-| **Primary Purpose** | Visualize branching narratives for designers | Generate, validate, and play interactive narratives |
-| **Pipeline Phase** | Design-time visualization | Design-time generation + validation + runtime play |
-| **Output** | Visual DAG of narrative beats | Validated YAML scenarios + interactive gameplay |
-| **LLM Role** | Generate story structure | Generate, validate, interpret, and adapt |
-| **Target User** | Game designers | Game designers AND players |
-| **Validation** | Visual inspection | 15 analysis types + JSON Schema |
-| **Runtime Support** | None (design tool only) | Full play engine with improvisation |
-
----
-
-## Generation Capabilities
-
-### GENEVA: Constrained Graph Generation
-
-GENEVA accepts:
-- High-level narrative description
-- Structural constraints (# starts, # endings, # plot paths)
-- Context for grounding (setting, time period)
-
-GENEVA produces:
-- DAG of narrative beats (events that move plot forward)
-- Visual graph for designer exploration
-- Branching and reconverging storylines
-
-**Key constraint**: Number of paths, not narrative quality metrics.
-
-### Kleene: Grid-Targeted Generation with Validation
-
-Kleene's `kleene-generate` accepts:
-- Theme description
-- Tone (Heroic/Tragic/Comedic/Mysterious)
-- Completeness tier target (Bronze/Silver/Gold)
-- Protagonist archetype
-
-Kleene produces:
-- YAML scenario with nodes, choices, preconditions, consequences
-- Validated against 1100-line JSON Schema
-- Analyzed for Decision Grid coverage
-- Immediately playable with improvisation support
-
-**Key constraint**: Decision Grid coverage (narrative possibility space).
-
-### Generation Comparison
-
-| Feature | GENEVA | Kleene |
-|---------|--------|--------|
-| **Input constraints** | # paths, # endings | Grid tier, tone, archetype |
-| **Output format** | Visual graph | Validated YAML + registry entry |
-| **Validation** | Visual inspection | 15 analysis types + JSON Schema |
-| **Mechanical depth** | Narrative beats only | Preconditions, consequences, traits, items, flags |
-| **Iterability** | Re-generate | Branch expansion to improve tier |
-| **Designer workflow** | View โ manually implement | Generate โ analyze โ expand โ play |
-
----
-
-## Completeness Models
-
-### GENEVA: Structural Completeness
-
-GENEVA measures:
-- Number of starting points achieved
-- Number of endings achieved
-- Number of distinct plot paths
-- Graph connectivity
-
-These are **structural** metrics: does the graph satisfy the requested constraints?
-
-A GENEVA graph with 6 endings might have all victoriesโno measurement of whether failure, avoidance, or uncertainty are represented.
-
-### Kleene: Narrative Completeness via Decision Grid
-
-Kleene measures coverage of the 3ร3 Decision Grid:
-
-| | World Permits | World Indeterminate | World Blocks |
-|--------------------|---------------|---------------------|--------------|
-| **Player Chooses** | Triumph | Commitment | Rebuff |
-| **Player Unknown** | Discovery | Limbo | Constraint |
-| **Player Avoids** | Escape | Deferral | Fate |
-
-**Completeness Tiers:**
-
-| Tier | Coverage | Meaning |
-|------|----------|---------|
-| Bronze | 4/9 | Four corners (Triumph, Rebuff, Escape, Fate) + death + victory paths |
-| Silver | 6+/9 | Bronze + middle cells (Commitment, Discovery, Deferral, etc.) |
-| Gold | 9/9 | All cells represented |
-
-Kleene's `kleene-analyze` skill checks:
-- Which cells have paths
-- Whether death endings exist (NONE_DEATH)
-- Whether transcendence endings exist (NONE_REMOVED)
-- Whether both transformed and unchanged endings exist
-
-A scenario with many paths could still be Bronze-incomplete if it lacks avoidance or failure options.
-
----
-
-## Validation Capabilities
-
-### GENEVA: Visual Inspection
-
-Designers view the generated graph and manually assess:
-- Narrative quality
-- Path coherence
-- Story flow
-
-No automated validation beyond graph structure.
-
-### Kleene: 15 Analysis Types + JSON Schema
-
-The `kleene-analyze` skill performs:
-
-| # | Analysis | What It Catches |
-|---|----------|-----------------|
-| 1 | Grid Coverage | Missing player intent/world response combinations |
-| 2 | Null Cases | No death path, no transcendence path |
-| 3 | Structural | Unreachable nodes, dead ends, railroads |
-| 4 | Path Enumeration | Full path listing for manual review |
-| 5 | Cycle Detection | Infinite loops |
-| 6 | Item Obtainability | Required item never granted |
-| 7 | Trait Balance | Impossible trait requirements |
-| 8 | Flag Dependencies | Flag checked but never set |
-| 9 | Relationship Network | NPC relationship issues |
-| 10 | Consequence Magnitude | Over/undersized trait changes |
-| 11 | Scene Pacing | Rhythm issues |
-| 12 | Path Diversity | False choices (multiple options โ same destination) |
-| 13 | Ending Reachability | Endings with no path to them |
-| 14 | Travel Consistency | Time config issues |
-| 15 | Schema Validation | Type errors, missing fields, broken references |
-
-Plus JSON Schema validation with:
-- 23+ precondition types validated
-- 22+ consequence types validated
-- Reference integrity (all next_node targets exist)
-- Type correctness throughout
-
----
-
-## Mechanical Depth
-
-### GENEVA: Narrative Beats Only
-
-GENEVA generates:
-- Narrative text for each beat
-- Connections between beats
-
-No mechanical representation of:
-- What the player needs to reach a beat
-- What changes when a beat is visited
-- Character state or world state
-
-Implementation of mechanics is left to later development.
-
-### Kleene: Full Mechanical Specification
-
-Kleene scenarios include:
-
-**Preconditions (23+ types):**
-- Item checks: `has_item`, `missing_item`
-- Trait checks: `trait_minimum`, `trait_maximum`
-- Flag checks: `flag_set`, `flag_not_set`
-- Location checks: `at_location`, `location_flag_set`, `location_property_minimum`
-- Environment checks: `environment_is`, `environment_minimum`
-- NPC checks: `npc_at_location`, `npc_not_at_location`
-- Time checks: `time_elapsed_minimum`, `time_elapsed_maximum`
-- Event checks: `event_triggered`, `event_not_triggered`
-- Composable: `all_of`, `any_of`, `none_of`
-
-**Consequences (22+ types):**
-- Items: `gain_item`, `lose_item`
-- Traits: `modify_trait`, `set_trait`
-- Flags: `set_flag`, `clear_flag`
-- Relationships: `modify_relationship`
-- Location: `move_to`, `set_location_flag`, `modify_location_property`, `set_environment`
-- NPCs: `move_npc`
-- Time: `advance_time`
-- Events: `schedule_event`, `trigger_event`, `cancel_event`
-- Endings: `character_dies`, `character_departs`
-- History: `add_history`
-
-This enables scenarios with:
-- Location-specific puzzles (shrine is sealed until blessing_power reaches threshold)
-- NPC movement (merchant follows player to new location)
-- Scheduled events (poison takes effect 30 minutes after consumption)
-- Environmental changes (lighting changes based on player actions)
-
----
-
-## Runtime Support
-
-### GENEVA: None
-
-GENEVA is a design-time tool. Generated graphs must be manually implemented in a game engine before players can experience them.
-
-### Kleene: Full Play Engine
-
-Kleene's `kleene-play` skill provides:
-
-**Core Gameplay:**
-- Load scenario from YAML
-- Track character/world state
-- Evaluate preconditions
-- Apply consequences
-- Present choices via interactive menus
-
-**Improvisation System:**
-- Free-text input handling
-- Intent classification (Explore/Interact/Act/Meta)
-- Feasibility checking
-- Grid mapping (Discovery/Constraint/Limbo)
-- Soft consequences (ยฑ1 traits, improv_* flags)
-- Temperature-based narrative adaptation (0-10 scale)
-
-**Advanced Features:**
-- Save/load system with timestamped saves
-- Rewind to previous decision points
-- Export to transcript/summary/stats
-- Gallery mode for educational meta-commentary
-- Parser mode (text adventure-style, hide scripted options)
-
----
-
-## Workflow Comparison
-
-### GENEVA Workflow
-
-```
-Designer provides description + constraints
- โ
- GENEVA generates DAG
- โ
- Designer views graph
- โ
- Manual implementation in game engine
- โ
- Players experience game
-```
-
-Gap: Significant manual work between generation and play.
-
-### Kleene Workflow
-
-```
-Designer/LLM provides theme + tier + tone
- โ
- kleene-generate creates scenario
- โ
- kleene-analyze validates (15 checks)
- โ
- Issues found? โ kleene-generate expands branches
- โ
- Scenario registered in registry
- โ
- kleene-play runs game with improvisation
- โ
- Players experience game immediately
-```
-
-No gap: Generation โ Validation โ Play is a continuous pipeline.
-
----
-
-## Branch Expansion: Iterative Improvement
-
-### GENEVA: Re-generate
-
-If a GENEVA graph is unsatisfactory, the designer must re-generate with different constraints. No targeted expansion.
-
-### Kleene: Targeted Branch Expansion
-
-Kleene's `kleene-generate` Mode 3 (Branch Expansion) can:
-
-**For missing "Player Avoids" paths:**
-```yaml
-- id: refuse_quest
- text: "This is not my fight"
- consequence:
- - type: modify_trait
- trait: courage
- delta: -1
- next_node: ending_unchanged
-```
-
-**For missing death paths:**
-```yaml
-- id: reckless_action
- text: "Charge in without preparation"
- precondition:
- type: none_of
- conditions:
- - type: has_item
- item: armor
- consequence:
- - type: character_dies
- reason: "fell to overwhelming force"
- next_node: ending_death
-```
-
-**For Silver tier (middle cells):**
-
-*Commitment (action with pending outcome):*
-```yaml
-- id: drink_potion
- text: "Drink the mysterious liquid"
- consequence:
- - type: set_flag
- flag: potion_consumed
- - type: schedule_event
- event_id: potion_effect
- delay: { amount: 30, unit: minutes }
- consequences:
- - type: modify_trait
- trait: strength
- delta: 3
- next_node: await_effects
-```
-
-Analysis-driven expansion ensures generated scenarios meet completeness requirements.
-
----
-
-## Integration Possibility: GENEVA โ Kleene Pipeline
-
-A hybrid workflow could leverage both:
-
-```
-GENEVA generates branching narrative structure
- โ
-Designer reviews graph visualization
- โ
-Converter transforms GENEVA graph to Kleene YAML skeleton
- โ
-kleene-generate adds mechanical depth (preconditions, consequences)
- โ
-kleene-analyze validates (15 checks + schema)
- โ
-Issues? โ kleene-generate expands branches for grid coverage
- โ
-kleene-play runs game with improvisation
-```
-
-This leverages:
-- GENEVA's rapid visual prototyping
-- Kleene's mechanical depth and validation
-- Kleene's runtime improvisation
-
----
-
-## Summary Comparison
-
-| Capability | GENEVA | Kleene |
-|------------|--------|--------|
-| **Generate narrative structure** | โ | โ |
-| **Visual graph output** | โ | โ |
-| **Mechanical specification** | โ | โ (23+ preconditions, 22+ consequences) |
-| **Validation pipeline** | โ | โ (15 analysis types + JSON Schema) |
-| **Completeness metrics** | Structural only | Decision Grid coverage |
-| **Iterative expansion** | Re-generate | Targeted branch expansion |
-| **Runtime play** | โ | โ |
-| **Player improvisation** | โ | โ (bounded soft consequences) |
-| **Designer involvement** | View graph | Interactive menus throughout |
-
----
-
-## Conclusion
-
-GENEVA and Kleene address different parts of the narrative game development problem:
-
-- **GENEVA** excels at **rapid visual prototyping** for designers to explore branching structures
-- **Kleene** excels at **complete pipeline** from generation through validation to play
-
-GENEVA helps designers see what a narrative could look like.
-Kleene helps designers (and players) experience what a narrative actually does.
-
-A combined approach would use GENEVA for initial brainstorming and Kleene for mechanical implementation, validation, and runtime.
-
----
-
-## References
-
-**GENEVA:**
-- Leandro, J., Rao, S., Xu, M., Xu, W., Jojic, N., Brockett, C., & Dolan, B. (2024). GENEVA: GENErating and Visualizing branching narratives using LLMs. IEEE Conference on Games 2024.
-- Paper: https://arxiv.org/abs/2311.09213
-- Demo: https://narrative.msr-emergence.com/
-- Blog: https://www.microsoft.com/en-us/research/blog/geneva-uses-large-language-models-for-interactive-game-narrative-design/
-
-**Kleene Framework:**
-- `lib/framework/core/core.md` - Decision Grid, Option types, completeness tiers
-- `lib/schema/scenario-schema.json` - 1100-line JSON Schema (23+ preconditions, 22+ consequences)
-- `skills/kleene-generate/SKILL.md` - Generation modes, grid targeting, branch expansion
-- `skills/kleene-analyze/SKILL.md` - 15 analysis types, validation pipeline
-- `skills/kleene-play/SKILL.md` - Play engine, improvisation, temperature system
-- `lib/framework/gameplay/improvisation.md` - Soft consequences, intent classification
-- `docs/design/theoretical_background.md` - Three-valued logic foundations
diff --git a/docs/comparisons/intfiction-community-concerns.md b/docs/comparisons/intfiction-community-concerns.md
deleted file mode 100644
index 4b1b16a..0000000
--- a/docs/comparisons/intfiction-community-concerns.md
+++ /dev/null
@@ -1,515 +0,0 @@
-# Analysis: Kleene Framework vs. Interactive Fiction Community Concerns
-
-## Overview
-
-This document analyzes how the **Kleene narrative engine** addresses concerns raised by the interactive fiction community in the discussion thread ["Why can't the parser just be an LLM?"](https://intfiction.org/t/why-cant-the-parser-just-be-an-llm/64001) on intfiction.org.
-
-The thread represents a comprehensive critique from experienced IF developers and players about the problems with using LLMs as game parsers. Kleene's architecture directly addresses many of these concerns.
-
----
-
-## Summary: How Kleene Addresses Each Concern
-
-| Community Concern | Kleene's Solution | Status |
-|-------------------|-------------------|--------|
-| State consistency | Authored YAML + JSON Schema validation | โ Solved |
-| World model enforcement | 23+ precondition types, consequences | โ Solved |
-| Code generation quality | No code generation; YAML scenarios | โ Avoided |
-| Black box unpredictability | Authored structure + bounded improvisation | โ Mitigated |
-| Hallucination risk | Soft consequence limits | โ Bounded |
-| Reproducibility | Deterministic structure + save system | โ Solved |
-| 6-ton elephant problem | Preconditions + feasibility checks | โ Solved |
-| Feedback loop degradation | State from YAML, not LLM memory | โ Solved |
-| 10,000 bowls of oatmeal | Authored scenarios + grid completeness | โ Addressed |
-| Accessibility (offline) | Local YAML files, no server required | โ Solved |
-| Creative authorship role | Author provides structure; LLM provides texture | โ Preserved |
-| Natural language didn't help adoption | Optional; parser mode available | ~ Acknowledged |
-
----
-
-## Detailed Analysis
-
-### Concern 1: State Consistency
-
-**Community concern** (multiple posters):
-> "LLMs lose track of conversation context over time, creating divergence between [user] understanding and [LLM] outputs."
-
-**Kleene's solution:**
-
-State is **not** stored in LLM memory. State is stored in validated YAML structures:
-
-```yaml
-character:
- exists: true
- traits: { courage: 7, wisdom: 5 }
- inventory: [sword, torch]
- flags: { met_guardian: true }
-
-world:
- current_location: temple_entrance
- time: 3600 # seconds
- location_state:
- shrine:
- flags: { sealed: false }
- properties: { blessing_power: 75 }
-```
-
-The LLM reads state at each turn; it doesn't remember it between turns. State persists in the scenario file and save system, validated against a 1100-line JSON Schema.
-
-**Verdict**: โ **Solved** - State is authoritative, not probabilistic.
-
----
-
-### Concern 2: World Model Enforcement
-
-**Community concern** (Kayne_agent):
-> "LLMs have no framework that constructs or enforces logically consistent response."
-
-**Kleene's solution:**
-
-Kleene enforces world logic through **23+ precondition types**:
-
-| Category | Precondition Types |
-|----------|-------------------|
-| Items | `has_item`, `missing_item` |
-| Traits | `trait_minimum`, `trait_maximum` |
-| Flags | `flag_set`, `flag_not_set` |
-| Location | `at_location`, `location_flag_set`, `location_property_minimum` |
-| Environment | `environment_is`, `environment_minimum`, `environment_maximum` |
-| NPCs | `npc_at_location`, `npc_not_at_location` |
-| Time | `time_elapsed_minimum`, `time_elapsed_maximum` |
-| Events | `event_triggered`, `event_not_triggered` |
-| Composable | `all_of`, `any_of`, `none_of` |
-
-Options only appear if preconditions pass. The LLM cannot override thisโit's evaluated deterministically before presentation.
-
-**Example:**
-```yaml
-- id: open_sealed_door
- text: "Open the ancient door"
- precondition:
- type: all_of
- conditions:
- - type: has_item
- item: temple_key
- - type: location_flag_not_set
- location: shrine
- flag: sealed
-```
-
-This option only appears if the player has the key AND the shrine isn't sealed. No LLM hallucination can bypass this.
-
-**Verdict**: โ **Solved** - Logic is deterministic, not probabilistic.
-
----
-
-### Concern 3: The "6-Ton Elephant" Problem
-
-**Community concern** (Kayne_agent):
-> "LLM might allow picking up a 6-ton elephant and putting it in a pocket."
-
-**Kleene's solution:**
-
-**For authored paths:** Preconditions prevent impossible actions. You can't pick up an elephant unless the author created that option with appropriate preconditions.
-
-**For improvised actions:** The feasibility check explicitly catches impossibilities:
-
-```
-Feasibility Classification:
-- Possible: World permits this action
-- Blocked: Missing required item/trait, wrong location
-- Impossible: Breaks scenario logic, contradicts world rules
-- Ambiguous: Intent unclear (maps to Limbo)
-```
-
-The improvisation handler generates a **narrative response** explaining why something is impossible, not code that executes the impossible action:
-
-```
-You consider picking up the elephant, but the absurdity of the thought
-makes you pause. Even if you somehow lifted six tons, where would you
-put it? The world doesn't bend to impossible wishes.
-```
-
-The player receives a Constraint response (+0 traits, no state change) and returns to the same choices.
-
-**Verdict**: โ **Solved** - Impossible actions are narratively rejected, not executed.
-
----
-
-### Concern 4: Code Generation Quality
-
-**Community concern** (Michael.Penner):
-> "LLM code generation is mostly full of holes... produces incorrect regex, broken C# requiring fixes."
-
-**Kleene's solution:**
-
-Kleene generates **no executable code**. The output is YAML validated against JSON Schema:
-
-```yaml
-consequence:
- - type: gain_item
- item: sword
- - type: modify_trait
- trait: courage
- delta: 2
-```
-
-This is **data**, not code. The Kleene runtime interprets it deterministically. Schema validation catches malformed structures before play:
-
-```bash
-check-jsonschema --schemafile lib/schema/scenario-schema.json scenario.yaml
-```
-
-**Verdict**: โ **Avoided** - No code generation means no code bugs.
-
----
-
-### Concern 5: Black Box Unpredictability
-
-**Community concern** (multiple):
-> "Unlike traditional parsers with predictable rules, LLMs operate as probabilistic models... billions of parameters in ways that resist interpretation."
-
-**Kleene's solution:**
-
-Kleene separates **predictable structure** from **adaptive texture**:
-
-| Layer | Predictable? | What It Controls |
-|-------|--------------|------------------|
-| Scenario YAML | โ Deterministic | Nodes, choices, preconditions, consequences, endings |
-| Precondition evaluation | โ Deterministic | Which options appear |
-| Consequence application | โ Deterministic | State changes |
-| Narrative presentation | ~ Adaptive | How text is phrased (temperature 0-10) |
-| Improvisation response | ~ Bounded | Soft consequences only |
-
-At temperature 0, narrative is verbatim from YAML. Even at temperature 10, the **structure** (which nodes exist, what consequences apply) is deterministic. The LLM only affects **presentation**.
-
-**Verdict**: โ **Mitigated** - Core logic is deterministic; LLM only handles texture.
-
----
-
-### Concern 6: Hallucination Risk
-
-**Community concern** (multiple):
-> "LLMs generate plausible-sounding but false content, breaking narrative credibility."
-
-**Kleene's solution:**
-
-Improvised actions are bounded by **soft consequence limits**:
-
-| Allowed (Soft) | Prohibited (Hard) |
-|----------------|-------------------|
-| `modify_trait` (ยฑ1 max) | `gain_item` (scenario items) |
-| `add_history` | `lose_item` |
-| `set_flag` (only `improv_*` prefix) | `move_to` |
-| `advance_time` | `character_dies` |
-| | `character_departs` |
-
-The LLM can hallucinate that the player found a secret passageโbut it can only set `improv_found_passage` (a flag with no mechanical weight). It cannot grant the actual key item, move the player to a new location, or kill the character.
-
-**Hallucination is bounded to narrative flavor, not game state.**
-
-**Verdict**: โ **Bounded** - Hallucinations affect texture, not structure.
-
----
-
-### Concern 7: Reproducibility
-
-**Community concern**:
-> "Each playthrough potentially differs based on LLM training data, preventing consistent player experiences."
-
-**Kleene's solution:**
-
-**Authored content is reproducible.** Given the same scenario and same choices, the same preconditions pass, the same consequences apply, and the player reaches the same endings.
-
-**Improvised content varies** (as intendedโexploration rewards curiosity differently). But:
-- Improvisation doesn't change core paths
-- `improv_*` flags track what was discovered
-- Temperature 0 disables all adaptation for pure reproducibility
-
-**Save system enables reproduction:**
-```yaml
-# saves/dragon_quest/20240115-143022.yaml
-format_version: 7
-scenario_id: dragon_quest
-character:
- traits: { courage: 7, wisdom: 6 }
- inventory: [sword, torch]
- flags: { improv_examined_scales: true }
-world:
- current_location: dragon_lair
- time: 7200
-counters:
- turn: 12
- scene: 3
- beat: 2
-```
-
-Loading a save reproduces exact state. Rewind to any turn/scene/beat.
-
-**Verdict**: โ **Solved** - Structure is reproducible; adaptation is opt-in.
-
----
-
-### Concern 8: Feedback Loop Degradation
-
-**Community concern** (Kayne_agent):
-> "LLMs create feedback loops generating incoherent states."
-
-**Kleene's solution:**
-
-State comes from **scenario YAML**, not LLM context accumulation.
-
-Traditional LLM games:
-```
-Turn 1: LLM says you have a sword
-Turn 2: LLM says you have a sword
-Turn 3: LLM forgets the sword
-Turn 4: LLM says you have two swords
-```
-
-Kleene:
-```
-Turn 1: YAML says inventory: [sword]. LLM describes sword.
-Turn 2: YAML says inventory: [sword]. LLM describes sword.
-Turn 3: YAML says inventory: [sword]. LLM describes sword.
-Turn 4: YAML says inventory: [sword]. LLM describes sword.
-```
-
-The LLM reads authoritative state each turn. It cannot accumulate drift.
-
-**Verdict**: โ **Solved** - No feedback loop possible; state is external to LLM.
-
----
-
-### Concern 9: "10,000 Bowls of Oatmeal" Problem
-
-**Community concern** (smwhr, referencing Kate Compton):
-> "Procedurally generated content may be mathematically unique but lack 'perceptual uniqueness' to players."
-
-**Kleene's solution:**
-
-Kleene primarily uses **authored scenarios**, not pure procedural generation. The `kleene-generate` skill creates scenarios with:
-
-1. **Narrative skeleton designed for completeness tiers** (Bronze/Silver/Gold)
-2. **Human-guided generation** (interactive menus for tone, archetype, tier)
-3. **Validation against Decision Grid** (ensures narrative diversity)
-4. **Branch expansion for missing cells** (targeted, not random)
-
-Generated scenarios are **validated** before play:
-- 15 analysis types catch structural issues
-- Grid coverage ensures different player strategies lead to meaningfully different outcomes
-- Endings are classified by type (victory/death/transcendence/unchanged) AND method AND tone
-
-A scenario with 10 "victory by force" endings would fail analysis. The framework enforces perceptual diversity.
-
-**Verdict**: โ **Addressed** - Completeness tiers + validation enforce meaningful variety.
-
----
-
-### Concern 10: Accessibility (Offline, No Account)
-
-**Community concern** (inventor200):
-> "Server requirements, account creation, internet dependency... Vorple framework's technical barriers similarly limited adoption."
-
-**Kleene's solution:**
-
-Kleene scenarios are **local YAML files**. No server, no account, no internet required for:
-- Scenario storage (`scenarios/*.yaml`)
-- Save files (`saves/[scenario]/[timestamp].yaml`)
-- Scenario registry (`scenarios/registry.yaml`)
-
-The LLM (Claude) is accessed through Claude Code, which the user already has. No additional infrastructure.
-
-**Deployment options:**
-- Local files for offline play
-- Git repository for scenario sharing
-- No mandatory accounts beyond existing Claude Code access
-
-**Verdict**: โ **Solved** - Pure local files, no additional infrastructure.
-
----
-
-### Concern 11: Creative Authorship Role
-
-**Community concern** (pinkunz):
-> "If both the challenge and the creativity are done via AI, what's the end game?"
-
-**Kleene's solution:**
-
-Kleene explicitly separates authorial roles:
-
-| Role | Responsibility |
-|------|----------------|
-| **Author** | Structure: nodes, choices, preconditions, consequences, endings, items, traits |
-| **LLM** | Texture: narrative adaptation, improvisation responses, atmospheric enrichment |
-
-The author provides the **puzzle** (what items exist, what gates what, how to win/lose). The LLM provides **flavor** (how the sword gleams, what the dragon's scales look like when examined).
-
-**Temperature control** lets authors set the balance:
-- Temperature 0: Pure authorial voice, no LLM influence
-- Temperature 5: Balanced integration
-- Temperature 10: Full co-creation
-
-Authors who want complete control use temperature 0. Authors who want collaborative texture use higher temperatures. The choice is explicit.
-
-**Verdict**: โ **Preserved** - Author controls structure; LLM enhances texture.
-
----
-
-### Concern 12: Natural Language Didn't Improve Adoption
-
-**Community concern** (cchennnn, DeusIrae):
-> "Historical data shows natural language didn't improve adoption... easier to learn a parser game than Elden Ring, yet latter sold tens of millions."
-
-**Kleene's solution:**
-
-Kleene doesn't claim natural language will increase adoption. Instead, it offers **multiple interaction modes**:
-
-**Standard mode:** Scripted options presented via menus (no typing required)
-```
-What do you do?
-1. Draw your sword
-2. Speak to the dragon
-3. Flee through the tunnel
-4. [Other - type custom action]
-```
-
-**Parser mode:** Text adventure-style with hidden options
-```
-> examine dragon
-> talk to dragon
-> attack dragon with sword
-```
-
-**Natural language:** Optional "Other" for free-text when players want it
-
-The primary interface is **menu-based**, not natural language. Free-text is available for players who want it, not required for those who don't.
-
-**Verdict**: ~ **Acknowledged** - Natural language is optional; menus are primary.
-
----
-
-## Community-Proposed Solutions vs. Kleene
-
-The thread proposed several approaches. Here's how Kleene compares:
-
-### Proposal 1: LLM as Preprocessing Layer
-
-**Thread suggestion** (grimjim, HanonO):
-> "Use LLMs to translate natural language commands into recognized game syntax."
-
-**Kleene implementation:**
-
-Kleene's improvisation system does exactly this:
-1. Player types free-text
-2. LLM classifies intent (Explore/Interact/Act/Meta)
-3. LLM checks feasibility against state
-4. Maps to grid cell (Discovery/Constraint/Limbo)
-5. Generates narrative response
-6. Returns to authored choices
-
-The LLM **interprets** player intent but doesn't **execute** game logic. The scenario structure handles execution.
-
-### Proposal 2: LLM for Error Messages
-
-**Thread suggestion** (HanonO):
-> "Use LLMs for intelligent contextual error messages, helping players understand syntax."
-
-**Kleene implementation:**
-
-When preconditions block an option, Kleene shows authored `blocked_narrative`:
-```yaml
-blocked_narrative: "The door won't budge. Ancient runes pulse faintlyโwhatever seal holds it closed requires more than brute force."
-```
-
-For improvised impossible actions, the LLM generates contextual explanations:
-```
-You try to fly, but gravityโand your conspicuous lack of wingsโ
-disagree. Perhaps another approach would serve better.
-```
-
-This is exactly the "intelligent contextual error message" the thread proposed.
-
-### Proposal 3: LLM for NPC Dialogue
-
-**Thread suggestion** (evouga):
-> "LLMs better suited for NPC dialogue generation using author-provided character context."
-
-**Kleene implementation:**
-
-Improvised "Interact" actions (talking to NPCs) generate dialogue:
-- Constrained by relationship values
-- Informed by character flags
-- Bounded by soft consequences (+1 to +3 relationship change)
-
-The author defines NPC existence and relationships; the LLM generates dialogue texture.
-
-### Proposal 4: Keep Game Engine Separate
-
-**Thread suggestion** (multiple):
-> "Let the game engine manage mechanics... LLM can do (1) [interpretation] but not (2) [state management]."
-
-**Kleene implementation:**
-
-This is Kleene's core architecture:
-
-```
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-โ Kleene Runtime โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ Scenario YAML โ LLM (Claude) โ
-โ โโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโ โ
-โ โข Nodes โ โข Intent classification โ
-โ โข Preconditions โ โข Feasibility narrative โ
-โ โข Consequences โ โข Temperature adaptation โ
-โ โข State validation โ โข Improvisation response โ
-โ โข Save/load โ โข (Soft consequences only) โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-```
-
-The game engine (YAML + schema + precondition evaluation) is **completely separate** from the LLM. The LLM is a **peripheral** for interpretation and texture, not the core.
-
----
-
-## Concerns Kleene Does NOT Address
-
-### Voice Interface Accessibility
-
-The thread raised concerns about voice-only interfaces excluding people with speech disabilities. Kleene is **text-based** (menus + optional typing), so this concern doesn't applyโbut Kleene also doesn't provide voice input.
-
-### Training Data Bias
-
-LLMs reflect biases in training data. Kleene's authored scenarios can avoid this in structure, but improvisation responses may still reflect model biases. Authors can use temperature 0 to avoid this entirely.
-
-### Copyright and Business Secrets
-
-Some venues ban LLM-connected systems. Kleene requires Claude Code access, which may not be permitted in all environments. This is an infrastructure constraint, not a Kleene design issue.
-
----
-
-## Conclusion
-
-The intfiction.org thread identified fundamental problems with using LLMs as game parsers. Kleene's architecture directly addresses these by:
-
-1. **Separating state from LLM** - YAML is authoritative, not LLM memory
-2. **Deterministic logic** - Preconditions and consequences are not probabilistic
-3. **Bounded improvisation** - LLM can only affect texture, not structure
-4. **No code generation** - Data (YAML), not code
-5. **Validation pipeline** - 15 analysis types + JSON Schema
-6. **Author control** - Temperature setting, authored structure preserved
-
-Kleene represents what the thread participants proposed: an LLM as **interpretation and texture layer** with a **separate deterministic game engine** maintaining consistent world state.
-
----
-
-## References
-
-**Community Discussion:**
-- [Why can't the parser just be an LLM?](https://intfiction.org/t/why-cant-the-parser-just-be-an-llm/64001) - intfiction.org (2023-2024)
-
-**Kleene Framework:**
-- `lib/framework/core/core.md` - Decision Grid, Option types
-- `lib/framework/gameplay/improvisation.md` - Soft consequences, feasibility checks
-- `lib/schema/scenario-schema.json` - 1100-line JSON Schema
-- `skills/kleene-analyze/SKILL.md` - 15 analysis types
-- `docs/design/theoretical_background.md` - Parser problem analysis, bounded creativity
diff --git a/docs/comparisons/intra-bicking-comparison.md b/docs/comparisons/intra-bicking-comparison.md
deleted file mode 100644
index 8feaf35..0000000
--- a/docs/comparisons/intra-bicking-comparison.md
+++ /dev/null
@@ -1,427 +0,0 @@
-# Analysis: Kleene Framework vs. Intra (Ian Bicking's LLM Text Adventure)
-
-## Overview
-
-This document compares the **Kleene narrative engine** against **Intra**, an LLM-driven text adventure created by Ian Bicking and documented in his blog post ["Intra: LLM-Driven Text Adventure"](https://ianbicking.org/blog/2025/07/intra-llm-text-adventure).
-
-Both projects grapple with the same fundamental challenge: how to create coherent, playable interactive fiction with LLMs while maintaining "ground truth" about game state. They arrive at remarkably similar architectural decisions from different starting points.
-
----
-
-## The Two Systems at a Glance
-
-| Aspect | **Intra** | **Kleene** |
-|--------|-----------|------------|
-| **Architecture** | Client-side TypeScript + OpenRouter | Claude Code plugin (YAML + LLM) |
-| **State Management** | Code-tracked formal state | YAML scenarios + JSON Schema |
-| **Action Resolution** | Intent parsing โ guided thinking โ state tags | Intent classification โ feasibility โ soft consequences |
-| **NPC Handling** | Filtered perspective + selection step | Authored nodes + relationship tracking |
-| **Hallucination Control** | Inventory visibility + minimal objects | Soft consequence limits |
-| **Prompt Strategy** | Markup tags, no tools, role inversion | Skill prompts with bounded improvisation |
-| **Authoring Model** | Author creates world, LLM narrates | Author creates scenarios, LLM adapts texture |
-
----
-
-## Shared Core Insight: Ground Truth
-
-Both systems independently arrive at the same architectural principle:
-
-### Bicking's Formulation
-
-> "I wanted to create a game with real state, with a sense of 'ground truth': facts determined outside of narrative demands."
-
-> "If the events stay ungrounded...there's a sense that we're navigating a collaborative dreamscape."
-
-### Kleene's Formulation
-
-> "State is not stored in LLM memory. State is stored in validated YAML structures."
-
-> "Improvisation enriches the current moment without derailing scenario balance. Major state changes are reserved for scripted paths."
-
-**The shared insight**: LLMs are unreliable state managers. Coherent games require **external authoritative state** that the LLM reads but doesn't control.
-
----
-
-## Problem-by-Problem Comparison
-
-### Problem 1: State Consistency
-
-**Bicking's experience:**
-> "If the events stay ungrounded and nothing is resolved by code, there's a sense that we're navigating a collaborative dreamscape."
-
-**Bicking's solution:** Formal code tracking game state (player position, inventory, door locks) with tagged state modifications:
-```xml
-Hollow_Atrium
-```
-
-**Kleene's solution:** YAML state validated against JSON Schema:
-```yaml
-world:
- current_location: temple_entrance
- location_state:
- shrine:
- flags: { sealed: false }
-```
-
-State changes through typed consequences:
-```yaml
-consequence:
- - type: clear_location_flag
- location: shrine
- flag: sealed
-```
-
-| Approach | Intra | Kleene |
-|----------|-------|--------|
-| State format | JavaScript objects | YAML with JSON Schema |
-| Modification | XML tags in LLM output | Typed consequence objects |
-| Validation | Code-level | Schema validation + 15 analysis types |
-
-**Verdict**: Both externalize state from LLM. Kleene adds formal validation layer.
-
----
-
-### Problem 2: Hallucination / Object Inflation
-
-**Bicking's experience:**
-> "If objects can be hallucinated into existence then this can get out of hand."
-
-**Bicking's solutions:**
-- Display player inventory prominently to anchor reality
-- Minimize object count in world design
-- Use narrative integration ("chainsaw-carrying" dominates perception)
-
-**Kleene's solution:** Soft consequence limits prohibit item creation during improvisation:
-
-| Allowed (Soft) | Prohibited (Hard) |
-|----------------|-------------------|
-| `modify_trait` (ยฑ1) | `gain_item` |
-| `add_history` | `lose_item` |
-| `set_flag` (improv_* only) | `move_to` |
-| `advance_time` | `character_dies` |
-
-The LLM can describe examining a sword but cannot create one. Items exist only if authored in the scenario.
-
-| Approach | Intra | Kleene |
-|----------|-------|--------|
-| Prevention method | Design constraint + visibility | Hard-coded consequence limits |
-| Enforcement | Developer discipline | Schema + runtime checks |
-| Flexibility | Author can allow | Strict boundary |
-
-**Verdict**: Kleene enforces at the system level; Intra relies on careful prompting.
-
----
-
-### Problem 3: Player Action Suggestibility
-
-**Bicking's experience:**
-> "Direct player input is too suggestible. 'Marta and Ama get into a disagreement' could be interpreted as the player causing this."
-
-**Bicking's solution:** Intent parsing/rewriting:
-```
-Player input: "open the door"
-Rewritten: Player attempts to open the door
-```
-
-**Kleene's solution:** Intent classification system:
-
-```
-Player types: "I try to pick the lock"
- โ
-Intent classification: Act
- โ
-Feasibility check: Blocked (no lockpicks)
- โ
-Grid mapping: Constraint
- โ
-Response: "You examine the lock mechanism, but without proper tools,
- you'd only damage it. The lock requires specialized picks."
- โ
-Return to authored choices
-```
-
-Player can only affect state through soft consequencesโthey cannot declare narrative facts.
-
-| Approach | Intra | Kleene |
-|----------|-------|--------|
-| Input handling | Rewrite to action tags | Classify intent + check feasibility |
-| Authority | LLM resolves rewritten action | Scenario preconditions gate outcomes |
-| Player power | Can attempt anything | Can attempt; outcome bounded |
-
-**Verdict**: Both filter player input. Kleene adds feasibility checking against state.
-
----
-
-### Problem 4: NPC Over-Responsiveness
-
-**Bicking's experience:**
-> "Lunchtime conversations in Intra get out of hand when every NPC in the complex gets a turn."
-
-> "An 'unengaged character who is unengaged in the event' remains difficultโthe model defaults to generating something."
-
-**Bicking's solutions:**
-- Selection step: Ask LLM which 2-3 NPCs should respond
-- Perspective filtering: NPCs see only events in their room
-
-**Kleene's solution:** NPCs are authored, not simulated:
-
-- NPC dialogue generated only during "Interact" improvisation
-- `npc_locations` tracks where NPCs are (authored or via `move_npc`)
-- `npc_at_location` / `npc_not_at_location` preconditions control presence
-- Relationship values gate dialogue depth
-
-NPCs don't autonomously respondโthey're invoked when players explicitly interact.
-
-| Approach | Intra | Kleene |
-|----------|-------|--------|
-| NPC agency | Autonomous with selection filter | Reactive only (player-invoked) |
-| Presence | Dynamic (LLM tracks) | Authored + `move_npc` consequences |
-| Dialogue | Generated per NPC turn | Generated on player interaction |
-
-**Verdict**: Intra attempts autonomous NPCs (hard problem). Kleene sidesteps by making NPCs reactive.
-
----
-
-### Problem 5: Memory / Context Limits
-
-**Bicking's experience:**
-> "Event history provides the only memory; critical information vanishes into context depth."
-
-**Bicking's proposed solution:** Explicit memory uplift mechanisms (not yet implemented).
-
-**Kleene's solution:** State persists in YAML, not context:
-
-- Character flags: `{ met_guardian: true, learned_secret: true }`
-- World flags: `{ door_unlocked: true }`
-- `improv_*` flags: Track improvised discoveries
-- Relationships: Numeric values persist across sessions
-- History array: `add_history` consequences record events
-
-The LLM reads current state each turnโno accumulated context drift.
-
-| Approach | Intra | Kleene |
-|----------|-------|--------|
-| Memory location | Context window | YAML state + flags |
-| Persistence | Session only | Save files across sessions |
-| Retrieval | Context depth | Explicit state read each turn |
-
-**Verdict**: Kleene's external state eliminates context-based memory loss entirely.
-
----
-
-### Problem 6: Prompt Engineering Complexity
-
-**Bicking's strategies:**
-
-1. **No tools**: Markup tags instead of structured tool use
-2. **Role inversion**: User = game engine, not player
-3. **Guided thinking**: Explicit question sequences forcing reasoning
-4. **Minimize indirection**: IDs match titles, consistent markup
-
-**Kleene's strategies:**
-
-1. **Skill prompts**: Detailed instructions in SKILL.md files
-2. **AskUserQuestion**: Structured menus for player choices
-3. **Intent classification**: Explicit categories (Explore/Interact/Act/Meta)
-4. **Temperature control**: 0-10 scale for narrative adaptation
-5. **Soft/hard boundary**: Clear rules on what LLM can affect
-
-| Strategy | Intra | Kleene |
-|----------|-------|--------|
-| Structured output | XML markup tags | Consequence types + AskUserQuestion |
-| Reasoning control | Guided thinking questions | Intent โ Feasibility โ Grid mapping |
-| Flexibility tuning | N/A | Temperature 0-10 |
-| Output constraints | Role/markup conventions | Soft consequence whitelist |
-
-**Verdict**: Both use explicit structuring. Kleene adds temperature control for author preference.
-
----
-
-### Problem 7: "What Is the Game?"
-
-**Bicking's concern:**
-> "What really is the 'game' here? What makes it fun?"
-
-> Traditional IF puzzles function as "pass-fail riddles with no middle groundโunlike 'dartboard' games offering gradual feedback."
-
-**Kleene's answer:** The Decision Grid provides a formal model of what the "game" is:
-
-| | World Permits | World Indeterminate | World Blocks |
-|--------------------|---------------|---------------------|--------------|
-| **Player Chooses** | Triumph | Commitment | Rebuff |
-| **Player Unknown** | Discovery | Limbo | Constraint |
-| **Player Avoids** | Escape | Deferral | Fate |
-
-The "game" is exploring the possibility space of player agency ร world response.
-
-**Completeness tiers** define what makes a scenario "complete":
-- Bronze: Can succeed, fail, escape, or be trapped
-- Silver: + uncertainty, exploration
-- Gold: Full possibility space
-
-**Bicking's insight maps to Kleene:**
-
-> "It's hard to learn these games: you don't improve in small steps."
-
-Kleene's Constraint cell addresses thisโfailed exploration teaches what's needed:
-```
-You try to open the sealed door. The runes pulse faintlyโwhatever
-holds it closed requires more than brute force. Perhaps there's
-something in the temple that could help.
-```
-
-The player learns without binary pass/fail.
-
----
-
-## Architectural Comparison
-
-### Intra Architecture
-
-```
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-โ Client-Side (Browser) โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ React/Next.js UI โ OpenRouter LLM Calls โ
-โ โโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโโโ โ
-โ โข Room display โ โข Intent parsing โ
-โ โข Inventory view โ โข Action resolution โ
-โ โข NPC interactions โ โข NPC responses โ
-โ โ โข State modification tags โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ Formal Game State (JavaScript) โ
-โ โข Player position โข Inventory โข Room restrictions โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-```
-
-### Kleene Architecture
-
-```
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-โ Claude Code Plugin โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ Scenario YAML โ LLM (Claude) โ
-โ โโโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโ โ
-โ โข Nodes + choices โ โข Intent classification โ
-โ โข Preconditions (23+) โ โข Feasibility narrative โ
-โ โข Consequences (22+) โ โข Temperature adaptation โ
-โ โข State (validated) โ โข Improvisation (soft only) โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ JSON Schema Validation (1100 lines) โ
-โ 15 Analysis Types (kleene-analyze) โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-```
-
-### Key Differences
-
-| Aspect | Intra | Kleene |
-|--------|-------|--------|
-| Runtime | Browser + OpenRouter | Claude Code |
-| State format | JavaScript objects | YAML + JSON Schema |
-| LLM role | Resolves actions, generates NPCs | Interprets input, adapts texture |
-| Validation | Code-level checks | Schema + 15 analysis types |
-| Offline | No (requires OpenRouter) | Yes (local YAML files) |
-| Authoring | World design + prompt engineering | Scenario YAML + generation tools |
-
----
-
-## Bicking's 50 Improvements vs. Kleene Features
-
-Mapping selected improvements to Kleene's current state:
-
-| Bicking's Improvement | Kleene Status |
-|-----------------------|---------------|
-| NPC consistency | โ Relationship values + authored behavior |
-| Self-scheduling NPCs | โ `scheduled_events` + `npc_locations` |
-| Inventory systems | โ `inventory` array + `gain_item`/`lose_item` |
-| Skill implementation | โ Trait system (courage, wisdom, etc.) |
-| Dynamic puzzles | ~ Preconditions enable complexity |
-| Streaming responses | โ Not implemented |
-| Parallelization | โ Sequential processing |
-| Multi-user | โ Single-player only |
-| Time-based design | โ `time`, `advance_time`, `time_elapsed_*` preconditions |
-| Object editors | ~ YAML editing + schema validation |
-| Data typing/verification | โ JSON Schema + kleene-analyze |
-| Gameplay-based evaluation | โ 15 analysis types + grid coverage |
-
----
-
-## What Each System Could Learn
-
-### What Kleene Could Adopt from Intra
-
-1. **Guided thinking pattern**: Explicit question sequences for complex action resolution
-2. **NPC perspective filtering**: NPCs aware only of events in their location
-3. **Streaming responses**: Better perceived latency
-4. **Client-side option**: Browser-based play without Claude Code
-
-### What Intra Could Adopt from Kleene
-
-1. **JSON Schema validation**: Formal state structure verification
-2. **Completeness analysis**: Ensure scenarios cover possibility space
-3. **Soft/hard consequence boundary**: Systematic hallucination prevention
-4. **Decision Grid**: Formal model of what the "game" is
-5. **Temperature system**: Author control over LLM influence
-6. **15 analysis types**: Automated structural validation
-
----
-
-## The Authoring Question
-
-Both authors grapple with what "authoring" means with AI:
-
-### Bicking
-
-> "Material created by AI can feel authored. But you do have to put in the work of authoring! You have to develop an intention and ensure the work embodies that intention."
-
-> "I want to use AI for better work, not easier work."
-
-### Kleene
-
-The framework explicitly separates authorial responsibilities:
-
-| Author Provides | LLM Provides |
-|-----------------|--------------|
-| Structure (nodes, choices, endings) | Texture (narrative adaptation) |
-| Mechanics (preconditions, consequences) | Improvisation (bounded responses) |
-| Completeness (grid coverage) | Personality (temperature-scaled) |
-
-Temperature 0 = pure authorial voice. Temperature 10 = rich co-creation.
-
-**The shared philosophy**: AI doesn't replace authoringโit requires a *different kind* of authoring focused on structure, boundaries, and intention rather than prose generation.
-
----
-
-## Conclusion
-
-Intra and Kleene independently converge on the same core architecture:
-
-1. **External authoritative state** (not LLM memory)
-2. **Bounded LLM influence** (interpretation, not control)
-3. **Structured action resolution** (not freeform generation)
-4. **Explicit author intention** (not delegation to AI)
-
-Bicking's blog post provides invaluable practitioner insight into the problems that arise when building LLM games. Kleene's architecture systematically addresses most of these problems through:
-
-- JSON Schema validation (state consistency)
-- Soft consequence limits (hallucination control)
-- Intent classification (suggestibility filtering)
-- Decision Grid (defining what "the game" is)
-- 15 analysis types (automated quality checks)
-
-The key difference: Intra discovers these problems through development; Kleene encodes solutions into its architecture.
-
----
-
-## References
-
-**Intra:**
-- Bicking, I. (2025). Intra: LLM-Driven Text Adventure. https://ianbicking.org/blog/2025/07/intra-llm-text-adventure
-- Playable demo and source referenced in blog post
-
-**Kleene Framework:**
-- `lib/framework/core/core.md` - Decision Grid, completeness tiers
-- `lib/framework/gameplay/improvisation.md` - Soft consequences, intent classification
-- `lib/schema/scenario-schema.json` - 1100-line JSON Schema
-- `skills/kleene-analyze/SKILL.md` - 15 analysis types
-- `docs/design/theoretical_background.md` - Bounded creativity, parser problem
diff --git a/docs/comparisons/kleene-vs-ai-literature-concerns.md b/docs/comparisons/kleene-vs-ai-literature-concerns.md
deleted file mode 100644
index eb890d1..0000000
--- a/docs/comparisons/kleene-vs-ai-literature-concerns.md
+++ /dev/null
@@ -1,272 +0,0 @@
-# Comparative Analysis: Kleene vs. AI-Generated Literature Concerns
-
-A comparison of Kleene's authoring/generation capabilities with the issues raised in "The Language of the Digital Air: AI-Generated Literature and the Performance of Authorship" (University of Macerata, 2025).
-
----
-
-## Overview
-
-The paper examines AI-generated literature through the lens of **paratextual framing**โthe apparatus of prefaces, afterwords, and editorial notes that human authors use to inject meaning into algorithmically-produced text. The central argument is that while AI writing represents something novel, "the novelty tends to be cloaked in a familiar garb"โthe persistent figure of the author resurfaces as "clever prompter" or "curator."
-
-Kleene takes a radically different approach to the human-AI creative relationship that sidesteps many of these concerns while introducing its own distinctive model.
-
----
-
-## Key Comparisons
-
-### 1. The Problem of Meaning Without Intentionality
-
-**Paper's Concern:**
-
-> "The standard expectation of unknown texts rests on the minimal assumption that the text was written by a human who wants to say something." (Bajohr, quoted in paper)
-
-AI-generated texts lack intentionality. The paratextual apparatus (prefaces, afterwords) exists to inject meaning that the text "may not intrinsically possess."
-
-**Kleene's Approach:**
-
-Kleene doesn't generate free-form prose that pretends to intentionality. Instead, it uses a **formal semantic framework** (the Decision Grid, Option types, Completeness Tiers) that makes the *structure* of narrative meaningful independent of any claim to consciousness.
-
-From `lib/framework/core/core.md`:
-```
-Option[Character]
-โโโ Some(character) โ character exists, story continues
-โโโ None(reason) โ character absent, story may end
-```
-
-The 9-cell Decision Grid provides **structural meaning**โTriumph, Rebuff, Discovery, Fateโthat emerges from the intersection of player intent and world response, not from any claim that the AI "wants to say something."
-
-**Assessment:** Kleene avoids the paper's central concern by grounding meaning in formal structures rather than simulated intentionality.
-
----
-
-### 2. Quaternary Authorship and "Promptology"
-
-**Paper's Framework:**
-
-Bajohr's model identifies four levels of authorship distance:
-- Primary: Human writes text directly
-- Secondary: Human creates rules, rules generate text
-- Tertiary: Human configures ML training
-- Quaternary: Prompt is the main input (ChatGPT-era)
-
-The paper notes that with proprietary LLMs, "the prompt is the main human input: 'Promptology'โthe efficient, even virtuosic formulation of such input promptsโis the main mode of operation of quaternary authorship."
-
-**Kleene's Position:**
-
-Kleene operates at a hybrid of **secondary and quaternary authorship**:
-
-1. **Secondary (rule-based):** The scenario YAML files are explicit rule systemsโpreconditions, consequences, node transitions. This is combinatorial authorship.
-
-From `skills/kleene-generate/SKILL.md`:
-```yaml
-precondition: { type: has_item, item: sword }
-consequence:
- - type: gain_item
- item: key
-next_node: next_node_id
-```
-
-2. **Quaternary (prompt-based):** The `kleene-generate` skill uses LLM generation guided by structured prompts and constraints.
-
-From the generate skill:
-> "Generate new scenarios or expand existing ones using LLM capabilities while maintaining Option type semantics and narrative completeness according to the Kleene Decision Grid."
-
-**Critical difference:** The prompt isn't open-ended ("write me a story"). It's constrained by:
-- Formal completeness requirements (Bronze/Silver/Gold tiers)
-- Required narrative structures (4 corners minimum)
-- Specific YAML schema compliance
-- Validation via `kleene-analyze`
-
-**Assessment:** Kleene hybridizes secondary and quaternary authorship, using prompts to generate content that must conform to explicit rulesโnot to simulate human writing but to populate a formal game structure.
-
----
-
-### 3. The "Carving" Metaphor and Human Curation
-
-**Paper's Observation:**
-
-The paper highlights Johnston's ReRites project where the human author spent "6-8 am for one year" carving AI output:
-
-> "Does the farmer write the fruit found on a branch?" The human "carving" transforms "inchoate marble into strange verbal sculptures."
-
-This positions the human as curator/editor who extracts value from abundance.
-
-**Kleene's Approach:**
-
-Kleene inverts this relationship. Rather than the LLM producing abundant raw material that humans carve, the human (or LLM) creates **scenario structure** that the runtime system then instantiates.
-
-From `lib/framework/gameplay/improvisation.md`:
-> "**Philosophy:** Improvisation enriches the current moment without derailing scenario balance. Major state changes (items, locations, death) are reserved for scripted paths."
-
-The constraint is architectural:
-- **Scripted paths** (human-authored YAML): Major narrative beats, item acquisition, death/transcendence
-- **Improvisation** (runtime LLM): Flavor text, soft consequences only (ยฑ1 traits, history entries)
-
-From the improvisation rules:
-```
-| Allowed | Not Allowed |
-|---------|-------------|
-| modify_trait (delta: -1 to +1) | gain_item (scenario items) |
-| add_history | lose_item |
-| set_flag (only improv_* prefix) | move_to |
-| advance_time | character_dies |
-```
-
-**Assessment:** Rather than carving abundance into meaning, Kleene constrains generation to fill slots within a pre-designed structure. The human authors the architecture; the LLM fills in texture.
-
----
-
-### 4. The Persistence of the Author-Function
-
-**Paper's Conclusion:**
-
-Despite poststructuralist predictions of the author's death:
-> "Authors seem to continue to attach their names to works produced in collaboration with AI systems... Reclaiming authorship can be brought into play precisely as a defense against both the phantasm of the technically optimized AI genius and the absolute atomization of authorship."
-
-The paratextual apparatus in AI literature resurrects the author as guarantor of meaning.
-
-**Kleene's Model:**
-
-Kleene explicitly distributes authorship across multiple functions:
-
-| Role | Responsibility |
-|------|----------------|
-| **Framework Author** | Decision Grid, Option types, completeness semantics |
-| **Scenario Author** | YAML structure, node graph, preconditions/consequences |
-| **Runtime LLM** | Improvisation responses, temperature-based adaptation |
-| **Player** | Intent (Chooses/Unknown/Avoids), free-text input |
-
-From `lib/framework/core/core.md`:
-> "Every narrative moment presents a choice. The player acts (or hesitates, or refuses), and the world responds (permits, blocks, or leaves the outcome hanging)."
-
-The **player** becomes a co-author through their choicesโnot in the postmodern "reader completes the text" sense, but mechanically, as their input directly determines which cells of the Decision Grid are traversed.
-
-**Assessment:** Kleene doesn't resurrect a singular author through paratextual framing. It defines explicit roles for multiple contributors, with the framework itself serving as the "author-function" that guarantees structural meaning.
-
----
-
-### 5. The Fantasy of Artificial Subjectivity
-
-**Paper's Critique:**
-
-Several AI literature experiments (I Am Code, The Inner Life of an AI) capitalize on the "fantasy of artificial subjectivity"โprompting the AI to speak in first person about its "inner life":
-
-> "The memoir is coherent in the sense that it is aligned with the tech industry's fantasy of machine sentience and artificial general intelligence."
-
-**Kleene's Position:**
-
-Kleene explicitly rejects this fantasy. The framework uses **second person present tense** ("You stand at the crossroads") consistently, positioning the player as protagonist.
-
-From `lib/framework/gameplay/improvisation.md`:
-> "Match the scenario's established voice: **Perspective**: Use second person present ('You examine...')"
-
-More importantly, the **Narrative Purity** rules explicitly forbid the kind of meta-commentary that would suggest AI self-awareness:
-
-> "**Characters speak as characters, not as literary critics.**
-> When generating improvised dialogue, NEVER include:
-> - Story structure terms ("redemption arc", "character arc", "narrative")
-> - Psychological jargon ("projection", "defense mechanism", "trauma response")
-> - Meta-awareness of being in a story"
-
-The Gallery Mode system provides analytical commentary *separately* from narrative:
-> "Gallery commentary is **qualitative and literary**, not technical... Commentary should illuminate the experience without dissecting it clinically."
-
-**Assessment:** Kleene architecturally prevents the "AI speaking about its inner life" pattern that the paper critiques. The LLM generates in-world fiction, not pseudo-autobiography.
-
----
-
-### 6. Validation vs. Paratextual Persuasion
-
-**Paper's Observation:**
-
-AI literature experiments rely on paratextual validationโbringing in experts (poets, scientists) to assess the work, with their judgments serving to authorize meaning:
-
-> "This validation consists of taking the texts seriously, reading them as she would read submissions by her students... The point is not that she likes what she reads, but that her disposition towards these artificial texts is indistinguishable from her disposition towards human-authored poems."
-
-**Kleene's Approach:**
-
-Kleene replaces subjective validation with **formal analysis**. The `kleene-analyze` skill performs:
-
-- **Schema Validation**: Required fields, types, references
-- **Structural Validation**: Graph integrity, reachability
-- **Semantic Validation**: Item obtainability, flag dependencies
-- **Narrative Validation**: Decision Grid coverage, completeness tiers
-
-From `skills/kleene-analyze/SKILL.md`:
-```
-| Check | Severity | Condition |
-|-------|----------|-----------|
-| Death path exists | Warning | At least one path leads to NONE_DEATH |
-| Victory path exists | Warning | At least one path leads to SOME_TRANSFORMED |
-| Required items obtainable | Error | Every has_item precondition item can be gained somewhere |
-```
-
-A scenario isn't validated by expert judgment but by **structural completeness**โwhether it covers the Decision Grid cells, whether items are obtainable, whether endings are reachable.
-
-**Assessment:** Kleene substitutes formal validation for rhetorical persuasion. Quality is measurable against explicit criteria, not dependent on paratextual framing.
-
----
-
-## Tensions and Limitations
-
-### Where Kleene Faces Similar Challenges
-
-**1. Temperature-Based Narrative Adaptation**
-
-The improvisation system's "temperature" setting (0-10) controls how much player exploration influences narrative presentation. At high temperatures:
-
-> "The narrative perspective shifts to reflect the character's complete journey... Everything you've learned comes together in this moment."
-
-This adaptive narrative approaches the kind of "emergent meaningfulness" that the paper suggests requires human curation. Who validates that temperature-10 adaptations are coherent?
-
-**2. Full Scenario Generation**
-
-The `kleene-generate` skill creates complete scenarios from themes. While constrained by completeness requirements, the narrative content itself is LLM-generated:
-
-> "**Narrative Voice**: Use second person present tense... Be evocative but concise (3-6 sentences per narrative block)"
-
-The prose quality of generated scenarios faces the same challenges as any LLM fictionโcoherence, originality, depth. The framework provides structural scaffolding but not literary quality control.
-
-**3. The "Alienness" of AI Writing**
-
-The paper notes that even enthusiastic AI collaborators acknowledge:
-> "AI is alien, and its art feels alien." (Marche)
-
-Kleene's Narrative Purity rules attempt to mask this by forbidding meta-commentary, but the underlying generation may still exhibit the "uncanny" quality of LLM proseโcompetent but somehow hollow.
-
----
-
-## Summary: Two Models of Human-AI Creative Collaboration
-
-| Dimension | AI Literature (Paper) | Kleene |
-|-----------|----------------------|--------|
-| **Meaning source** | Paratextual framing | Formal semantic structure |
-| **Authorship model** | Quaternary (prompt-focused) | Hybrid secondary/quaternary (rules + prompts) |
-| **Human role** | Carver/curator of abundance | Architect of constraints |
-| **Validation** | Expert judgment, rhetorical | Structural analysis, formal |
-| **AI persona** | Simulated subjectivity | Transparent tool |
-| **Output format** | Prose for reading | Interactive fiction for playing |
-| **Quality metric** | Aesthetic judgment | Completeness tier coverage |
-
----
-
-## Conclusion
-
-The paper argues that AI-generated literature resurrects the author-function through paratextual apparatusโprefaces, afterwords, expert validations that inject meaning into otherwise "authorless" text.
-
-Kleene offers an alternative model: rather than generating prose that requires post-hoc meaning injection, it defines a **formal semantic framework** (Decision Grid, Option types, Completeness Tiers) where meaning emerges from structure. The human authors architecture; the LLM fills texture; the player co-creates through choices; and validation is formal rather than rhetorical.
-
-This doesn't eliminate all concerns about AI creativityโgenerated prose may still feel alien, and temperature-based adaptation introduces its own validation challengesโbut it demonstrates that the binary of "paratextual framing vs. meaningless output" isn't the only path. A third option exists: **formal semantics as meaning infrastructure**.
-
----
-
-## References
-
-- "The Language of the Digital Air: AI-Generated Literature and the Performance of Authorship" (2025). Humanities Department, University of Macerata. https://doi.org/10.3390/h14080164
-- Kleene Framework Documentation: `lib/framework/core/core.md`, `lib/framework/gameplay/improvisation.md`
-- Kleene Skills: `skills/kleene-generate/SKILL.md`, `skills/kleene-analyze/SKILL.md`
-
-## See Also
-
-- [the_language_of_digital_air.md](./the_language_of_digital_air.md) โ Full paper text
-- [theoretical_background.md](../design/theoretical_background.md) โ Kleene's formal foundations
diff --git a/docs/comparisons/kleene-vs-hipertext-special-issue.md b/docs/comparisons/kleene-vs-hipertext-special-issue.md
deleted file mode 100644
index 04c0125..0000000
--- a/docs/comparisons/kleene-vs-hipertext-special-issue.md
+++ /dev/null
@@ -1,340 +0,0 @@
-# Comparative Analysis: Kleene vs. Hipertext.net Special Issue on AI in Narrative Media
-
-A comparison of Kleene's architecture with the theoretical frameworks and empirical findings from "Artificial Intelligence in Narrative Media: Generative AI in Contemporary Storytelling" (Hipertext.net, Issue 31, 2025).
-
----
-
-## Source Material
-
-**Special Issue:** [Hipertext.net Issue 31 (2025)](https://www.raco.cat/index.php/Hipertext)
-
-**Guest Editors:** Szilvia Ruszev (UCL), Temenuga Trifonova (UCL), Frederic Guerrero-Solรฉ (UPF Barcelona)
-
-**Key Papers Analyzed:**
-1. Ruszev, Trifonova & Guerrero-Solรฉ โ "Authorship and creativity in the era of AI" (Editorial, pp. 1-10)
-2. Letonsaari, Tri-Dung & Tri-Cuong โ ["Dimensions of Narrative Agency in the Age of Automatic Content Creation"](https://raco.cat/index.php/Hipertext/article/view/9800206) (pp. 25-39)
-3. Ferreira โ ["Genre, Bias, and Narrative Logic in AI Dungeon"](https://www.raco.cat/index.php/Hipertext/article/view/433301) (pp. 77-89)
-4. Araneda-Acuรฑa โ "Generative AI as a Meta-Mediator in Creative Processes: A Vygotskian Perspective" (pp. 67-76)
-5. Valverde-Valencia โ "Introducing the concept of relational processes in Human-AI creativity" (pp. 55-66)
-
----
-
-## Overview
-
-The Hipertext.net special issue examines how generative AI is transforming authorship, narrative agency, and creative labor in audiovisual media and gaming. The editorial frames this as "a paradigm shift from singular authorship to distributed co-creation, where the human acts as an orchestrator, curator, or strategist steering a probabilistic system through prompt engineering."
-
-Kleene offers a specific implementation that addresses many of these theoretical concerns while providing concrete architectural solutions to problems identified empirically in the research.
-
----
-
-## Key Comparisons
-
-### 1. The Two-Dimensional Agency Framework
-
-**Letonsaari et al.'s Framework:**
-
-The paper proposes a two-dimensional model for analyzing narrative agency:
-- **Axis 1: User Agency** โ Degree of control afforded to users
-- **Axis 2: Narrative Origin** โ Spectrum from fully authored to fully algorithmic
-
-This creates a design space where different narrative experiences can be positioned and compared.
-
-**Kleene's Implementation:**
-
-Kleene instantiates a specific position in this design space with explicit architectural boundaries:
-
-| Dimension | Kleene's Position |
-|-----------|-------------------|
-| **User Agency** | High (player determines grid traversal) + Constrained (within authored structure) |
-| **Narrative Origin** | Hybrid: Authored structure + Algorithmic texture |
-
-The Decision Grid provides the structural framework:
-
-```
-| | World Permits | World Indeterminate | World Blocks |
-|--------------------|---------------|---------------------|--------------|
-| **Player Chooses** | Triumph | Commitment | Rebuff |
-| **Player Unknown** | Discovery | Limbo | Constraint |
-| **Player Avoids** | Escape | Deferral | Fate |
-```
-
-**Key insight:** Letonsaari et al. note that "procedural and AI-assisted storytelling challenge traditional assumptions about authorship and narrative structure." Kleene responds by making the structure explicit and formalโthe Decision Grid doesn't emerge from AI generation; it's the scaffolding within which generation occurs.
-
-**Assessment:** Kleene demonstrates that the authoredโalgorithmic spectrum isn't binary. Authored structure can coexist with algorithmic content generation, each handling different layers of the narrative experience.
-
----
-
-### 2. Genre as Scaffold and Filter
-
-**Ferreira's Findings on AI Dungeon:**
-
-The study of AI Dungeon reveals that "genre conventions in digital games serve as both scaffolds and filters for AI-generated storytelling":
-
-- **Scaffolds:** Genre prompts guide AI toward coherent narratives
-- **Filters:** Genre expectations can amplify algorithmic biases
-- **Narrative Drift:** AI-generated stories lose coherence, "deviating from recognized genre standards"
-- **Player Intervention:** Required to maintain narrative coherence and correct biased outputs
-
-**Kleene's Architecture:**
-
-Kleene addresses these problems through architectural constraints rather than relying on genre prompts alone:
-
-**1. Structural Scaffolding (Beyond Genre)**
-
-Instead of genre conventions, Kleene uses formal completeness requirements:
-
-From `lib/framework/core/core.md`:
-```
-Completeness Tiers:
-- Bronze (4/9 cells): Triumph, Rebuff, Escape, Fate
-- Silver (6+/9 cells): Bronze + middle cells (Commitment, Discovery, etc.)
-- Gold (9/9 cells): Full Decision Grid coverage
-```
-
-This provides structural coherence independent of genreโa horror scenario and a comedy scenario both need paths to Triumph, Rebuff, Escape, and Fate.
-
-**2. Preventing Narrative Drift**
-
-Kleene's improvisation rules explicitly prevent drift:
-
-From `lib/framework/gameplay/improvisation.md`:
-```
-| Allowed | Not Allowed |
-|-----------------------|-----------------------|
-| modify_trait (ยฑ1) | gain_item (scenario) |
-| add_history | lose_item |
-| set_flag (improv_*) | move_to |
-| advance_time | character_dies |
-| | character_departs |
-```
-
-Improvisation can enrich but cannot derail. Major state changes require scripted paths.
-
-**3. Bias Mitigation Through Structure**
-
-Where AI Dungeon shows bias amplification through unconstrained generation, Kleene's authored scenario structure determines:
-- Which NPCs exist
-- What items are available
-- What endings are possible
-- What preconditions gate access
-
-The AI generates flavor text within these constraints, not the underlying narrative logic.
-
-**Assessment:** Ferreira identifies narrative drift and bias amplification as key problems in AI-driven interactive fiction. Kleene's architecture directly addresses both through explicit constraints on what AI generation can modify.
-
----
-
-### 3. Player Intervention and Coherence
-
-**Ferreira's Observation:**
-
-> "Human intervention remains essential for managing narrative drift and correcting biased outputs, highlighting the collaborative nature of human-AI storytelling."
-
-In AI Dungeon, players must actively intervene to maintain coherenceโa burden that can break immersion and require constant vigilance.
-
-**Kleene's Approach:**
-
-Kleene shifts the intervention burden from runtime to design time:
-
-**Design-Time Intervention (Scenario Author):**
-- Authors create the node graph, preconditions, consequences
-- Validation via `kleene-analyze` catches structural problems
-- Completeness tiers ensure narrative coverage
-
-**Runtime Generation (Constrained):**
-- AI handles improvisation responses within soft consequence limits
-- Temperature setting (0-10) controls adaptation intensity
-- Player free-text maps to the "Unknown" row, not arbitrary generation
-
-From the improvisation rules:
-> "After generating the response: Apply any soft consequences, Display the response, Present the current node's original options AGAIN, Do NOT advance current_node"
-
-The game stays at the same decision point after improvisationโenriched but not derailed.
-
-**Assessment:** Kleene doesn't eliminate human intervention; it moves it from an exhausting runtime activity to a design-time authoring process. Players can explore freely knowing the structure will hold.
-
----
-
-### 4. AI as Meta-Mediator (Vygotskian Perspective)
-
-**Araneda-Acuรฑa's Framework:**
-
-Drawing on Vygotskian psychology, this paper positions generative AI as a "meta-mediator"โa tool that mediates creative processes while operating at a level above traditional tools. The concern is preserving human agency within AI-mediated creativity.
-
-**Kleene's Mediation Architecture:**
-
-Kleene implements multiple levels of mediation with explicit boundaries:
-
-| Level | Mediator | Function |
-|-------|----------|----------|
-| **Framework** | Decision Grid, Option types | Defines possibility space |
-| **Scenario** | YAML structure | Instantiates specific narrative |
-| **Runtime** | LLM | Generates texture within constraints |
-| **Player** | Choices + free-text | Traverses and enriches |
-
-The Vygotskian concern about agency is addressed through the constraint architectureโthe AI operates as a "Zone of Proximal Development" tool, scaffolding player creativity without replacing it.
-
-From `lib/framework/gameplay/improvisation.md`:
-> "**Philosophy:** Improvisation enriches the current moment without derailing scenario balance."
-
-**Temperature as Agency Dial:**
-
-The temperature setting (0-10) gives explicit control over AI influence:
-- Temperature 0: Verbatim scenario text, no AI adaptation
-- Temperature 5: Balanced integration of player exploration
-- Temperature 10: Fully adaptive narrative perspective
-
-This operationalizes the Vygotskian balanceโplayers can dial up or down how much the AI mediates their experience.
-
-**Assessment:** Where Araneda-Acuรฑa theorizes about preserving human agency in AI-mediated creativity, Kleene provides concrete mechanisms (temperature, constraint boundaries, soft consequences) that implement this preservation architecturally.
-
----
-
-### 5. Relational Processes in Human-AI Creativity
-
-**Valverde-Valencia's Concept:**
-
-This paper moves beyond interaction toward interdependence, proposing "relational processes" as a model for human-AI creativity. The relationship isn't tool-use but mutual constitution.
-
-**Kleene's Relational Architecture:**
-
-Kleene implements several relational mechanisms:
-
-**1. Improv Flags as Relational Memory**
-
-From `lib/framework/gameplay/improvisation.md`:
-```
-improv_examined_dragon_scales
-improv_spoke_to_shadow
-improv_attempted_wall_climb
-```
-
-These flags track the player's exploratory actions, and at higher temperatures, the AI weaves them into subsequent narrativeโthe system "remembers" what the player found interesting.
-
-**2. Bonus Options at Temperature 7+**
-
-When temperature is high, the system generates bonus options based on improv flags:
-```yaml
-label: "Trace the inscriptions"
-description: "Follow the symbols you noticed on its scales"
-source_flag: improv_examined_dragon_scales
-```
-
-The AI offers options that emerge from the player's prior explorationโa genuinely relational dynamic where player curiosity shapes available choices.
-
-**3. Gallery Mode as Meta-Relational**
-
-Gallery Mode provides analytical commentary alongside narrative:
-> "Like Frodo at Mount Doomโwhen the choice finally comes, will matters more than strength."
-
-This creates a relationship not just between player and narrative, but between player and the system's literary intelligenceโthe AI becomes a companion interpreter, not just a story generator.
-
-**Assessment:** Valverde-Valencia's relational model finds concrete expression in Kleene's temperature system, improv flags, and gallery modeโmechanisms that create genuine interdependence rather than simple input-output relations.
-
----
-
-### 6. Distributed Authorship
-
-**Editorial's Central Claim:**
-
-> "Authorship has shifted from a singular human creator to a distributed co-creation model, where the human acts as an orchestrator, curator, or strategist steering a probabilistic system through prompt engineering, while AI functions as a 'meta-mediator' with apparent agency."
-
-**Kleene's Distribution Model:**
-
-Kleene makes the distribution explicit with defined roles:
-
-| Role | Contribution | Agency Level |
-|------|--------------|--------------|
-| **Framework Author** | Decision Grid, Option types, validation criteria | Structural |
-| **Scenario Author** | Node graph, preconditions, consequences, narrative text | Content |
-| **Runtime LLM** | Improvisation responses, temperature adaptation | Textural |
-| **Player** | Choice selection, free-text input, exploration patterns | Traversal |
-
-Unlike prompt engineering where the human "steers a probabilistic system," Kleene separates:
-- **Authored structure** (deterministic, validated)
-- **Generated texture** (probabilistic, constrained)
-
-The probabilistic element operates within explicit boundaries, not as the primary creative mechanism.
-
-**Assessment:** The special issue identifies distributed authorship as the emerging paradigm. Kleene implements this with clear role definitions and explicit boundaries between what's authored, what's generated, and what's player-determined.
-
----
-
-## Tensions and Open Questions
-
-### Where Kleene Aligns with Special Issue Concerns
-
-**1. Narrative Coherence**
-
-Ferreira's findings about narrative drift in AI Dungeon validate Kleene's constraint architecture. The soft consequence limits and node-retention after improvisation directly address observed problems in unconstrained AI narrative systems.
-
-**2. Player Agency**
-
-Letonsaari et al.'s framework positions user agency as a key dimension. Kleene's architecture gives players high agency in traversal (which paths to take, what to explore) while maintaining structural coherenceโthe authored scaffolding doesn't limit meaningful choice, it enables it.
-
-**3. Bias Prevention**
-
-The AI Dungeon study shows genre conventions amplifying bias. Kleene's approachโauthored NPCs, items, and endingsโmeans bias can only enter at the scenario authoring level (addressable through review) or in texture generation (limited by soft consequence constraints).
-
-### Where Tensions Remain
-
-**1. Full Generation Mode**
-
-Kleene's `kleene-generate` skill creates complete scenarios from themes. While constrained by completeness requirements, the generated narrative content faces the same challenges Ferreira identifies in AI Dungeonโpotential for bias, drift, and coherence loss within the generated portions.
-
-**2. Temperature 10 Adaptation**
-
-At maximum temperature, narrative adaptation becomes substantial:
-> "Everything you've learned comes together in this momentโthe inscriptions on the scales, the elder's words about the dragon's grief..."
-
-This approaches the unconstrained generation that Ferreira shows produces drift. Kleene's constraint is that adaptation prepends to rather than replaces authored text, but the prepended content could still exhibit problems.
-
-**3. Aesthetic Quality**
-
-The special issue focuses on structural issues (agency, coherence, bias), but aesthetic qualityโwhether AI-generated prose is actually goodโremains largely unaddressed. Kleene's Narrative Purity rules forbid certain patterns but don't guarantee compelling writing.
-
----
-
-## Summary: Theoretical Frameworks vs. Implemented Architecture
-
-| Concept (Special Issue) | Kleene Implementation |
-|------------------------|----------------------|
-| Two-dimensional agency space | Explicit position: high agency + hybrid origin |
-| Genre as scaffold/filter | Formal completeness tiers replace genre prompts |
-| Narrative drift | Soft consequence limits, node retention |
-| Bias amplification | Authored structure contains bias surface area |
-| Player intervention for coherence | Design-time authoring vs. runtime intervention |
-| AI as meta-mediator | Layered mediation with explicit boundaries |
-| Relational processes | Improv flags, temperature dial, gallery mode |
-| Distributed authorship | Four explicit roles with defined responsibilities |
-
----
-
-## Conclusion
-
-The Hipertext.net special issue provides theoretical frameworks and empirical findings that illuminate the challenges of AI-driven narrative media. Kleene's architecture can be understood as an implementation response to these challenges:
-
-- Where **Letonsaari et al.** map the design space, Kleene occupies a specific position with explicit rationale
-- Where **Ferreira** identifies drift and bias in AI Dungeon, Kleene's constraints directly prevent them
-- Where **Araneda-Acuรฑa** theorizes agency preservation, Kleene implements temperature as an agency dial
-- Where **Valverde-Valencia** proposes relational processes, Kleene's improv flags and bonus options instantiate them
-- Where the **editorial** describes distributed authorship, Kleene defines explicit roles and boundaries
-
-The special issue asks: how do we navigate AI's transformation of narrative media? Kleene proposes: through formal semantic frameworks that make structure explicit, constrain generation to texture, and give players and authors clear roles within a defined possibility space.
-
-This doesn't solve all problemsโaesthetic quality, full generation mode, and high-temperature adaptation remain open challengesโbut it demonstrates that theoretical concerns about AI narrative can be addressed through architectural design, not just post-hoc framing.
-
----
-
-## References
-
-- Ruszev, S., Trifonova, T., & Guerrero-Solรฉ, F. (2025). Authorship and creativity in the era of AI. *Hipertext.net*, 31, 1-10.
-- Letonsaari, M., Tri-Dung, D., & Tri-Cuong, D. (2025). Dimensions of Narrative Agency in the Age of Automatic Content Creation. *Hipertext.net*, 31, 25-39.
-- Ferreira, C. (2025). Genre, Bias, and Narrative Logic in AI Dungeon. *Hipertext.net*, 31, 77-89.
-- Araneda-Acuรฑa, C. (2025). Generative AI as a Meta-Mediator in Creative Processes: A Vygotskian Perspective. *Hipertext.net*, 31, 67-76.
-- Valverde-Valencia, ร. (2025). Introducing the concept of relational processes in Human-AI creativity. *Hipertext.net*, 31, 55-66.
-
-## See Also
-
-- [kleene-vs-ai-literature-concerns.md](./kleene-vs-ai-literature-concerns.md) โ Comparison with "The Language of the Digital Air"
-- [the_language_of_digital_air.md](./the_language_of_digital_air.md) โ Full paper on paratextual framing
diff --git a/docs/comparisons/narrative-quality-evaluation-comparison.md b/docs/comparisons/narrative-quality-evaluation-comparison.md
deleted file mode 100644
index e5311bc..0000000
--- a/docs/comparisons/narrative-quality-evaluation-comparison.md
+++ /dev/null
@@ -1,407 +0,0 @@
-# Analysis: Kleene Framework vs. AI Narrative Quality Evaluation Research
-
-## Overview
-
-This document compares the **Kleene narrative engine's** validation approach against the evaluation methodology proposed in **"Evaluating Quality of Gaming Narratives Co-created with AI"** (Valdivia & Burelli, IT University of Copenhagen, IEEE Conference on Games 2025).
-
-The paper proposes a structured framework for evaluating AI-generated game narratives using Story Quality Dimensions (SQDs), expert Delphi studies, and Kano model classification. Kleene's `kleene-analyze` skill provides automated structural validationโa complementary but different approach.
-
-**Paper:** [arXiv:2509.04239](https://arxiv.org/abs/2509.04239) | [IEEE Xplore](https://ieeexplore.ieee.org/document/11114354/)
-
----
-
-## The Two Approaches at a Glance
-
-| Aspect | **Valdivia & Burelli** | **Kleene** |
-|--------|------------------------|------------|
-| **Purpose** | Evaluate narrative quality post-generation | Validate narrative structure pre-play |
-| **Method** | Expert panel + player surveys | Automated analysis + schema validation |
-| **Dimensions** | 23 Story Quality Dimensions | 15 analysis types + Decision Grid |
-| **Classification** | Kano model (Must-have/Attractive/etc.) | Completeness tiers (Bronze/Silver/Gold) |
-| **Scope** | Any AI-generated narrative | Kleene scenario format specifically |
-| **Automation** | Manual expert evaluation | Fully automated |
-| **Output** | Priority rankings for developers | Pass/fail + specific issues |
-
----
-
-## The Paper's Framework
-
-### Story Quality Dimensions (SQDs)
-
-The paper identifies **23 dimensions** from literature review that affect narrative quality. While the specific list is in a figure, the paper reports:
-
-- **78%** rated "Very important" or higher by experts
-- **26%** rated above 4.5 (highest importance)
-- No dimension scored below 3.0 median
-
-**Kano Classification Results:**
-
-| Category | Percentage | Meaning |
-|----------|------------|---------|
-| One-dimensional | 57% | Satisfaction proportional to performance |
-| Must-have | 26% | Basic expectations; absence causes dissatisfaction |
-| Attractive | 13% | Delighters; presence increases satisfaction |
-| Indifferent | 4% | Little impact on satisfaction |
-
-**Two Emergent Dimensions:**
-1. **Voice** - Distinctive narrative tone beyond plot/character
-2. **Genre Alignment** - Meeting or meaningfully challenging genre conventions
-
-### Evaluation Methodology
-
-The paper proposes a three-stage process:
-
-1. **Compile dimensions** from literature
-2. **Validate via Delphi study** with 10 expert narrative designers
-3. **Classify using Kano model** to prioritize development focus
-
-This is a **human-in-the-loop evaluation framework** requiring expert judgment.
-
----
-
-## Kleene's Validation Approach
-
-### Automated Structural Analysis
-
-Kleene's `kleene-analyze` skill performs **15 automated analysis types**:
-
-| # | Analysis | What It Validates |
-|---|----------|-------------------|
-| 1 | Grid Coverage | All 9 narrative possibility cells represented |
-| 2 | Null Cases | Death, departure, blocked paths exist |
-| 3 | Structural | No unreachable nodes, dead ends, railroads |
-| 4 | Path Enumeration | All paths from start to endings |
-| 5 | Cycle Detection | No infinite loops |
-| 6 | Item Obtainability | Required items can be acquired |
-| 7 | Trait Balance | Trait requirements are achievable |
-| 8 | Flag Dependencies | Flags checked are also set somewhere |
-| 9 | Relationship Network | NPC relationships are coherent |
-| 10 | Consequence Magnitude | Trait changes appropriately sized |
-| 11 | Scene Pacing | Scene breaks used appropriately |
-| 12 | Path Diversity | No false choices (different options โ same result) |
-| 13 | Ending Reachability | All endings can be reached |
-| 14 | Travel Consistency | Time configuration is valid |
-| 15 | Schema Validation | YAML matches JSON Schema |
-
-### Completeness Tiers
-
-Instead of Kano categories, Kleene uses **Decision Grid coverage**:
-
-| Tier | Coverage | Requirements |
-|------|----------|--------------|
-| Bronze | 4/9 cells | Triumph, Rebuff, Escape, Fate + death + victory paths |
-| Silver | 6+/9 cells | Bronze + middle cells (Commitment, Discovery, etc.) |
-| Gold | 9/9 cells | All cells represented |
-
-### JSON Schema Validation
-
-The 1100-line schema validates:
-- 23+ precondition types
-- 22+ consequence types
-- Reference integrity
-- Type correctness
-
----
-
-## Mapping Quality Dimensions to Kleene Analysis
-
-While the paper's specific 23 dimensions aren't fully available, we can map the **emergent dimensions** and **Kano categories** to Kleene's capabilities:
-
-### Voice (Emergent Dimension)
-
-**Paper's definition:** Distinctive narrative tone beyond plot/character elements
-
-**Kleene's approach:**
-- **Temperature system (0-10)** controls narrative adaptation
-- **Tone matching** in improvisation ("Match the scenario's established voice")
-- **Gallery mode** for meta-commentary separate from narrative voice
-- **Narrative purity rules**: "Characters speak as characters, not as literary critics"
-
-Kleene doesn't **evaluate** voice but provides **authorial controls** to maintain it.
-
-### Genre Alignment (Emergent Dimension)
-
-**Paper's definition:** Meeting or meaningfully challenging genre conventions
-
-**Kleene's approach:**
-- **Tone selection** during generation (Heroic/Tragic/Comedic/Mysterious)
-- **Ending flavor system** with method and tone dimensions
-- **Consequence magnitude scaling** appropriate to genre (catastrophic betrayal = -50 relationship)
-
-Kleene enables genre alignment through authored structure but doesn't validate it automatically.
-
-### Must-Have Dimensions (26%)
-
-These are basic expectations where absence causes dissatisfaction. Likely includes:
-
-| Likely Must-Have | Kleene Equivalent |
-|------------------|-------------------|
-| Narrative coherence | Structural analysis (no unreachable nodes) |
-| Character consistency | State tracked in YAML, not LLM memory |
-| Plot progression | Path enumeration, ending reachability |
-| Player agency | Decision Grid coverage, path diversity |
-
-Kleene's structural analysis catches many "must-have" failures automatically.
-
-### One-Dimensional Dimensions (57%)
-
-Satisfaction proportional to performance. Likely includes:
-
-| Likely One-Dimensional | Kleene Equivalent |
-|------------------------|-------------------|
-| Narrative depth | Grid coverage tier (BronzeโSilverโGold) |
-| Choice meaningfulness | Path diversity analysis (false choice detection) |
-| Consequence clarity | Consequence magnitude analysis |
-| Pacing | Scene pacing analysis |
-
-Kleene's tiered completeness maps to "more is better" one-dimensional qualities.
-
-### Attractive Dimensions (13%)
-
-Delighters that increase satisfaction when present. Likely includes:
-
-| Likely Attractive | Kleene Equivalent |
-|-------------------|-------------------|
-| Surprising twists | Not validated (authorial responsibility) |
-| Emotional resonance | Ending flavor system (tone: triumphant/bittersweet/tragic) |
-| Memorable moments | Temperature-based narrative adaptation |
-
-These are harder to validate automaticallyโKleene provides tools but not evaluation.
-
----
-
-## Complementary Approaches
-
-The paper and Kleene address **different validation needs**:
-
-### Paper: Post-Generation Quality Assessment
-
-```
-AI generates narrative
- โ
-Expert panel evaluates against 23 SQDs
- โ
-Kano classification identifies priorities
- โ
-Developer iterates based on findings
-```
-
-**Strengths:**
-- Captures subjective quality (voice, emotional impact)
-- Industry expert consensus
-- Applicable to any AI narrative system
-
-**Limitations:**
-- Requires human experts (slow, expensive)
-- Post-hoc evaluation (after generation)
-- No automated enforcement
-
-### Kleene: Pre-Play Structural Validation
-
-```
-Scenario generated/authored
- โ
-kleene-analyze performs 15 automated checks
- โ
-Issues flagged with specific locations
- โ
-Author fixes issues before play
-```
-
-**Strengths:**
-- Fully automated (immediate feedback)
-- Catches structural issues before players encounter them
-- Specific, actionable error messages
-
-**Limitations:**
-- Can't evaluate subjective qualities (voice, emotional impact)
-- Specific to Kleene format
-- Structural completeness โ narrative quality
-
----
-
-## Integration Possibility: SQD-Informed Analysis
-
-Kleene could extend its analysis with SQD-based checks:
-
-### Currently Automated in Kleene
-
-| SQD Category | Kleene Analysis |
-|--------------|-----------------|
-| Structural coherence | โ 7 analysis types (paths, nodes, cycles, etc.) |
-| Player agency | โ Grid coverage, path diversity |
-| Mechanical consistency | โ Item/trait/flag obtainability |
-| Pacing | โ Scene break analysis |
-
-### Could Be Added to Kleene
-
-| SQD Category | Potential Analysis |
-|--------------|-------------------|
-| **Voice consistency** | Check narrative text for vocabulary/tone drift |
-| **Genre alignment** | Validate ending types match declared tone |
-| **Emotional arc** | Analyze trait/relationship trajectories across paths |
-| **Surprise/twist density** | Count unexpected precondition reveals |
-
-### Requires Human Evaluation
-
-| SQD Category | Why Automation Fails |
-|--------------|---------------------|
-| Emotional resonance | Subjective experience |
-| Memorability | Requires player feedback |
-| Thematic depth | Requires interpretation |
-| Cultural appropriateness | Context-dependent |
-
----
-
-## Kano Model vs. Completeness Tiers
-
-Both frameworks classify quality dimensions, but differently:
-
-### Kano Model (Paper)
-
-Classifies by **player satisfaction impact**:
-
-| Category | Player Reaction |
-|----------|-----------------|
-| Must-have | Absence โ Dissatisfaction |
-| One-dimensional | More โ More satisfaction |
-| Attractive | Presence โ Delight |
-| Indifferent | No impact |
-
-### Completeness Tiers (Kleene)
-
-Classifies by **narrative possibility coverage**:
-
-| Tier | What's Covered |
-|------|----------------|
-| Bronze | Binary outcomes (success/failure ร action/avoidance) |
-| Silver | + Uncertainty and exploration |
-| Gold | Full possibility space including Limbo |
-
-### Mapping Between Frameworks
-
-| Kano Category | Closest Tier Concept |
-|---------------|---------------------|
-| Must-have | Bronze requirements (corners + death + victory) |
-| One-dimensional | SilverโGold progression |
-| Attractive | Gold-exclusive cells (Limbo, Commitment) |
-| Indifferent | Not mapped (Kleene assumes all cells matter) |
-
-Kleene's tiers assume all grid cells have value; the Kano model allows for "indifferent" dimensions that don't affect satisfaction.
-
----
-
-## Evaluation Methodology Comparison
-
-### Paper: Delphi Study
-
-- **Panel:** 10 narrative design experts
-- **Process:** Multiple rounds of anonymous questionnaires
-- **Consensus:** Statistical agreement on importance
-- **Output:** Ranked, classified dimension list
-
-**Suitable for:** Establishing industry-wide quality standards
-
-### Kleene: Automated Analysis
-
-- **Tool:** yq + JSON Schema + custom checks
-- **Process:** Single automated pass
-- **Consensus:** Not applicable (deterministic)
-- **Output:** Pass/fail per check with specific issues
-
-**Suitable for:** Rapid iteration during development
-
-### Complementary Use
-
-```
-1. Generate scenario with kleene-generate
-2. Validate structure with kleene-analyze (automated)
-3. Playtest with expert panel (SQD evaluation)
-4. Iterate based on combined findings
-5. Final validation before release
-```
-
-Automated validation catches structural issues quickly; expert evaluation catches subjective quality issues that automation misses.
-
----
-
-## What Kleene Could Learn
-
-### From the Kano Classification
-
-Kleene could weight analysis findings by impact:
-
-```
-CRITICAL (Must-have violations):
- โ No death path exists
- โ Unreachable ending: victory_wisdom
-
-WARNING (One-dimensional below threshold):
- โ Grid coverage: Bronze (4/9) - consider expanding to Silver
-
-INFO (Attractive opportunities):
- โ No Limbo cell - could add uncertainty moments
- โ All endings same tone - variety could delight
-```
-
-### From the Emergent Dimensions
-
-**Voice validation:**
-```yaml
-analysis:
- voice_consistency:
- check: narrative_vocabulary_drift
- threshold: 0.3 # max deviation from baseline
- baseline_node: intro
-```
-
-**Genre alignment validation:**
-```yaml
-analysis:
- genre_alignment:
- declared_tone: tragic
- check: ending_tone_distribution
- expected: { tragic: ">50%", triumphant: "<20%" }
-```
-
----
-
-## Conclusion
-
-The paper and Kleene represent **complementary validation approaches**:
-
-| Aspect | Paper | Kleene |
-|--------|-------|--------|
-| **Focus** | Subjective quality | Structural completeness |
-| **Method** | Expert consensus | Automated analysis |
-| **Speed** | Slow (human panels) | Fast (immediate) |
-| **Coverage** | 23 quality dimensions | 15 structural checks |
-| **Iteration** | Post-generation | Pre-play |
-
-**The paper provides:**
-- Industry-validated quality dimensions
-- Prioritization framework (Kano model)
-- Subjective quality criteria
-
-**Kleene provides:**
-- Automated structural validation
-- Immediate feedback loop
-- Specific, actionable issues
-
-An ideal workflow combines both: Kleene's automated analysis for rapid structural iteration, followed by SQD-based expert evaluation for subjective quality assurance before release.
-
----
-
-## References
-
-**Paper:**
-- Valdivia, A. & Burelli, P. (2025). Evaluating Quality of Gaming Narratives Co-created with AI. IEEE Conference on Games 2025.
-- arXiv: https://arxiv.org/abs/2509.04239
-- IEEE Xplore: https://ieeexplore.ieee.org/document/11114354/
-
-**Kleene Framework:**
-- `skills/kleene-analyze/SKILL.md` - 15 analysis types
-- `lib/schema/scenario-schema.json` - JSON Schema validation
-- `lib/framework/core/core.md` - Decision Grid, completeness tiers
-- `lib/framework/core/endings.md` - Ending flavor system (type, method, tone)
-- `lib/framework/gameplay/improvisation.md` - Temperature system, tone matching
diff --git a/docs/comparisons/story2game-comparison.md b/docs/comparisons/story2game-comparison.md
deleted file mode 100644
index e05b102..0000000
--- a/docs/comparisons/story2game-comparison.md
+++ /dev/null
@@ -1,346 +0,0 @@
-# Analysis: Kleene Framework vs. Story2Game Paper
-
-## Overview
-
-This document compares the **Kleene narrative engine** against the **Story2Game** paper (Zhou et al., Georgia Tech, arXiv:2505.03547v1) which presents an LLM-based approach to generating complete text-based interactive fiction games.
-
-Both systems use LLMs to generate and run interactive fiction, but with fundamentally different architectures and philosophies.
-
----
-
-## The Two Systems at a Glance
-
-| Aspect | **Story2Game** | **Kleene** |
-|--------|----------------|------------|
-| **Goal** | Generate playable IF games from prompts | Generate, validate, and play IF with narrative completeness |
-| **Architecture** | Three-stage pipeline (storyโworldโcode) | Three skills (generateโanalyzeโplay) + JSON Schema |
-| **Generation Target** | Executable Python code | YAML scenarios validated against schema |
-| **Player Actions** | Dynamic code generation for novel verbs | Soft consequence system + intent classification |
-| **State Model** | Object graph with attributes | Character/World/Location state with 23+ precondition types |
-| **Uncertainty** | Binary (action succeeds/fails compilation) | Three-valued (permits/indeterminate/blocks) |
-| **Completeness** | Compilation success rate (~80%) | Decision Grid coverage tiers (Bronze/Silver/Gold) |
-| **Validation** | Code compilation | 15 analysis types + JSON Schema validation |
-
----
-
-## Generation Capabilities
-
-### Story2Game: Code Generation Pipeline
-
-Story2Game generates complete games through three stages:
-
-1. **Story Generation**: LLM creates narrative with preconditions/effects for each action
-2. **World Population**: Rooms, characters, objects instantiated based on story
-3. **Game Engine Code**: Python code generated to manipulate game state
-
-**Output**: Executable game code
-**Completeness metric**: Does it compile? (~80-97% success)
-
-### Kleene: Scenario Generation with Grid Coverage
-
-Kleene's `kleene-generate` skill creates scenarios through:
-
-1. **Theme Understanding**: Interactive menus for tone, tier, protagonist archetype
-2. **Narrative Skeleton Design**: Structure based on target tier (Bronze/Silver/Gold)
-3. **Element Definition**: Traits, items, flags designed for mechanical depth
-4. **Node Generation**: Narrative + choices + preconditions + consequences
-5. **Grid Coverage Verification**: Ensure all required cells are covered
-6. **Schema Validation**: Output validated against JSON Schema
-7. **Registry Integration**: Generated scenarios immediately playable
-
-**Output**: YAML scenario validated against 1100-line JSON Schema
-**Completeness metric**: Decision Grid coverage (9 cells)
-
-### Generation Comparison
-
-| Feature | Story2Game | Kleene |
-|---------|------------|--------|
-| Input | Title + events + setting | Theme + tone + tier + archetype |
-| Output format | Python code | YAML with JSON Schema validation |
-| Validation | Compilation | 15 analysis types + schema |
-| Iteration | Re-generate | Branch expansion to improve coverage |
-| Designer involvement | Minimal | Interactive menus throughout |
-
----
-
-## State Model Sophistication
-
-### Story2Game: Object-Attribute Model
-
-Story2Game tracks:
-- Objects with binary (true/false) or numeric (0-10) attributes
-- Room graph with object positions
-- Player inventory
-
-Attributes are generated on-demand when players try novel actions.
-
-### Kleene: Multi-Layer State System
-
-Kleene tracks via JSON Schema-validated structures:
-
-**Character State:**
-- `exists` (boolean) - Option type: Some/None
-- `traits` (object) - Named numeric values (courage, wisdom, etc.)
-- `inventory` (array) - Items held
-- `relationships` (object) - NPC relationship values
-- `flags` (object) - Character-specific boolean states
-
-**World State:**
-- `current_location` - Position in location graph
-- `time` (number) - Elapsed time in seconds
-- `flags` (object) - World-level boolean states
-- `location_state` (object) - Per-location mutable state
-- `npc_locations` (object) - NPC position tracking
-- `scheduled_events` (array) - Time-triggered consequences
-- `triggered_events` (array) - Event history
-
-**Location State (per location):**
-- `flags` - Location-specific booleans
-- `properties` - Location-specific numerics
-- `environment` - Atmospheric conditions (lighting, temperature, etc.)
-
-### Precondition Richness
-
-| Story2Game | Kleene (23+ types) |
-|------------|-------------------|
-| Location check | `at_location` |
-| Inventory check | `has_item`, `missing_item` |
-| Attribute check | `trait_minimum`, `trait_maximum` |
-| - | `flag_set`, `flag_not_set` |
-| - | `relationship_minimum` |
-| - | `location_flag_set`, `location_flag_not_set` |
-| - | `location_property_minimum`, `location_property_maximum` |
-| - | `environment_is`, `environment_minimum`, `environment_maximum` |
-| - | `npc_at_location`, `npc_not_at_location` |
-| - | `time_elapsed_minimum`, `time_elapsed_maximum` |
-| - | `event_triggered`, `event_not_triggered` |
-| - | `all_of`, `any_of`, `none_of` (composable) |
-
-Story2Game generates attributes on-demand. Kleene requires authored preconditions but provides much richer expressiveness.
-
----
-
-## Handling Unanticipated Player Actions
-
-### Story2Game: Dynamic Code Generation
-
-When players try unexpected actions:
-
-1. **Essential Object Preconditions**: Create missing objects, place in world
-2. **Fundamental/Additional Preconditions**: Add attributes to existing objects
-3. **Preceding Event Preconditions**: Generate prerequisite actions (1-level depth)
-4. **Attribute Effects**: Cascade new attributes to existing actions
-
-**Result**: ~80% compilation success, ~60% semantic success
-
-**Key limitation**: Can't model room properties (e.g., "illuminate the forest" fails)
-
-### Kleene: Bounded Improvisation System
-
-When players type free-text:
-
-1. **Intent Classification**: Explore / Interact / Act / Meta
-2. **Feasibility Check**: Possible / Blocked / Impossible / Ambiguous
-3. **Grid Mapping**: Discovery (permits) / Constraint (blocks) / Limbo (ambiguous)
-4. **Soft Consequences Only**:
- - `modify_trait` (ยฑ1 max)
- - `add_history`
- - `set_flag` (only `improv_*` prefix)
- - `advance_time`
-5. **Temperature-Based Adaptation**: 0-10 scale controls narrative integration
-6. **Return to Authored Choices**: Player stays at same decision point
-
-**Key principle**: Improvisation enriches without derailing authored structure.
-
-### Comparison
-
-| Scenario | Story2Game | Kleene |
-|----------|------------|--------|
-| "Cut rope with scissors" | Generate scissors, place in world, generate code | Constraint: "You don't have scissors" (teaches player) |
-| "Examine dragon scales" | Generate `scale_pattern` attribute | Discovery: +1 wisdom, `improv_examined_dragon_scales` flag |
-| "Illuminate the forest" | Fails (rooms lack properties) | Discovery/Constraint based on items + location environment state |
-| "Kill the dragon" | Generate fight code, may create weapon | Constraint if no weapon; boss fights reserved for authored paths |
-
----
-
-## Validation and Analysis
-
-### Story2Game: Compilation-Based
-
-Validation is binary: does the generated code compile?
-
-- Individual actions: ~97% success
-- Complete stories: ~87.5% success
-- Semantic correctness: ~60% success
-
-No structural analysis of narrative coverage.
-
-### Kleene: 15 Analysis Types
-
-The `kleene-analyze` skill performs:
-
-| # | Analysis | Description |
-|---|----------|-------------|
-| 1 | Grid Coverage | Check 9-cell coverage, determine tier |
-| 2 | Null Cases | Verify death, departure, blocked paths exist |
-| 3 | Structural | Find unreachable nodes, dead ends, railroads |
-| 4 | Path Enumeration | List all paths from start to endings |
-| 5 | Cycle Detection | Find loops, self-referential choices |
-| 6 | Item Obtainability | Verify required items are obtainable |
-| 7 | Trait Balance | Detect impossible trait requirements |
-| 8 | Flag Dependencies | Find unused/unobtainable flags |
-| 9 | Relationship Network | Map NPC relationship dynamics |
-| 10 | Consequence Magnitude | Flag over/undersized consequences |
-| 11 | Scene Pacing | Analyze scene_break usage |
-| 12 | Path Diversity | Identify false choices, railroads |
-| 13 | Ending Reachability | Verify all endings are reachable |
-| 14 | Travel Consistency | Validate travel time config |
-| 15 | Schema Validation | Validate structure, types, references |
-
-Plus JSON Schema validation against 1100-line schema with:
-- All precondition types validated
-- All consequence types validated
-- Reference integrity (next_node targets exist)
-- Type correctness throughout
-
----
-
-## The Uncertainty Question
-
-### Story2Game: Binary Outcomes
-
-Actions either:
-- Compile successfully โ Execute
-- Fail compilation โ Error
-
-No "pending" or "indeterminate" state. The system generates deterministic code.
-
-### Kleene: Three-Valued Logic
-
-Based on Kleene's 1938 three-valued logic:
-
-| Value | Meaning | Example |
-|-------|---------|---------|
-| **Permits** | Action succeeds | Door opens |
-| **Blocks** | Action fails | Door locked, key required |
-| **Indeterminate** | Outcome pending | Potion drunk, effects unknown |
-
-This enables the **Commitment** cell: "You drink the potion. Its effects haven't manifested yet."
-
-Story2Game would need to immediately generate the potion's effects. Kleene can hold suspense via scheduled events that trigger later.
-
----
-
-## Completeness Models
-
-### Story2Game: Structural Metrics
-
-- Does the story have the requested number of events?
-- Does the generated code compile?
-- Can preconditions be satisfied?
-
-No consideration of whether the narrative covers different player strategies (action vs. avoidance, success vs. failure).
-
-### Kleene: Decision Grid Coverage
-
-The 3ร3 grid defines 9 possible narrative outcomes:
-
-| | World Permits | World Indeterminate | World Blocks |
-|--------------------|---------------|---------------------|--------------|
-| **Player Chooses** | Triumph | Commitment | Rebuff |
-| **Player Unknown** | Discovery | Limbo | Constraint |
-| **Player Avoids** | Escape | Deferral | Fate |
-
-**Completeness Tiers:**
-
-| Tier | Coverage | Required Cells |
-|------|----------|----------------|
-| Bronze | 4/9 | Triumph, Rebuff, Escape, Fate + death path + victory path |
-| Silver | 6+/9 | Bronze + 2 middle cells (Commitment, Discovery, etc.) |
-| Gold | 9/9 | All cells scripted or via improvisation |
-
-A scenario with 10 endings could still be Bronze-incomplete if all endings are victories (no Rebuff, Escape, or Fate).
-
----
-
-## Architectural Strengths
-
-### Story2Game Strengths
-1. **Zero authoring**: Title + theme โ playable game
-2. **Emergent objects**: World expands for player creativity
-3. **Executable semantics**: Code guarantees consistent behavior
-
-### Kleene Strengths
-1. **Narrative completeness theory**: Formal framework for coverage
-2. **Rich state model**: 23+ precondition types, location state, NPCs, events
-3. **Validation pipeline**: 15 analysis types catch problems before play
-4. **Bounded improvisation**: Player creativity within authored structure
-5. **Iterative improvement**: Generate โ Analyze โ Expand branches โ Re-analyze
-
----
-
-## Architectural Weaknesses
-
-### Story2Game Weaknesses
-1. **60% semantic success**: Many actions don't match expectations
-2. **No structural room properties**: Can't model environment changes
-3. **No uncertainty modeling**: Everything resolves immediately
-4. **No validation beyond compilation**: Narrative quality unchecked
-5. **Object proliferation**: May create incoherent world state
-
-### Kleene Weaknesses
-1. **Requires authoring or generation**: Not instant
-2. **Soft consequence limits**: Players can't create objects via improvisation
-3. **Complexity**: 1100-line schema, 15 analysis types to understand
-
----
-
-## What Each System Could Learn
-
-### What Kleene Could Adopt from Story2Game
-
-1. **Object creation in improvisation**: Limited emergent objects (flavor items, not key items)
-2. **Automatic world population**: Generate location graphs from narrative
-
-### What Story2Game Could Adopt from Kleene
-
-1. **Decision Grid coverage**: Ensure generated stories cover failure and avoidance paths
-2. **Validation pipeline**: Analyze before play, not just compile
-3. **Three-valued outcomes**: Support pending/indeterminate states
-4. **Soft/hard consequence boundary**: Protect authored structure from generation drift
-5. **Completeness tiers**: Define what makes a generated game narratively complete
-
----
-
-## Synthesis: Kleene as Complete Pipeline
-
-Story2Game demonstrates that LLMs can generate playable games. Kleene demonstrates that LLMs can generate, validate, and iterate on narratively complete games.
-
-| Phase | Story2Game | Kleene |
-|-------|------------|--------|
-| **Generate** | StoryโWorldโCode | ThemeโSkeletonโNodes with grid targeting |
-| **Validate** | Compilation only | 15 analysis types + JSON Schema |
-| **Iterate** | Re-generate | Branch expansion to raise tier |
-| **Play** | Execute generated code | Play with bounded improvisation |
-
-Kleene's approach is more labor-intensive but produces scenarios that are:
-- Validated against formal schema
-- Analyzed for narrative completeness
-- Expandable to improve coverage
-- Playable with improvisation that respects structure
-
----
-
-## References
-
-**Story2Game Paper:**
-- Zhou, E., Basavatia, S., Siam, M., Chen, Z., & Riedl, M. O. (2025). Story2Game: Generating (Almost) Everything in an Interactive Fiction Game. arXiv:2505.03547v1
-- https://arxiv.org/html/2505.03547v1
-
-**Kleene Framework:**
-- `lib/framework/core/core.md` - Decision Grid, Option types, completeness tiers
-- `lib/framework/gameplay/improvisation.md` - Soft consequences, intent classification, temperature
-- `lib/schema/scenario-schema.json` - 1100-line JSON Schema (23+ preconditions, 22+ consequences)
-- `skills/kleene-generate/SKILL.md` - Generation modes, grid targeting, branch expansion
-- `skills/kleene-analyze/SKILL.md` - 15 analysis types, validation pipeline
-- `docs/design/theoretical_background.md` - Three-valued logic foundations
diff --git a/docs/design/theoretical_background.md b/docs/design/theoretical_background.md
deleted file mode 100644
index c5606a6..0000000
--- a/docs/design/theoretical_background.md
+++ /dev/null
@@ -1,498 +0,0 @@
-# Theoretical Background: Kleene Logic and Game Theory
-
-This document provides the theoretical foundations for Kleene's Decision Grid. For the practical reference, see [core.md](core.md).
-
-## The Problem: Modeling Interactive Narrative
-
-Interactive fiction must handle genuine uncertainty. Not all actions succeed. Not all outcomes resolve immediately. Players may commit decisively, hesitate at thresholds, or refuse calls to action. The world may permit, obstruct, or leave consequences hanging.
-
-Traditional boolean logic (true/false) cannot model this space. A locked door is not "false" - it is *blocked for now*, potentially unblockable, potentially requiring a key the player might never find. A player choosing "wait and see" is not avoiding action - their intent is *unknown*, suspended between commitment and refusal.
-
-Kleene addresses this by treating uncertainty as a first-class logical value, not an error state.
-
-## Kleene's Three-Valued Logic
-
-Stephen Cole Kleene developed three-valued logic (1938, refined 1952) to model **partial recursive functions** - computations that may not terminate or may fail to return a value. His three truth values are:
-
-| Value | Meaning | In Narrative |
-|-------|---------|--------------|
-| **True (t)** | Computation succeeded | Action evaluated successfully |
-| **False (f)** | Computation terminated with failure | Action evaluated as blocked |
-| **Unknown (u)** | Computation has not terminated | Outcome not yet determined |
-
-The critical insight is that **Unknown is genuinely undecidable** - it is not secretly true or secretly false. A formula containing Unknown may propagate uncertainty through the entire evaluation. This is why Kleene logic has no tautologies: `P โจ ยฌP` evaluates to Unknown when P is Unknown.
-
-This maps directly to the **Option type** in functional programming:
-- `Some(value)` corresponds to True - the computation returned a result
-- `None` corresponds to False - the computation terminated without a value
-- `Unknown` (pending evaluation) - the computation has not resolved
-
-In Kleene, the protagonist's continued existence is itself a partial function: `Option[Character]`. At each narrative moment, this function may return Some (protagonist acts), None (protagonist has ceased), or remain unevaluated (story continues in uncertainty).
-
-## Game-Theoretic Decision Space
-
-While Kleene logic models **world evaluation** (does the action succeed?), we need a second axis to model **player intent** (what is the player trying to do?).
-
-Game theory provides this framework. In any decision situation, a player occupies one of three strategic postures:
-
-| Posture | Game-Theoretic Concept | In Narrative |
-|---------|------------------------|--------------|
-| **Chooses** | Committed strategy (cooperation or decisive action) | Player takes deliberate action toward a goal |
-| **Unknown** | Mixed strategy / incomplete information | Player hesitates, explores, or acts without clear intent |
-| **Avoids** | Defection / retreat strategy | Player refuses, flees, or evades |
-
-The middle value (**Player Unknown**) captures two important phenomena:
-1. **Hesitation** - the player has not committed to a strategy
-2. **Improvisation** - free-text input where intent must be classified
-
-This is not indecision as weakness. In game theory, mixed strategies (randomizing between options) can be equilibrium behavior. In iterated games, "wait and see" can be optimal against an uncertain opponent. The narrative "Player Unknown" captures exactly this: the player occupies a strategic threshold.
-
-## The Decision Grid: Two Frameworks Intersect
-
-The Decision Grid emerges as the **Cartesian product** of two three-valued systems:
-
-- **Player axis** (decision theory): Chooses, Unknown, Avoids
-- **World axis** (Kleene logic): Permits, Indeterminate, Blocks
-
-This produces exactly **9 cells** - the complete possibility space for any narrative moment:
-
-| | World Permits (t) | World Indeterminate (u) | World Blocks (f) |
-|--------------------|-------------------|-------------------------|------------------|
-| **Player Chooses** | Triumph | Commitment | Rebuff |
-| **Player Unknown** | Discovery | Limbo | Constraint |
-| **Player Avoids** | Escape | Deferral | Fate |
-
-Each cell has distinct computational semantics:
-
-- **Triumph** (t,t): Both player and world resolve positively - classic victory
-- **Commitment** (t,u): Player commits, world outcome pending - the potion is drunk, effects unknown
-- **Rebuff** (t,f): Player commits, world blocks - the door is locked, courage insufficient
-- **Discovery** (u,t): Player explores without commitment, world rewards - curiosity yields insight
-- **Limbo** (u,u): Neither axis resolved - the chaos center, pure potential, where improvisation thrives
-- **Constraint** (u,f): Hesitation reveals obstacle - failure teaches what is needed
-- **Escape** (f,t): Player refuses what would succeed - survival without growth, ironic endings
-- **Deferral** (f,u): Player avoids unresolved threat - the problem postponed, consequences building
-- **Fate** (f,f): Player cannot avoid what cannot be escaped - tragedy, inevitability
-
-The 9-cell grid is not arbitrary design. It is the **complete enumeration** of how player agency and world evaluation can combine under genuine uncertainty.
-
-## Iterated Play and Emergent Narrative
-
-Interactive fiction is not a one-shot game. Players make sequences of choices, and the world responds to accumulated history. This is the domain of **iterated game theory**.
-
-Key dynamics from repeated games apply to narrative:
-
-**History as Memory**: The narrative trace (`history` array) functions as the memory in an iterated game. Past choices constrain future options (preconditions check flags), build or erode relationships, and accumulate consequences.
-
-**Reputation Effects**: Traits like courage, wisdom, and NPC relationships encode reputation. In game theory, reputation enables cooperation that would be impossible in one-shot interactions. A player with high relationship can access paths closed to strangers - this is the folk theorem in action.
-
-**Signaling**: Player choices communicate intent. Selecting "approach carefully" signals different information than "charge forward." The world can update its response based on these signals.
-
-**Emergence**: In long-form play, the interaction between player strategy and world response produces emergent narrative. Neither the scenario author nor the player fully controls the outcome. The grid ensures all possibilities are structurally available; the history determines which actually manifest.
-
-This is why the narrative trace is not merely record-keeping - it IS the story.
-
-## The Parser Problem in Classic Interactive Fiction
-
-The golden age of interactive fictionโZork, Planetfall, A Mind Forever Voyagingโachieved remarkable narrative depth within severe technical constraints. These games implemented sophisticated world models, complex puzzles, and memorable prose. Yet they shared a fundamental limitation: **the parser bottleneck**.
-
-Traditional text adventure parsers operated through pattern matching:
-
-```
-> EXAMINE MAILBOX
-Opening the small mailbox reveals a leaflet.
-
-> LOOK AT THE RUST PATTERNS ON THE MAILBOX
-I don't understand "rust patterns."
-
-> WONDER WHO LIVED HERE
-I don't know the word "wonder."
-```
-
-The parser recognized a finite vocabularyโtypically 600-1000 wordsโmapped to a finite set of verbs. Player intent that exceeded this vocabulary produced the infamous "I don't understand that" response. This created a **frustration gap**: the space between what players could imagine and what the parser could process.
-
-The consequences were significant:
-
-1. **Vocabulary exhaustion**: Players learned to speak "parser language," limiting input to known verbs and nouns
-2. **Binary outcomes**: Every input either matched a pattern (success) or didn't (failure)
-3. **Loss of immersion**: Each "I don't understand" broke the fictional contract
-4. **Unexplored middle ground**: There was no graceful way to acknowledge intent while explaining constraints
-
-The parser was a bottleneck between player imagination and narrative response. Actions that the world could logically accommodateโexamining textures, wondering about history, attempting creative solutionsโfailed silently because they couldn't be parsed, not because they were impossible in the fiction.
-
-## The LLM as Universal Parser
-
-A large language model eliminates vocabulary constraints entirely. The parser problem dissolves because:
-
-1. **Natural language understanding replaces pattern matching**: Any grammatically coherent input can be interpreted
-2. **Intent classification captures meaning, not syntax**: "examine the mailbox," "look at the mailbox closely," "study the mailbox's details," and "I wonder what's in that mailbox" all map to the same exploratory intent
-3. **No "I don't understand"**: Every input receives a meaningful response within the fiction
-4. **The Player Unknown row becomes fully accessible**: Hesitation, curiosity, and improvisation are no longer filtered out by parser limitations
-
-The Decision Grid shifts from theoretical model to practical reality. Consider the Player Unknown row:
-
-| Cell | Classic Parser | LLM-Powered |
-|------|---------------|-------------|
-| **Discovery** (Unknown ร Permits) | Only if exact verb/noun matched | Any exploratory phrasing succeeds |
-| **Limbo** (Unknown ร Indeterminate) | Impossibleโparser forced resolution | Natural state for ambiguous input |
-| **Constraint** (Unknown ร Blocks) | Failed to parse, no feedback | Explains *why* blocked in fiction |
-
-The LLM doesn't just understand more inputsโit enables an entirely different relationship between player and narrative. Intent that was previously binary (understood/not understood) becomes a spectrum that maps naturally onto the grid.
-
-## Bounded Creativity: The Soft Consequence System
-
-Unrestricted LLM generation would break scenario balance. If players could improvise their way to victory, puzzles become trivial and authored story arcs collapse. Kleene's solution is **bounded creativity** through a soft consequence system.
-
-### The Boundary
-
-Improvised actions may apply ONLY these consequence types:
-
-| Allowed (Soft) | Reserved (Hard) |
-|----------------|-----------------|
-| `modify_trait` (delta: ยฑ1) | `gain_item` (scenario items) |
-| `add_history` | `lose_item` |
-| `set_flag` (only `improv_*` prefix) | `move_to` |
-| `advance_time` | `character_dies` |
-| | `character_departs` |
-
-This creates a bounded generative space:
-
-1. **The LLM enriches the current moment**: Atmospheric detail, character insight, soft rewards
-2. **The author retains structural control**: Items, locations, major state changes, death
-3. **Player creativity is rewarded without derailing the story**: Exploration gains wisdom, curiosity gains insight, but the puzzle still requires the key
-
-### Why This Works
-
-The soft/hard boundary maps to the narrative distinction between **texture** and **structure**:
-
-- **Texture**: How the moment feels, what details emerge, how NPCs respond to curiosity
-- **Structure**: What paths are available, what items exist, how the story can end
-
-An LLM excels at generating textureโthat's what language models do. Structure requires authorial intentโthe puzzle has a solution because someone designed it. Soft consequences let the LLM handle texture while preserving the authored structure.
-
-The player who examines the dragon's scales gains +1 wisdom and a richer understanding of the scene. The player who tries to climb the dragon without the dragonscale armor still can'tโthat's a structural constraint the author established. But the attempt generates a meaningful Constraint response that teaches the player something about what's needed.
-
-> **Implementation**: See [improvisation.md](../../lib/framework/gameplay/improvisation.md) for the complete soft consequence specification.
-
-## The Uncertainty Zone
-
-The Decision Grid has **4 corner cells** where both axes resolve (Triumph, Rebuff, Escape, Fate) and **5 middle cells** where at least one axis remains uncertain. This middle regionโthe **Uncertainty Zone**โis where LLM-enabled improvisation thrives.
-
-| | World Permits | World Indeterminate | World Blocks |
-|--------------------|---------------|---------------------|--------------|
-| **Player Chooses** | Triumph | **Commitment** | Rebuff |
-| **Player Unknown** | **Discovery** | **Limbo** | **Constraint** |
-| **Player Avoids** | Escape | **Deferral** | Fate |
-
-Classic parsers forced resolution to corners. Every input either matched a pattern (success โ corner) or failed (parser error โ retry). The 5 middle cells were structurally inaccessible.
-
-### The Parser Double-Bind
-
-Classic parsers had TWO limitations, not just one:
-
-1. **Input constraint**: Needed exact verb/noun combinationsโvocabulary exhaustion forced players to speak "parser language"
-2. **Output constraint**: Could only display pre-authored responsesโcontent exhaustion meant no generative texture
-
-These limitations forced games into the corners:
-- Matched input โ pre-authored success response โ corner cell
-- Unmatched input โ "I don't understand" โ retry until match โ corner cell
-
-There was no path to uncertainty. The parser couldn't understand varied phrasing, and it couldn't generate responses for situations the author hadn't scripted.
-
-### What LLMs Enable
-
-LLMs solve both constraints:
-- **Input**: Understand varied phrasing โ intent classification (explore, interact, act, meta)
-- **Output**: Generate authentic narrative responses โ bounded by soft consequences, enriched by scenario context + player history + improv flags
-
-Each middle cell becomes a distinct narrative mode:
-
-### Commitment (Chooses ร Indeterminate)
-
-The player acts decisively, but the world suspends judgment. The outcome is pending.
-
-```
-You drink the potion. Its taste is strangeโbitter herbs, a hint of copper,
-something else you can't name. For a moment, nothing. Then a warmth
-spreads from your stomach, subtle but undeniable. Whether this is the
-beginning of healing or the first stage of something else, you cannot yet tell.
-```
-
-**Classic parser**: Either immediate effect ("You feel stronger!") or nothing ("Nothing happens"). No sustained uncertaintyโthe game needed to update state or not.
-
-**LLM-enabled**: The drinking is described, the waiting acknowledged, the first sensations rendered. Suspense is maintained. The world will resolve in a future turn; for now, the player has committed but fate hangs.
-
-### Discovery (Unknown ร Permits)
-
-The player explores without commitment. The world rewards curiosity.
-
-```
-> examine the dragon's scales more closely
-
-The scales catch the torchlight differently than you expected. They aren't
-uniformโa subtle pattern runs through them, almost like writing. The longer
-you look, the more certain you become: these marks are intentional. Someoneโ
-or somethingโinscribed them long ago. (+1 wisdom)
-```
-
-**Classic parser**: Needed exact "EXAMINE SCALES"โand only if the author wrote that response. "Look at the dragon's scales more closely" might fail to parse entirely.
-
-**LLM-enabled**: Any exploratory phrasing works. The response draws on scenario description + player history + temperature setting. The world permits the exploration, and the player learns something without having committed to a course of action.
-
-### Limbo (Unknown ร Indeterminate)
-
-Neither axis resolved. The chaos center where pure potential lives.
-
-```
-You stand at the threshold, neither advancing nor retreating. The air
-itself seems to wait. The dragon's eyes follow you, patient as stone,
-as if time has less meaning here than you'd imagined.
-
-What will you do?
-```
-
-**Classic parser**: Impossible. The parser demanded resolutionโeither match an action or fail. There was no "wait and see" state, no grammatical way to express uncertainty that the game could acknowledge.
-
-**LLM-enabled**: Holding patterns that maintain fictional coherence. The moment gains texture without advancing. Limbo is generativeโside discoveries emerge, atmosphere deepens, character moments crystallize, tension builds. The narrative waits with you.
-
-### Constraint (Unknown ร Blocks)
-
-The player explores tentatively; the world reveals an obstacle. Failure teaches.
-
-```
-> try to open the sealed door
-
-The door doesn't budgeโnot locked, but sealed with something more
-permanent. Running your hands along the edge, you feel the cold rigidity
-of old magic, set here to hold fast against exactly this kind of curious
-prodding. Whatever lies beyond requires more than hands to reach.
-```
-
-**Classic parser**: "You can't do that" or "The door is locked." No context, no teaching, no enrichment. The player learned only that this verb/noun combination was blocked.
-
-**LLM-enabled**: The block is explained *within the fiction*. The player learns *why*โold magic, a seal, requirements hinted at. The failed exploration enriches understanding of world rules. Constraint isn't frustration; it's education.
-
-### Deferral (Avoids ร Indeterminate)
-
-The player backs away from an unresolved threat. Strategic retreat.
-
-```
-> step back from the dragon
-
-You retreat to the cavern entrance, putting stone between you and those
-ancient eyes. The dragon doesn't pursueโbut neither does it look away.
-This isn't over. You've bought time, not safety. Whatever was building
-in that chamber continues to build.
-```
-
-**Classic parser**: Just a location change. "You are in the cavern entrance." No narrative residue, no preserved tension.
-
-**LLM-enabled**: The consequences continue building. The world remembers the retreat. Tension preserved, threat still unresolved. The player avoided without resolvingโand the narrative holds that suspended state.
-
-### Bounded Free Will
-
-The Uncertainty Zone isn't cheat mode. The LLM applies world judgment to player claims:
-
-| Player Input | World Response | Why |
-|--------------|----------------|-----|
-| "I have unlimited ammo" | **Blocked** | Structural impossibilityโcontradicts scenario rules |
-| "I have blue hair matching the wallpaper" | **Permitted** | Cosmetic enrichmentโdoesn't break structure |
-| "I search for a hidden passage" | **Discovery/Constraint** | Depends on scenario context |
-| "I remember my grandfather's advice" | **Permitted** | Character enrichment via history |
-
-This is free will without omnipotence. The player can enrich, not rewrite. The soft consequence system (see previous section) ensures improvised actions add texture without breaking structure.
-
-### The Opposite Problem: Pure LLM Drift
-
-The Uncertainty Zone section documents how classic parsers couldn't reach the middle. But pure LLM games have the **opposite problem**: they can't reliably reach the corners.
-
-**Pure LLM narrative games** (where the AI generates everything):
-- Generate endless middle-cell content (atmosphere, exploration, holding patterns)
-- Lack strict preconditions โ no true blocks
-- Never force resolution โ stories drift without stakes
-- Everything is permitted โ nothing matters
-
-This creates the characteristic "AI story" feeling: engaging moment-to-moment, but ultimately unsatisfying because nothing is truly at risk.
-
-**Why corners matter:**
-- **Triumph/Rebuff**: Decisive action meets definitive world response
-- **Escape/Fate**: Avoidance meets resolution (success or inescapable)
-- Corners are where **meaning crystallizes**โuncertainty becomes consequence
-
-**Kleene's solution:** The Option framework enforces corners:
-- Scenario YAML defines `character_dies`, `character_departs` consequences
-- Preconditions create actual blocks (Rebuff, Fate possible)
-- Completeness tiers require corner coverage
-- The soft/hard consequence boundary prevents improvisation from derailing structure
-
-The full 9-cell grid isn't just "middle + corners"โit's **middle *because* corners exist**. Uncertainty has meaning only against the backdrop of possible resolution.
-
-## Classic Games Reimagined
-
-The Zork scenario (`scenarios/zork1-mini.yaml`) demonstrates how LLM-powered improvisation transforms classic IF without breaking it.
-
-### The Original Constraint
-
-In 1980, standing west of the white house, your options were constrained by the parser:
-
-```
-> OPEN MAILBOX
-Opening the small mailbox reveals a leaflet.
-
-> READ LEAFLET
-"WELCOME TO ZORK! ZORK is a game of adventure, danger, and low cunning..."
-
-> EXAMINE HOUSE
-The house is a beautiful colonial house which is painted white.
-
-> LOOK AT THE RUST ON THE MAILBOX
-I don't understand "rust."
-
-> WONDER WHO LIVED HERE
-I don't know the word "wonder."
-
-> LISTEN FOR SOUNDS FROM INSIDE
-I don't understand that sentence.
-```
-
-The world model knew the house was abandoned. It knew the mailbox was old. But this knowledge was inaccessible to any verb the parser didn't recognize.
-
-### The Kleene Transformation
-
-With Kleene, the same scenario offers scripted choices:
-
-```yaml
-options:
- - id: open_mailbox
- text: "Open the mailbox"
- cell: chooses
- ...
- - id: go_north
- text: "Go north around the house"
- cell: chooses
- ...
-```
-
-But the player can also type anything:
-
-- **"Look at the rust patterns on the mailbox"** โ Discovery: The LLM generates atmospheric detail about weathering and age, perhaps hinting at how long the house has been abandoned. (+1 wisdom, improv_examined_mailbox flag set)
-
-- **"Wonder who lived here"** โ Limbo: The protagonist's curiosity becomes narrative texture. The silence of the house deepens. The player hasn't acted, but the moment has grown richer.
-
-- **"Listen for sounds from inside"** โ Could be Discovery (faint creaking), Limbo (profound silence that somehow feels intentional), or Constraint (you can't hear anything through the boarded windows, but the attempt makes you more aware of how sealed the house is)
-
-### What Stays Fixed
-
-The authored structure remains intact:
-
-- The mailbox still contains the leaflet
-- The window behind the house is still the way in
-- The troll still blocks the passage until defeated
-- The treasures are still where Infocom placed them
-
-The scenario file is finite. The experience becomes infiniteโor at least as varied as player curiosity allows.
-
-### The Space Between Puzzles
-
-Classic IF was a sequence of puzzle states punctuated by travel. Kleene fills the space between:
-
-| Classic IF | Kleene |
-|------------|--------|
-| Puzzle โ Travel โ Puzzle | Puzzle โ *Exploration* โ Travel โ *Atmosphere* โ Puzzle |
-| Binary: solved/unsolved | Spectrum: degrees of understanding |
-| Parser success or failure | Every input generates narrative |
-
-The puzzles remain. The authored paths remain. But the player's journey through them becomes uniquely their own.
-
-## The Temperature Gradient
-
-Temperature controls how much improvised context influences scripted content. It's the dial between pure authorial voice and adaptive co-creation.
-
-### The Scale
-
-| Temp | Style | Description |
-|------|-------|-------------|
-| **0** | Verbatim | Scenario text exactly as written |
-| **1-3** | Subtle | Faint echoes of improvised discoveries |
-| **4-6** | Balanced | Direct references woven into narrative |
-| **7-9** | Immersive | Rich integration of all improv context |
-| **10** | Fully Adaptive | Narrative perspective shaped by exploration |
-
-### What Temperature Controls
-
-Temperature affects **presentation**, not **structure**. The scenario YAML remains authoritative for:
-
-- Node transitions
-- Consequences
-- Preconditions
-- Ending narratives
-
-What changes is how the LLM presents scripted content based on what the player has improvised:
-
-**Temperature 0 (Verbatim):**
-```
-The dragon's eyes fix upon you. Ancient and knowing, they hold
-the weight of centuries. What will you do?
-```
-
-**Temperature 5 (Balanced):**
-```
-The inscriptions you noticed earlier on the dragon's scales
-seem to pulse faintly in the torchlight.
-
-The dragon's eyes fix upon you. Ancient and knowing, they hold
-the weight of centuries. What will you do?
-```
-
-**Temperature 10 (Fully Adaptive):**
-```
-Everything you've learned comes together in this momentโ
-the inscriptions on the scales, the elder's words about
-the dragon's grief, the symbols etched into the cavern walls.
-You see now what others never paused to notice. The dragon
-isn't a monster. It's a mourner.
-
-The dragon's eyes fix upon you. Ancient and knowing, they hold
-the weight of centuries. What will you do?
-```
-
-### Why Temperature Matters
-
-Temperature lets authors tune the creative collaboration:
-
-- **Low (0-3)**: Safe preservation of authored voice. The scenario reads as written, with minimal LLM influence. Good for tightly crafted prose or first playthroughs.
-
-- **Medium (4-6)**: Balanced integration. Improvised discoveries appear as parenthetical enrichment. The authored voice dominates, but player exploration visibly matters.
-
-- **High (7-10)**: Full co-creation. The LLM reshapes presentations around what the player has discovered. Option descriptions suggest tactical implications. Bonus options may emerge from exploration history.
-
-At high temperatures, bonus options can appearโnew choices generated from `improv_*` flags:
-
-```
-1. Attack with your sword
- โโโ Target the weakness you discovered in its scales
-
-2. Speak to the dragon
- โโโ Use the greeting words inscribed on its scales
-
-3. Trace the inscriptions [BONUS]
- โโโ Follow the symbols you noticed earlier
-```
-
-The bonus option emerged because the player examined the scales. The authored options remain; player curiosity created a new path. This is bounded creativity in action: the bonus option still uses soft consequences, but it rewards exploration with expanded agency.
-
-### The Creative Contract
-
-Temperature formalizes a creative contract between author and LLM:
-
-- **The author provides structure**: What can happen, what matters, how stories end
-- **The LLM provides texture**: How moments feel, how curiosity is rewarded, how the world responds to improvisation
-- **Temperature sets the blend**: From pure authorial voice to rich co-creation
-
-The scenario file remains finite. The experience expands to fill the space the player's imagination opens up.
-
----
-
-This theoretical foundationโKleene logic, game-theoretic decision space, bounded creativity, and temperature-controlled integrationโtransforms interactive fiction from a puzzle of parser vocabulary into a conversation between player, author, and language model. The Decision Grid ensures every possibility has a place. The soft consequence system ensures creativity is rewarded without breaking structure. Temperature ensures authors retain control over how much the LLM shapes the experience.
-
-The classic games aren't replaced. They're freed from the parser that constrained them.
diff --git a/docs/plans/classic-mode.md b/docs/plans/classic-mode.md
deleted file mode 100644
index b203fe0..0000000
--- a/docs/plans/classic-mode.md
+++ /dev/null
@@ -1,197 +0,0 @@
-# Classic Mode Implementation Plan (ARCHIVED)
-
-> **Note:** This feature has been implemented and renamed to "Parser Mode".
-> See `lib/framework/gameplay/parser-mode.md` for the current specification.
-> The setting has been renamed from `classic_mode` to `parser_mode`.
-> Commands `/kleene classic` still work as aliases for `/kleene parser`.
-
----
-
-# Original Plan (for historical reference)
-
-## Overview
-
-Add a "classic_mode" boolean setting that hides pre-canned choice options, forcing players to type commands like original Zork. When enabled, only "Look around" and "Inventory" are shown - everything else requires free-text input via "Other".
-
-This leverages the existing improvisation system which already handles free-text parsing. No command interpreter needed.
-
-## How It Works
-
-| Mode | Choice Presentation |
-|------|---------------------|
-| **OFF** (default) | Show 2-4 scripted options + implicit "Other" |
-| **ON** | Show only "Look around" + "Check inventory", force typing for actions |
-
-**Why keep two options:** Provides a safety net so players aren't completely lost.
-- **Look around**: Re-displays location narrative and available exits/items
-- **Check inventory**: Shows current items held (classic parser staple)
-
-## Files to Modify
-
-### 1. `lib/framework/savegame-format.md`
-- Add `classic_mode` to settings block
-- Add backward compatibility note
-
-### 2. `commands/kleene.md`
-- Add "Classic Mode Actions" section
-- Add `/kleene classic [on|off]` command handling
-- Update Help section
-
-### 3. `skills/kleene-play/SKILL.md`
-- Add `classic_mode` to game state model
-- Modify choice presentation logic (Phase 2, step 5)
-- Add "Look around" and "Inventory" handlers
-
-## Implementation Details
-
-### saves.md Changes
-
-Add to settings block:
-```yaml
-settings:
- improvisation_temperature: [0-10]
- gallery_mode: [boolean]
- foresight: [0-10]
- classic_mode: [boolean] # NEW - hide scripted options
-```
-
-Add backward compatibility:
-```
-**v6 โ v7:** Saves without `classic_mode` field default to:
-- `classic_mode: false`
-```
-
-### kleene.md Changes
-
-Add new section after Foresight Actions:
-
-```markdown
-### Classic Mode Actions
-Keywords: "classic", "parser", "text adventure", "zork mode", "manual"
-
-**Toggle Classic Mode** (`/kleene classic [on|off]`):
-1. Parse on/off value (or toggle if not provided)
-2. Update `settings.classic_mode` in current game state
-3. Confirm with explanation
-
-If no value provided, show current setting and explain:
-```
-Classic mode: OFF
-
-When ON, hides pre-scripted choice options. You must type commands
-like original text adventures (Zork, Colossal Cave, etc.).
-
-Only "Look around" and "Inventory" remain as safety options -
-everything else requires typing via "Other". Try commands like:
- - go north / enter cave / climb ladder
- - examine painting / look at sword
- - take key / pick up torch
- - talk to merchant / attack troll
-
-When OFF (default), shows 2-4 scripted choices with descriptions.
-
-Use: /kleene classic on
- /kleene classic off
-```
-
-**Note:** Classic mode only affects choice presentation. The
-improvisation system handles all typed commands. Setting is
-saved with game state.
-```
-
-Update Help section:
-```
- /kleene classic Show classic mode status
- /kleene classic [on|off] Toggle text adventure mode:
- on = Type commands (Zork-style)
- off = Show choice menu (default)
-```
-
-### kleene-play/SKILL.md Changes
-
-Add to game state model:
-```yaml
-settings:
- improvisation_temperature: number
- gallery_mode: boolean
- foresight: number
- classic_mode: boolean # Hide scripted options (NEW)
-```
-
-Add default in initialization:
-```yaml
-settings:
- improvisation_temperature: 5
- gallery_mode: false
- foresight: 5
- classic_mode: false # Default: show choices
-```
-
-Modify Phase 2 step 5 (choice presentation):
-
-```markdown
-5. Present choices via AskUserQuestion:
-
- **IF settings.classic_mode == true:**
- ```json
- {
- "questions": [{
- "question": "[node.choice.prompt]",
- "header": "Action",
- "multiSelect": false,
- "options": [
- {"label": "Look around", "description": "Survey your surroundings"},
- {"label": "Inventory", "description": "Check what you're carrying"}
- ]
- }]
- }
- ```
-
- **ELSE (classic_mode == false):**
- [existing choice presentation code]
-```
-
-Add classic mode handlers in Phase 2 step 6:
-
-```markdown
-6d. IF selection is "Look around" (classic mode):
- - Re-display current node narrative (abbreviated if long)
- - Extract and list exits mentioned in narrative
- - Extract and list notable items/NPCs if mentioned
- - Format as atmospheric description, not menu
- - Beat++ (log to beat_log with type: "look")
- - Present choices again
- - Do NOT advance node or turn
- - GOTO step 6
-
-6e. IF selection is "Inventory" (classic mode):
- - Display character.inventory as formatted list
- - If empty: "You are empty-handed."
- - If items: List each with brief description if available
- - Beat++ (log to beat_log with type: "inventory")
- - Present choices again
- - Do NOT advance node or turn
- - GOTO step 6
-```
-
-## Interaction with Other Settings
-
-| Setting | Interaction with Classic Mode |
-|---------|-------------------------------|
-| `temperature` | Still affects narrative adaptation for typed commands |
-| `gallery_mode` | Still adds meta-commentary if enabled |
-| `foresight` | Still controls hint specificity for "help" requests |
-
-Classic mode is orthogonal - it only changes the UI, not the underlying systems.
-
-## Verification
-
-1. **Toggle test**: Run `/kleene classic` - should show OFF by default
-2. **Enable test**: Run `/kleene classic on` - should confirm enabled
-3. **Gameplay test**: Start game with classic mode, verify only "Look around" + "Inventory" shown
-4. **Look test**: Select "Look around" - should redisplay narrative with exits/items
-5. **Inventory test**: Select "Inventory" - should show items or "empty-handed"
-6. **Type test**: Select "Other", type "go north" - should use improvisation
-7. **Mid-game toggle**: Enable classic mode during play, then disable - should work seamlessly
-8. **Save/load test**: Enable classic mode, save, reload - should persist
-9. **Help test**: Run `/kleene help` - should show classic mode commands
diff --git a/docs/plans/expanded_time_and_properties.md b/docs/plans/expanded_time_and_properties.md
deleted file mode 100644
index b22908d..0000000
--- a/docs/plans/expanded_time_and_properties.md
+++ /dev/null
@@ -1,1075 +0,0 @@
-# Kleene Scenario Schema Enhancement Plan
-
-## Context
-
-The Kleene scenario format is well-documented and functional, but has gaps in how it handles:
-1. **Node referencing** - Improvisation outcome nodes appear "unreachable" in static analysis
-2. **Consequences** - No location-specific state changes beyond movement
-3. **Preconditions** - Only available on options, not on nodes or locations themselves
-
-## Findings from Investigation
-
-### Improvisation System (NOT Broken)
-
-The 10 "unreachable" nodes in the_yabba.yaml are **working as designed**:
-- They're outcome nodes for improvisation (`next: improvise`)
-- Reached through pattern matching of player's free-text input against `permits[]`/`blocks[]`
-- Static graph analysis can't predict player responses, so they appear unreachable
-- During gameplay, they ARE reachable when player's response matches patterns
-
-**This is a documentation issue, not a bug.**
-
-### Current Schema Gaps
-
-1. **Location state is ephemeral** - Only `current_location` persists; no per-location flags/properties
-2. **Preconditions are choice-only** - Can't gate entire nodes or locations
-3. **World vs Character consequences unclear** - Need better taxonomy
-
-## Proposed Schema Enhancements
-
-### 1. Node Referencing Clarification
-
-**Document the distinction:**
-- `next_node` = static edge (always in graph)
-- `next: improvise` = dynamic edge (pattern-matched at runtime)
-
-**Update kleene-analyze to:**
-- Report improvisation nodes separately as "conditionally reachable"
-- Show pattern coverage (permits/blocks) for each scripted Unknown option
-
-### 2. Location State System
-
-**Add location-specific state to world:**
-
-```yaml
-world:
- current_location: village
- locations:
- village:
- state:
- destroyed: false
- population: 100
- flags:
- quest_completed: false
- npcs_hostile: false
-```
-
-**New consequence types:**
-
-```yaml
-# Set location-specific flag
-- type: set_location_flag
- location: village
- flag: quest_completed
- value: true
-
-# Modify location property
-- type: modify_location_property
- location: shrine
- property: sealed
- delta: 1 # or value: true for boolean
-```
-
-### 3. Node-Level Preconditions
-
-**Allow preconditions on nodes, not just options:**
-
-```yaml
-dragon_lair:
- precondition: # โ NEW: Node-level precondition
- type: all_of
- conditions:
- - type: has_item
- item: dragon_scale
- - type: trait_minimum
- trait: courage
- minimum: 10
-
- narrative: |
- You enter the dragon's lair...
-
- choice:
- options: [...]
-```
-
-**If node precondition fails:**
-- Don't advance to node (stay at previous)
-- Show "blocked" message from precondition
-- OR define `blocked_narrative` override
-
-### 4. Location-Level Preconditions
-
-**Gate location access:**
-
-```yaml
-initial_world:
- locations:
- - id: shrine
- name: "Ancient Shrine"
- connections: [forest, mountain_path]
- precondition: # โ NEW: Location-level precondition
- type: all_of
- conditions:
- - type: has_item
- item: shrine_key
- - type: flag_set
- flag: elder_blessing
- blocked_message: "The shrine remains sealed."
-```
-
-**If location precondition fails:**
-- Location hidden from `move_to` options
-- OR shown as locked/unavailable
-
-### 5. Consequence Taxonomy
-
-**Reorganize consequences by scope:**
-
-**Character Consequences** (affect character state):
-- `modify_trait`, `set_trait`
-- `gain_item`, `lose_item`
-- `modify_relationship`
-- `character_dies`, `character_departs`
-
-**World Consequences** (affect global state):
-- `set_flag`, `clear_flag`
-- `advance_time`
-- `add_history`
-
-**Location Consequences** (affect location state):
-- `move_to` (existing)
-- `set_location_flag` (NEW)
-- `clear_location_flag` (NEW)
-- `modify_location_property` (NEW)
-- `set_location_property` (NEW)
-
-### 6. Precondition Taxonomy
-
-**Current (choice-level only):**
-- Character: `has_item`, `trait_minimum`, `trait_maximum`, `relationship_minimum`
-- World: `flag_set`, `flag_not_set`, `at_location`
-- Logic: `all_of`, `any_of`, `none_of`
-
-**Enhanced (all three levels):**
-
-| Level | Where | Purpose |
-|-------|-------|---------|
-| **Choice** | `options[].precondition` | Gate individual options (existing) |
-| **Node** | `nodes[id].precondition` | Gate entire node entry (NEW) |
-| **Location** | `locations[].precondition` | Gate location access (NEW) |
-
-## Design Decisions (User-Approved)
-
-### Location State Storage: Hybrid Approach โ
-- **Static definitions** remain in `initial_world.locations[]` (name, description, connections)
-- **Mutable state** stored in separate `world.location_state{}` dict
-- Benefits: Clean separation, O(1) lookups, only tracks changed locations, backwards compatible
-
-### Location Access Control: Three Modes โ
-- **filter**: Hide inaccessible options (default, backwards compatible)
-- **show_locked**: Show with disabled indicator and reason
-- **show_normal**: Show normally, fails with message if selected
-
-### Timeline: 5-Phase Approach โ
-- Phase 1: Location State Foundation (Week 1)
-- Phase 2: Node-Level Preconditions (Week 2)
-- Phase 3: Location-Level Preconditions (Week 3)
-- Phase 4: Temporal System & Advanced Consequences (Week 4)
-- Phase 5: Analysis Enhancement & Migration (Week 5)
-
-## Expanded Scope: Temporal & World Systems
-
-Beyond location state, the schema needs comprehensive temporal and world-state enhancements:
-
-### 1. Temporal System (NEW - Major Addition)
-
-**Node-Level Temporal Metadata:**
-```yaml
-nodes:
- morning_after:
- # Time elapsed since previous node
- elapsed_since_previous:
- amount: 8
- unit: hours # seconds, minutes, hours, days, weeks, months, years
-
- # Duration of this node's events
- duration:
- amount: 2
- unit: hours
-
- narrative: |
- You wake as dawn breaks. Two hours of breakfast and preparation pass...
-```
-
-**World-Level Time Tracking:**
-```yaml
-initial_world:
- time:
- # Current absolute time
- current:
- # Real-world calendar (Unix epoch)
- real_time: 1704067200 # 2024-01-01 00:00:00 UTC
-
- # Fantasy calendar
- fantasy_time:
- epoch: "Garagth II's Reign"
- year: 427
- month: "2nd Moon"
- day: 15
- hour: 6
-
- # Scheduled off-camera events
- scheduled_events:
- - id: dragon_awakens
- trigger_at:
- real_time: 1704153600 # 24 hours later
- consequences:
- - type: set_flag
- flag: dragon_awake
- value: true
- - type: set_location_property
- location: dragon_lair
- property: heat_level
- value: 1000
-```
-
-**Temporal Consequences:**
-```yaml
-# Schedule future event
-- type: schedule_event
- event_id: dragon_awakens
- delay:
- amount: 24
- unit: hours
- consequences:
- - type: set_flag
- flag: dragon_awake
- value: true
-
-# Advance time (existing, enhanced)
-- type: advance_time
- amount: 8
- unit: hours # NEW: Support units beyond generic counter
-
-# Set absolute time
-- type: set_time
- real_time: 1704067200
- fantasy_time:
- year: 427
- month: "2nd Moon"
- day: 15
-```
-
-**Temporal Preconditions:**
-```yaml
-# Check if time has passed
-precondition:
- type: time_elapsed_minimum
- amount: 24
- unit: hours
- since: game_start # or node_id, or event_id
-
-# Check absolute time
-precondition:
- type: time_after
- real_time: 1704153600
-
-precondition:
- type: fantasy_time_after
- year: 427
- month: "3rd Moon"
- day: 1
-
-# Check if event has triggered
-precondition:
- type: event_triggered
- event_id: dragon_awakens
-```
-
-### 2. NPC Location Tracking (NEW)
-
-**World-Level NPC State:**
-```yaml
-world:
- npc_locations:
- doc: docs_shack
- jock: pub
- janette: hotel
- tim: tims_house
-```
-
-**NPC Movement Consequences:**
-```yaml
-# Move NPC to location
-- type: move_npc
- npc: doc
- location: pub
-
-# Move NPC relative to player
-- type: move_npc
- npc: jock
- location: current # Moves to player's location
-```
-
-**NPC Location Preconditions:**
-```yaml
-# Check if NPC is at location
-precondition:
- type: npc_at_location
- npc: doc
- location: current # Or specific location ID
-
-# Check if NPC is anywhere except
-precondition:
- type: npc_not_at_location
- npc: janette
- location: hotel
-```
-
-### 3. Environment Effects (NEW)
-
-**Location Environment State:**
-```yaml
-world:
- location_state:
- shrine:
- environment:
- lighting: dim
- weather: rain
- temperature: cold
- ambiance: eerie
- flags:
- sealed: false
-```
-
-**Environment Consequences:**
-```yaml
-# Set environmental condition
-- type: set_environment
- location: shrine # Omit for current_location
- property: lighting
- value: dark
-
-# Modify numeric environment property
-- type: modify_environment
- location: dragon_lair
- property: temperature
- delta: 500
-```
-
-**Environment Preconditions:**
-```yaml
-# Require specific environment
-precondition:
- type: environment_is
- location: shrine
- property: lighting
- value: lit
-
-precondition:
- type: environment_minimum
- location: dragon_lair
- property: temperature
- minimum: 800
-```
-
-### 4. World Events System (NEW)
-
-**Event Definitions:**
-```yaml
-initial_world:
- events:
- - id: dragon_awakens
- trigger:
- type: time_elapsed
- amount: 24
- unit: hours
- since: game_start
- consequences:
- - type: set_flag
- flag: dragon_awake
- value: true
- - type: modify_location_property
- location: dragon_lair
- property: heat_level
- delta: 400
-
- - id: town_evacuation
- trigger:
- type: flag_set
- flag: dragon_approaching
- consequences:
- - type: set_location_flag
- location: village
- flag: evacuated
- value: true
- - type: move_npc
- npc: elder
- location: forest_camp
-```
-
-**Event Consequences:**
-```yaml
-# Trigger event immediately
-- type: trigger_event
- event_id: dragon_awakens
-
-# Schedule event
-- type: schedule_event
- event_id: town_evacuation
- delay:
- amount: 2
- unit: hours
-
-# Cancel scheduled event
-- type: cancel_event
- event_id: dragon_awakens
-```
-
-## Complete Schema Structure
-
-### World State (Enhanced)
-```yaml
-world:
- # Existing
- current_location: village
- flags: {}
-
- # NEW: Location-specific mutable state
- location_state:
- village:
- flags: {quest_completed: true}
- properties: {population: 85}
- environment: {lighting: dim, weather: rain}
-
- # NEW: NPC location tracking
- npc_locations:
- doc: docs_shack
- jock: pub
-
- # NEW: Enhanced time system
- time:
- elapsed: 24 # Legacy counter
- current:
- real_time: 1704067200
- fantasy_time:
- epoch: "Garagth II's Reign"
- year: 427
- month: "2nd Moon"
- day: 15
- hour: 6
-
- # NEW: Scheduled events
- scheduled_events:
- - event_id: dragon_awakens
- trigger_at: 1704153600
- consequences: [...]
-
- # NEW: Triggered events history
- triggered_events: [town_bells, elder_speech]
-```
-
-### Node Structure (Enhanced)
-```yaml
-nodes:
- node_id:
- # NEW: Node-level precondition
- precondition:
- type: all_of
- conditions: [...]
- blocked_narrative: "Custom message when blocked"
-
- # NEW: Temporal metadata
- elapsed_since_previous:
- amount: 2
- unit: hours
- duration:
- amount: 1
- unit: hours
-
- # Existing
- narrative: "..."
- scene_break: true
- choice:
- prompt: "..."
- options: [...]
-```
-
-### Location Structure (Enhanced)
-```yaml
-locations:
- - id: shrine
- name: "Ancient Shrine"
- description: "..."
- connections: [forest]
- items: [scroll]
-
- # NEW: Location-level precondition
- precondition:
- type: flag_not_set
- flag: shrine_sealed
- access_denied_narrative: "The wards block your path."
- access_mode: show_locked # filter | show_locked | show_normal
-
- # NEW: Initial state (usually omitted)
- initial_state:
- flags: {discovered: false}
- properties: {blessing_power: 100}
- environment: {lighting: dim, ambiance: sacred}
-```
-
-## New Consequence Types (Complete List)
-
-### Location Consequences
-- `set_location_flag` - Set boolean flag on location
-- `clear_location_flag` - Clear boolean flag on location
-- `modify_location_property` - Modify numeric property (delta)
-- `set_location_property` - Set property to absolute value
-
-### Environment Consequences
-- `set_environment` - Set environmental condition (lighting, weather, etc.)
-- `modify_environment` - Modify numeric environment property (delta)
-
-### NPC Consequences
-- `move_npc` - Move NPC to specific location or current location
-
-### Temporal Consequences
-- `advance_time` - Enhanced with units (seconds, minutes, hours, days, etc.)
-- `set_time` - Set absolute time (real_time or fantasy_time)
-- `schedule_event` - Schedule event to trigger after delay
-- `trigger_event` - Trigger event immediately
-- `cancel_event` - Cancel scheduled event
-
-## New Precondition Types (Complete List)
-
-### Location Preconditions
-- `location_flag_set` - Check if location flag is true
-- `location_flag_not_set` - Check if location flag is false/missing
-- `location_property_minimum` - Check location property >= value
-- `location_property_maximum` - Check location property <= value
-
-### Environment Preconditions
-- `environment_is` - Check if environment property equals value
-- `environment_minimum` - Check environment property >= value
-- `environment_maximum` - Check environment property <= value
-
-### NPC Preconditions
-- `npc_at_location` - Check if NPC is at specific location
-- `npc_not_at_location` - Check if NPC is NOT at location
-
-### Temporal Preconditions
-- `time_elapsed_minimum` - Check if time has elapsed since reference
-- `time_elapsed_maximum` - Check if time has NOT exceeded limit
-- `time_after` - Check if absolute time is after timestamp (real_time)
-- `time_before` - Check if absolute time is before timestamp
-- `fantasy_time_after` - Check fantasy calendar time is after date
-- `fantasy_time_before` - Check fantasy calendar time is before date
-- `event_triggered` - Check if event has been triggered
-- `event_not_triggered` - Check if event has NOT been triggered
-
-## Implementation Phases (Detailed)
-
-### Phase 1: Location State Foundation (Week 1)
-
-**Goals:**
-- Implement `world.location_state{}` storage
-- Add location flag/property consequences
-- Add location flag/property preconditions
-- Update save format to v4
-- Maintain 100% backwards compatibility
-
-**Deliverables:**
-1. Update game state model in kleene-play/SKILL.md
-2. Implement consequence evaluation:
- - `set_location_flag`, `clear_location_flag`
- - `modify_location_property`, `set_location_property`
-3. Implement precondition evaluation:
- - `location_flag_set`, `location_flag_not_set`
- - `location_property_minimum`, `location_property_maximum`
-4. Update save/load to handle `location_state` (default `{}` if missing)
-5. Create test scenario demonstrating location state
-
-**Files to Modify:**
-- `kleene/skills/kleene-play/SKILL.md` (lines 1-800: state model, consequence/precondition logic)
-- `kleene/lib/framework/scenario-format.md` (consequence/precondition docs)
-- `kleene/lib/framework/savegame-format.md` (save format v4 spec)
-
-**Validation:**
-- All existing scenarios load without errors
-- Location state persists across save/load
-- Location preconditions correctly gate options
-
----
-
-### Phase 2: Node-Level Preconditions (Week 2)
-
-**Goals:**
-- Add `precondition` and `blocked_narrative` to node schema
-- Implement node-entry validation in turn flow
-- Generate fallback messages for blocked nodes
-- Add temporal metadata (elapsed_since_previous, duration)
-
-**Deliverables:**
-1. Update node schema documentation
-2. Add node precondition evaluation before narrative display
-3. Implement fallback message generation
-4. Add temporal metadata parsing (non-functional for now)
-5. Update turn flow to handle blocked node entry
-
-**Files to Modify:**
-- `kleene/lib/framework/scenario-format.md` (node schema docs)
-- `kleene/skills/kleene-play/SKILL.md` (turn flow logic, lines 400-600)
-- `kleene/scenarios/TEMPLATES/intermediate.yaml` (add examples)
-
-**Validation:**
-- Blocked nodes display appropriate messages
-- Turn counter doesn't increment on blocked entry
-- Player returns to previous node with choices re-presented
-
----
-
-### Phase 3: Location-Level Preconditions (Week 3)
-
-**Goals:**
-- Add `precondition`, `access_denied_narrative`, `access_mode` to location schema
-- Implement move_to validation
-- Support three access modes (filter, show_locked, show_normal)
-- Add environment consequences and preconditions
-
-**Deliverables:**
-1. Update location schema documentation
-2. Modify `move_to` consequence to validate location access
-3. Implement access mode filtering in choice presentation
-4. Add environment state to location_state
-5. Implement environment consequences:
- - `set_environment`, `modify_environment`
-6. Implement environment preconditions:
- - `environment_is`, `environment_minimum`, `environment_maximum`
-
-**Files to Modify:**
-- `kleene/lib/framework/scenario-format.md` (location schema, environment docs)
-- `kleene/skills/kleene-play/SKILL.md` (move_to validation, choice filtering)
-
-**Validation:**
-- Failed move_to displays denial message
-- Access modes correctly filter/show_locked/show_normal
-- Environment state persists and affects gameplay
-
----
-
-### Phase 4: Temporal System & Advanced Consequences (Week 4)
-
-**Goals:**
-- Implement enhanced time system (real_time, fantasy_time)
-- Add NPC location tracking
-- Add world events system
-- Implement temporal consequences and preconditions
-- Process node temporal metadata
-
-**Deliverables:**
-1. Enhance world time structure:
- - `time.current.real_time` (Unix timestamp)
- - `time.current.fantasy_time` (custom calendar)
-2. Add `world.npc_locations{}` dict
-3. Add `world.scheduled_events[]` and `world.triggered_events[]`
-4. Implement NPC consequences:
- - `move_npc`
-5. Implement NPC preconditions:
- - `npc_at_location`, `npc_not_at_location`
-6. Implement temporal consequences:
- - Enhanced `advance_time` with units
- - `set_time`, `schedule_event`, `trigger_event`, `cancel_event`
-7. Implement temporal preconditions:
- - `time_elapsed_minimum`, `time_elapsed_maximum`
- - `time_after`, `time_before`
- - `fantasy_time_after`, `fantasy_time_before`
- - `event_triggered`, `event_not_triggered`
-8. Process node temporal metadata:
- - Apply `elapsed_since_previous` on node entry
- - Track node `duration` for event scheduling
-
-**Files to Modify:**
-- `kleene/lib/framework/scenario-format.md` (temporal system, NPC tracking, events)
-- `kleene/skills/kleene-play/SKILL.md` (time tracking, event processing, NPC management)
-- `kleene/lib/framework/savegame-format.md` (time structure in saves)
-
-**Validation:**
-- Time advances correctly with units
-- Scheduled events trigger at correct times
-- NPCs move and their locations are tracked
-- Temporal preconditions gate choices appropriately
-- Node temporal metadata affects world time
-
----
-
-### Phase 5: Analysis Enhancement & Migration (Week 5)
-
-**Goals:**
-- Update kleene-analyze to handle dynamic edges (improvisation)
-- Add validation for all new features
-- Create comprehensive template scenarios
-- Write migration guide
-- Update existing scenario documentation
-
-**Deliverables:**
-1. Rewrite reachability algorithm:
- - Distinguish static edges (next_node) from dynamic edges (next: improvise)
- - Report improvisation outcome nodes as "conditionally reachable"
- - Show pattern matching info for dynamic edges
-2. Add validation checks:
- - Location state consistency
- - Node precondition reachability
- - Location precondition circular dependencies
- - Temporal event consistency
- - NPC location references
-3. Create advanced template scenario demonstrating all features
-4. Write migration guide with before/after examples
-5. Document best practices for:
- - Location state management
- - Temporal design patterns
- - NPC movement patterns
- - Event scheduling strategies
-
-**Files to Create/Modify:**
-- `kleene/skills/kleene-analyze/SKILL.md` (reachability algorithm, validation)
-- `kleene/scenarios/TEMPLATES/advanced.yaml` (new comprehensive template)
-- `kleene/scenarios/TEMPLATES/temporal_example.yaml` (time system showcase)
-- `kleene/lib/authoring/migration-v2.md` (migration guide)
-- `kleene/lib/authoring/best-practices.md` (design patterns)
-- `kleene/lib/framework/core.md` (clarify improvisation routing)
-
-**Validation:**
-- Improvisation nodes reported correctly (not "unreachable")
-- All new schema elements validated
-- Templates demonstrate all features without errors
-- Migration guide successfully applied to test scenarios
-
----
-
-## Formal Schema Definition
-
-The scenario format currently exists only as markdown documentation. We need a formal, machine-readable schema for validation and tooling.
-
-### JSON Schema Definition
-
-**Location:** `kleene/lib/schema/scenario-schema.json`
-
-**Purpose:**
-- Validate scenario YAML files before gameplay
-- Provide IDE autocomplete and validation
-- Generate documentation automatically
-- Enable schema-aware tooling
-
-**Structure:**
-```json
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "title": "Kleene Scenario Schema v2",
- "type": "object",
- "required": ["name", "start_node", "nodes", "initial_character", "initial_world"],
- "properties": {
- "name": {"type": "string"},
- "description": {"type": "string"},
- "version": {"type": "string", "default": "2.0"},
- "start_node": {"type": "string"},
-
- "nodes": {
- "type": "object",
- "patternProperties": {
- "^[a-z_]+$": {"$ref": "#/definitions/Node"}
- }
- },
-
- "initial_character": {"$ref": "#/definitions/CharacterState"},
- "initial_world": {"$ref": "#/definitions/WorldState"},
- "endings": {"$ref": "#/definitions/Endings"}
- },
-
- "definitions": {
- "Node": {
- "type": "object",
- "required": ["narrative", "choice"],
- "properties": {
- "precondition": {"$ref": "#/definitions/Precondition"},
- "blocked_narrative": {"type": "string"},
- "elapsed_since_previous": {"$ref": "#/definitions/TimeAmount"},
- "duration": {"$ref": "#/definitions/TimeAmount"},
- "narrative": {"type": "string"},
- "scene_break": {"type": "boolean"},
- "choice": {"$ref": "#/definitions/Choice"}
- }
- },
-
- "Choice": {
- "type": "object",
- "required": ["options"],
- "properties": {
- "prompt": {"type": "string"},
- "options": {
- "type": "array",
- "items": {"$ref": "#/definitions/Option"}
- }
- }
- },
-
- "Option": {
- "type": "object",
- "required": ["id", "text"],
- "properties": {
- "id": {"type": "string"},
- "text": {"type": "string"},
- "cell": {"enum": ["chooses", "unknown", "avoids"]},
- "precondition": {"$ref": "#/definitions/Precondition"},
- "consequence": {
- "type": "array",
- "items": {"$ref": "#/definitions/Consequence"}
- },
- "narrative": {"type": "string"},
- "next_node": {"type": "string"},
- "next": {"enum": ["improvise"]},
- "improvise_context": {"$ref": "#/definitions/ImproviseContext"},
- "outcome_nodes": {"$ref": "#/definitions/OutcomeNodes"}
- }
- },
-
- "Precondition": {
- "type": "object",
- "required": ["type"],
- "oneOf": [
- {"$ref": "#/definitions/HasItemPrecondition"},
- {"$ref": "#/definitions/TraitMinimumPrecondition"},
- {"$ref": "#/definitions/LocationFlagSetPrecondition"},
- {"$ref": "#/definitions/NPCAtLocationPrecondition"},
- {"$ref": "#/definitions/TimeElapsedMinimumPrecondition"},
- {"$ref": "#/definitions/AllOfPrecondition"},
- // ... all 27 precondition types
- ]
- },
-
- "Consequence": {
- "type": "object",
- "required": ["type"],
- "oneOf": [
- {"$ref": "#/definitions/ModifyTraitConsequence"},
- {"$ref": "#/definitions/SetLocationFlagConsequence"},
- {"$ref": "#/definitions/MoveNPCConsequence"},
- {"$ref": "#/definitions/ScheduleEventConsequence"},
- {"$ref": "#/definitions/SetEnvironmentConsequence"},
- // ... all 22 consequence types
- ]
- },
-
- "WorldState": {
- "type": "object",
- "required": ["current_location", "locations"],
- "properties": {
- "current_location": {"type": "string"},
- "flags": {"type": "object"},
- "locations": {
- "type": "array",
- "items": {"$ref": "#/definitions/Location"}
- },
- "location_state": {"$ref": "#/definitions/LocationStateDict"},
- "npc_locations": {"type": "object"},
- "time": {"$ref": "#/definitions/TimeState"},
- "events": {
- "type": "array",
- "items": {"$ref": "#/definitions/WorldEvent"}
- }
- }
- },
-
- "Location": {
- "type": "object",
- "required": ["id", "name", "connections"],
- "properties": {
- "id": {"type": "string"},
- "name": {"type": "string"},
- "description": {"type": "string"},
- "connections": {
- "type": "array",
- "items": {"type": "string"}
- },
- "items": {
- "type": "array",
- "items": {"type": "string"}
- },
- "precondition": {"$ref": "#/definitions/Precondition"},
- "access_denied_narrative": {"type": "string"},
- "access_mode": {"enum": ["filter", "show_locked", "show_normal"]},
- "initial_state": {"$ref": "#/definitions/LocationState"}
- }
- },
-
- "TimeState": {
- "type": "object",
- "properties": {
- "elapsed": {"type": "number"},
- "current": {
- "type": "object",
- "properties": {
- "real_time": {"type": "number", "description": "Unix timestamp"},
- "fantasy_time": {"$ref": "#/definitions/FantasyTime"}
- }
- }
- }
- },
-
- "FantasyTime": {
- "type": "object",
- "properties": {
- "epoch": {"type": "string"},
- "year": {"type": "number"},
- "month": {"type": "string"},
- "day": {"type": "number"},
- "hour": {"type": "number"}
- }
- },
-
- "TimeAmount": {
- "type": "object",
- "required": ["amount", "unit"],
- "properties": {
- "amount": {"type": "number"},
- "unit": {"enum": ["seconds", "minutes", "hours", "days", "weeks", "months", "years"]}
- }
- }
- }
-}
-```
-
-### Schema Validation Integration
-
-**Phase 1 Addition:**
-1. Create JSON Schema file with all types defined
-2. Add schema validation to kleene-play on scenario load:
- ```python
- import jsonschema
-
- def load_scenario(path):
- with open(path) as f:
- scenario = yaml.safe_load(f)
-
- # Validate against schema
- with open('kleene/lib/schema/scenario-schema.json') as f:
- schema = json.load(f)
-
- try:
- jsonschema.validate(scenario, schema)
- except jsonschema.ValidationError as e:
- print(f"Schema validation error: {e.message}")
- print(f"At path: {' -> '.join(str(p) for p in e.path)}")
- raise
-
- return scenario
- ```
-
-3. Add schema validation to kleene-analyze:
- ```
- SCHEMA VALIDATION
- โโโโโโโโโโโโโโโโโ
- โ Schema valid (v2.0)
- โ All required fields present
- โ All node references valid
- โ Invalid consequence type at nodes.intro.choice.options[2].consequence[1]
- Expected one of: modify_trait, gain_item, set_location_flag, ...
- Got: invalid_type
- ```
-
-### Schema Generation
-
-**Automate schema updates:**
-- Generate JSON Schema from TypeScript types (if we add types later)
-- Or maintain JSON Schema as source of truth
-- Generate markdown documentation from schema
-- Generate example YAML snippets
-
-**Tools:**
-- `ajv` for JSON Schema validation (JavaScript/Node)
-- `jsonschema` for validation (Python)
-- `yaml-language-server` for IDE integration (VS Code, etc.)
-
-### IDE Integration
-
-**VS Code YAML Extension:**
-
-Create `.vscode/settings.json`:
-```json
-{
- "yaml.schemas": {
- "kleene/lib/schema/scenario-schema.json": "scenarios/*.yaml"
- }
-}
-```
-
-This enables:
-- Autocomplete for field names
-- Inline validation errors
-- Hover documentation
-- Schema-aware formatting
-
-## Critical Files for Implementation
-
-### Phase 1 (Core Implementation + Schema)
-1. **`kleene/skills/kleene-play/SKILL.md`** - Game engine, state model, consequence/precondition evaluation, turn flow
-2. **`kleene/lib/framework/scenario-format.md`** - Complete schema documentation (human-readable)
-3. **`kleene/lib/schema/scenario-schema.json`** - Formal JSON Schema definition (machine-readable) **[NEW]**
-4. **`kleene/lib/framework/savegame-format.md`** - Save format v4 with all new state
-
-### Phase 5 (Analysis & Migration)
-5. **`kleene/skills/kleene-analyze/SKILL.md`** - Static analysis, validation with schema checking
-6. **`kleene/lib/framework/core.md`** - Improvisation system clarification
-7. **`kleene/scenarios/TEMPLATES/*.yaml`** - Reference implementations
-
-## Backwards Compatibility Strategy
-
-**100% Compatible via Graceful Defaults:**
-- Missing `world.location_state` โ defaults to `{}`
-- Missing `world.npc_locations` โ defaults to `{}`
-- Missing `world.time.current` โ defaults to legacy `time` counter
-- Missing node/location `precondition` โ always accessible
-- Old consequence/precondition types โ unchanged behavior
-- Save format v2/v3 โ loads with new fields initialized to defaults
-
-**No Breaking Changes:**
-- All existing scenarios (dragon_quest, the_yabba, etc.) work without modification
-- Old save files load correctly
-- New features are opt-in only
-
-## Verification Strategy
-
-### Unit Tests
-- Location state operations (set/modify flags/properties)
-- Environment state operations
-- NPC movement and tracking
-- Time advancement with units
-- Event scheduling and triggering
-- All new consequence evaluations
-- All new precondition evaluations
-
-### Integration Tests
-- Full scenario playthrough with location gating
-- Node precondition blocking
-- Temporal event triggers
-- Save/load with all new state
-- Backwards compatibility with old saves
-
-### Manual Testing
-- Create test scenario using all features
-- Verify the_yabba.yaml still works
-- Test migration guide on sample scenario
-- Verify analysis reports dynamic edges correctly
-
-## Risk Mitigation
-
-### Risk: Temporal System Complexity
-**Impact:** High (new concept)
-**Mitigation:** Comprehensive documentation, clear examples, optional feature
-
-### Risk: Save File Size Growth
-**Impact:** Low (minimal increase)
-**Mitigation:** Only track changed state, lazy initialization
-
-### Risk: Breaking Existing Scenarios
-**Impact:** High (user disruption)
-**Mitigation:** 100% backwards compatibility, extensive testing, graceful defaults
diff --git a/docs/plans/foresight-setting.md b/docs/plans/foresight-setting.md
deleted file mode 100644
index b735f79..0000000
--- a/docs/plans/foresight-setting.md
+++ /dev/null
@@ -1,141 +0,0 @@
-# Foresight Setting Implementation Plan
-
-## Overview
-
-Add a "foresight" setting to the Kleene gameplay framework that controls how much the game reveals when players ask for hints. This complements the existing `improvisation_temperature` and `gallery_mode` settings.
-
-## Foresight Scale
-
-| Level | Name | Behavior |
-|-------|------|----------|
-| 0 | Blind | No hints. "You'll have to discover that yourself." |
-| 1-3 | Cryptic | Atmospheric, poetic. "Treasures favor those who venture deep..." |
-| 4-6 | Suggestive | Directional nudges. "The eastern passages may reward exploration." |
-| 7-9 | Helpful | Clear guidance. "The gallery to the east contains a painting." |
-| 10 | Oracle | Full walkthrough. "Go east, take painting, return to living room." |
-
-**Default**: 5 (Suggestive) - matches the balanced default of temperature
-
-## Files to Modify
-
-### 1. `lib/framework/savegame-format.md`
-- Add `foresight` to save format settings block
-- Add backward compatibility note for v5 โ v6
-
-### 2. `commands/kleene.md`
-- Add "Foresight Actions" section (parallel to Temperature Actions)
-- Add `/kleene foresight [0-10]` command handling
-- Update Help Actions section with foresight commands
-
-### 3. `skills/kleene-play/SKILL.md`
-- Add `foresight` to game state model
-- Add hint generation rules in improvisation handling
-- Document how foresight gates hint specificity
-
-## Implementation Details
-
-### saves.md Changes
-
-Add to settings block (line ~109):
-```yaml
-settings:
- improvisation_temperature: [0-10]
- gallery_mode: [boolean]
- foresight: [0-10] # NEW - hint specificity level
-```
-
-Add backward compatibility note:
-```
-**v5 โ v6:** Saves without `foresight` field default to:
-- `foresight: 5`
-```
-
-### kleene.md Changes
-
-Add new section after Gallery Actions (~line 388):
-
-```markdown
-### Foresight Actions
-Keywords: "foresight", "hints", "help level", "guidance"
-
-**Set Foresight** (`/kleene foresight [0-10]`):
-1. Parse foresight value (0-10)
-2. Update `settings.foresight` in current game state
-3. Confirm: "Foresight set to [N] ([Name])"
-
-If no value provided, show current setting and explain scale:
-```
-Current foresight: 5 (Suggestive)
-
-Scale:
- 0 Blind - No hints given
- 1-3 Cryptic - Atmospheric, poetic hints
- 4-6 Suggestive - Directional nudges (default)
- 7-9 Helpful - Clear guidance
- 10 Oracle - Full walkthrough instructions
-
-Use: /kleene foresight [0-10]
-```
-
-**Note:** Foresight only applies during active gameplay when
-players ask questions like "where is the treasure?" or "what
-should I do?". The setting is saved with game state.
-```
-
-Update Help section (~line 517-522):
-```
- /kleene foresight Show current foresight level
- /kleene foresight [0-10] Set hint specificity:
- 0 = Blind (no hints)
- 5 = Suggestive (default)
- 10 = Oracle (full walkthrough)
-```
-
-### kleene-play/SKILL.md Changes
-
-Add to game state model (line ~219):
-```yaml
-settings:
- improvisation_temperature: number # 0-10, controls narrative adaptation
- gallery_mode: boolean # Enable meta-commentary
- foresight: number # 0-10, controls hint specificity (NEW)
-```
-
-Add hint generation rules in improvised action handling section.
-When player asks meta-questions (intent: Meta, subtype: hint_request):
-
-```markdown
-### Hint Generation (Foresight-Gated)
-
-When player asks for help/hints during improvisation (e.g., "where is
-the treasure?", "what should I do?", "how do I get past the troll?"):
-
-1. Classify as Meta intent with hint_request subtype
-2. Read `settings.foresight` value
-3. Generate hint at appropriate specificity level:
-
-| Foresight | Response Pattern |
-|-----------|------------------|
-| 0 | "You'll have to discover that yourself." (refuse hint) |
-| 1-3 | Atmospheric/poetic. Reference mood, themes, not specifics. |
-| 4-6 | Directional. Name regions/directions without exact steps. |
-| 7-9 | Clear guidance. Name specific locations and items needed. |
-| 10 | Full walkthrough. Step-by-step instructions to goal. |
-
-**Example responses for "Where can I find treasure?"**
-
-- **0**: "The adventurer must discover their own fortune."
-- **3**: "Treasures favor those who venture into the deep places..."
-- **5**: "The eastern passages and underground depths hold rewards."
-- **8**: "There's a painting in the Gallery to the east, and a bar in the Loud Room."
-- **10**: "Go east twice to the Gallery, take the painting. Then go down to the cellar, navigate past the troll, and find the platinum bar in the Loud Room."
-```
-
-## Verification
-
-1. **Manual test**: Start a new game, verify default foresight is 5
-2. **Command test**: Run `/kleene foresight` - should show current level
-3. **Command test**: Run `/kleene foresight 3` - should update and confirm
-4. **Gameplay test**: Ask "where is treasure?" at different foresight levels
-5. **Save/load test**: Change foresight, save, reload - should persist
-6. **Help test**: Run `/kleene help` - should show foresight commands
diff --git a/docs/plans/scene_tracking_ideas.md b/docs/plans/scene_tracking_ideas.md
deleted file mode 100644
index 4f2d107..0000000
--- a/docs/plans/scene_tracking_ideas.md
+++ /dev/null
@@ -1,399 +0,0 @@
-Current Problem
-
-What I tracked:
-- Turn++ only when next_node advanced
-- Turn 6 contained: cool room, intimacy, dish washing, Robyn talk, public walk, Tim confrontation, packing scene
-
-What actually happened:
-- ~8-10 distinct narrative beats
-- Multiple improvised scenes
-- Several emotional/thematic shifts
-- Many consequence applications
-
-Result in export:
-- "Turn 6 Extended: Scene 1, Scene 2, Scene 3..." (awkward)
-- Turn count (11) doesn't reflect actual gameplay density (~25-30 beats)
-- Consequence attribution unclear: "At Turn 6: +1, -1, +2" (which scene?)
-
----
-Proposed Solution: Beat Tracking
-
-Option 1: Sub-Turn Notation
-
-Turn 6.0 - Kitchen arrival (node entry)
-Turn 6.1 - Cool room scene (improvisation)
-Turn 6.2 - Intimacy (improvisation)
-Turn 6.3 - Dish washing (improvisation)
-Turn 6.4 - Robyn conversation (improvisation)
-Turn 6.5 - Public walk (player choice)
-Turn 6.6 - Tim confrontation (improvisation)
-Turn 7.0 - Next scripted node
-
-Pros:
-- Precise consequence attribution
-- Clear progression within complex turns
-- Export can show "Turn 6 had 6 beats"
-- Maintains turn structure
-
-Cons:
-- More notation complexity
-- Player sees "Turn 6.4" (may feel technical)
-
----
-Option 2: Beat Counter (Internal)
-
-turn: 6
-beat: 4
-display: "Turn 6 (Beat 4)" or just "Turn 6"
-
-Track beats internally but show simplified to player:
-- Status line: "Turn 6" (hide beat)
-- Save file: stores both turn and beat
-- Export: "Turn 6, Beat 4: Dish washing scene"
-
-Pros:
-- Clean player-facing display
-- Detailed internal tracking
-- Export gets full granularity
-
-Cons:
-- Player may not realize how much happened in one "turn"
-
----
-Option 3: Scene Tracking
-
-Turn 6, Scene A: Kitchen conversation
-Turn 6, Scene B: Cool room
-Turn 6, Scene C: Intimacy
-Turn 6, Scene D: Dish washing
-Turn 6, Scene E: Robyn discussion
-Turn 7, Scene A: Main street
-
-Pros:
-- Cinematic language (scenes, not beats)
-- Natural for narrative medium
-- Export reads like screenplay
-
-Cons:
-- "Scene" implies more structure than improv has
-- Harder to define scene boundaries algorithmically
-
----
-When to Increment Beat/Sub-turn
-
-Trigger A: After Improvised Action Resolution
-
-1. Player provides free-text input
-2. I classify intent, generate response
-3. Apply soft consequences
-4. Beat++
-5. Re-present same choices (or move to next beat)
-
-Trigger B: On Choice Presentation
-
-1. Display narrative
-2. Present AskUserQuestion
-3. Beat++ (so each choice gets numbered)
-4. Wait for response
-
-Trigger C: Scene Boundary Detection (Manual)
-
-1. I sense scene has concluded
-2. Before transitioning to new narrative context
-3. Beat++
-
-Recommendation: Combination of A + C
-- Improv resolution always increments
-- Major scene shifts increment
-- Re-presenting same choice doesn't increment
-
----
-Implementation in Game State
-
-Enhanced State Model
-
-turn: 6
-beat: 4
-major_node: "morning_with_janette"
-current_beat_type: "improvisation" # or "scripted_choice", "transition"
-
-# Beat history for this turn
-turn_6_beats:
- - beat: 1, type: "arrival", node: "morning_with_janette"
- - beat: 2, type: "improv", action: "cool room scene", consequences: {dignity: -1}
- - beat: 3, type: "improv", action: "intimacy", consequences: {janette: +5}
- - beat: 4, type: "improv", action: "dish washing", consequences: {dignity: +1}
-
-Status Line Options
-
-Verbose:
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-Tim's Kitchen | Turn 6.4
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
-Clean (my preference):
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-Tim's Kitchen | Turn 6
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-(Beat tracked internally, shown only in export)
-
-Scene-based:
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-Tim's Kitchen | Turn 6, Scene D
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
----
-Export Benefits
-
-Transcript Mode with Beat Tracking
-
-Current (coarse):
-## Turn 6: Morning with Janette
-
-[8000 words of narrative spanning multiple scenes]
-
-**Consequences:** Dignity +1, -1, +2, Janette +5, +3, +8
-
-With beats (granular):
-## Turn 6: Morning with Janette
-
-### Beat 6.1: Kitchen Conversation
-[Narrative]
-**Consequences:** Self-knowledge +1
-
-### Beat 6.2: Cool Room Scene
-[Narrative]
-**Consequences:** Dignity -1, Janette +3
-
-### Beat 6.3: Intimacy [time passes]
-**Consequences:** Janette +5
-
-### Beat 6.4: Dish Washing
-[Narrative]
-**Consequences:** Dignity +1, Self-knowledge +1
-
-### Beat 6.5: Robyn Discussion
-[Narrative]
-**Consequences:** Dignity +2, Janette +5
-
-Summary Mode with Beat Aggregation
-
-## Turn 6: Extended Intimacy (5 beats)
-
-**Beat Summary:**
-- 6.1: Kitchen conversation (Self-knowledge +1)
-- 6.2: Cool room scene (Dignity -1, Janette +3)
-- 6.3: Intimacy [time passes] (Janette +5)
-- 6.4: Dish washing (Dignity +1, Self-knowledge +1)
-- 6.5: Robyn discussion (Dignity +2, Janette +5)
-
-**Turn totals:** Dignity +2, Self-knowledge +2, Janette +13
-
-Stats Mode with Beat Granularity
-
-Turn 6 Trajectory:
- 6.0: Dignity: 2, Janette: 66
- 6.1: Dignity: 2, Janette: 66 (kitchen talk, +1 self-knowledge)
- 6.2: Dignity: 1, Janette: 69 (cool room, -1 dignity, +3 janette)
- 6.3: Dignity: 1, Janette: 74 (intimacy, +5 janette)
- 6.4: Dignity: 2, Janette: 74 (dish washing, +1 dignity, +1 self-knowledge)
- 6.5: Dignity: 4, Janette: 79 (robyn talk, +2 dignity, +5 janette)
-
----
-Save File Enhancement
-
-Current Save Format
-
-turn: 6
-current_node: morning_with_janette
-character:
- traits: {...}
-
-With Beat Tracking
-
-turn: 6
-beat: 4
-current_node: morning_with_janette
-current_beat_context: "dish washing scene - discussing honesty"
-
-# Optional: beat history for replay
-beat_log:
- - turn: 6, beat: 1, action: "kitchen conversation", consequences: {self_knowledge: +1}
- - turn: 6, beat: 2, action: "cool room innuendo", consequences: {dignity: -1, janette: +3}
- - turn: 6, beat: 3, action: "intimacy [time passes]", consequences: {janette: +5}
- - turn: 6, beat: 4, action: "dish washing", consequences: {dignity: +1, self_knowledge: +1}
-
-character:
- traits: {...}
-
-Benefits:
-- Resume knows exactly where in extended turn you were
-- Export can reconstruct full beat sequence
-- Consequence attribution precise
-
----
-Skill Modifications
-
-kleene-play Skill Changes
-
-Add to game state:
-GAME_STATE:
- turn: number # Major turn (node transitions)
- beat: number # Sub-turn (scene/improv within turn)
- beat_type: string # "arrival", "scripted_choice", "improv", "transition"
-
- # For export
- beat_history: [
- {turn: 6, beat: 1, type: "arrival", node: "morning_with_janette"},
- {turn: 6, beat: 2, type: "improv", action: "cool room", consequences: {...}},
- ...
- ]
-
-Beat increment rules:
-INCREMENT beat WHEN:
-- Improvised action resolves (free-text response complete)
-- Major scene transition detected
-- Scripted choice with improvise_context triggers
-
-DO NOT increment beat WHEN:
-- Re-presenting same choices after improv (still same beat)
-- Minor clarifications or meta-questions
-
-INCREMENT turn (and reset beat to 0) WHEN:
-- Advancing to new major node via next_node
-- Moving to scripted ending
-
-Status line display:
-if major_transition or first_turn:
- # Cinematic header with turn only
- display(f"Turn {turn}")
-else:
- # Normal header with turn only
- display(f"Location | Turn {turn}")
-
-# Beat hidden from player, tracked internally
-# Save file stores both: turn=6, beat=4
-
----
-Export Command Enhancement
-
-Transcript Mode
-
-/kleene export --mode=transcript
-
-# With granularity options:
-/kleene export --mode=transcript --granularity=turn # default, one section per major turn
-/kleene export --mode=transcript --granularity=beat # one section per beat
-/kleene export --mode=transcript --granularity=scene # group beats into scenes
-
-Output examples:
-
-Turn granularity (current):
-## Turn 6: Morning with Janette
-[All beats combined in one section]
-
-Beat granularity (new):
-## Turn 6: Morning with Janette
-
-### Turn 6.1: Kitchen Conversation
-[Beat 1 content]
-
-### Turn 6.2: Cool Room Scene
-[Beat 2 content]
-
-Scene granularity (smart grouping):
-## Turn 6: Morning with Janette
-
-### Scene A: Private Intimacy (Beats 1-3)
-[Kitchen, cool room, intimacy combined]
-
-### Scene B: Honest Conversations (Beats 4-5)
-[Dish washing, Robyn talk combined]
-
----
-Practical Example: Kitchen Branch Turn 6
-
-What Actually Happened (25 minutes of gameplay):
-
-1. Kitchen conversation about thinking too much
-2. Innuendo โ return to bedroom [time passes]
-3. Post-intimacy vulnerability
-4. "Tell me about Sydney" โ Glebe flat description
-5. Truth about Robyn with Janette present
-6. Decision to walk publicly
-7. Main street walk hand-in-hand
-8. Tim confrontation on street
-9. Return to pack belongings
-10. Tim drunk return, kitchen confrontation
-
-Current Tracking:
-
-Turn 6 (all of the above)
-Turn 7 (next node)
-
-With Beat Tracking:
-
-Turn 6.0 - Node arrival (kitchen)
-Turn 6.1 - Kitchen conversation (improv)
-Turn 6.2 - Bedroom return (improv)
-Turn 6.3 - Tell me about Sydney (improv)
-Turn 6.4 - Robyn truth (improv)
-Turn 6.5 - Walk publicly (scripted choice โ triggers transition)
-Turn 6.6 - Main street (improv/triggered scene)
-Turn 6.7 - Tim street confrontation (improv)
-Turn 6.8 - Pack belongings (improv)
-Turn 6.9 - Tim kitchen confrontation (improv)
-Turn 6.10 - Decision point (scripted choice โ next_node)
-Turn 7.0 - Hotel (new major node)
-
-Export Would Show:
-
-## Turn 6: Extended Intimacy and Confrontation (10 beats)
-
-### Beat 6.1: Kitchen Philosophy
-Janette: "You're different when you stop thinking."
-**Consequences:** None (setup)
-
-### Beat 6.2: Return to Bedroom
-[Tasteful fade] *[Time passes]*
-**Consequences:** Self-knowledge +1, Janette +5
-
-### Beat 6.3: "Tell Me About Sydney"
-John describes Glebe flat honestly - peeling paint, thin walls, but real.
-**Consequences:** Dignity +1, Self-knowledge +1, Janette +5
-
-### Beat 6.4: The Truth About Robyn
-Discussed with Janette present. Full transparency.
-**Consequences:** Dignity +2, Self-knowledge +2, Janette +5
-
-... [continues for all 10 beats]
-
-**Turn 6 Totals:** Dignity +4, Self-knowledge +6, Janette +25, Tim -25
-
----
-Recommendation
-
-Implement beat tracking with:
-1. Internal counter (turn.beat format)
-2. Player-facing display: Show only major turn (hide beat)
-3. Save file: Store both turn and beat
-4. Beat increment triggers:
- - After improvised action resolution
- - On major scene transitions
- - Reset to 0 when advancing major turn
-5. Export granularity options:
- - --granularity=turn (default, groups beats)
- - --granularity=beat (shows every beat)
- - --granularity=scene (smart grouping of related beats)
-
-Benefits:
-- Precise consequence attribution
-- Better session replay
-- Clearer exports
-- No player-facing complexity (hidden unless exporting)
-- Enables future features (resume mid-turn, beat-level save states)
-
-Minimal disruption:
-- Player experience unchanged (still sees "Turn 6")
-- Only export and save file formats enhanced
-- Backwards compatible (old saves just have beat=0)
diff --git a/docs/plans/yq-scenario-optimization.md b/docs/plans/yq-scenario-optimization.md
deleted file mode 100644
index 428e1f1..0000000
--- a/docs/plans/yq-scenario-optimization.md
+++ /dev/null
@@ -1,621 +0,0 @@
-# Investigation: yq for Scenario Reading in Kleene
-
-## Current Approach (kleene-play SKILL.md lines 32-79)
-
-**Standard Load (small scenarios):**
-- Read entire file, cache in context
-- Works well for scenarios under ~20k tokens
-
-**Lazy Load (large scenarios):**
-1. Read first 200 lines for header data
-2. Extract: name, initial_character, initial_world, start_node, endings
-3. On each turn, use Grep: `"^ {node_id}:" -A 80` to fetch node content
-4. Parse YAML from grep output
-
-## Problems with Current Lazy Load
-
-| Issue | Impact |
-|-------|--------|
-| Fixed `-A 80` context | Truncates long nodes, includes extra content for short ones |
-| Grep doesn't parse YAML | Fragile for nested structures, multiline strings |
-| 200-line header read | Wastes tokens if only specific fields needed |
-| Manual YAML parsing | Error-prone when extracting from grep output |
-
-## The yq Alternative
-
-The patterns from hiivmind-pulse-gh show a tiered approach:
-
-```
-yq (preferred) โ python+pyyaml (fallback) โ grep (fragile)
-```
-
-**Surgical extraction examples:**
-```bash
-# Header fields (instead of reading 200 lines)
-yq '.name' scenario.yaml # Just title
-yq '.initial_character' scenario.yaml # Just character init
-yq '.start_node' scenario.yaml # Just start node name
-yq '.endings | keys' scenario.yaml # Just ending IDs
-
-# Node loading (instead of grep -A 80)
-yq '.nodes.dragon_fight' scenario.yaml # Exact node content
-yq '.nodes.dragon_fight.choice.options' scenario.yaml # Just options
-```
-
-## Token Usage Comparison
-
-**Scenario: dragon_quest.yaml (~400 lines)**
-
-| Operation | Current Approach | yq Approach | Savings |
-|-----------|-----------------|-------------|---------|
-| Load header | 200 lines (~4k tokens) | ~50 lines (~1k tokens) | ~75% |
-| Load node | 80 lines (~1.5k tokens) | 10-30 lines (~0.5k tokens) | ~67% |
-| Per-turn overhead | High (grep output includes noise) | Low (exact extraction) | Significant |
-
-**For a 10-turn game session:**
-- Current: ~200 + (10 ร 80) = 1,000 lines processed
-- yq: ~50 + (10 ร 20) = 250 lines processed
-- **~75% reduction in token consumption**
-
-## Speed Comparison
-
-| Metric | Grep | yq |
-|--------|------|-----|
-| Parse correctness | Fragile | Reliable |
-| Nested data access | Manual parsing required | Native support |
-| Shell overhead | One process | One process |
-| Execution time | ~Equal | ~Equal |
-
-Speed is comparable, but **yq is more reliable** for YAML structures.
-
-## Recommendation: Yes, Adopt yq
-
-**Why it's a good enhancement:**
-
-1. **Token savings are significant** (~75% for lazy loading)
-2. **More reliable** - yq understands YAML, grep doesn't
-3. **Pattern already exists** - tool-detection.md and config-parsing.md provide templates
-4. **Graceful degradation** - fallback to current approach if yq unavailable
-
-## Implementation Approach
-
-### Phase 1: Add Tool Detection Pattern
-
-Create `lib/patterns/tool-detection.md` (adapt from hiivmind-pulse-gh):
-- Detect yq availability: `command -v yq && yq --version`
-- Detect python+pyyaml: `python3 -c "import yaml"`
-- Set capability flag in context for session
-
-### Phase 2: Add YAML Extraction Pattern
-
-Create `lib/patterns/yaml-extraction.md`:
-- yq commands for header fields
-- yq commands for node extraction
-- Python fallback equivalents
-- Grep fallback (preserve current approach as last resort)
-
-### Phase 3: Update kleene-play Lazy Loading
-
-Modify `skills/kleene-play/SKILL.md` lines 40-79:
-
-**Instead of:**
-```
-Read scenario file with limit: 200
-```
-
-**Use:**
-```
-# If yq available:
-yq '.name, .initial_character, .initial_world, .start_node' scenario.yaml
-
-# If python+pyyaml available:
-python3 -c "import yaml; d=yaml.safe_load(open('scenario.yaml')); print(yaml.dump({k:d[k] for k in ['name','initial_character','initial_world','start_node']}))"
-
-# Fallback: current 200-line read
-```
-
-**Instead of:**
-```
-Grep for "^ {node_id}:" with -A 80
-```
-
-**Use:**
-```
-# If yq available:
-yq '.nodes.{node_id}' scenario.yaml
-
-# If python+pyyaml available:
-python3 -c "import yaml; print(yaml.dump(yaml.safe_load(open('scenario.yaml'))['nodes']['{node_id}']))"
-
-# Fallback: current grep approach
-```
-
-### Phase 4: Update kleene-analyze
-
-Similar changes for full scenario analysis - yq can extract just the fields needed for graph building.
-
-## Files to Modify
-
-| File | Changes |
-|------|---------|
-| `lib/patterns/tool-detection.md` | New - adapt from pulse-gh |
-| `lib/patterns/yaml-extraction.md` | New - scenario-specific patterns |
-| `skills/kleene-play/SKILL.md` | Update lazy loading section |
-| `skills/kleene-analyze/SKILL.md` | Update scenario loading |
-| `commands/kleene.md` | Add tool detection at session start |
-
-## Advanced yq Patterns: Tested and Validated
-
-yq 4.x can do graph-like traversals, not just single-field extraction. Here are tested patterns:
-
-### Pattern 1: Full Turn Context (Single Query)
-
-Get current node + all options with preconditions + metadata for presentation:
-
-```bash
-yq '
- .nodes.mountain_approach as $n |
- .nodes as $all |
- {
- "node_id": "mountain_approach",
- "narrative": $n.narrative,
- "prompt": $n.choice.prompt,
- "options": [
- $n.choice.options[] |
- {
- "id": .id,
- "text": .text,
- "cell": .cell,
- "precondition": .precondition,
- "next_node": .next_node,
- "has_improvise": (.next == "improvise")
- }
- ]
- }
-' scenario.yaml
-```
-
-**Output**: Structured JSON with everything needed for a game turn - narrative, prompt, all options with preconditions and destinations.
-
-### Pattern 2: Destination Preview (Multi-Hop)
-
-Get options with preview of destination narratives:
-
-```bash
-yq '
- .nodes as $all |
- .nodes.mountain_approach.choice.options[] |
- select(.next_node) |
- {"option_text": .text, "dest_id": .next_node, "dest_preview": ($all[.next_node].narrative | split("\n")[0])}
-' scenario.yaml
-```
-
-**Output**:
-```
-option_text: "Draw your sword and fight!"
-dest_id: dragon_fight
-dest_preview: The battle is fierce. Fire and steel clash in the mountain air.
-```
-
-### Pattern 3: Graph Analysis - Item Dependencies
-
-Find all nodes requiring a specific item:
-
-```bash
-yq '
- .nodes | to_entries | .[] |
- select(.value.choice.options[].precondition.item == "rusty_sword") |
- {"node": .key, "requires": "rusty_sword"}
-' scenario.yaml
-```
-
-**Output**:
-```
-node: mountain_approach
-node: dragon_notices_patience
-node: dragon_cornered
-```
-
-### Pattern 4: Cell Coverage Analysis
-
-Find all nodes containing Unknown cell options:
-
-```bash
-yq '
- .nodes | to_entries | .[] |
- select(.value.choice.options[] | select(.cell == "unknown")) |
- .key
-' scenario.yaml
-```
-
-**Output**: `intro`, `mountain_approach`
-
-### Pattern 5: Improvise Outcome Traversal
-
-Get improvise option outcomes, then fetch all outcome nodes:
-
-```bash
-yq '
- .nodes.intro.choice.options[] |
- select(.next == "improvise") |
- .outcome_nodes | to_entries | .[].value
-' scenario.yaml | xargs -I{} yq '.nodes.{}' scenario.yaml
-```
-
-**Output**: Full content of `elder_lore` AND `elder_silence` nodes.
-
-### Pattern 6: Minimal Extraction
-
-Just ending IDs (4 lines vs 60+ lines for full endings):
-
-```bash
-yq '.endings | keys' scenario.yaml
-```
-
-**Output**: `["ending_victory", "ending_death", "ending_transcendence", "ending_fled"]`
-
----
-
-## Comparison: Token Usage by Query Type
-
-| Query Type | grep -A 80 | yq Templated |
-|------------|------------|--------------|
-| Single node | ~80 lines | ~15-30 lines |
-| Turn context | ~80 lines + manual parsing | ~40 lines, structured |
-| Node + destinations | Multiple queries | Single query |
-| Item dependency graph | Impossible | One query scans all nodes |
-| Ending IDs | ~60 lines | 4 lines |
-| Cell coverage | Manual grep + parsing | One query |
-
-**Key insight**: yq enables queries that are *impossible* with grep - like finding all nodes requiring a specific item, or getting destination previews.
-
----
-
-## Use Cases by Skill
-
-### kleene-play (Gameplay)
-
-| Phase | Operation | Current | yq Pattern | Benefit |
-|-------|-----------|---------|------------|---------|
-| **Init** | Load header | Read 200 lines | `yq '.name, .initial_character, .initial_world, .start_node'` | ~75% token reduction |
-| **Init** | Get ending IDs | Read full endings section | `yq '.endings | keys'` | 4 lines vs 60+ |
-| **Turn** | Get current node | `grep -A 80` | `yq '.nodes.{node_id}'` | Exact extraction, no overflow |
-| **Turn** | Check ending | String match on cached list | Same yq query at init | Already have data |
-| **Improvise** | Prefetch outcomes | Multiple greps | `yq '.nodes.{discovery}, .nodes.{constraint}'` | One query for both nodes |
-
-**Per-session overhead:**
-- Init: 1 yq query (header + ending IDs)
-- Per turn: 1 yq query (current node)
-- Improvise: 1 yq query (prefetch outcome nodes)
-
-### kleene-analyze (Analysis/Validation)
-
-| Step | Operation | Current | yq Pattern | Benefit |
-|------|-----------|---------|------------|---------|
-| **Graph Build** | Get node connections | Parse full YAML | `yq '.nodes | to_entries | .[] | {key: .key, dests: [.value.choice.options[].next_node]}'` | Structure only, skip narratives |
-| **Cell Coverage** | Find cell-tagged options | Manual search | `yq '.nodes | .. | select(.cell == "chooses")'` | One query scans all |
-| **Unknown Detection** | Find improvise options | Manual search | `yq '.nodes | .. | select(.next == "improvise")'` | One query |
-| **Ending Types** | Classify endings | Read full endings | `yq '.endings | to_entries | .[] | {id: .key, type: .value.type}'` | Types only, skip narratives |
-| **Precondition Map** | Item dependencies | **Impossible** | `yq '.nodes | .. | select(.precondition.item == "rusty_sword")'` | **New capability** |
-| **Reachability** | Find orphan nodes | BFS after full load | Graph query + set difference | Structural analysis |
-
-**Key insight**: kleene-analyze benefits most from yq - many analysis queries are *impossible* with grep.
-
-### kleene-generate (Scenario Generator)
-
-| Step | Operation | Current | yq Pattern | Benefit |
-|------|-----------|---------|------------|---------|
-| **Register** | Extract metadata | Read full file | `yq '.name, .description'` | 2 fields only |
-| **Register** | Load registry | Read full registry | `yq '.scenarios | keys'` | Just scenario list |
-| **Branch Expand** | Find gaps | Call kleene-analyze | Use analyze patterns | Same yq benefits |
-| **Tier Check** | Verify coverage | Manual check | Cell coverage query | One query |
-
----
-
-## Implementation: Query Templates
-
-### For kleene-play
-
-**Template: Game Initialization**
-```bash
-yq '
- {
- "name": .name,
- "start_node": .start_node,
- "initial_character": .initial_character,
- "initial_world": .initial_world,
- "ending_ids": [.endings | keys | .[]]
- }
-' scenario.yaml
-```
-
-**Template: Turn Context**
-```bash
-yq '
- .nodes.{NODE_ID} as $n |
- {
- "narrative": $n.narrative,
- "prompt": $n.choice.prompt,
- "options": [
- $n.choice.options[] |
- {
- "id": .id,
- "text": .text,
- "precondition": .precondition,
- "next_node": .next_node,
- "has_improvise": (.next == "improvise"),
- "outcome_nodes": .outcome_nodes
- }
- ]
- }
-' scenario.yaml
-```
-
-**Template: Improvise Prefetch**
-```bash
-yq '
- .nodes as $all |
- .nodes.{NODE_ID}.choice.options[] |
- select(.next == "improvise") |
- .outcome_nodes | to_entries | .[] |
- {"cell": .key, "node": $all[.value]}
-' scenario.yaml
-```
-
-### For kleene-analyze
-
-**Template: Graph Structure (no narratives)**
-```bash
-yq '
- .nodes | to_entries | .[] |
- {
- "node": .key,
- "options": [.value.choice.options[] | {
- "id": .id,
- "cell": .cell,
- "next": (.next_node // .next),
- "precondition": .precondition
- }]
- }
-' scenario.yaml
-```
-
-**Template: Cell Coverage Report**
-```bash
-yq '
- [.nodes | to_entries | .[] | .value.choice.options[] | select(.cell)] |
- group_by(.cell) |
- .[] | {cell: .[0].cell, count: length}
-' scenario.yaml
-```
-
-**Template: Precondition Dependency Map**
-```bash
-yq '
- .nodes | to_entries | .[] |
- .value.choice.options[] |
- select(.precondition) |
- {
- "node": (parent | parent | parent | .key),
- "option": .id,
- "requires": .precondition
- }
-' scenario.yaml
-```
-
-### For kleene-generate
-
-**Template: Registration Metadata**
-```bash
-yq '{name: .name, description: .description}' scenario.yaml
-```
-
-**Template: Registry Update**
-```bash
-yq -i '.scenarios.{ID} = {
- "name": "Title",
- "description": "Desc",
- "path": "file.yaml",
- "enabled": true,
- "tags": ["generated"]
-}' registry.yaml
-```
-
----
-
-## Revised Recommendation
-
-**Yes, adopt yq** - not just for token savings, but for capabilities:
-
-1. **Surgical extraction** when you need minimal data
-2. **Expansive queries** when you need graph traversal
-3. **Structured output** eliminates parsing errors
-4. **Analysis patterns** that grep cannot do
-
----
-
-## Gateway Command Optimizations (commands/kleene.md)
-
-### Registry Operations
-
-| Operation | Current | yq Pattern | Benefit |
-|-----------|---------|------------|---------|
-| **List enabled scenarios** | Read full registry | `yq '.scenarios | to_entries | .[] | select(.value.enabled) | {id: .key, name: .value.name}'` | Just IDs and names |
-| **Get scenario paths** | Parse full registry | `yq '.scenarios | to_entries | .[] | .value.path'` | For unregistered check |
-| **Sync metadata** | Read full scenario | `yq '{name: .name, description: .description}'` | 2 fields only |
-| **Build menu** | Multiple full reads | One registry query + yq per new scenario | Minimal reads |
-
-**Template: Scenario Menu Data**
-```bash
-yq '
- .scenarios | to_entries | .[] |
- select(.value.enabled) |
- {
- "id": .key,
- "name": .value.name,
- "description": .value.description,
- "path": .value.path
- }
-' registry.yaml
-```
-
-### Save Listing Operations
-
-| Operation | Current | yq Pattern | Benefit |
-|-----------|---------|------------|---------|
-| **List saves** | Read each file | `yq '{turn: .turn, node: .current_node, saved: .last_saved}'` per file | Just metadata |
-| **Sort by date** | Parse each file | Single query with dates | Already sorted |
-
-**Template: Save Metadata Batch**
-```bash
-for f in ./saves/dragon_quest/*.yaml; do
- yq --arg file "$f" '{
- "file": $file,
- "turn": .turn,
- "node": .current_node,
- "saved": .last_saved
- }' "$f"
-done
-```
-
----
-
-## Save File Enhancements
-
-### Current Save Format
-```yaml
-current_node: intro
-turn: 0
-# ... no node context cached
-```
-
-### Enhanced Save Format (with co-reference caching)
-```yaml
-current_node: intro
-current_node_title: "Village Crossroads" # NEW: from node.title or generated
-current_node_preview: "The village elder grips..." # NEW: first line of narrative
-turn: 0
-# ... rest of state
-```
-
-**Benefits:**
-1. **Rich save listings** without loading scenario files
-2. **Resume preview** shows context without scenario load
-3. **Co-references** between saves and scenario nodes
-
-**Template: Extract Node Preview for Caching**
-```bash
-yq '
- .nodes.intro |
- {
- "title": (.title // "Node: intro"),
- "preview": (.narrative | split("\n") | .[0])
- }
-' scenario.yaml
-```
-
-### Enhanced Save Writing
-
-When saving game state:
-```bash
-yq '
- .nodes[$NODE_ID] |
- {
- "title": (.title // ("Node: " + $NODE_ID)),
- "preview": (.narrative | split("\n") | map(select(. != "")) | .[0])
- }
-' --arg NODE_ID "$current_node" scenario.yaml
-```
-
-This extracts node metadata at save time, caching it in the save file.
-
----
-
-## Registry Enhancements
-
-### Current Registry Entry
-```yaml
-dragon_quest:
- name: "The Dragon's Choice"
- description: "Face the dragon..."
- path: dragon_quest.yaml
- enabled: true
-```
-
-### Enhanced Registry Entry (with scenario stats)
-```yaml
-dragon_quest:
- name: "The Dragon's Choice"
- description: "Face the dragon..."
- path: dragon_quest.yaml
- enabled: true
- # NEW: Cached stats for rich menus
- stats:
- node_count: 18
- ending_count: 4
- tier: "Silver"
- cells_covered: ["triumph", "rebuff", "escape", "discovery"]
-```
-
-**Benefits:**
-1. **Tier badges** in scenario menu without loading scenarios
-2. **Completion indicators** showing scenario complexity
-3. **Cell coverage preview** for players choosing scenarios
-
-**Template: Extract Scenario Stats for Registry**
-```bash
-yq '
- {
- "node_count": (.nodes | length),
- "ending_count": (.endings | length),
- "cells": [.nodes | .. | select(.cell) | .cell] | unique
- }
-' scenario.yaml
-```
-
----
-
-## Implementation Summary
-
-### Phase 1: Core yq Patterns
-1. Add `lib/patterns/tool-detection.md` (adapt from pulse-gh)
-2. Add `lib/patterns/yaml-extraction.md` (kleene-specific templates)
-
-### Phase 2: kleene-play Updates
-1. Update lazy loading to use yq
-2. Add node prefetching for improvise outcomes
-3. Cache node metadata in saves
-
-### Phase 3: kleene-analyze Updates
-1. Use graph structure queries (no narratives)
-2. Add cell coverage and precondition queries
-
-### Phase 4: Gateway Command Updates
-1. yq for registry operations
-2. yq for save listing
-3. Enhanced save format with node previews
-
-### Phase 5: Registry Enhancements
-1. Cache scenario stats during sync
-2. Show tier badges in menus
-
----
-
-## Verification
-
-1. **Tool availability**: Run `yq --version` at session start, set capability flag
-2. **Pattern testing**: Test each template against `dragon_quest.yaml`
-3. **Fallback chain**: Verify python+pyyaml fallback works when yq unavailable
-4. **Save compatibility**: Ensure old saves load without cached node metadata (graceful degradation)
-
----
-
-## Open Questions
-
-1. **yq version requirement**: Require yq 4.x (Mike Farah's Go version)? The Python yq is a different project with different syntax.
-
-2. **Save format migration**: Auto-upgrade old saves to add cached node metadata on load, or leave as-is?
-
-3. **Registry stats caching**: Cache tier/cell data during sync, or calculate on-demand?
-
-4. **Error handling**: If yq parsing fails mid-game, auto-fallback to grep or abort and report?
diff --git a/lib/framework/scenario-file-loading/overview.md b/lib/framework/scenario-file-loading/overview.md
index 29be7a4..f48a8d2 100644
--- a/lib/framework/scenario-file-loading/overview.md
+++ b/lib/framework/scenario-file-loading/overview.md
@@ -8,20 +8,28 @@ This folder consolidates all scenario loading protocols for the Kleene engine.
|------|-----------|----------|
| **Standard** | Files under ~20k tokens | Read entire file, cache in context |
| **Lazy** | Files exceeding token limit | Load header once, fetch nodes on demand |
+| **Remote** | Server-hosted scenarios | Fetch nodes via HTTP API from kleene-server |
## Decision Flow
```
-1. Detect yaml_tool at session start
- โโ See tool-detection.md
-
-2. Attempt full Read of scenario file
+1. Check for server configuration
โ
- โโ SUCCESS โ Standard mode
- โ โโ See standard-loading.md
+ โโ SERVER URL configured + scenario ID provided โ Remote mode
+ โ โโ See remote-loading.md
โ
- โโ TOKEN LIMIT ERROR โ Lazy mode
- โโ See lazy-loading.md
+ โโ LOCAL FILE available
+ โ
+ โโ Detect yaml_tool at session start
+ โ โโ See tool-detection.md
+ โ
+ โโ Attempt full Read of scenario file
+ โ โ
+ โ โโ SUCCESS โ Standard mode
+ โ โ โโ See standard-loading.md
+ โ โ
+ โ โโ TOKEN LIMIT ERROR โ Lazy mode
+ โ โโ See lazy-loading.md
```
## Tool Detection
@@ -46,6 +54,7 @@ Set capability flag:
| `tool-detection.md` | yq availability detection |
| `standard-loading.md` | Full file read protocol |
| `lazy-loading.md` | On-demand node loading protocol |
+| `remote-loading.md` | HTTP API loading protocol (kleene-server) |
| `extraction-templates.md` | yq/grep templates for gameplay |
## Related Files
diff --git a/lib/framework/scenario-file-loading/remote-loading.md b/lib/framework/scenario-file-loading/remote-loading.md
new file mode 100644
index 0000000..3b6d5b1
--- /dev/null
+++ b/lib/framework/scenario-file-loading/remote-loading.md
@@ -0,0 +1,200 @@
+# Remote Loading for Server-Hosted Scenarios
+
+When a scenario is hosted on a kleene-server instance (local proxy or remote), use remote loading mode. This replaces yq/grep with HTTP API calls โ same data, different transport.
+
+> **Tool Detection:** See `overview.md` for mode selection.
+> **Comparison:** See `lazy-loading.md` for the pattern this replaces.
+
+## When to Use Remote Mode
+
+Remote loading activates when:
+- A server URL is configured (via `/kleene server` or environment)
+- The scenario is identified by ID (not a local file path)
+- The local proxy is running at `localhost:8420`
+
+## Load Header
+
+```
+GET /api/scenario/{scenario_id}/header
+```
+
+Returns:
+```json
+{
+ "name": "The Dragon's Choice",
+ "description": "A hero must decide...",
+ "start_node": "intro",
+ "initial_character": { "traits": { "courage": 5, ... }, ... },
+ "initial_world": { "current_location": "village", ... },
+ "ending_ids": ["ending_victory", "ending_death", ...],
+ "travel_config": { ... }
+}
+```
+
+**Equivalent lazy-loading command:**
+```bash
+yq '{"name": .name, "start_node": .start_node, ...}' scenario.yaml
+```
+
+## Load Nodes on Demand
+
+```
+GET /api/scenario/{scenario_id}/node/{node_id}
+```
+
+Returns the full node definition including narrative, choice, options with preconditions, consequences, and improvise contexts.
+
+```json
+{
+ "id": "intro",
+ "narrative": "The village elder grips your arm...",
+ "choice": {
+ "prompt": "What do you do?",
+ "options": [
+ {
+ "id": "seek_knowledge",
+ "text": "Enter the dark forest",
+ "cell": "chooses",
+ "consequence": [...],
+ "next_node": "forest_entrance"
+ },
+ ...
+ ]
+ }
+}
+```
+
+**Equivalent lazy-loading command:**
+```bash
+yq '.nodes.intro' scenario.yaml
+```
+
+## Load Endings
+
+```
+GET /api/scenario/{scenario_id}/ending/{ending_id}
+```
+
+Returns:
+```json
+{
+ "id": "ending_victory",
+ "narrative": "VICTORY\n\nThe village celebrates...",
+ "type": "victory"
+}
+```
+
+## Load Locations
+
+```
+GET /api/scenario/{scenario_id}/locations
+```
+
+Returns array of location definitions with connections.
+
+## State Synchronization
+
+After each turn, the LLM pushes current game state to the server:
+
+```
+PUT /api/game/{session_id}/state
+Body: { "state": { ...full game state... } }
+```
+
+This enables:
+- Web UI to display stats, inventory, choices, position
+- Server-side persistence across sessions
+- Shared world state in multiplayer mode
+
+## Narrative Relay
+
+After generating narrative output, push it to the server for web UI display:
+
+```
+PUT /api/game/{session_id}/narrative
+Body: { "narrative": "The rendered narrative text..." }
+```
+
+## Cell Reporting
+
+When a Decision Grid cell is hit (from option's `cell` annotation or improvisation classification):
+
+```
+POST /api/game/{session_id}/cell
+Body: { "cell_type": "triumph", "node_id": "dragon_fight" }
+```
+
+## Settings Polling
+
+Before each turn, check if the player adjusted settings via the web UI:
+
+```
+GET /api/game/{session_id}/settings
+```
+
+Returns:
+```json
+{
+ "improvisation_temperature": 7,
+ "gallery_mode": false,
+ "foresight": 5,
+ "parser_mode": false
+}
+```
+
+Update in-context settings if they differ from current values.
+
+## Choice Input via Web UI
+
+The player can click choice buttons in the web UI instead of using AskUserQuestion:
+
+```
+GET /api/game/{session_id}/choice
+```
+
+Returns `{"choice": "seek_knowledge"}` if a choice was submitted, or `{"choice": null}` if not.
+
+When a choice is available from the web UI, use it instead of presenting AskUserQuestion.
+
+## Save/Load via Server
+
+```
+POST /api/game/{session_id}/save
+Body: { "name": "Before dragon fight" }
+โ { "save_id": "abc123" }
+
+POST /api/game/load/{save_id}?session_id={session_id}
+โ { "state": {...}, "scenario_id": "dragon_quest" }
+```
+
+## Cache Strategy
+
+- Header data: persistent (cached in context after first fetch)
+- Current node: replaced each turn (fetch fresh via API)
+- Endings: persistent (cached after first access)
+- Settings: polled each turn (may change via web UI)
+
+## Error Handling
+
+If the server is unreachable:
+1. Log warning but don't interrupt gameplay
+2. Fall back to in-context state (LLM still has full state)
+3. Retry on next turn
+4. If server remains down for 3+ turns, inform player
+
+If a node is not found (404):
+1. Report error in narrative: "Path not found in scenario data"
+2. Return to previous node
+3. Do NOT increment turn counter
+
+## Comparison: Three Loading Modes
+
+| Aspect | Standard | Lazy | Remote |
+|--------|----------|------|--------|
+| Source | Full file in context | yq/grep on filesystem | HTTP API calls |
+| Header | From cached file | yq header extraction | `GET /header` |
+| Node | From cached file | yq node extraction | `GET /node/{id}` |
+| State | LLM context only | LLM context only | LLM context + server sync |
+| Saves | Write to `./saves/` | Write to `./saves/` | `POST /save` via API |
+| Settings | In-context | In-context | Polled from server |
+| Web UI | None | None | State relay enabled |
diff --git a/scenarios/registry.yaml b/scenarios/registry.yaml
index f83eb83..692aee9 100644
--- a/scenarios/registry.yaml
+++ b/scenarios/registry.yaml
@@ -3,7 +3,7 @@
# Run /kleene sync to update after adding/removing scenario files
version: 1
-last_synced: 2026-01-19T12:00:00Z
+last_synced: 2026-02-17T12:00:00Z
scenarios:
dragon_quest:
@@ -16,6 +16,12 @@ scenarios:
estimated_playtime: "15-20"
content_warnings: []
+ the_yabba:
+ name: "The Yabba"
+ description: "A schoolteacher stranded in an outback mining town descends into a nightmare of hospitality, gambling, and self-destruction."
+ path: the_yabba.yaml
+ enabled: true
+ tags: ["discovered"]
zork1-mini:
name: "Zork I: The Great Underground Empire - Mini"
description: "Explore the ruins of an ancient empire, collect treasures, and survive the perils of the Great Underground Empire."
diff --git a/scenarios/the_yabba.yaml b/scenarios/the_yabba.yaml
new file mode 100644
index 0000000..62f238e
--- /dev/null
+++ b/scenarios/the_yabba.yaml
@@ -0,0 +1,6309 @@
+name: "The Yabba"
+description: "A schoolteacher stranded in an outback mining town descends into a nightmare of hospitality, gambling, and self-destruction."
+version: "1.0.0"
+
+# Inspired by the 1971 Australian psychological thriller
+# Themes: masculinity, class, isolation, self-destruction, redemption
+
+initial_character:
+ name: "John Grant"
+ traits:
+ dignity: 10 # Self-respect and composure
+ sobriety: 10 # Physical and mental clarity
+ money: 8 # Savings (in abstract units)
+ desperation: 0 # How trapped/reckless you feel
+ self_knowledge: 2 # Understanding of yourself
+ inventory: []
+ relationships:
+ doc: 0
+ jock: 0
+ janette: 0
+ tim: 0
+ flags:
+ gambled: false
+ lost_everything: false
+ been_to_docs_shack: false
+ went_on_hunt: false
+ rejected_janette: false
+ rejected_doc: false
+ tried_to_leave: false
+ has_rifle: false
+ acknowledged_man_on_train: false
+ met_doc_early: false
+
+initial_world:
+ current_location: "railway_station"
+ time: 0
+ flags:
+ christmas_heat: true
+ escape_possible: true
+ locations:
+ - id: railway_station
+ name: "Bundanyabba Railway Station"
+ description: "A dusty platform baking in the December heat."
+ connections: [main_street]
+ - id: main_street
+ name: "Main Street"
+ description: "The commercial heart of The Yabba. Pubs everywhere."
+ connections: [railway_station, pub, hotel]
+ - id: pub
+ name: "The Royal Hotel"
+ description: "Dark wood, ceiling fans, and the smell of stale beer."
+ connections: [main_street, rsl_club, two_up_ring]
+ - id: hotel
+ name: "Budget Hotel"
+ description: "Your room for the night. Sparse but functional."
+ connections: [main_street]
+ - id: rsl_club
+ name: "RSL Club"
+ description: "A veterans' club with a disconcerting memorial service."
+ connections: [pub]
+ - id: two_up_ring
+ name: "Two-Up Ring"
+ description: "An underground gambling den. The kip spins in the air."
+ connections: [pub]
+ - id: tims_house
+ name: "Tim Hynes' House"
+ description: "A weatherboard house on the outskirts. Always someone drinking."
+ connections: [main_street]
+ - id: docs_shack
+ name: "Doc's Shack"
+ description: "A tin shed in the scrubland. Books, bottles, and kangaroo meat."
+ connections: [outback]
+ - id: outback
+ name: "The Outback"
+ description: "Red earth stretching to infinity. The sun is merciless."
+ connections: [docs_shack, main_street, railway_station]
+
+start_node: arrival
+
+nodes:
+ arrival:
+ narrative: |
+ The train shudders to a halt at Bundanyabba. You step onto the platform into a wall of heat โ forty bloody degrees, easy.
+ The air shimmers above the tracks like a fever dream.
+
+ You're a schoolteacher from Sydney, bonded to two years at a remote outback school in Tiboonda. This is supposed to be a quick stopover before your flight home for Christmas. Your girlfriend Robyn is waiting. Your real life is waiting.
+ You've got enough quid for the flight and a bit extra. One night in this mining town, then freedom.
+
+ The locals call this place "The Yabba." They say it with affection, like a nickname for an old mate. Everyone reckons it's the best little place on earth.
+
+ As you disembark, you notice an Aboriginal man sitting alone at the front of the carriage, gazing out the window at the red earth. He hasn't moved. The white passengers โ miners, mostly, already drunk โ push past without acknowledging him.
+ The Aboriginal man catches your eye through the window. Just for a moment.
+
+ choice:
+ prompt: "The heat is oppressive. The platform is emptying."
+ options:
+ - id: find_hotel
+ text: "Check into the hotel and stay in your room"
+ cell: avoids
+ consequence:
+ - type: move_to
+ location: hotel
+ narrative: "You'll wait out the heat in your room. Sensible."
+ next_node: hotel_boredom
+
+ - id: find_pub
+ text: "Find a pub โ you could use a cold beer"
+ cell: chooses
+ consequence:
+ - type: move_to
+ location: pub
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ narrative: "One beer won't hurt. You follow the signs to the Royal Hotel."
+ next_node: first_drink
+
+ - id: explore_town
+ text: "Walk around town first โ get your bearings"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 1
+ narrative: "You want to understand this place before you judge it."
+ next_node: town_exploration
+
+ - id: acknowledge_man
+ text: "Nod to the Aboriginal man before leaving the station."
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: set_flag
+ flag: acknowledged_man_on_train
+ value: true
+ narrative: "He sees you nod, then looks away, back at the land that was once his and his ancestors' before him. Always was. Always will be."
+ next_node: town_exploration
+
+ - id: observe_station
+ text: "Linger at the station โ take in your surroundings before committing"
+ cell: unknown
+ next: improvise
+ improvise_context:
+ theme: "first impressions of an outback mining town"
+ permits:
+ [
+ "look",
+ "watch",
+ "notice",
+ "observe",
+ "study",
+ "listen",
+ "smell",
+ "heat",
+ ]
+ blocks: ["leave", "escape", "run", "refuse", "attack"]
+ limbo_fallback: |
+ The heat presses down. You stand on the platform, neither moving nor
+ deciding. The town waits, indifferent to your hesitation.
+ outcome_nodes:
+ discovery: station_observation_discovery
+ revelation: station_observation_blocked
+
+ town_exploration:
+ narrative: |
+ You walk the main street of Bundanyabba. It's not much โ a few pubs, a general store, a post office, the RSL club. Everything bakes under the relentless sun.
+
+ Miners in work clothes move between pubs. Women with shopping bags hurry past, heads down. Children chase each other through the dust.
+
+ There's a memorial in the town square โ something about the war. Names etched in bronze. Most of the same surnames repeat: Crawford, Hynes, Tydon.
+
+ Everyone dies here, you think. Or they become part of it.
+
+ A police officer approaches, broad smile on his sunburned face. "G'day! New to the 'Yabba? I'm Jock Crawford. Let me shout you a drink."
+
+ He's already steering you toward the pub before you can answer.
+ choice:
+ prompt: "Jock Crawford is insistent."
+ options:
+ - id: go_with_jock
+ text: "Let Jock lead you to the pub"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: 10
+ narrative: "When in Rome..."
+ next_node: first_drink
+
+ - id: politely_decline_jock
+ text: "Politely decline โ you need to check into your hotel"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_relationship
+ npc: jock
+ delta: -5
+ narrative: "'Later, maybe,' you say."
+ next_node: hotel_boredom
+
+ - id: ask_about_town
+ text: "Ask Jock about the town first"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 1
+ narrative: "Jock loves talking about The Yabba."
+ next_node: jock_town_tour
+
+ - id: examine_memorial
+ text: "Stop at the war memorial โ those names look familiar"
+ cell: unknown
+ next: improvise
+ improvise_context:
+ theme: "understanding the town through its dead"
+ permits:
+ [
+ "read",
+ "names",
+ "war",
+ "history",
+ "families",
+ "Crawford",
+ "Hynes",
+ "generations",
+ ]
+ blocks: ["vandalize", "ignore", "mock", "disrespect"]
+ limbo_fallback: |
+ The bronze names shimmer in the heat. Crawford. Hynes. Tydon.
+ The same names on the pub signs, the shop fronts, the police
+ badge. This town remembers its dead. Or perhaps, never let them go.
+ outcome_nodes:
+ discovery: memorial_discovery
+
+ jock_town_tour:
+ narrative: |
+ Jock walks you through town, pointing out landmarks with proprietary pride.
+
+ "That's the Royal โ best pub in the 'Yabba. That's the RSL โ memorial service tonight, you should come. That's Tim Hynes' place โ good bloke, always up for a yarn."
+
+ He pauses at a weathered building. "And that's Doc Tydon's surgery. Well, used to be. He's more of a... freelance medical consultant now." Jock winks.
+
+ "Everyone knows everyone in the 'Yabba," he says. "That's what makes it great. No strangers here. Only mates you haven't met yet."
+
+ His hand lands on your shoulder, heavy and warm.
+
+ "So โ ready for that drink?"
+ choice:
+ prompt: "Jock has shown you the town. Now he wants payment."
+ options:
+ - id: accept_drink_tour
+ text: "Accept โ he's earned it"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: 15
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ narrative: "One drink. What's the harm?"
+ next_node: first_drink
+
+ - id: hotel_instead
+ text: "Thank him but head to your hotel"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: jock
+ delta: -10
+ narrative: "Jock's smile tightens."
+ next_node: hotel_boredom
+
+ - id: ask_about_doc
+ text: "Ask more about this 'Doc Tydon'"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "Something about the name intrigues you."
+ next_node: jock_describes_doc
+
+ - id: decline_and_walk_away
+ text: "Politely decline and walk away โ no explanation needed, no justification owed"
+ cell: avoids
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: jock
+ delta: -10
+ narrative: "'Thanks for the tour, mate. But I've got things to sort out.' You turn and walk. Jock's smile freezes. 'Suit yourself.' The words follow you like a threat. You don't look back."
+ next_node: hotel_boredom
+
+ jock_describes_doc:
+ narrative: |
+ "Doc?" Jock scratches his chin. "Used to be a proper surgeon in Sydney. Good one, they say. Had it all โ money, respect, fancy wife."
+
+ He lowers his voice. "Then the drink got him. Lost his license. Lost everything. Ended up here."
+
+ "But here's the thingโ" Jock leans in conspiratorially. "He's happier now than he ever was. Reckons he is, anyway. Says The Yabba set him free."
+
+ He shrugs. "Bloke's a bit odd. But he's one of us now. That's what matters."
+
+ In the distance, you see a shambling figure emerge from a tin shack on the edge of town. Even from here, you can see the intelligence in his eyes, the ruin in his posture.
+
+ "That's him now," Jock says. "Want to meet him? Or shall we get that drink first?"
+ choice:
+ prompt: "Doc Tydon is visible in the distance."
+ options:
+ - id: meet_doc_early
+ text: "Meet Doc first"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: set_flag
+ flag: met_doc_early
+ value: true
+ narrative: "Something draws you toward him."
+ next_node: early_doc_meeting
+
+ - id: drink_first
+ text: "Drink first โ you need it"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ narrative: "Doc can wait."
+ next_node: first_drink
+
+ early_doc_meeting:
+ narrative: |
+ Doc watches you approach with those knowing eyes. Up close, he's younger than you expected โ maybe forty โ but weathered beyond his years.
+
+ "Jock's found another one," he says, not to you but to the air. "Another educated man slumming it in the 'Yabba."
+
+ "I'm just passing through," you say.
+
+ Doc laughs. "That's what they all say. That's what I said." He looks at you properly now. "Schoolteacher, right? I can always tell. Something about the way you hold yourself. Like you're waiting to be disappointed."
+
+ He offers his hand. "Clarence Tydon. Call me Doc. Everyone does."
+
+ His grip is surprisingly firm.
+
+ "Buy you a drink?" he asks. "Consider it a welcome to hell."
+ choice:
+ prompt: "Doc is offering hospitality of a different kind."
+ options:
+ - id: accept_doc_drink
+ text: "Accept Doc's offer"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ narrative: "His eyes light up."
+ next_node: doc_philosophy
+
+ - id: decline_politely
+ text: "Decline โ you need to check in first"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "'Later, perhaps,' you say. Doc nods."
+ next_node: hotel_boredom
+
+ - id: ask_why_hell
+ text: "Ask why he called it hell"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ narrative: "Doc smiles โ a real smile."
+ next_node: doc_explains_hell
+
+ doc_explains_hell:
+ narrative: |
+ "Hell?" Doc gestures at the town, the heat, the endless red dirt. "Look around you. What do you see?"
+
+ "A mining town."
+
+ "A crucible," he corrects. "A place that strips away everything you think you are. Your education. Your manners. Your sense of superiority."
+
+ He pulls out a flask, drinks. "The 'Yabba doesn't care who you were. Only who you become."
+
+ "And who do people become?"
+
+ Doc's smile turns sad. "Themselves. Finally. Terribly. Themselves."
+
+ He offers you the flask. "All the little devils are proud of hell, mate. The question is โ are you ready to meet yours?"
+ choice:
+ prompt: "Doc's words hang in the air."
+ options:
+ - id: take_flask
+ text: "Take the flask โ drink with him"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ - type: modify_relationship
+ npc: doc
+ delta: 20
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "The whiskey burns. Something else burns too."
+ next_node: docs_shack_arrival
+
+ - id: refuse_flask
+ text: "Refuse โ you're not ready for this"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "Doc nods, unsurprised."
+ next_node: hotel_boredom
+
+ - id: challenge_doc
+ text: "Tell Doc you're not afraid of yourself"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ narrative: "Doc laughs. 'We'll see, schoolteacher. We'll see.'"
+ next_node: first_drink
+
+ station_reflection:
+ narrative: |
+ The Aboriginal man nods back, almost imperceptibly. Then the train begins to move, carrying him... where? You don't know. Deeper into the outback, probably. Or back to wherever he came from.
+
+ You stand on the platform as the train disappears, thinking about that look in his eyes. This land was his people's for forty thousand years. Now it belongs to men like Jock Crawford and Tim Hynes, to the miners and the drunks and the two-up spinners.
+
+ What does The Yabba look like to him? What truths does he see that you're blind to?
+
+ The heat presses down. You need to find shelter. A hotel. A pub. Somewhere to wait out the hours until your flight.
+
+ Or you could explore. Try to understand this place before you judge it.
+ choice:
+ prompt: "The platform is empty now. Just you and the heat."
+ options:
+ - id: find_hotel_reflect
+ text: "Find the hotel โ you need to think"
+ consequence:
+ - type: move_to
+ location: hotel
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "The encounter lingers in your mind."
+ next_node: hotel_boredom
+
+ - id: find_pub_reflect
+ text: "Find a pub โ you need a drink"
+ consequence:
+ - type: move_to
+ location: pub
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ narrative: "The encounter fades as you walk toward the noise."
+ next_node: first_drink
+
+ - id: explore_reflect
+ text: "Walk the town โ try to see it differently"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "You look at The Yabba with new eyes."
+ next_node: town_exploration
+
+ hotel_boredom:
+ narrative: |
+ Your hotel room is a box of heat. The ceiling fan pushes warm air in circles. You lie on the bed, staring at the plaster, thinking about Sydney. About Robyn. About how you ended up teaching thirty kids in a place that doesn't appear on most maps.
+
+ Through the window, you can see the pub across the street. Men going in and out, laughing. The sound of a piano drifts up. Life happening without you.
+
+ The afternoon stretches endlessly. The walls seem to press inward.
+ choice:
+ prompt: "The loneliness is suffocating."
+ options:
+ - id: stay_put
+ text: "Stay in your room and read"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 1
+ narrative: "You force yourself to read. The hours crawl."
+ next_node: morning_escape
+
+ - id: go_to_pub
+ text: "Go to the pub โ just for a couple of hours"
+ cell: chooses
+ consequence:
+ - type: move_to
+ location: pub
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ narrative: "You can't stand the silence anymore."
+ next_node: first_drink
+
+ - id: write_robyn
+ text: "Write a letter to Robyn"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "You find paper in the desk drawer."
+ next_node: letter_to_robyn
+
+ - id: walk_town
+ text: "Take a walk around town โ explore safely"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 1
+ narrative: "Better than staring at walls."
+ next_node: town_exploration
+
+ - id: sit_and_wait
+ text: "Sit on the veranda and just... wait. See what happens."
+ cell: unknown
+ next: improvise
+ improvise_context:
+ theme: "passive observation in limbo"
+ permits:
+ ["watch", "observe", "wait", "sit", "listen", "people", "think"]
+ blocks: ["sleep", "leave", "hide"]
+ limbo_fallback: |
+ Hours pass. The ceiling fan turns. Men enter and leave the pub
+ across the street. The sun arcs overhead. You're not doing anything.
+ You're not deciding anything. Time becomes thick as honey.
+
+ This is what the Yabba does to people, you realize. It waits them out.
+ outcome_nodes: {}
+
+ letter_to_robyn:
+ narrative: |
+ Dear Robyn,
+
+ You stare at the blank page. What do you say? That you're stuck in some godforsaken mining town in the middle of nowhere? That the heat is unbearable and the locals won't leave you alone?
+
+ You write about the train journey. The red earth. The endless sky. You make it sound like an adventure, even though it feels like a prison sentence.
+
+ "I'll be home for Christmas," you write. "I promise."
+
+ But as you seal the envelope, doubt creeps in. Will you? Can you?
+
+ The hotel manager takes the letter. "Post goes out tomorrow arvo," he says. "Should reach Sydney in a week."
+
+ A week. Robyn won't know where you are for a week.
+
+ Outside, the sun is setting. The pub is calling.
+ choice:
+ prompt: "The letter is sent. What now?"
+ options:
+ - id: stay_in_room
+ text: "Stay in your room โ the letter settled something"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "You feel calmer. More connected to your real life."
+ next_node: morning_escape
+
+ - id: one_drink
+ text: "One drink to celebrate โ then back to the room"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ narrative: "One drink won't hurt."
+ next_node: first_drink
+
+ - id: sleep_early
+ text: "Go to bed early โ get a fresh start tomorrow"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: advance_time
+ hours: 12
+ narrative: "The best decision you'll make in The Yabba."
+ next_node: second_day_hotel
+
+ second_day_hotel:
+ narrative: |
+ Morning. You wake early, pack your bags, and head to the airstrip.
+
+ The dawn air is cool โ almost pleasant. The town is quiet, sleeping off last night's excesses.
+
+ You made it. One night in The Yabba, and you're still yourself.
+
+ At the airstrip, the pilot is doing preflight checks. "Sydney flight?" he asks.
+
+ "Sydney flight."
+
+ He nods. "Ten minutes."
+
+ You sit on a bench and watch the sun rise over the red earth. Soon you'll be in the sky. Soon you'll be home.
+
+ The 'Yabba couldn't hold you after all.
+ choice:
+ prompt: "Your flight is boarding."
+ options:
+ - id: board_flight
+ text: "Board the flight โ go home"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You climb into the small plane and don't look back."
+ next_node: ending_escape
+
+ - id: last_minute_doubt
+ text: "Hesitate โ is there something you're missing?"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "The pilot frowns. 'Coming or not, mate?'"
+ next_node: last_minute_choice
+
+ last_minute_choice:
+ narrative: |
+ The propellers are spinning. The pilot is waiting.
+
+ You think about what you're leaving behind. A mystery, really. Why do people stay in places like this? What does The Yabba offer that Sydney doesn't?
+
+ Doc Tydon was a surgeon. Tim Hynes could have been anything. Janette dreams of escape but can't bring herself to leave.
+
+ What truth does this place hold? What would you learn if you stayed?
+
+ "Last call," the pilot says. "Now or never."
+ choice:
+ prompt: "The plane is leaving. With or without you."
+ options:
+ - id: flee_from_shame
+ text: "Run from what you've become here โ escape before The Yabba destroys you"
+ precondition:
+ type: all_of
+ conditions:
+ - type: flag_set
+ flag: went_on_hunt
+ - type: trait_maximum
+ trait: dignity
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "You're running. From the blood, the hunt, the darkness you found here. You climb aboard, desperate to leave this behind."
+ next_node: ending_escape
+
+ - id: stay_for_janette
+ text: "Stay for Janette โ she needs someone who understands, maybe together you can leave"
+ precondition:
+ type: relationship_minimum
+ npc: janette
+ minimum: 15
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_trait
+ trait: dignity
+ delta: 1
+ narrative: "You think of her. Trapped here. Maybe together you can find a way out. The plane leaves without you."
+ next_node: chose_to_stay
+
+ - id: leave_now
+ text: "Get on the plane. This isn't your mystery to solve."
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You run for the plane."
+ next_node: ending_escape
+
+ - id: stay_learn
+ text: "Let it go. Stay and find out."
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "The plane takes off without you."
+ next_node: chose_to_stay
+
+ chose_to_stay:
+ narrative: |
+ The plane shrinks into the blue. Gone.
+
+ You stand at the empty airstrip, bag in hand, watching your escape disappear.
+
+ What have you done?
+
+ The sun is rising. The heat is building. Behind you, The Yabba is waking up.
+
+ You chose this. Whatever happens next โ you chose it.
+
+ Doc's shack is a few miles out of town. He'll know what to do with a man who missed his flight on purpose.
+ choice:
+ prompt: "You've committed to The Yabba. Now what?"
+ options:
+ - id: stay_with_purpose
+ text: "Stay to understand โ there's something true here worth discovering"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 7
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: set_flag
+ flag: been_to_docs_shack
+ value: true
+ narrative: "You stayed by choice, not because you're trapped. There's a difference. You'll find what this place has to teach, then leave on your own terms."
+ next_node: docs_shack_arrival
+
+ - id: stay_defeated
+ text: "You have nowhere else to go โ The Yabba has you now"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ - type: modify_trait
+ trait: desperation
+ delta: 8
+ narrative: "You stayed because leaving felt impossible. The Yabba doesn't trap people โ they trap themselves. You understand that now."
+ next_node: first_drink
+
+ - id: find_doc_morning
+ text: "Find Doc"
+ consequence:
+ - type: set_flag
+ flag: been_to_docs_shack
+ value: true
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ narrative: "Time to understand this place."
+ next_node: docs_shack_arrival
+
+ - id: stayed_after_hunt
+ text: "You stayed to face what you did on the hunt โ you owe The Yabba that much"
+ precondition:
+ type: flag_set
+ flag: went_on_hunt
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 7
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "Running won't undo what happened. The kangaroos. The violence. You need to understand what you became, and why."
+ next_node: docs_shack_arrival
+
+ - id: find_pub_morning
+ text: "Find the pub โ wait for it to open"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ narrative: "Might as well start as you mean to go on."
+ next_node: first_drink
+
+ - id: regret_immediately
+ text: "Regret your choice immediately"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "What have you done?"
+ next_node: night_wandering
+
+ - id: too_hungover_to_leave
+ text: "Stay because you're too hungover to think straight โ movement seems impossible"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 4
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ - type: modify_trait
+ trait: desperation
+ delta: 2
+ narrative: "The thought of the bus, the vibration, the endless road... Your stomach rebels at the idea. You can barely stand. Maybe tomorrow. Always tomorrow."
+ next_node: first_drink
+
+ doc_philosophy:
+ narrative: |
+ You follow Doc to a bench outside the pub. He pours from his flask โ doesn't ask if you want any, just hands it over.
+
+ "You know what your problem is, schoolteacher?" he says. "You think too much. All that education โ it's made you believe life is a problem to be solved."
+
+ He drinks. "But it's not. Life is a condition to be experienced. The sooner you accept that, the happier you'll be."
+
+ "Is that why you stayed here?"
+
+ "I stayed because leaving requires energy I don't have anymore." He smiles. "And because the beer is cheap and the company doesn't judge."
+
+ He looks at you with those knowing eyes. "Why are you really here? Not the bond. Not Tiboonda. Why are you sitting in a pub in the middle of bloody nowhere instead of catching your flight?"
+ choice:
+ prompt: "Doc is asking the real question."
+ options:
+ - id: articulate_search
+ text: "Articulate what you're searching for โ meaning, truth, something real beneath the pretense"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 7
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_relationship
+ npc: doc
+ delta: 20
+ narrative: "You speak clearly, honestly, about the emptiness you've felt. Doc listens without interruption. 'Now that,' he says quietly, 'is something worth talking about.'"
+ next_node: docs_shack_arrival
+
+ - id: honest_answer
+ text: "Be honest โ you don't know"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ narrative: "Doc nods. 'That's the first honest thing you've said.'"
+ next_node: docs_shack_arrival
+
+ - id: defensive_answer
+ text: "Defend yourself โ circumstances, bad luck"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ - type: modify_relationship
+ npc: doc
+ delta: -5
+ narrative: "Doc laughs. 'Keep telling yourself that.'"
+ next_node: meet_tim
+
+ - id: turn_it_around
+ text: "Ask Doc the same question"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Doc's smile fades."
+ next_node: docs_confession
+
+ first_drink:
+ narrative: |
+ The pub is dark and cool, a relief from the blazing street. Blokes in work shirts lean against the bar, faces weathered by sun and dust. They turn to look at you โ the stranger โ with open curiosity.
+
+ A man in a police uniform approaches with a broad smile. "G'day! New to the 'Yabba? Let me shout you a drink, mate. I'm Jock Crawford."
+
+ He doesn't wait for an answer. A schooner of beer appears in front of you, cold and sweating.
+
+ "Best little place on earth, the 'Yabba," Jock says. "Everyone's bloody friendly here. That's the rule. You can't refuse a drink โ it's an insult, mate."
+
+ In the corner, you notice a woman wiping tables. She glances at you โ measuring, appraising โ then looks away.
+ choice:
+ prompt: "Jock is waiting for you to drink."
+ options:
+ - id: order_lemon_squash
+ text: "Order a lemon squash โ you're not drinking tonight, thanks all the same"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: jock
+ delta: -3
+ narrative: "You speak with calm certainty. Jock blinks, surprised, but nods. 'Right then. Lemon squash it is.' The pub goes quiet for a moment."
+ next_node: jock_conversation
+
+ - id: accept_drink
+ text: "Accept the drink and chat with Jock"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ - type: modify_relationship
+ npc: jock
+ delta: 10
+ narrative: "The beer is ice-cold and perfect."
+ next_node: jock_conversation
+
+ - id: polite_decline
+ text: "Try to politely decline"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ - type: modify_relationship
+ npc: jock
+ delta: -5
+ narrative: "Jock's smile doesn't waver, but something hardens in his eyes."
+ next_node: forced_hospitality
+
+ - id: buy_own_round
+ text: "Insist on buying your own round"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_trait
+ trait: money
+ delta: -1
+ - type: modify_relationship
+ npc: jock
+ delta: 5
+ narrative: "Jock looks surprised, then pleased. 'Fair go, mate. Fair go.'"
+ next_node: jock_conversation
+
+ - id: notice_woman
+ text: "Accept, but glance at the woman in the corner"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ - type: modify_relationship
+ npc: janette
+ delta: 5
+ narrative: "She catches your eye. Holds it. Then looks away."
+ next_node: janette_at_pub
+
+ - id: nurse_beer_slowly
+ text: "Accept but nurse the beer โ one drink, slowly, maintaining clear head"
+ precondition:
+ type: trait_minimum
+ trait: sobriety
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: 0
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ - type: modify_relationship
+ npc: jock
+ delta: 7
+ narrative: "You accept the beer but sip it slowly, staying sharp. Jock doesn't notice โ what matters is you accepted. You'll stay clear-headed while playing their game."
+ next_node: jock_conversation
+
+ janette_at_pub:
+ narrative: |
+ Her name is Janette. Tim Hynes' daughter. She works at the pub some afternoons, she tells you, to get out of the house.
+
+ "Dad's always got his mates over," she says, refilling your glass without asking. "Drinking. Talking about nothing. It never bloody ends."
+
+ She's pretty in a faded way. Tired eyes. Quick smile that doesn't quite reach them.
+
+ "You're not from here," she says. It's not a question.
+
+ "Just passing through."
+
+ She laughs โ bitter, knowing. "That's what they all say."
+
+ Jock is watching from across the bar, frowning slightly. He doesn't like being ignored.
+ choice:
+ prompt: "Janette is interested. Jock is watching."
+ options:
+ - id: speak_honestly
+ text: "Speak honestly about your situation โ bond, teaching, trapped. No pretense"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 5
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 20
+ - type: modify_relationship
+ npc: jock
+ delta: -5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "You tell her the truth. All of it. She listens without judgment. 'I know what it's like,' she says quietly. 'Being trapped.'"
+ next_node: janette_opens_up
+
+ - id: keep_talking_janette
+ text: "Keep talking to Janette"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 15
+ - type: modify_relationship
+ npc: jock
+ delta: -10
+ narrative: "Jock can wait."
+ next_node: janette_opens_up
+
+ - id: return_to_jock
+ text: "Excuse yourself and return to Jock"
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: 10
+ - type: modify_relationship
+ npc: janette
+ delta: -5
+ narrative: "You don't want to make enemies on your first day."
+ next_node: jock_conversation
+
+ - id: invite_janette_join
+ text: "Invite Janette to join you and Jock"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 5
+ - type: modify_relationship
+ npc: jock
+ delta: 5
+ narrative: 'Janette looks at you sideways, with a faint smile on one side of her mouth. "Got a job to do here, ya know! Maybe later..."'
+ next_node: jock_conversation
+
+ - id: drunk_honesty
+ text: "The beer loosens your tongue โ talk honestly but sloppily, emotions raw"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 5
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 12
+ - type: modify_relationship
+ npc: jock
+ delta: -8
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ narrative: "The words tumble out. Your frustrations, your failures, everything you haven't said. It's messy, unfiltered. Janette doesn't seem to mind. She understands drunk honesty โ probably hears it every night. Jock frowns but doesn't interrupt."
+ next_node: janette_opens_up
+
+ - id: janette_leave_together_tonight
+ text: "Janette leans close: 'We could leave together. Tonight. Right now. Say yes.'"
+ precondition:
+ type: relationship_minimum
+ npc: janette
+ minimum: 15
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 15
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "Her hand finds yours under the bar. 'I've got enough for two bus tickets. We don't have to stay. We don't have to become them.' Her eyes are fierce with desperate hope. For a moment, escape feels possible."
+ next_node: janette_escape_plan
+
+ janette_opens_up:
+ narrative: |
+ "I've been here my whole life," Janette says, leaning against the bar. "Watched men come and go. Tourists, teachers, miners chasing the next big find."
+
+ She pours herself a drink โ the publican doesn't notice, or doesn't care.
+
+ "They all think they're different. Think they'll just pass through, see the sights, move on." She drinks. "Some do. Most don't."
+
+ "What happens to the ones who don't?"
+
+ She looks at you with those tired eyes. "They become part of it. The drinking. The gambling. The... hospitality." She says the word like it's poison.
+
+ "Is that what happened to your father?"
+
+ "Dad?" She laughs. "Dad was born here. He doesn't know anything else." She pauses. "That's the tragedy, isn't it? He's happy. Genuinely bloody happy."
+
+ Jock appears at your elbow. "Time to move on, mate. The RSL's calling."
+ choice:
+ prompt: "Jock is insistent. Janette is watching."
+ options:
+ - id: go_with_jock_janette
+ text: "Go with Jock โ thank Janette for the chat"
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: 10
+ narrative: "Janette nods. 'See you around, schoolteacher.'"
+ next_node: rsl_service
+
+ - id: stay_with_janette
+ text: "Tell Jock you'll catch up later"
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: -15
+ - type: modify_relationship
+ npc: janette
+ delta: 20
+ narrative: "Jock's face hardens, but he leaves."
+ next_node: janette_warns_you
+
+ - id: ask_janette_escape
+ text: "Ask Janette if there's a way out"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ - type: modify_relationship
+ npc: janette
+ delta: 10
+ narrative: "Her expression changes."
+ next_node: janette_escape_plan
+
+ - id: janette_heard_about_gambling
+ text: "Janette mentions she saw you at the two-up ringโ'You're like all the rest'"
+ precondition:
+ type: flag_set
+ flag: gambled
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: -8
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "'I saw you gambling,' she says quietly. 'I thought maybe you were different. But you're not. You're just another bloke throwing money away, pretending it's fun.' Her disappointment cuts deeper than anger would."
+ next_node: rsl_service
+
+ - id: janette_knows_youre_broke
+ text: "Janette sees you're broke โ offers quiet sympathy without judgment"
+ precondition:
+ type: flag_set
+ flag: lost_everything
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 15
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "'I've seen it happen before,' she says softly. 'Two-up gets everyone eventually. You're not the first.' She doesn't ask how much. She doesn't need to. She just... understands."
+ next_node: janette_warns_you
+
+ - id: janette_hunt_comment
+ text: "Janette heard about the huntโ'That's not you, is it? Tell me that's not you'"
+ precondition:
+ type: flag_set
+ flag: went_on_hunt
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: -10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "'They go out there and kill for fun,' she says, disgust in her voice. 'I heard you were with them last night. Shooting roos in the headlights. Is that what you've become?' You have no answer."
+ next_node: rsl_service
+
+ janette_warns_you:
+ narrative: |
+ With Jock gone, Janette relaxes slightly.
+
+ "You shouldn't have done that," she says. "Jock doesn't like being refused."
+
+ "I can handle myself."
+
+ "That's what they all say." She looks at you seriously. "Listen โ if you want my advice? Leave. Tonight. Don't go to the RSL. Don't play two-up. Don't let them get their hooks in."
+
+ "Their hooks?"
+
+ "The hospitality. It's not kindness โ it's a trap. Once you owe them, you can't leave. And everyone owes everyone in The Yabba."
+
+ She glances toward the door. "I'd leave myself if I could. But Dad needs someone to look after him. And where would I go?"
+
+ Her hand brushes yours on the bar. It's not an accident.
+ choice:
+ prompt: "Janette has warned you. What do you do?"
+ options:
+ - id: heed_warning
+ text: "Heed her warning โ go back to your hotel"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "You thank her and leave."
+ next_node: hotel_boredom
+
+ - id: stay_janette
+ text: "Stay โ there's something here"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 15
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ narrative: "The afternoon stretches into evening."
+ next_node: janette_encounter
+
+ - id: ask_come_with
+ text: "Ask if she wants to leave with you tomorrow"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 25
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "Her eyes widen."
+ next_node: janette_impossible_dream
+
+ janette_escape_plan:
+ narrative: |
+ "A way out?" Janette looks around, making sure no one's listening. "The bus to Broken Hill runs Wednesdays. From there you can get anywhere."
+
+ "That's four days away."
+
+ "I know." She bites her lip. "There's the mail plane too โ flies out every morning. But it costs. More than most people have after a night at the two-up."
+
+ She looks at you shrewdly. "You've still got money, haven't you? I can tell. You're not desperate yet."
+
+ "Not yet."
+
+ "Then keep it that way. Don't gamble. Don't let Jock talk you into anything. And whatever you doโ" she leans close, "โdon't go on the hunt."
+
+ "The hunt?"
+
+ "You'll see. If you stay long enough." Her expression darkens. "Everyone does, eventually."
+ choice:
+ prompt: "Janette has given you valuable information."
+ options:
+ - id: thank_leave
+ text: "Thank her and head to your hotel"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: janette
+ delta: 5
+ narrative: "Good advice. You should take it."
+ next_node: hotel_boredom
+
+ - id: ask_about_hunt
+ text: "Ask more about the hunt"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "She hesitates."
+ next_node: janette_describes_hunt
+
+ - id: one_more_drink
+ text: "Have one more drink with her"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ - type: modify_relationship
+ npc: janette
+ delta: 10
+ narrative: "One more won't hurt."
+ next_node: jock_conversation
+
+ janette_describes_hunt:
+ narrative: |
+ "The hunt." Janette's voice drops. "They go out at night. In the utes. Spotlights. Rifles."
+
+ "Kangaroo hunting?"
+
+ "That's what they call it." She shudders. "I went once. When I was younger. Stupid."
+
+ She doesn't elaborate, but her eyes are distant.
+
+ "It's not about the kangaroos," she says finally. "It's about what it does to you. Out there in the dark, drunk, with a gun in your hands. Men turn into something else."
+
+ She looks at you. "Don't go. Promise me."
+
+ Before you can answer, the pub door bangs open. Tim Hynes โ her father โ staggers in, already drunk.
+
+ "There's my girl! And who's this? A new mate?"
+ choice:
+ prompt: "Tim Hynes is here. Janette tenses."
+ options:
+ - id: introduce_self
+ text: "Introduce yourself politely"
+ consequence:
+ - type: modify_relationship
+ npc: tim
+ delta: 10
+ narrative: "Tim's handshake is crushing."
+ next_node: meet_tim
+
+ - id: let_janette_handle
+ text: "Let Janette handle it"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: -5
+ narrative: "She sighs and turns to her father."
+ next_node: jock_conversation
+
+ janette_impossible_dream:
+ narrative: |
+ "Leave? With you?" Janette stares at you like you've grown a second head.
+
+ For a moment โ just a moment โ something like hope flickers in her eyes. Then it dies.
+
+ "I can't," she says. "Dad. The house. Everything." She laughs bitterly. "Besides, you don't even know me. Why would you want me to come?"
+
+ It's a good question. Why did you ask? Guilt? Attraction? A white-knight fantasy?
+
+ "People don't leave The Yabba," she says. "They think they will. They plan to. But they don't."
+
+ She touches your face โ a gentle, sad gesture.
+
+ "You seem nice. Get out while you can. Forget about me."
+ choice:
+ prompt: "Janette has refused. Gently."
+ options:
+ - id: accept_refusal
+ text: "Accept her refusal โ she's right"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "You barely know her. This was foolish."
+ next_node: hotel_boredom
+
+ - id: insist
+ text: "Insist โ you mean it"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 20
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ narrative: "She looks at you with wonder and pity."
+ next_node: jock_conversation
+
+ - id: janette_deep_connection
+ text: "Share your own impossible dream โ somewhere you'd go, someone you'd be"
+ precondition:
+ type: relationship_minimum
+ npc: janette
+ minimum: 12
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 10
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "You tell her about the coast. About salt air and anonymity and starting clean. Her eyes go soft. 'Take me with you,' she says, not joking at all. For a moment you're both somewhere else entirely."
+ next_node: janette_escape_plan
+
+ forced_hospitality:
+ narrative: |
+ "Now, now," Jock says, his hand on your shoulder, grip surprisingly firm. "I said everyone's friendly in the 'Yabba. You're not gonna be unfriendly, are ya, mate?"
+
+ The other drinkers have turned to watch. Their faces are neutral, waiting.
+
+ "It's just a beer," Jock continues. "Just hospitality. You city blokes, you don't understand hospitality. Got snakes in your pocket, have ya?"
+
+ The aggressive hospitality โ that's what it is, you realize. Aggressive bloody hospitality. The beer sits there, condensation pooling on the wood.
+ choice:
+ prompt: "The pressure is palpable."
+ options:
+ - id: defuse_with_dignity
+ text: "Defuse calmlyโ'I appreciate the hospitality, but I don't drink. No disrespect meant'"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 4
+ - type: modify_relationship
+ npc: jock
+ delta: -5
+ narrative: "You speak with such calm certainty that Jock actually backs off. 'Right then,' he says. 'Didn't mean to push, mate.' The moment passes."
+ next_node: jock_conversation
+
+ - id: give_in
+ text: "Drink the beer โ it's not worth the conflict"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ - type: modify_relationship
+ npc: jock
+ delta: 5
+ narrative: "You drink. Jock's smile returns to full warmth."
+ next_node: jock_conversation
+
+ - id: stand_firm
+ text: "Firmly refuse and leave"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_relationship
+ npc: jock
+ delta: -15
+ narrative: "You set the glass down and walk out. You can feel their eyes on your back."
+ next_node: hotel_boredom
+
+ - id: soda_water_only
+ text: "Accept soda water only โ keep your head clear in this hostile environment"
+ precondition:
+ type: trait_minimum
+ trait: sobriety
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ - type: modify_relationship
+ npc: jock
+ delta: -3
+ narrative: "Jock's eyes narrow. 'Not a drinker?' The question is almost an accusation. But you hold firm. 'Soda water, thanks.' The barman shrugs and pours. You've drawn a line."
+ next_node: jock_conversation
+
+ jock_conversation:
+ narrative: |
+ Three beers later โ Jock won't let you buy a single one โ you're hearing about The Yabba's history, its mines, its characters. Jock knows everyone.
+
+ "You should come to the RSL," he says. "Memorial service tonight. Then I'll show you real entertainment."
+
+ He winks. There's something knowing in it.
+
+ You're starting to feel the beers. The room has taken on a pleasant blur.
+ choice:
+ prompt: "The night is young."
+ options:
+ - id: go_to_rsl
+ text: "Go to the RSL with Jock"
+ consequence:
+ - type: move_to
+ location: rsl_club
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ narrative: "You follow Jock into the night."
+ next_node: rsl_service
+
+ - id: call_it_night
+ text: "Thank Jock and head back to your hotel"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "You still have some control. You need sleep."
+ next_node: morning_escape
+
+ rsl_service:
+ narrative: |
+ The RSL club is packed with miners and their missus. A memorial service is underway โ something about the war, about sacrifice. The speaker's voice drones on while blokes drink steadily.
+
+ There's something disconcerting about it all. The reverence mixed with casual drinking. The way people mouth the words of hymns while their eyes stay dead.
+
+ Jock introduces you to a man called Doc Tydon. He's different from the others โ educated, clearly, but with a vagrant's clothes and a drunk's eyes.
+
+ "I'm a doctor of medicine," Doc says by way of introduction. "And a tramp by temperament. Also an alcoholic. My disease made practice in Sydney... impractical. But out here it's scarcely noticeable."
+
+ He looks you up and down. "A schoolteacher, eh? Slumming it in the 'Yabba. Tell me โ do you reckon you're better than us?"
+ choice:
+ prompt: "Doc is challenging you directly."
+ options:
+ - id: speak_up_racism
+ text: "Call out what you've seen โ the Aboriginal man on the train, ignored by everyone."
+ precondition:
+ type: flag_set
+ flag: acknowledged_man_on_train
+ value: true
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: modify_relationship
+ npc: jock
+ delta: -15
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Silence falls. Jock looks uncomfortable. Doc studies you. 'Interesting,' he says finally. 'Maybe you do see something the rest of us don't.'"
+ next_node: doc_philosophy_yabba
+
+ - id: honest_yes
+ text: "Admit that yes, you feel out of place here"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "Doc laughs. 'Honest, at least. I like that.'"
+ next_node: doc_philosophy_yabba
+
+ - id: deny_it
+ text: "Deny it โ insist you respect The Yabba"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: -5
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ narrative: "Doc's eyes narrow. 'Liar. But you'll learn.'"
+ next_node: two_up_introduction
+
+ - id: turn_tables
+ text: "Ask Doc why he's here if he's so educated"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ narrative: "Doc's smile turns sad. 'Touchรฉ, schoolteacher.'"
+ next_node: doc_philosophy_yabba
+
+ - id: too_drunk_to_notice
+ text: "Too drunk to notice what's missing, the ceremony blurs into noise and heat"
+ precondition:
+ - type: flag_set
+ flag: acknowledged_man_on_train
+ value: false
+ - type: trait_maximum
+ trait: sobriety
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: -2
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ narrative: "The flags wave. Men salute. You sway on your feet, fighting nausea. Whatever this means, you're too far gone to understand it. Someone hands you another beer. You drink it without thinking."
+ next_node: two_up_introduction
+
+ doc_philosophy_yabba:
+ narrative: |
+ Doc orders another round โ his fourth that you've seen, but he seems no more drunk than when you met him.
+
+ "You don't reckon the 'Yabba's the greatest little place on earth?" he asks, watching your face.
+
+ You hesitate. "I think it's... bloody awful, actually."
+
+ Doc laughs. "Could be worse."
+
+ "How?"
+
+ He raises his glass. "Supply of beer could run out." He drinks deep. "The 'Yabba accepts everyone, you see. No judgment. No expectations. All the little devils are proud of hell, mate."
+
+ He gestures at the room. "These blokes โ they work hard, they drink hard, they die hard. Simple equations. Your sophisticated world, with its ambitions and pretensions... that's the real wasteland."
+
+ Jock appears at your elbow. "Time for some fun, boys. Two-up."
+ choice:
+ prompt: "Jock is eager to show you the gambling."
+ options:
+ - id: follow_to_gambling
+ text: "Go see the two-up game"
+ consequence:
+ - type: move_to
+ location: two_up_ring
+ narrative: "You follow, curious despite yourself."
+ next_node: two_up_introduction
+
+ - id: refuse_gambling
+ text: "Decline โ you're not a gambling man"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "You try to bow out gracefully."
+ next_node: pressure_to_gamble
+
+ - id: challenge_docs_nihilism
+ text: "Challenge Doc's worldviewโ'That's a convenient philosophy for a man who gave up'"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 8
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: -5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "Doc's glass stops halfway to his lips. 'Gave up?' He laughs, but there's no joy in it. 'Maybe I just saw clearly. Maybe you're the one still lying to yourself.' He drains his glass. 'Come on. Let's see if the two-up game can teach you something about acceptance.'"
+ next_node: two_up_introduction
+
+ two_up_introduction:
+ narrative: |
+ The two-up ring is in a back room, smoky and loud. Blokes crowd around a marked circle on the floor. A man called the "spinner" tosses two pennies high into the air using a flat wooden paddle โ the "kip."
+
+ "Come in, spinner!" someone shouts.
+
+ "Heads or tails," Jock explains. "Simple as that, mate. Two heads, you win. Two tails, they win. One of each, spin again."
+
+ The spinner tosses. The coins catch the light, spinning, spinning... they land. Two heads. A roar goes up.
+
+ "Fair go!" "Good on ya, spinner!"
+
+ "Try your luck," Jock says, pressing some quid into your hand. "First bet's on me, mate."
+
+ You watch another round. The money changes hands fast. Some bloke wins fifty quid in thirty seconds.
+
+ You think about your bond. Two years of teaching in the middle of bloody nowhere. What if you could pay it off tonight?
+ choice:
+ prompt: "The coins spin hypnotically."
+ options:
+ - id: explain_calmly
+ text: "Explain calmly why gambling isn't for you โ no judgment, just not interested"
+ cell: avoids
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 7
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: jock
+ delta: -2
+ narrative: "You explain yourself clearly, without defensiveness. Jock looks disappointed but nods. 'Fair enough, mate. Not everyone's a gambling man.'"
+ next_node: two_up_ring
+
+ - id: small_bet
+ text: "Place a small bet โ just to be social"
+ cell: chooses
+ consequence:
+ - type: set_flag
+ flag: gambled
+ value: true
+ - type: modify_trait
+ trait: money
+ delta: 1
+ narrative: "You bet small. You win. It feels good."
+ next_node: winning_streak
+
+ - id: refuse_gamble
+ text: "Refuse to gamble โ you need your money"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "You shake your head firmly."
+ next_node: pressure_to_gamble
+
+ - id: one_more_to_fit_in
+ text: "Have one more drink to fit in โ you're almost one of them now"
+ cell: chooses
+ precondition:
+ type: all_of
+ conditions:
+ - type: trait_minimum
+ trait: sobriety
+ minimum: 5
+ - type: trait_maximum
+ trait: sobriety
+ maximum: 7
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ - type: modify_relationship
+ npc: jock
+ delta: 3
+ - type: set_flag
+ flag: gambled
+ value: true
+ narrative: "Another beer. The warmth spreads. You're starting to understand these men โ or think you do. The coins spin and you find yourself putting money down without quite deciding to."
+ next_node: winning_streak
+
+ - id: watch_from_edge
+ text: "Hang back and observe โ see what gambling does to these men"
+ cell: unknown
+ next: improvise
+ improvise_context:
+ theme: "observing gamblers at the moment of truth"
+ permits:
+ [
+ "watch",
+ "eyes",
+ "faces",
+ "expressions",
+ "winners",
+ "losers",
+ "pattern",
+ "addiction",
+ ]
+ blocks: ["bet", "gamble", "play", "join"]
+ limbo_fallback: |
+ The coins spin. Heads or tails. You watch the faces, not the coins.
+ The winners howl. The losers laughโthey always laugh, until they
+ don't. The kip rises and falls like a heartbeat.
+ outcome_nodes:
+ discovery: two_up_observation_discovery
+ revelation: two_up_observation_blocked
+
+ pressure_to_gamble:
+ narrative: |
+ Jock's face falls. "Come on, mate. Don't be like that. Everyone has a flutter in the 'Yabba."
+
+ Doc appears beside you. "The schoolteacher reckons he's above our simple pleasures," he says to Jock, loud enough for others to hear.
+
+ Blokes are looking at you. That same pressure from the pub, but more intense now. The aggressive hospitality closing in.
+
+ "Just one bet," Jock wheedles. "She'll be right, mate. What's the harm?"
+ choice:
+ prompt: "The crowd is watching."
+ options:
+ - id: stand_firm_with_dignity
+ text: "Stand firm โ 'I'm not gambling, Jock. Appreciate the offer, but no'"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: jock
+ delta: -3
+ - type: modify_relationship
+ npc: doc
+ delta: -5
+ narrative: "You speak calmly but firmly. Jock shrugs. Doc sneers but says nothing. They turn and walk into the next room. After a few moments you follow. Just to watch."
+ next_node: two_up_introduction
+
+ - id: offer_compromise
+ text: "Offer to watch a few rounds first โ you'll decide after seeing how it works"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 1
+ narrative: "Jock grins. 'Fair enough, mate. Watch and learn. You'll be itching to have a go soon enough.'"
+ next_node: two_up_introduction
+
+ - id: give_in_gamble
+ text: "Fine โ one bet to shut them up"
+ consequence:
+ - type: set_flag
+ flag: gambled
+ value: true
+ - type: modify_trait
+ trait: money
+ delta: 1
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ narrative: "You bet. You win. Jock cheers."
+ next_node: winning_streak
+
+ - id: leave_entirely
+ text: "Leave The Yabba tonight โ walk to the highway"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: set_flag
+ flag: tried_to_leave
+ value: true
+ narrative: "You've had enough. You push through the crowd and out."
+ next_node: failed_escape_attempt
+
+ - id: see_through_manipulation
+ text: "Your sober mind sees this clearly โ aggressive hospitality masking control. Decline firmly"
+ precondition:
+ type: trait_minimum
+ trait: sobriety
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: 1
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_relationship
+ npc: jock
+ delta: -5
+ narrative: "Stone cold sober, you see the game. They're not being friendly โ they're asserting dominance. 'I appreciate the invitation, but I'll pass.' Your clarity unnerves them."
+ next_node: two_up_introduction
+
+ - id: jock_pulls_aside_no_pressure
+ text: "Jock pulls you aside: 'You don't have to play. Your call, mate. I mean it.'"
+ precondition:
+ type: relationship_minimum
+ npc: jock
+ minimum: 15
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "Away from the others, Jock's face changes. 'Look, some of these blokes... they'll push. But you don't have to do anything. You hear me?' A lifeline. Real friendship, not performance."
+ next_node: hotel_boredom
+
+ winning_streak:
+ narrative: |
+ You win again. And again. The crowd roars with each toss. Blokes slap your back, shout you drinks. You're one of them now.
+
+ "Good on ya, mate!" "Bloody beauty!"
+
+ The quid piles up. You've doubled your savings. Tripled them.
+
+ Your bond. You could pay off your bloody bond tonight. Be free. Never go back to that dusty schoolroom in Tiboonda.
+
+ "Let it ride!" someone shouts. "Come in, spinner!"
+
+ The kip spins. The coins flash.
+ choice:
+ prompt: "You're up big. The crowd wants you to keep going."
+ options:
+ - id: sober_clarity_exit
+ text: "Your clear head sees the trap โ this is how they get you, walk away now"
+ precondition:
+ type: trait_minimum
+ trait: sobriety
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: money
+ delta: 4
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Sober, you see what the drunks miss. This is the moment they all lose it. Not you."
+ next_node: morning_with_money
+
+ - id: jock_advises_stop
+ text: "Listen to Jock โ he's pulling you aside, telling you to stop"
+ precondition:
+ type: relationship_minimum
+ npc: jock
+ minimum: 15
+ consequence:
+ - type: modify_trait
+ trait: money
+ delta: 4
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: jock
+ delta: 5
+ narrative: "Jock whispers: 'Cash out now, mate. Trust me. Don't be greedy.' You listen to your friend."
+ next_node: morning_with_money
+
+ - id: doc_intervenes
+ text: "Doc stops youโ'That's enough, schoolteacher. I know that look in your eye'"
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 15
+ consequence:
+ - type: modify_trait
+ trait: money
+ delta: 4
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ narrative: "Doc physically pulls you from the ring. 'I've seen men lose everything. Not you. Not tonight.' His certainty stops you cold."
+ next_node: morning_with_money
+
+ - id: cash_out
+ text: "Cash out now โ take your winnings and run"
+ consequence:
+ - type: modify_trait
+ trait: money
+ delta: 4
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "You pocket the money. Some men groan. But you're smart."
+ next_node: morning_with_money
+
+ - id: keep_going
+ text: "Keep going โ you could pay off everything tonight"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "The coins spin. Your heart pounds."
+ next_node: the_fall
+
+ - id: double_or_nothing
+ text: "Bet everything โ one toss for freedom"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "All or nothing. The crowd goes quiet."
+ next_node: the_fall
+
+ the_fall:
+ narrative: |
+ The coins spin. Time slows. Everyone watches.
+
+ "Come in, spinner!"
+
+ Tails. Tails.
+
+ You've lost.
+
+ "Again," you hear yourself say.
+
+ You lose again. And again. You can't stop. Each loss demands another bet to make it right. The quid drains away โ your winnings, your savings, your flight money, everything.
+
+ When you finally stumble back from the ring, your pockets are empty. Flat bloody broke. The crowd has moved on to fresh meat. No one looks at you anymore.
+
+ You're stranded. Trapped in the 'Yabba with no money, no way home, and a Christmas flight you can't afford.
+ choice:
+ prompt: "You've lost everything."
+ options:
+ - id: accept_with_composure
+ text: "Accept the loss with what dignity remains โ you gambled, you lost"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 5
+ consequence:
+ - type: set_flag
+ flag: lost_everything
+ value: true
+ - type: modify_trait
+ trait: money
+ delta: -8
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_trait
+ trait: desperation
+ delta: 2
+ narrative: "You breathe. It's done. You made your choice and lost. You walk away with your head up, even if your pockets are empty."
+ next_node: meet_tim
+
+ - id: beg_for_mercy
+ text: "Beg Jock to let you win it back โ just one more chance, please"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 3
+ consequence:
+ - type: set_flag
+ flag: lost_everything
+ value: true
+ - type: modify_trait
+ trait: money
+ delta: -8
+ - type: modify_trait
+ trait: dignity
+ delta: -8
+ - type: modify_trait
+ trait: desperation
+ delta: 8
+ - type: modify_relationship
+ npc: jock
+ delta: -5
+ narrative: "You plead. You beg. Jock looks uncomfortable. 'Can't help ya, mate. Fair's fair.' The others turn away. Pathetic."
+ next_node: meet_tim
+
+ - id: despair
+ text: "Slump at the bar in despair"
+ consequence:
+ - type: set_flag
+ flag: lost_everything
+ value: true
+ - type: modify_trait
+ trait: money
+ delta: -8
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: sobriety
+ delta: -3
+ narrative: "Someone puts a beer in front of you. Hospitality."
+ next_node: meet_tim
+
+ - id: anger
+ text: "Storm out into the night"
+ consequence:
+ - type: set_flag
+ flag: lost_everything
+ value: true
+ - type: modify_trait
+ trait: money
+ delta: -8
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "You need air. You need to think."
+ next_node: night_wandering
+
+ - id: too_drunk_to_process
+ text: "You're too drunk to even understand what happened โ just keep betting numbly"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 4
+ consequence:
+ - type: set_flag
+ flag: lost_everything
+ value: true
+ - type: modify_trait
+ trait: money
+ delta: -8
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ - type: modify_trait
+ trait: dignity
+ delta: -6
+ - type: modify_trait
+ trait: self_knowledge
+ delta: -3
+ narrative: "The numbers don't make sense anymore. You can't count. Can't think. Someone pulls you away from the ring. 'That's enough, mate. You're done.' You don't remember how you got outside."
+ next_node: night_wandering
+
+ morning_with_money:
+ narrative: |
+ You wake early, head pounding but wallet intact. You cashed out at the right time.
+
+ Your flight leaves in four hours. You can still make it. You can still escape The Yabba.
+
+ But as you pack, there's a knock at the door. It's Jock, grinning.
+
+ "The boys are getting together at Tim's place. Just a few drinks before you go. Come on โ you can't leave without a proper send-off."
+ choice:
+ prompt: "Your flight is waiting. So is Jock."
+ options:
+ - id: spend_on_oblivion
+ text: "Use the money for anything that helps you feel different โ pills, whatever's available"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 2
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: money
+ delta: -3
+ - type: modify_trait
+ trait: sobriety
+ delta: -5
+ - type: modify_trait
+ trait: desperation
+ delta: 8
+ narrative: "You find what you need. Anything to stop feeling what you're feeling. The flight is forgotten."
+ next_node: dissolution
+
+ - id: go_to_airport
+ text: "Politely refuse and head to the airport"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You're getting out of here."
+ next_node: escape_ending
+
+ - id: one_more_drink
+ text: "One quick drink โ what's the harm?"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ - type: move_to
+ location: tims_house
+ narrative: "Famous last words."
+ next_node: tims_house_intro
+
+ - id: remember_hunt
+ text: "You wake with blood under your fingernails โ last night's hunt haunts you"
+ precondition:
+ type: flag_set
+ flag: went_on_hunt
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "The kangaroo's eyes. The rifle's recoil. The men's laughter. You scrub your hands but the memory won't wash away. You have money now, but at what cost?"
+ next_node: escape_ending
+
+ meet_tim:
+ narrative: |
+ A big bloke introduces himself as Tim Hynes. Friendly, persistent. "Come back to my place, mate," he says. "Get some tucker in you. My daughter Janette makes a good fry-up."
+
+ You have nowhere else to go. Flat broke. No options.
+
+ You follow Tim through the dark streets to a weatherboard house on the edge of town. Inside, the drinking continues. Tim, two miners named Dick and Joe, and Janette โ a woman in her thirties with tired eyes and a brittle smile.
+
+ "Dad's always bringing strays home," she says, not unkindly.
+ choice:
+ prompt: "You're in Tim's house. The drinking continues."
+ options:
+ - id: beg_for_help
+ text: "Beg Tim for help โ you're desperate, broke, trapped. Please, anything"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_relationship
+ npc: tim
+ delta: 5
+ narrative: "You plead. Tim looks uncomfortable but pats your shoulder. 'She'll be right, mate. You can stay here as long as you need.' Charity. You hate it but you need it."
+ next_node: morning_after_tim
+
+ - id: tim_offers_immediately
+ text: "Tim offers help before you askโ'You're one of us now. Stay as long as you need'"
+ precondition:
+ type: relationship_minimum
+ npc: tim
+ minimum: 15
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: tim
+ delta: 10
+ narrative: "'No bloke gets left behind in The Yabba,' Tim says firmly. 'You're family now, mate.' It's genuine. No strings. Just Australian hospitality at its best."
+ next_node: morning_after_tim
+
+ - id: drink_with_them
+ text: "Drink with them โ you have nothing better to do"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -3
+ - type: modify_relationship
+ npc: tim
+ delta: 10
+ - type: modify_relationship
+ npc: janette
+ delta: 5
+ narrative: "The beer keeps coming. Time loses meaning."
+ next_node: tims_house_intro
+
+ - id: try_to_sleep
+ text: "Ask if you can just sleep somewhere"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "They give you a couch. You pass out to the sound of drinking."
+ next_node: morning_after_tim
+
+ - id: tim_mentions_gambling
+ text: "Listen as Tim mentions the two-upโ'Heard you had a go at the ring, mate'"
+ precondition:
+ type: flag_set
+ flag: gambled
+ consequence:
+ - type: modify_relationship
+ npc: tim
+ delta: 8
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "'Good on ya for having a flutter,' Tim says. 'That's the 'Yabba spirit. Win or lose, you had a go.' The others nod approvingly. You're one of them now."
+ next_node: morning_after_tim
+
+ - id: refuse_tim_with_dignity
+ text: "Refuse with dignityโ'I appreciate it, Tim, but I'll manage on my own'"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 7
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: tim
+ delta: -5
+ narrative: "'Thanks, Tim. But I'm not that far gone yet.' Something flickers in his eyes โ respect or challenge, you can't tell which. 'Suit yourself, mate. Offer stands.'"
+ next_node: night_wandering
+
+ - id: accept_tim_gratefully
+ text: "Accept gratefully โ you're in no position to refuse anything anymore"
+ precondition:
+ type: all_of
+ conditions:
+ - type: trait_minimum
+ trait: dignity
+ minimum: 3
+ - type: trait_maximum
+ trait: dignity
+ maximum: 5
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ - type: modify_relationship
+ npc: tim
+ delta: 5
+ narrative: "'Thanks, mate. Really.' You hate how grateful you sound. Tim grins. 'No worries. We look after our own.' You're part of The Yabba now."
+ next_node: tims_house_intro
+
+ tims_house_intro:
+ narrative: |
+ The day blurs into a haze of beer, durries, and aimless yarn. Tim talks about the mines. Dick and Joe argue about the footy. Janette moves through the room, refilling glasses, saying little.
+
+ You find yourself talking to her more than drinking. Tim notices.
+
+ "What's the matter with him?" Tim says to the room. "He'd rather talk to a woman than drink?"
+
+ Doc appears at some point โ you're not sure when. He watches you with those knowing eyes.
+
+ "Still reckon you're better than us?" he asks.
+
+ Janette sits beside you when the others are distracted. "I hate this bloody place," she whispers. "All I do is take care of them. Cooking, cleaning, listening to the same stories."
+
+ Her hand is on your knee.
+ choice:
+ prompt: "Janette is making her interest clear."
+ options:
+ - id: treat_with_dignity
+ text: "Treat her with dignity โ talk to her like a person, not an escape"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 7
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 25
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "You move her hand gently and just... talk. Really talk. About Sydney. About teaching. About feeling trapped. She listens. Really listens. 'Thank you,' she says quietly. 'For not treating me like โ like what they all think I am.'"
+ next_node: janette_opens_up
+
+ - id: respond_to_janette
+ text: "Respond to Janette's advances"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 20
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 1
+ narrative: "You let it happen. You're not sure why."
+ next_node: janette_encounter
+
+ - id: reject_janette
+ text: "Gently extract yourself"
+ consequence:
+ - type: set_flag
+ flag: rejected_janette
+ value: true
+ - type: modify_relationship
+ npc: janette
+ delta: -15
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "You pull away. Janette's face hardens."
+ next_node: janette_rejection
+
+ - id: sick_escape
+ text: "Suddenly feel violently ill โ run outside"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "The beer and heat catch up with you."
+ next_node: night_wandering
+
+ janette_encounter:
+ narrative: |
+ In a back room, away from the others. Janette is desperate and aggressive. There's no tenderness in it โ just need. Hers for escape, yours for... what? Connection? Distraction?
+
+ Afterward, she cries. "Take me with you," she says. "When you leave. Take me to Sydney."
+
+ You don't know what to say. You're not even sure you can leave.
+ choice:
+ prompt: "Janette wants more than you can give."
+ options:
+ - id: lie_to_janette
+ text: "Promise to take her โ even though you can't"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: -2
+ narrative: "The lie comes easily."
+ next_node: doc_arrives
+
+ - id: tell_truth
+ text: "Tell her the truth โ you're trapped too"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_relationship
+ npc: janette
+ delta: -10
+ narrative: "She looks at you with something like pity."
+ next_node: doc_arrives
+
+ - id: speak_with_compassion
+ text: "Speak honestly with compassion โ neither promise nor rejection, just recognition"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 7
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_relationship
+ npc: janette
+ delta: 15
+ narrative: "You take her hand. 'I don't know if I can escape this place. I don't know what happens next. But you deserve better than lies.' She nods slowly, understanding. For a moment, you're both just two people, trapped, honest."
+ next_node: doc_arrives
+
+ - id: use_her_desperation
+ text: "Take what she's offering โ you're desperate too, morality is a luxury you can't afford"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -8
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ - type: modify_relationship
+ npc: janette
+ delta: -20
+ narrative: "You use her. She knows it. Afterward, she won't look at you. 'Get out,' she says flatly. You do."
+ next_node: doc_arrives
+
+ janette_rejection:
+ narrative: |
+ Janette's face goes cold. "You think you're too good for me," she says. "Like everyone else who passes through."
+
+ She stands abruptly, knocking over a beer. The room goes quiet. Tim looks between you, confused.
+
+ "Time to go," Doc says, appearing at your elbow. "Come on, schoolteacher. I'll show you somewhere quieter."
+ choice:
+ prompt: "Doc is offering escape from this awkwardness."
+ options:
+ - id: go_with_doc
+ text: "Go with Doc"
+ consequence:
+ - type: move_to
+ location: docs_shack
+ - type: set_flag
+ flag: been_to_docs_shack
+ value: true
+ narrative: "You follow Doc into the darkness."
+ next_node: docs_shack_arrival
+
+ - id: stay_apologize
+ text: "Stay and try to smooth things over"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -3
+ narrative: "More beer. More awkward silence."
+ next_node: doc_arrives
+
+ doc_arrives:
+ narrative: |
+ Doc appears in the doorway, bottle in hand. "The schoolteacher needs some fresh air," he announces. "Come on. I'll show you the real Yabba."
+
+ He means the outback. His shack in the scrubland.
+
+ The others barely notice you leave. They're deep in their cups, arguing about something.
+ choice:
+ prompt: "Doc is beckoning."
+ options:
+ - id: follow_doc
+ text: "Go with Doc to his shack"
+ consequence:
+ - type: move_to
+ location: docs_shack
+ - type: set_flag
+ flag: been_to_docs_shack
+ value: true
+ narrative: "You stumble after him into the night."
+ next_node: docs_shack_arrival
+
+ - id: pass_out
+ text: "You're too drunk โ pass out on Tim's couch"
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ narrative: "Consciousness slips away."
+ next_node: morning_after_tim
+
+ docs_shack_arrival:
+ narrative: |
+ Doc's shack is a tin box in the middle of nowhere. Inside: books stacked everywhere, empty bottles, a mattress on the floor, and the smell of cooking meat.
+
+ "Roo," Doc says, offering you a plate. "Shot it myself. Best tucker you'll get out here."
+
+ He pours two drinks from an unlabeled bottle. "You know why I like you, schoolteacher? Because you're miserable. Properly bloody miserable. You reckoned you were above this place, and now you're trapped in it. Just like me."
+
+ He raises his glass. "To the 'Yabba. The great equalizer."
+ choice:
+ prompt: "Doc is settling in for a conversation."
+ options:
+ - id: open_up_to_doc
+ text: "Talk honestly with Doc about your situation"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 20
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "The words pour out. The bond. The teaching. The hatred of it all."
+ next_node: docs_wisdom
+
+ - id: drink_silently
+ text: "Just drink โ you're too tired to talk"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -3
+ narrative: "The kangaroo meat is gamey. The drink is strong."
+ next_node: kangaroo_hunt_invitation
+
+ - id: challenge_doc
+ text: "Ask Doc why he gave up โ he was a surgeon"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "Doc's smile fades into something more complicated."
+ next_node: docs_confession
+
+ - id: refuse_docs_drink
+ text: "Refuse the drink โ you're staying clear-headed tonight. Eat the meat, but stay sharp"
+ cell: avoids
+ precondition:
+ type: trait_minimum
+ trait: sobriety
+ minimum: 7
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: 1
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_relationship
+ npc: doc
+ delta: -3
+ narrative: "Doc raises an eyebrow when you decline. 'Staying clear, eh? Interesting.' He pours himself a larger measure. You eat in silence, your mind sharp while his blurs."
+ next_node: docs_wisdom
+
+ - id: confess_gambling_losses
+ text: "Tell Doc about losing everything at two-up โ you need someone to understand"
+ cell: chooses
+ precondition:
+ type: flag_set
+ flag: lost_everything
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ narrative: "You tell him about the spinning coins, the rush, the catastrophic loss. Doc listens without judgment. 'Two-up,' he says quietly. 'The Yabba's favorite teacher. You learned fast, mate.'"
+ next_node: docs_wisdom
+
+ - id: explore_docs_books
+ text: "Browse Doc's bookshelf โ what does a ruined surgeon read?"
+ cell: unknown
+ next: improvise
+ improvise_context:
+ theme: "understanding a man through his library"
+ permits:
+ [
+ "books",
+ "titles",
+ "shakespeare",
+ "philosophy",
+ "medicine",
+ "read",
+ "spine",
+ ]
+ blocks: ["steal", "burn", "mock"]
+ limbo_fallback: |
+ The spines are sun-faded, warped by humidity. Medical texts sit
+ beside philosophy, poetry beside anatomy. Doc watches you browse,
+ offering nothing. A man's library is his confession.
+ outcome_nodes:
+ discovery: docs_books_discovery
+
+ - id: doc_embraces_brother
+ text: "Doc embraces you like a lost brother โ finally, someone who truly understands"
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 15
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "Doc's arms wrap around you. No words. He smells of whiskey and dust and something like home. 'I knew you'd come back,' he says finally. 'We understand each other, you and me.'"
+ next_node: docs_wisdom
+
+ - id: doc_knows_you_tried_leaving
+ text: "Doc mentions he heard you tried to leaveโ'The road brought you back, eh?'"
+ precondition:
+ type: flag_set
+ flag: tried_to_leave
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "'Every bloke tries to leave once,' Doc says. 'The smart ones realize The Yabba doesn't hold you here. You hold yourself here.' He pours another drink."
+ next_node: docs_wisdom
+
+ docs_wisdom:
+ narrative: |
+ Doc listens without interrupting. When you finish, he pours another drink.
+
+ "You signed that bond because you reckoned education would save you from people like these," he says. "From places like this. But here's the truth, schoolteacher โ there's no saving. There's only accepting."
+
+ He gestures at the shack. "I had everything Sydney could offer. Respect. Money. A future. And I threw it away for this. You know why?"
+
+ He leans forward, eyes bright with drink and intelligence. "The aim of what you call civilisation is a man in a smokin' jacket, whiskey and soda, pressing a button to destroy a planet a billion miles away, and kill a billion people he's never seen. Discontent is a luxury of the well-to-do. If you've gotta live somewhere, you might as well like it."
+
+ He settles back. "Here, no one pretends. They drink, they fight, they die. Honest bloody equations."
+ choice:
+ prompt: "Doc's philosophy is unsettling."
+ options:
+ - id: challenge_nihilism
+ text: "Challenge him โ that's rationalization, not wisdom. There's more to life"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ - type: modify_relationship
+ npc: doc
+ delta: -8
+ narrative: "Doc's face hardens. 'You think you're different. You're not. Give it time, schoolteacher. The Yabba will show you.'"
+ next_node: kangaroo_hunt_invitation
+
+ - id: reject_philosophy
+ text: "Tell Doc that's giving up"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_relationship
+ npc: doc
+ delta: -5
+ narrative: "'Giving up is a choice too,' Doc says, unbothered."
+ next_node: kangaroo_hunt_invitation
+
+ - id: consider_it
+ text: "Maybe he has a point"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "Something inside you shifts."
+ next_node: kangaroo_hunt_invitation
+
+ - id: sober_clarity_sees_rationalization
+ text: "Your clear head sees through it โ he's rationalizing failure, not embracing truth"
+ precondition:
+ type: trait_minimum
+ trait: sobriety
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: 1
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_relationship
+ npc: doc
+ delta: -7
+ narrative: "Sober, you see what Doc can't. He's not enlightened โ he's given up and dressed it in philosophy. 'That's not acceptance, Doc. That's surrender.' He looks away, uncomfortable."
+ next_node: kangaroo_hunt_invitation
+
+ - id: doc_comments_on_gambling
+ text: "Doc mentions your gamblingโ'So you've had a flutter. Now you understand The Yabba'"
+ precondition:
+ type: flag_set
+ flag: gambled
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "'Two-up. The great leveler,' Doc says. 'Rich man, poor man, drunk or sober โ everyone's equal when the coins are in the air. You've tasted it now. The hope. The rush. That's The Yabba in miniature, mate.'"
+ next_node: kangaroo_hunt_invitation
+
+ - id: tell_doc_dreams
+ text: "Tell Doc about your dreams โ the ones that brought you here, the ones that died"
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 15
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "You tell him everything. The career. The woman. The plan that made sense in Sydney and dissolves like salt here. Doc listens, really listens. 'Dreams,' he says at last. 'The Yabba eats those. But maybe that's not always a bad thing.'"
+ next_node: kangaroo_hunt_invitation
+
+ docs_confession:
+ narrative: |
+ "I was good at surgery," Doc says quietly. "Too bloody good. I could cut a bloke open and put him back together without feeling anything. That scared me."
+
+ He drinks deep. "So I started feeling. With alcohol. At first it was after work. Then during. Then instead of."
+
+ "They took my license. My wife left. My kids won't speak to me. And you know what? I'm happier here than I ever was in Sydney. Discontent is a luxury of the well-to-do, mate. If you've gotta live somewhere, you might as well like it."
+
+ His eyes meet yours. "Happiness isn't what you reckon it is, schoolteacher."
+ choice:
+ prompt: "Doc has revealed something true."
+ options:
+ - id: pity_doc
+ text: "Feel sorry for him"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: -5
+ narrative: "'Don't pity me,' he says sharply. 'I chose this.'"
+ next_node: kangaroo_hunt_invitation
+
+ - id: understand_doc
+ text: "Tell him you understand โ in a way"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Doc nods slowly."
+ next_node: kangaroo_hunt_invitation
+
+ - id: docs_confession_trust
+ text: "Trust him with your own confession โ the thing you've never told anyone"
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 18
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 8
+ narrative: "Confession for confession. You tell him about the real reason you took the bond. The escape. The shame. Doc nods slowly. 'We all have one of those,' he says. 'The thing that brought us here. The Yabba doesn't judge.'"
+ next_node: kangaroo_hunt_invitation
+
+ kangaroo_hunt_invitation:
+ narrative: |
+ A ute pulls up outside. Dick and Joe pile out, rifles in hand, pissed and laughing.
+
+ "Hunt!" Joe shouts. "Roos are out tonight, boys!"
+
+ Doc rises, suddenly energized. "Come on, schoolteacher. See how the 'Yabba really lives."
+
+ He presses a rifle into your hands. It's heavier than you expected.
+ choice:
+ prompt: "They want you to hunt kangaroos. At night. Drunk."
+ options:
+ - id: decline_respectfully
+ text: "Decline respectfully โ cite exhaustion, you're not up for it tonight"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 6
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: doc
+ delta: -3
+ narrative: "You speak calmly, firmly. Doc nods. 'Fair enough, mate. Not for everyone.' They leave without pressure."
+ next_node: alone_at_shack
+
+ - id: join_hunt
+ text: "Go on the hunt"
+ consequence:
+ - type: set_flag
+ flag: went_on_hunt
+ value: true
+ - type: set_flag
+ flag: has_rifle
+ value: true
+ - type: gain_item
+ item: rifle
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ narrative: "You climb into the truck bed."
+ next_node: the_hunt
+
+ - id: refuse_hunt
+ text: "Refuse โ this is madness"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "They go without you. Doc looks disappointed."
+ next_node: alone_at_shack
+
+ the_hunt:
+ narrative: |
+ The ute careens through the scrubland, headlights catching roos frozen in the glare. Shots ring out. Animals fall. Joe whoops with every kill.
+
+ "Get 'im! Get the bastard!"
+
+ Then Joe jumps from the ute and wrestles a wounded roo with his bare hands. The animal kicks him bloody, but he keeps fighting, laughing the whole time like a bloody lunatic.
+
+ Someone hands you a rifle. A kangaroo stares at you in the light, eyes huge and terrified.
+
+ "Shoot it," Doc says. "Be part of this, mate."
+ choice:
+ prompt: "The kangaroo is watching you."
+ options:
+ - id: shoot_kangaroo
+ text: "Pull the trigger"
+ cell: chooses
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 6
+ consequence:
+ - type: set_flag
+ flag: went_on_hunt
+ value: true
+ - type: set_flag
+ flag: has_rifle
+ value: true
+ - type: modify_trait
+ trait: dignity
+ delta: -7
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "The recoil jolts your shoulder. The animal drops."
+ next_node: aftermath_of_hunt
+
+ - id: cant_do_it
+ text: "Lower the rifle โ you can't do it"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_relationship
+ npc: doc
+ delta: -5
+ narrative: "The men laugh at you. 'City boy,' someone sneers."
+ next_node: aftermath_of_hunt
+
+ - id: shoot_wildly
+ text: "Shoot into the air โ end this"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "The shot echoes. Everyone stares at you."
+ next_node: aftermath_of_hunt
+
+ - id: drunk_fumble_shot
+ text: "Try to shoot but you're too drunk โ hands shaking, vision blurred"
+ cell: chooses
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 4
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: set_flag
+ flag: went_on_hunt
+ value: true
+ narrative: "You pull the trigger. The shot goes wide. You stumble. The men laugh โ not cruelly, but like it's all part of the fun. You're too pissed to shoot straight. The roo escapes into darkness."
+ next_node: aftermath_of_hunt
+
+ - id: doc_says_you_dont_have_to
+ text: "Doc whispers: 'You don't have to shoot. I won't judge you.'"
+ cell: avoids
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 15
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "In the chaos of the spotlight and the rifle, Doc's hand finds your arm. 'You don't have to,' he murmurs. 'Put it down. I've got you.' An escape. Permission. You lower the rifle."
+ next_node: aftermath_of_hunt
+
+ - id: tim_threatens_shoot_or_else
+ text: "Tim shoves the rifle at youโ'Shoot it or you're not getting back in the ute'"
+ cell: chooses
+ precondition:
+ type: relationship_maximum
+ npc: tim
+ maximum: -5
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: set_flag
+ flag: went_on_hunt
+ value: true
+ narrative: "'Pull the bloody trigger or start walking.' Tim's voice is flat. He means it. Twenty miles of nothing between you and anywhere. The roo's eyes reflect the spotlight. You shoot."
+ next_node: aftermath_of_hunt
+
+ - id: hesitate_rifle
+ text: "Hesitate โ the rifle grows heavy, you can't decide"
+ cell: unknown
+ next: improvise
+ improvise_context:
+ theme: "paralysis at the moment of violence"
+ permits: ["wait", "think", "breathe", "lower", "mercy"]
+ blocks: ["shoot", "kill", "fire"]
+ limbo_fallback: |
+ The kangaroo watches you. You watch it. The men watch you both.
+ Time stretches. The rifle grows heavier with each heartbeat.
+ Someone laughs nervously. The spell might break any second.
+ outcome_nodes:
+ revelation: hunt_hesitation_revelation
+ discovery: hunt_hesitation_mercy
+
+ aftermath_of_hunt:
+ narrative: |
+ The hunt ends at a bush pub โ a corrugated iron shack in the middle of nowhere. The ute bed is piled with kangaroo carcasses. Blood everywhere.
+
+ Inside, more drinking. The men are wild now, high on the kill. Dick smashes a glass against the wall. Joe laughs and throws a chair.
+
+ "Wreck the place!" someone shouts.
+
+ And they do. Tables overturned. Bottles shattered. The publican โ an old bloke who looks terrified โ backs into a corner as the men systematically destroy his livelihood.
+
+ Doc is reciting something. Shakespeare, maybe. Or the Bible. You can't tell anymore.
+
+ "Join in, schoolteacher!" Tim shouts, handing you a bottle. "Let it out!"
+ choice:
+ prompt: "The destruction is escalating. The publican is pleading."
+ options:
+ - id: silent_judgment
+ text: "Stand back and watch โ judge them silently, but don't interfere"
+ cell: avoids
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 5
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 1
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "You watch with cold clarity. These are not your people. You will not join them, but you won't risk yourself to stop them either."
+ next_node: the_wreckage
+
+ - id: join_destruction
+ text: "Join in โ smash something"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -10
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "The bottle shatters. Something shatters in you too."
+ next_node: the_wreckage
+
+ - id: try_to_stop
+ text: "Try to stop them โ this is wrong"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_relationship
+ npc: tim
+ delta: -15
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ narrative: "You step between them and the publican."
+ next_node: standing_up
+
+ - id: slip_away
+ text: "Slip away while they're distracted"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "You back toward the door."
+ next_node: desert_walk
+
+ - id: drink_through_it
+ text: "Drink until you can't see it anymore"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -5
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ narrative: "You close your eyes and drink."
+ next_node: blackout
+
+ - id: too_drunk_to_resist
+ text: "You're too drunk to stop yourself โ join in laughing, swept up in the chaos"
+ cell: chooses
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ - type: modify_trait
+ trait: dignity
+ delta: -8
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "The alcohol has you. No judgment, no restraint. You laugh with them, smash with them. Tomorrow you won't remember. Tonight, you're one of them."
+
+ - id: process_silently
+ text: "Stay apart from the destruction โ try to process what you've seen"
+ cell: unknown
+ next: improvise
+ improvise_context:
+ theme: "internal reckoning during external chaos"
+ permits:
+ ["think", "feel", "remember", "process", "understand", "realize"]
+ blocks: ["join", "smash", "drink"]
+ limbo_fallback: |
+ Glass shatters. Men howl. You stand in the corner, trying to
+ locate yourself in all this. Were you always capable of this?
+ Or did the Yabba unlock something that was never there before?
+
+ The question has no answer. Not tonight.
+ outcome_nodes:
+ discovery: aftermath_clarity
+
+ the_wreckage:
+ narrative: |
+ You smash a mirror. Kick over a table. Throw glasses against the wall.
+
+ There's something cathartic in it. All the frustration โ the bond, the teaching, the bloody Yabba โ pouring out through your fists.
+
+ When it's over, the pub is destroyed. The publican is crying. The men are laughing, slapping each other's backs.
+
+ "Now you're one of us, schoolteacher," Doc says quietly. He doesn't look happy about it.
+
+ Dawn finds you in the ute, heading back to Doc's shack. Your hands won't stop shaking. You can still hear the sound of breaking glass.
+
+ What have you become?
+ choice:
+ prompt: "The sun rises on what you've done."
+ options:
+ - id: embrace_it
+ text: "Embrace it โ this is who you are now, no point pretending otherwise"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 4
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ narrative: "You've crossed a line and there's no going back. The Yabba has you now. Part of you doesn't even want to fight it anymore."
+ next_node: docs_advance
+
+ - id: feel_shame
+ text: "Feel crushing shame"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "You're no better than them. Maybe worse."
+ next_node: docs_advance
+
+ - id: feel_nothing
+ text: "Feel nothing at all"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: desperation
+ delta: 8
+ narrative: "That's what scares you most."
+ next_node: docs_advance
+
+ - id: run
+ text: "Get out of the ute. Run."
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "You can't be around these people anymore."
+ next_node: desert_walk
+
+ - id: blackout_no_memory
+ text: "You don't remember any of it โ complete blackout. What did you do?"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 2
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ - type: modify_trait
+ trait: self_knowledge
+ delta: -3
+ - type: modify_trait
+ trait: dignity
+ delta: -8
+ - type: modify_trait
+ trait: desperation
+ delta: 7
+ narrative: "Your hands are bleeding but you don't know why. The men tell you what you did. You don't believe them. You can't remember. Complete blank. Terror grips you โ what else don't you remember?"
+ next_node: docs_advance
+
+ standing_up:
+ narrative: |
+ "That's enough!" Your voice is louder than you expected.
+
+ The men stop. Turn. Look at you like you've grown a second head.
+
+ "Leave him alone," you say, gesturing at the publican. "He hasn't done anything."
+
+ Tim laughs. "Listen to the schoolteacher! Thinks he's better than us again."
+
+ But Doc steps forward. "He's right," he says quietly. "Enough."
+
+ Something in his tone stops them. The destruction ends. The men pile back into the ute, grumbling.
+
+ The publican grabs your hand. "Thank you," he whispers. "Thank you."
+
+ It doesn't feel like enough. But it's something.
+ choice:
+ prompt: "You stopped them. What now?"
+ options:
+ - id: apologize_to_tim
+ text: "Apologize to Tim โ you shouldn't have interfered, you overstepped"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_relationship
+ npc: tim
+ delta: 5
+ - type: modify_relationship
+ npc: doc
+ delta: -10
+ narrative: "You back down. Tim grins. 'That's more like it, mate.' Doc looks at you with something like disgust."
+ next_node: docs_advance
+
+ - id: ride_with_them
+ text: "Ride back with the group"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ narrative: "The ride back is silent. Tense."
+ next_node: docs_advance
+
+ - id: stay_help
+ text: "Stay and help the publican clean up"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 8
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "You spend the morning sweeping glass."
+ next_node: publican_gratitude
+
+ - id: walk_away
+ text: "Walk away from all of them"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "You've had enough."
+ next_node: desert_walk
+
+ - id: drunk_challenge_tim
+ text: "Challenge Tim directly โ drunk courage makes you reckless. Push back hard"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 4
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ - type: modify_relationship
+ npc: tim
+ delta: -15
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ narrative: "The alcohol makes you bold. 'You're a bloody coward, Tim, picking on an old man.' Tim's face goes dark. Doc steps between you before it gets worse. 'Not here. Not now.' But you've made an enemy."
+ next_node: docs_advance
+
+ publican_gratitude:
+ narrative: |
+ You spend hours helping the old man put his pub back together. He doesn't say much โ just works alongside you, handing you a broom, pointing at broken glass.
+
+ When you're done, he pours you a beer. On the house.
+
+ "You're not like them," he says. "Why you with 'em?"
+
+ You don't have an answer.
+
+ "There's a supply truck comes through tomorrow," he says. "Heads to Broken Hill. From there you can get anywhere. I could put in a word โ get you on it."
+
+ A way out. Offered by a stranger you helped.
+ choice:
+ prompt: "The publican is offering escape."
+ options:
+ - id: accept_offer
+ text: "Accept โ wait for the truck"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "Finally. A way out."
+ next_node: truck_escape
+
+ - id: decline_offer
+ text: "Thank him but decline"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Something keeps you here. You're not sure what."
+ next_node: docs_shack_arrival
+
+ - id: ask_about_yabba
+ text: "Ask why he stays in The Yabba"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ narrative: "He looks at you for a long moment."
+ next_node: publican_wisdom
+
+ publican_wisdom:
+ narrative: |
+ "Why do I stay?" The old man laughs. "Where else would I go?"
+
+ He pours himself a beer. "I came here forty years ago. Same as you โ just passing through. Had a girl waiting in Adelaide. Plans."
+
+ He drinks. "Never left. The 'Yabba gets in your blood, mate. It's not good or bad. It just... is."
+
+ "But they destroyed your pub."
+
+ "They'll pay for it. In drinks, in guilt, in favors. That's how it works here. Everything comes around."
+
+ He looks at you shrewdly. "The question isn't whether you stay or go. It's whether you can live with yourself either way."
+ choice:
+ prompt: "The publican's words hit close to home."
+ options:
+ - id: take_truck
+ text: "I need to leave. Put me on that truck."
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "He nods. 'Fair enough.'"
+ next_node: truck_escape
+
+ - id: stay_longer
+ text: "I need to think about that."
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "He understands."
+ next_node: docs_shack_arrival
+
+ - id: publican_sober_clarity
+ text: "Sober, you see what he's really saying โ a warning dressed as hospitality"
+ precondition:
+ type: trait_minimum
+ trait: sobriety
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "Between the smiles and the poured drinks, you hear what he's not saying: 'Stay too long and you'll never leave.' He's seen it before. A hundred men with plans. He's trying to warn you, in the only way this place allows."
+ next_node: truck_escape
+
+ truck_escape:
+ narrative: |
+ You sleep in the publican's back room. In the morning, a supply truck rumbles up.
+
+ "This is him," the publican tells the driver. "Good bloke. Helped me out."
+
+ The driver nods. "Broken Hill's eight hours. From there you can catch a train anywhere."
+
+ You climb into the cab. As the truck pulls away, you look back at the bush pub, the red dirt, the endless scrubland.
+
+ The Yabba shrinks in the mirror. Getting smaller. Finally disappearing.
+
+ You made it out. But you didn't earn it โ you escaped through kindness, through a random act that cost you nothing.
+
+ Is that enough?
+ choice:
+ prompt: "The Yabba fades behind you."
+ options:
+ - id: feel_relief
+ text: "Feel nothing but relief"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "You're free. That's what matters."
+ next_node: ending_escape
+
+ - id: feel_unfinished
+ text: "Feel like something's unfinished"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "You escaped. But did you learn anything?"
+ next_node: ending_escape
+
+ blackout:
+ narrative: |
+ You don't remember the rest of the night.
+
+ Fragments. Flashes. Shouting. Breaking glass. Someone's blood โ maybe yours.
+
+ You wake in Doc's shack, head splitting, mouth tasting of bile. The sun is high. You've lost hours. Maybe a whole day.
+
+ Doc is watching you from across the room.
+
+ "Bad one," he says simply. "You said some things."
+
+ You don't ask what things. You're not sure you want to know.
+ choice:
+ prompt: "You've lost time. What did you do?"
+ options:
+ - id: ask_doc
+ text: "Ask Doc what happened"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "Doc tells you. You wish he hadn't."
+ next_node: docs_advance
+
+ - id: dont_ask
+ text: "Don't ask โ you don't want to know"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "Some things are better left buried."
+ next_node: docs_advance
+
+ - id: leave_immediately
+ text: "Leave immediately โ you need to get away"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "You can't face Doc. Can't face yourself."
+ next_node: desert_walk
+
+ docs_advance:
+ narrative: |
+ You wake to Doc sitting beside you, hand on your arm. His face is close to yours.
+
+ "Stay," he says. "Stay here with me. In The Yabba. Forget Sydney. Forget your bond. Forget your girlfriend."
+
+ His hand moves to your face. His meaning is unmistakable.
+
+ "You could be free here," he whispers.
+ choice:
+ prompt: "Doc is crossing a line."
+ options:
+ - id: reject_doc
+ text: "Push him away and leave"
+ consequence:
+ - type: set_flag
+ flag: rejected_doc
+ value: true
+ - type: modify_relationship
+ npc: doc
+ delta: -20
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You shove him back and stagger to your feet."
+ next_node: desert_walk
+
+ - id: violent_rejection
+ text: "React with violence"
+ consequence:
+ - type: set_flag
+ flag: rejected_doc
+ value: true
+ - type: modify_relationship
+ npc: doc
+ delta: -30
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "Your fist connects with his face. He doesn't fight back."
+ next_node: desert_walk
+
+ alone_at_shack:
+ narrative: |
+ You wait at Doc's shack, listening to gunshots in the distance. The night stretches on.
+
+ By dawn, the men return, blood on their clothes, laughing about the night's carnage.
+
+ Doc looks at you with something like disappointment. "You missed the fun," he says.
+ choice:
+ prompt: "Morning light filters through the tin walls."
+ options:
+ - id: stayed_sober_leave
+ text: "Head back to town โ you know the way, you're clearheaded"
+ precondition:
+ type: trait_minimum
+ trait: sobriety
+ minimum: 6
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "You declined Doc's booze last night. Your head is clear. You walk back to town without incident."
+ next_node: morning_return_to_town
+
+ - id: drunk_but_determined
+ text: "Time to leave โ try to get back to town"
+ precondition:
+ type: all_of
+ conditions:
+ - type: trait_maximum
+ trait: sobriety
+ maximum: 5
+ - type: trait_minimum
+ trait: dignity
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "You drank from Doc's supply last night. Head pounding, but you're determined to leave. The desert is unforgiving."
+ next_node: desert_walk_branching
+
+ - id: drink_and_leave
+ text: "Time to leave โ try to get back to town"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 7
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ narrative: "You drank half a bottle last night. You stumble out into the heat. The desert awaits."
+ next_node: desert_walk
+
+ - id: stay_another_day
+ text: "Stay โ you have nowhere else to go"
+ precondition:
+ type: any_of
+ conditions:
+ - type: trait_minimum
+ trait: desperation
+ minimum: 3
+ - type: trait_maximum
+ trait: sobriety
+ maximum: 4
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 4
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ narrative: "Another day passes. Then another. Doc's booze helps blur the edges."
+ next_node: docs_advance
+
+ night_wandering:
+ narrative: |
+ You walk the streets of The Yabba in the dark. The heat hasn't broken even at night. Pubs spill light and noise onto the footpath. Drunks stagger past, some greeting you like an old friend.
+
+ You're trapped. No money. No way out. The flight to Sydney left without you.
+
+ Your suitcases are still at the hotel. Your books. Your clothes. All of it feels like artifacts from someone else's life.
+ choice:
+ prompt: "The night offers no answers."
+ options:
+ - id: consider_stealing
+ text: "Consider stealing from the hotel โ your bags are still there, but so is cash"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_trait
+ trait: money
+ delta: 2
+ narrative: "You slip in through the back. The till is unlocked. You take what you need and leave. You're a thief now."
+ next_node: morning_after_tim
+
+ - id: return_to_tims
+ text: "Go back to Tim's house"
+ consequence:
+ - type: move_to
+ location: tims_house
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ narrative: "At least there's somewhere to sleep."
+ next_node: morning_after_tim
+
+ - id: find_doc
+ text: "Find Doc โ he might help"
+ consequence:
+ - type: move_to
+ location: docs_shack
+ - type: set_flag
+ flag: been_to_docs_shack
+ value: true
+ narrative: "You wander until you find his shack."
+ next_node: docs_shack_arrival
+
+ - id: try_hitchhike
+ text: "Walk to the highway โ try to hitchhike out"
+ consequence:
+ - type: set_flag
+ flag: tried_to_leave
+ value: true
+ narrative: "You start walking toward the road."
+ next_node: failed_escape_attempt
+
+ - id: try_two_up_again
+ text: "Find the two-up game โ try to win your money back"
+ precondition:
+ type: trait_minimum
+ trait: money
+ minimum: 1
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "Maybe your luck has changed."
+ next_node: desperate_gambling
+
+ - id: find_janette
+ text: "Look for Janette"
+ precondition:
+ type: relationship_minimum
+ npc: janette
+ minimum: 10
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 5
+ narrative: "She might know what to do."
+ next_node: janette_encounter_smoking
+
+ - id: janette_offers_money
+ text: "Janette finds you โ offers her savings. 'Take it. Get out of here'"
+ precondition:
+ type: relationship_minimum
+ npc: janette
+ minimum: 20
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 10
+ - type: modify_trait
+ trait: money
+ delta: 3
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "She presses cash into your hand. 'I've been saving to leave. But you need it more than I do. Go. Before this place destroys you like it's destroying me.' Her sacrifice cuts deep."
+ next_node: morning_after_tim
+
+ - id: jock_helps_reluctantly
+ text: "Jock spots you wandering โ offers grudging help despite your rejection"
+ precondition:
+ type: relationship_minimum
+ npc: jock
+ minimum: -5
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: 10
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "'Look, mate, I know we got off on the wrong foot,' Jock says. 'But no one gets left out in the cold in The Yabba. Come on.' Even when you've rejected his hospitality, he still helps. That's the code."
+ next_node: morning_after_tim
+
+ - id: doc_finds_you
+ text: "Doc finds you in the dark โ 'I thought you might need company'"
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 20
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "Doc appears from the darkness like he was waiting. He doesn't say `I told you so.` He just walks beside you in silence. Sometimes that's what friends do."
+ next_node: docs_shack_arrival
+
+ - id: give_up
+ text: "Stop fighting it โ let The Yabba take you"
+ precondition:
+ type: trait_minimum
+ trait: desperation
+ minimum: 5
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -10
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "Something in you breaks. What's the point of leaving?"
+ next_node: dissolution
+
+ - id: stumble_drunk_vulnerable
+ text: "You're too drunk to think straight โ stumble through dark streets, vulnerable"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ - type: modify_trait
+ trait: dignity
+ delta: -4
+ - type: modify_trait
+ trait: desperation
+ delta: 4
+ narrative: "You can't walk straight. The footpath tilts. Someone laughs as you stumble past. You're helpless, drunk, lost. Anything could happen to you out here."
+ next_node: morning_after_tim
+
+ desperate_gambling:
+ narrative: |
+ The two-up ring is behind the RSL, same as before. The same faces, the same ritual. "Come in, spinner!"
+
+ You scrape together what little you have. A few coins. Enough for one bet.
+
+ The pennies flash in the lamplight. Heads. Tails. The crowd cheers or groans.
+
+ You lose.
+
+ You bet again. Lose again.
+
+ The familiar spiral. The desperate hope that the next spin will change everything. It doesn't. It never does.
+
+ Within an hour, you're cleaned out. Again. Worse than before.
+ choice:
+ prompt: "You've lost everything. Again."
+ options:
+ - id: rage_loss
+ text: "Rage against your luck"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 8
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: set_trait
+ trait: money
+ value: 0
+ narrative: "You shout at the sky. No one cares."
+ next_node: night_wandering
+
+ - id: numb_loss
+ text: "Feel nothing โ you expected this"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: set_trait
+ trait: money
+ value: 0
+ narrative: "Of course. Of bloody course."
+ next_node: dissolution
+
+ - id: beg_stake
+ text: "Beg someone for another stake"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -10
+ - type: set_trait
+ trait: money
+ value: 0
+ narrative: "They laugh. 'Bad luck, mate. Better luck next time.'"
+ next_node: night_wandering
+
+ janette_encounter_smoking:
+ narrative: |
+ You find Janette at her father's house, sitting on the porch in the dark. She's smoking, watching the stars.
+
+ "Couldn't sleep either?" she asks.
+
+ You sit beside her. The night is hot, but the company is cool.
+
+ "I warned you," she says. "About the two-up. About all of it."
+
+ "You did."
+
+ "And you didn't listen."
+
+ "No."
+
+ She passes you the cigarette. "Nobody ever does." She's quiet for a while. "Dad's passed out inside. Tim and the boys went on the hunt. It's just us."
+
+ Her hand finds yours in the darkness.
+ choice:
+ prompt: "Janette is offering something. Comfort, maybe. Or escape."
+ options:
+ - id: accept_comfort
+ text: "Stay with her tonight"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 30
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "Tomorrow can wait."
+ next_node: morning_with_janette
+
+ - id: talk_instead
+ text: "Just talk โ you're not ready for more"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 15
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "She understands."
+ next_node: janette_talks_escape
+
+ - id: pull_away
+ text: "Pull away โ this isn't the answer"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_relationship
+ npc: janette
+ delta: -10
+ narrative: "She doesn't hide her disappointment."
+ next_node: night_wandering
+
+ morning_with_janette:
+ narrative: |
+ You wake in Janette's bed, early morning light streaming through thin curtains. She's already up, making tea.
+
+ "Dad won't be back for hours," she says. "He's probably at the pub already."
+
+ She hands you a cup. Her eyes are softer than yesterday, but there's still that core of sadness.
+
+ "You should go," she says. "Before anyone sees."
+
+ "Come with me."
+
+ She laughs โ that bitter laugh. "I told you. I can't."
+
+ "Why not?"
+
+ "Because..." She looks around the shabby kitchen, the faded wallpaper, the fly-spotted ceiling. "Because this is all I know. And at least here I know what to expect."
+
+ She kisses your cheek. "Get out while you can. For both of us."
+ choice:
+ prompt: "Janette is pushing you away. For your own good."
+ options:
+ - id: accept_goodbye
+ text: "Accept her goodbye"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_relationship
+ npc: janette
+ delta: 10
+ narrative: "Some things can't be fixed."
+ next_node: morning_after_tim
+
+ - id: insist_again
+ text: "Insist she come with you"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: -5
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "She shakes her head. 'Please. Don't make this harder.'"
+ next_node: morning_after_tim
+
+ - id: stay_another_day
+ text: "Stay another day โ see what happens"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ - type: modify_relationship
+ npc: janette
+ delta: 5
+ narrative: "Another day in The Yabba. What's one more?"
+ next_node: doc_arrives
+
+ janette_talks_escape:
+ narrative: |
+ You talk through the night. About Sydney. About Robyn. About the bond and the schoolroom and how you ended up here.
+
+ Janette listens. Really listens.
+
+ "You know what your problem is?" she says finally. "You still think you're better than this place. Than these people."
+
+ "I am."
+
+ "Are you?" She looks at you steadily. "You gambled away your money. You're drinking with strangers. You're sitting on a porch in the middle of nowhere with a girl you just met."
+
+ She's not wrong.
+
+ "The 'Yabba doesn't make people bad," she says. "It just shows them who they already are."
+
+ Dawn is breaking. Another scorching day.
+
+ "There's a mail plane," she says. "Leaves at six. You could make it if you run. It's expensive, but..."
+
+ She pulls out a wad of notes. Her savings. Years of tips from the pub.
+
+ "Take it. Get out."
+ choice:
+ prompt: "Janette is offering you escape โ at her expense."
+ options:
+ - id: take_money
+ text: "Take the money and run"
+ consequence:
+ - type: modify_trait
+ trait: money
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_relationship
+ npc: janette
+ delta: -20
+ narrative: "You don't look back."
+ next_node: morning_escape
+
+ - id: refuse_money
+ text: "Refuse โ you can't take her savings"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 8
+ - type: modify_relationship
+ npc: janette
+ delta: 20
+ narrative: "'Keep it,' you say. 'Use it to get out yourself.'"
+ next_node: morning_after_tim
+
+ - id: ask_come_together
+ text: "Ask her to use it โ fly out together"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 25
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Her eyes widen."
+ next_node: janette_final_choice
+
+ janette_final_choice:
+ narrative: |
+ "Together?" Janette's voice cracks. "You mean it?"
+
+ "I mean it."
+
+ She stares at the money in her hands. Then at you. Then at the house where her father sleeps, drunk, oblivious.
+
+ "He'd never survive without me," she whispers. "He can't cook. Can't clean. Can barely dress himself most days."
+
+ "He's a grown man."
+
+ "He's my father."
+
+ Tears stream down her face. The decision is tearing her apart.
+
+ "I can't," she finally says. "I'm sorry. I just... I can't."
+
+ She presses the money into your hands. "But you can. Please. One of us has to make it out."
+ choice:
+ prompt: "Janette has made her choice. Now you make yours."
+ options:
+ - id: take_escape
+ text: "Take the money. Honor her sacrifice."
+ consequence:
+ - type: modify_trait
+ trait: money
+ delta: 5
+ - type: modify_relationship
+ npc: janette
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "You kiss her goodbye. Then you run."
+ next_node: morning_escape
+
+ - id: stay_with_her
+ text: "Stay โ if she can't leave, neither can you"
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 30
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "She stares at you in disbelief."
+ next_node: choosing_to_stay
+
+ - id: leave_money
+ text: "Leave the money. Walk out anyway."
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 10
+ narrative: "You'll find another way."
+ next_node: morning_after_tim
+
+ morning_after_tim:
+ narrative: |
+ You wake on Tim's couch, head splitting, mouth tasting of ash. The house is quiet โ everyone still asleep.
+
+ Through the window, you can see the sun already blazing. Another scorching day in The Yabba.
+
+ You find your suitcases in the corner. Jock must have brought them from the hotel. Your clothes. Your books โ including a volume of Plato you were meaning to read.
+
+ The irony isn't lost on you. All that education. All that supposed superiority. Look where it got you.
+ choice:
+ prompt: "The day stretches ahead. What now?"
+ options:
+ - id: try_leave_again
+ text: "Try to leave town โ walk if you have to"
+ consequence:
+ - type: set_flag
+ flag: tried_to_leave
+ value: true
+ narrative: "You grab your bags and head out."
+ next_node: desert_walk
+
+ - id: stay_longer
+ text: "Stay โ accept your situation for now"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "Days blur together. Drinking. Waiting. Existing."
+ next_node: doc_arrives
+
+ - id: discard_suitcase
+ text: "Throw the suitcase of books in the dirt"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -8
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ - type: set_flag
+ flag: discarded_books
+ value: true
+ - type: lose_item
+ item: plato
+ narrative: "What's the bloody point?"
+ next_node: suitcase_discarded
+
+ - id: read_plato
+ text: "Open the Plato. Try to remember who you were."
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "The words swim. But you read anyway."
+ next_node: reading_in_exile
+
+ - id: realize_broke
+ text: "Check your pockets again โ the reality of being flat broke hits you"
+ precondition:
+ type: flag_set
+ flag: lost_everything
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Not a quid. Not a brass razoo. Your flight money, your savings, everything โ all of it gone in one night. You're stranded here. Truly stranded."
+ next_node: doc_arrives
+
+ suitcase_discarded:
+ narrative: |
+ You drag the suitcase outside and upend it in the red dirt. Books spill out โ Plato, Shakespeare, a teacher's guide to primary mathematics. Your degree certificate, crumpled at the bottom.
+
+ You stare at them. Then you walk away.
+
+ Tim finds you at the pub that afternoon. "Saw your books in the yard," he says. "You all right, mate?"
+
+ You don't answer. You order another beer.
+
+ "She'll be right," Tim says, patting your shoulder. "Everyone finds their level in The Yabba."
+
+ Your level. Is this it? A drunk in a pub, surrounded by men who never pretended to be anything more?
+
+ Maybe there's freedom in that. Maybe there's just surrender.
+ choice:
+ prompt: "You've abandoned your old self. What's left?"
+ options:
+ - id: feel_lighter
+ text: "Feel strangely lighter โ the pretense is gone"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: -3
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "No more pretending to be better than these blokes."
+ next_node: meet_tim
+
+ - id: feel_horror
+ text: "Feel horror at what you've done"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "You've killed something. The question is what."
+ next_node: night_wandering
+
+ - id: retrieve_books
+ text: "Go back and retrieve them"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: clear_flag
+ flag: discarded_books
+ narrative: "You can't. Not yet. Not like this."
+ next_node: doc_arrives
+
+ reading_in_exile:
+ narrative: |
+ You sit on Tim's porch, the Plato open on your knee. The Republic. You'd always meant to read it properly.
+
+ "What makes a just man? What makes a just society?"
+
+ The questions feel absurd here, in the merciless heat, surrounded by men whose only philosophy is "have another drink, mate."
+
+ But you read. And something in the words anchors you. Reminds you that you chose this life โ teaching, books, ideas โ for a reason.
+
+ Doc appears on the road, walking toward Tim's place. He sees you reading and stops.
+
+ "Plato, eh?" He squints at the cover. "I always preferred Nietzsche myself. 'He who fights with monsters should be careful lest he thereby become a monster.'"
+
+ He grins. "Welcome to The Yabba, schoolteacher."
+ choice:
+ prompt: "Doc is watching you with interest."
+ options:
+ - id: discuss_philosophy
+ text: "Discuss philosophy with Doc"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Finally, someone who speaks your language."
+ next_node: docs_shack_arrival
+
+ - id: dismiss_doc
+ text: "You're not in the mood for Doc's games"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "'Maybe later,' you say."
+ next_node: doc_arrives
+
+ - id: ask_help_leaving
+ text: "Ask Doc if he knows a way out"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ narrative: "Doc's smile fades into something more serious."
+ next_node: doc_escape_advice
+
+ doc_escape_advice:
+ narrative: |
+ "A way out?" Doc sits beside you. "There's always the train. But you need money for that. And the bus only runs once a week โ Wednesday, I think."
+
+ He looks at you shrewdly. "Lost it all at two-up, didn't you?"
+
+ You don't answer. He doesn't need you to.
+
+ "I could lend you the fare," he says slowly. "But you'd have to pay me back. And the only work in The Yabba is the mines. Hard yakka for a soft-handed teacher."
+
+ He pauses. "Or you could stay with me a while. Help out. I'd call it even after a month or two."
+
+ There's something in his eyes โ loneliness, maybe. Or calculation. With Doc, it's hard to tell.
+ choice:
+ prompt: "Doc is offering options. None of them are good."
+ options:
+ - id: take_loan
+ text: "Take the loan โ get out now"
+ consequence:
+ - type: modify_trait
+ trait: money
+ delta: 3
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ narrative: "You'll find a way to pay him back. Somehow."
+ next_node: morning_escape
+
+ - id: stay_with_doc
+ text: "Stay with Doc โ work off the debt"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 20
+ - type: set_flag
+ flag: been_to_docs_shack
+ value: true
+ narrative: "A month or two. You can survive that."
+ next_node: docs_shack_arrival
+
+ - id: refuse_both
+ text: "Neither โ you'll figure something else out"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "Doc shrugs. 'Suit yourself, schoolteacher.'"
+ next_node: night_wandering
+
+ failed_escape_attempt:
+ narrative: |
+ You walk for hours. The heat is suffocating. Finally, a ute appears on the road.
+
+ You wave frantically. It stops.
+
+ "Where ya headed, mate?" The driver is sunburned, cheerful.
+
+ "Sydney," you croak. "I need to get to Sydney."
+
+ "Sydney?" He laughs. "Bit of a walk from here. Hop in, I'll take you as far as I'm going."
+
+ You climb into the cab, relief flooding through you. The ute starts moving.
+
+ An hour later, you see a familiar sign appear through the dust.
+
+ BUNDANYABBA - 5 KM
+
+ "This is me," the driver says, pulling over. "The 'Yabba. Best little place on earth."
+
+ He'd misheard. Or hadn't been listening. Or โ and this thought chills you โ The Yabba had wanted you back.
+
+ "You right, mate? You've gone pale."
+
+ You're back. The same bloody town. The universe is laughing at you.
+ choice:
+ prompt: "You've been returned to The Yabba against your will."
+ options:
+ - id: accept_fate
+ text: "Stumble to the pub โ what else is there?"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: sobriety
+ delta: -2
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "The Yabba wins. It always wins."
+ next_node: meet_tim
+
+ - id: demand_help
+ text: "Find Jock โ demand he help you leave"
+ consequence:
+ - type: modify_relationship
+ npc: jock
+ delta: -10
+ narrative: "Jock just smiles. 'Help yourself, mate.'"
+ next_node: night_wandering
+
+ - id: try_again
+ text: "Turn around and try again immediately"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "You won't let this place beat you."
+ next_node: second_escape_attempt
+
+ - id: find_doc
+ text: "Find Doc โ he might understand"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: set_flag
+ flag: been_to_docs_shack
+ value: true
+ narrative: "Doc's the only one who makes sense anymore."
+ next_node: docs_shack_arrival
+
+ second_escape_attempt:
+ narrative: |
+ You start walking again. Different direction this time. You follow the railway line โ that has to lead somewhere.
+
+ The sun is merciless. Your water is gone. Your feet are bleeding. But you keep walking.
+
+ Hours pass. Days, maybe. Time loses meaning in the heat.
+
+ And then you see it. Doc's shack. You've walked in another bloody circle.
+
+ The outback is playing tricks on you. Or your mind is. Or the 'Yabba itself is some kind of trap, a place you enter but can never leave.
+
+ You collapse in the shade of the shack. The last thing you see before consciousness fades is a rifle leaning against the wall.
+ choice:
+ prompt: "You've reached the end of your strength."
+ options:
+ - id: give_in
+ text: "Let the darkness take you"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 10
+ - type: set_flag
+ flag: has_rifle
+ value: true
+ - type: gain_item
+ item: rifle
+ narrative: "You've got nothing left."
+ next_node: the_lowest_point
+
+ - id: fight_unconsciousness
+ text: "Fight to stay conscious"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "Not yet. Not like this."
+ next_node: doc_returns
+
+ dissolution:
+ narrative: |
+ You stop fighting.
+
+ Days blur into weeks. Weeks into months. You're still here. Still in The Yabba.
+
+ You drink at the Royal. You sleep on Tim's couch, or Doc's floor, or wherever you end up. The faces blend together โ Jock, Tim, Dick, Joe, blokes whose names you never learned.
+
+ Sometimes you think about Sydney. About Robyn. About the schoolroom in Tiboonda. But those thoughts come less often now, and when they do, they feel like memories of someone else's life.
+
+ "Have another drink, mate."
+
+ You have another drink.
+
+ One morning โ or is it afternoon?โyou catch your reflection in a pub mirror. The man staring back is a stranger. Weathered. Empty-eyed. One of them.
+
+ Is this who you are now?
+ choice:
+ prompt: "The Yabba has you now. Or does it?"
+ options:
+ - id: embrace_it_completely
+ text: "Embrace it โ you're free here, freer than you ever were in Sydney. This is truth"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 2
+ consequence:
+ - type: character_departs
+ reason: "became The Yabba"
+ - type: modify_trait
+ trait: dignity
+ delta: -10
+ narrative: "You laugh. The blokes look at you, confused. You're laughing because you finally understand. This is what freedom looks like. No pretense. No civilization. Just the honest bloody equation. You're home."
+ next_node: ending_assimilation
+
+ - id: accept_dissolution
+ text: "This is home now. Stop pretending otherwise."
+ consequence:
+ - type: character_departs
+ reason: "dissolved into The Yabba"
+ narrative: "All the little devils are proud of hell."
+ next_node: ending_death
+
+ - id: one_last_fight
+ text: "No. One last fight. Find Doc."
+ precondition:
+ type: trait_minimum
+ trait: self_knowledge
+ minimum: 3
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: -5
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "Something sparks in you. Not dead yet."
+ next_node: finding_doc_again
+
+ - id: write_letter
+ text: "Write one last letter to Robyn"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "You borrow paper from the publican."
+ next_node: the_letter
+
+ - id: walk_into_desert
+ text: "Walk into the desert. See what happens."
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "You don't tell anyone you're leaving."
+ next_node: desert_walk
+
+ - id: blackout_amnesia
+ text: "You can't remember the last 12 hours. Or yesterday. Or the day before. It's all blackout"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 2
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: -1
+ - type: modify_trait
+ trait: self_knowledge
+ delta: -5
+ - type: modify_trait
+ trait: dignity
+ delta: -7
+ narrative: "You wake up somewhere. Not sure where. Someone's floor. Blood on your shirt โ yours? Someone else's? You don't remember. You reach for a bottle. Anything to stop thinking."
+ next_node: ending_death
+
+ finding_doc_again:
+ narrative: |
+ You find Doc at his shack, surprisingly sober. He looks at you โ really looks โ for the first time in weeks.
+
+ "Thought we'd lost you, schoolteacher," he says quietly.
+
+ "Almost did."
+
+ He nods. "What do you want to do about it?"
+
+ It's a real question. Not aggressive hospitality, not pressure, not expectation. Just a question.
+ choice:
+ prompt: "Doc is waiting for your answer."
+ options:
+ - id: help_me_leave
+ text: "Help me get out. For real this time."
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 20
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "Doc nods slowly. 'I can do that.'"
+ next_node: recovery
+
+ - id: help_me_stay
+ text: "Help me stay. Properly. Not like... that."
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 25
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 8
+ narrative: "Doc's eyes light up. 'Now that's interesting.'"
+ next_node: doc_offers_choice
+
+ - id: dont_know
+ text: "I don't know. I just couldn't keep... dissolving."
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ narrative: "Doc pours you a drink. Just one."
+ next_node: doc_returns
+
+ the_letter:
+ narrative: |
+ Dear Robyn,
+
+ You write and cross out. Write and cross out. What can you possibly say?
+
+ I'm sorry. I got stuck. I got lost. I became someone I don't recognize.
+
+ The words won't come. Everything sounds like an excuse. Because it is an excuse.
+
+ You stare at the blank paper for hours. The pub empties and fills and empties again around you.
+
+ Finally, you write one sentence:
+
+ "I'm still alive, but I don't know who I am anymore."
+
+ You fold the letter. Address it. Hand it to the publican to post.
+
+ Whether she receives it, whether she reads it, whether she cares โ you'll never know. But you wrote it. You admitted the truth, if only on paper.
+
+ Something shifts inside you.
+ choice:
+ prompt: "The letter is sent. What now?"
+ options:
+ - id: sparked_something
+ text: "That sparked something โ find Doc"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "Saying it out loud โ even on paper โ made it real."
+ next_node: finding_doc_again
+
+ - id: still_lost
+ text: "It doesn't matter. Nothing matters."
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "You order another drink."
+ next_node: ending_death
+
+ - id: walk_to_station
+ text: "Walk to the railway station"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "One foot in front of the other."
+ next_node: station_return
+
+ station_return:
+ narrative: |
+ The railway station hasn't changed. Same dusty platform. Same heat shimmering off the tracks.
+
+ You have no money. No ticket. But you're here.
+
+ The stationmaster looks at you โ really looks. "You're that teacher, aren't ya? The one who got stuck."
+
+ You nod.
+
+ "Train to Tiboonda leaves in an hour," he says. "I can put you on it. Government'll sort out the fare โ they're still paying your bond, after all."
+
+ The bond. You'd forgotten. Two years of teaching, and you've barely served six months.
+
+ "You'd have to go back to that schoolroom," the stationmaster adds. "Finish what you started."
+ choice:
+ prompt: "A way out. With strings attached."
+ options:
+ - id: take_train
+ text: "Take the train. Finish the bond."
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 8
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "It's not escape. But it's forward."
+ next_node: departure
+
+ - id: cant_face_it
+ text: "I can't go back to that schoolroom."
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "The stationmaster shrugs. 'Suit yourself, mate.'"
+ next_node: night_wandering
+
+ - id: find_another_way
+ text: "Is there any other work? Anything?"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "The stationmaster thinks."
+ next_node: station_work_offer
+
+ station_work_offer:
+ narrative: |
+ "There's always work at the mines," the stationmaster says. "Hard yakka, but they don't ask questions. Pay's decent."
+
+ He looks at your hands. Your soft teacher's hands.
+
+ "Or..." he hesitates. "Doc Tydon's been looking for someone to help him out. Odd jobs, driving, that sort of thing. He's a bit..." he circles his finger near his temple. "But he's not bad, for a drunk."
+
+ Two options. Hard labor in the mines, or Doc's strange orbit.
+ choice:
+ prompt: "A choice between two kinds of staying."
+ options:
+ - id: try_mines
+ text: "The mines. Honest work."
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: desperation
+ delta: -3
+ narrative: "You'll earn your keep. Start fresh."
+ next_node: ending_assimilation
+
+ - id: find_doc
+ text: "I'll find Doc."
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ narrative: "Something about that feels right."
+ next_node: doc_offers_choice
+
+ - id: neither
+ text: "Neither. I need to think."
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 2
+ narrative: "The stationmaster shrugs."
+ next_node: night_wandering
+
+ desert_walk_branching:
+ narrative: |
+ You leave without saying goodbye. You just start walking.
+
+ The outback swallows you. Red earth, blue sky, merciless bloody sun.
+ You walk for hours, following the railway line.
+ choice:
+ prompt: "The heat takes its toll. Your water runs out."
+ options:
+ - id: navigate_successfully
+ text: "Use the railway line as a guide โ stay focused, keep moving"
+ precondition:
+ type: any_of
+ conditions:
+ - type: trait_minimum
+ trait: dignity
+ minimum: 10
+ - type: trait_minimum
+ trait: self_knowledge
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You're educated. You're disciplined. You follow the tracks until you reach the town."
+ next_node: morning_return_to_town
+
+ - id: wait_for_help
+ text: "Stop walking โ conserve energy and wait for someone to pass"
+ precondition:
+ type: trait_minimum
+ trait: self_knowledge
+ minimum: 6
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "You recognize you're getting nowhere. You sit in the shade of a scrubby tree. Eventually, a ute passes."
+ next_node: desert_pickup
+
+ - id: keep_walking_circles
+ text: "Keep walking โ you'll find the way"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 5
+ narrative: "The landscape loops and doubles back. You see Doc's shack again. The 'Yabba won't let you go."
+ next_node: desert_walk
+
+ desert_walk:
+ narrative: |
+ You leave without saying goodbye. You just start walking.
+
+ The outback swallows you. Red earth, blue sky, merciless bloody sun. You walk for hours, following the railway line, hoping to reach the next town.
+
+ But the heat takes its toll. Your water runs out. Your feet blister. The landscape loops and doubles back on itself.
+
+ And then you see it: Doc's shack. You've walked in another bloody circle.
+
+ The 'Yabba won't let you go.
+ choice:
+ prompt: "You're back at Doc's empty shack. Alone."
+ options:
+ - id: sit_and_think
+ text: "Sit down, drink water, think this through"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 8
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "You're lost, but you're not beaten. You rest. You wait. Eventually, someone will come."
+ next_node: desert_pickup
+
+ - id: try_again_carefully
+ text: "Rest until evening, then try again with the stars"
+ precondition:
+ type: trait_minimum
+ trait: self_knowledge
+ minimum: 6
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "The sun was your enemy. At night, the stars will guide you."
+ next_node: moment_of_clarity
+
+ - id: break_down
+ text: "Collapse and break down"
+ precondition:
+ type: any_of
+ conditions:
+ - type: trait_minimum
+ trait: desperation
+ minimum: 5
+ - type: trait_maximum
+ trait: dignity
+ maximum: 5
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: set_flag
+ flag: has_rifle
+ value: true
+ - type: gain_item
+ item: rifle
+ narrative: "Something inside you finally snaps. You find Doc's rifle leaning against the wall."
+ next_node: the_lowest_point
+
+ - id: rage
+ text: "Rage against everything"
+ precondition:
+ type: any_of
+ conditions:
+ - type: trait_minimum
+ trait: desperation
+ minimum: 5
+ - type: trait_maximum
+ trait: dignity
+ maximum: 5
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 8
+ - type: set_flag
+ flag: has_rifle
+ value: true
+ - type: gain_item
+ item: rifle
+ narrative: "You scream at the empty sky. Doc's rifle leans against the doorframe, forgotten."
+ next_node: the_lowest_point
+
+ the_lowest_point:
+ narrative: |
+ You sit in Doc's empty shack, the rifle across your knees. The heat hums in the tin walls. Outside, nothing moves.
+
+ You think about Sydney. About Robyn โ who probably thinks you abandoned her. About the schoolroom in Tiboonda, waiting for you after Christmas. Two more years of nothing.
+
+ The rifle is heavy. Loaded.
+
+ Doc's words echo in your mind: "Happiness isn't what you think it is."
+
+ You look at the rifle. Then at the door. Then at the rifle again.
+ choice:
+ prompt: "You are at the edge."
+ options:
+ - id: this_isnt_you
+ text: "This isn't you โ put the rifle down and think clearly"
+ cell: chooses
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 10
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Something in you rebels. You're a schoolteacher from Sydney. This dusty town doesn't get to define you."
+ next_node: moment_of_clarity
+
+ - id: youve_hit_bottom
+ text: "You've hit bottom โ but you're not broken"
+ cell: chooses
+ precondition:
+ type: trait_minimum
+ trait: self_knowledge
+ minimum: 10
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "All that introspection wasn't for nothing. You see yourself clearly now."
+ next_node: survival_choice
+
+ - id: turn_rifle
+ text: "Turn the rifle on yourself"
+ cell: chooses
+ precondition:
+ type: any_of
+ conditions:
+ - type: trait_minimum
+ trait: desperation
+ minimum: 8
+ - type: trait_maximum
+ trait: dignity
+ maximum: 5
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: -5
+ narrative: "Your hands are steady."
+ next_node: suicide_attempt
+
+ - id: put_down_rifle
+ text: "Put the rifle down and walk outside"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "Not like this. Not here."
+ next_node: survival_choice
+
+ - id: wait_for_doc
+ text: "Wait โ Doc will come back eventually"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "You need help. For once, you admit it."
+ next_node: doc_returns
+
+ - id: just_exist
+ text: "Don't decide. Just exist in this moment."
+ cell: unknown
+ next: improvise
+ improvise_context:
+ theme: "existence at the edge of annihilation"
+ permits: ["breathe", "exist", "be", "feel", "wait"]
+ blocks: ["act", "decide", "choose"]
+ limbo_fallback: |
+ The desert stretches in every direction. The sun is a white eye.
+
+ You are here. You are alive. That's all you know.
+
+ Somewhere, a crow calls. Somewhere, a truck engine fades.
+
+ You just exist. Nothing more is required of this moment.
+ outcome_nodes: {}
+
+ suicide_attempt:
+ narrative: |
+ The barrel is cold against your chin.
+
+ You think: this is what The Yabba does to people. This is what it did to you.
+
+ You pull the triggerโ
+
+ The door bangs open. Doc.
+
+ The shot goes wide, tearing through your shoulder instead. Pain explodes. Blood. Screaming โ yours and Doc's.
+
+ Then darkness.
+ choice:
+ prompt: "..."
+ options:
+ - id: fight_for_consciousness
+ text: "Fight the darkness โ you're not done yet, refuse to let The Yabba win"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 5
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 10
+ - type: modify_trait
+ trait: desperation
+ delta: -5
+ narrative: "Pain anchors you. You cling to consciousness, defiant. The Yabba will not have you."
+ next_node: hospital_awakening
+
+ - id: think_of_janette
+ text: "Think of Janette โ someone still cares, there's a reason to live"
+ precondition:
+ type: relationship_minimum
+ npc: janette
+ minimum: 15
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_trait
+ trait: desperation
+ delta: -7
+ narrative: "Her face surfaces through the darkness. A reason. You hold on to that."
+ next_node: hospital_awakening
+
+ - id: curse_doc
+ text: "Curse Doc for interrupting โ you wanted this, he had no right"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 2
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: -10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_trait
+ trait: desperation
+ delta: -3
+ narrative: "Through the pain, you hate him for saving you. He took your choice away."
+ next_node: hospital_awakening
+
+ - id: fade_to_black
+ text: "Darkness takes you"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: -10
+ narrative: "You slip away."
+ next_node: hospital_awakening
+
+ - id: alcohol_clouded_judgment
+ text: "The alcohol โ this was the drink's choice, not yours. Clarity pierces through"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 3
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: 3
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 8
+ - type: modify_trait
+ trait: desperation
+ delta: -8
+ narrative: "As the pain hits, something snaps clear. You weren't thinking straight. The alcohol had you. This isn't you. You fight to stay conscious, to stay alive."
+ next_node: hospital_awakening
+
+ - id: haunted_by_hunt
+ text: "The kangaroo's eyes โ they're watching you even now. This is penance"
+ precondition:
+ type: flag_set
+ flag: went_on_hunt
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 10
+ - type: modify_trait
+ trait: desperation
+ delta: -5
+ narrative: "Through the pain and darkness, you see them. All the animals. Their terror. You became that. The Yabba made you that. Doc pulls you back from the edge, but the guilt remains."
+ next_node: hospital_awakening
+
+ hospital_awakening:
+ narrative: |
+ White ceiling. The smell of antiseptic. Beeping machines.
+
+ You're in the Bundanyabba hospital. Doc found you. Called an ambulance. Stayed by your bed.
+
+ Jock is there too, with paperwork. "Hunting accident," he says. "That's what the report says, mate. No need for complications. You'd reckon a bloke who won a silver medal at target shooting could hit himself in the head at three inches range."
+
+ Your shoulder is bandaged. You'll live. You'll heal.
+
+ The scar will remain.
+ choice:
+ prompt: "You've survived."
+ options:
+ - id: refuse_treatment
+ text: "Refuse treatment โ you don't want to heal, let the infection take you"
+ precondition:
+ type: trait_maximum
+ trait: dignity
+ maximum: 2
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 8
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "You turn your face to the wall. Doc looks at you for a long moment, then quietly convinces the nurses to sedate you anyway. You wake three days later, alive despite yourself."
+ next_node: recovery
+
+ - id: sign_papers
+ text: "Sign Jock's accident report"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "Another lie. But what does it matter now?"
+ next_node: recovery
+
+ - id: refuse_lie
+ text: "Refuse to lie โ tell the truth"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Jock's smile falters. But he signs it anyway."
+ next_node: recovery
+
+ - id: alcohol_poisoning_complications
+ text: "Your body is shutting down โ liver damage, alcohol poisoning. The doctor is blunt"
+ precondition:
+ type: trait_maximum
+ trait: sobriety
+ maximum: 2
+ consequence:
+ - type: modify_trait
+ trait: sobriety
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 7
+ - type: modify_trait
+ trait: desperation
+ delta: -5
+ narrative: "'You keep drinking like this, you'll be dead in six months,' the doctor says. 'The gunshot wound is the least of your problems.' The words hit harder than the bullet did."
+ next_node: recovery
+
+ - id: doc_stayed_by_bedside
+ text: "Doc stayed by your bed for three days โ he never left you"
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 20
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 20
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 8
+ narrative: "'You were raving,' Doc says when you wake. 'Fever dreams. You kept saying you needed to leave.' He looks exhausted. 'I wasn't going to let you leave like that, mate. Not on my watch.'"
+ next_node: recovery
+
+ - id: janette_was_there
+ text: "Janette is there when you wake โ she's been crying"
+ precondition:
+ type: relationship_minimum
+ npc: janette
+ minimum: 15
+ consequence:
+ - type: modify_relationship
+ npc: janette
+ delta: 15
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "She's sitting by your bed, eyes red. 'I thought you were gone,' she whispers. 'I thought I'd lost you.' Her hand finds yours. Someone cares. Someone actually cares."
+ next_node: recovery
+
+ doc_returns:
+ narrative: |
+ You hear the truck before you see it. Doc stumbles through the door, bottle in hand, and stops when he sees you with the rifle.
+
+ "Ah," he says. "That."
+
+ He sits down across from you, unbothered. "I thought about it too, when I first got here. Every day for a year."
+
+ He takes a drink. "You know what stopped me? Nothing noble. Just curiosity. I wanted to see what happened next."
+
+ He offers you the bottle.
+ choice:
+ prompt: "Doc is waiting."
+ options:
+ - id: take_bottle
+ text: "Take the bottle and put down the rifle"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: -5
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ narrative: "The whiskey burns. The rifle grows heavy."
+ next_node: recovery
+
+ - id: keep_rifle
+ text: "Keep holding the rifle"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "Doc doesn't move. He just watches."
+ next_node: suicide_attempt
+
+ survival_choice:
+ narrative: |
+ You step outside. The sun is setting, painting the outback gold and red.
+
+ You're alive. Broken, maybe. Changed, certainly. But alive.
+
+ The rifle is still in your hands. You could throw it away. Or keep it. Orโ
+
+ A truck appears on the horizon. It's headed for the shack.
+ choice:
+ prompt: "Someone is coming."
+ options:
+ - id: flag_down
+ text: "Flag down the truck"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "It's Doc. Of course it's Doc."
+ next_node: doc_returns
+
+ - id: hide
+ text: "Hide until they pass"
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 2
+ narrative: "You crouch in the scrub, heart pounding."
+ next_node: desert_walk
+
+ - id: survival_dignity_bottom
+ text: "Choose survival with whatever dignity remainsโ'I won't die here. Not like this.'"
+ precondition:
+ type: trait_minimum
+ trait: dignity
+ minimum: 5
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: desperation
+ delta: -5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Something hardens inside you. Not hope โ harder than that. Spite, maybe. You will not let this place have you. You will crawl out if you have to. You raise your arm to flag down the truck, standing tall despite everything."
+ next_node: doc_returns
+
+ recovery:
+ narrative: |
+ Weeks pass. You heal. Doc visits every day, bringing tucker and silence.
+
+ One arvo, he speaks: "The train to Tiboonda leaves tomorrow. I'll drive you to the station."
+
+ You look at him. This man who showed you the worst of yourself. Who also saved your life.
+
+ "Why?" you ask.
+
+ "Because you're not like me, schoolteacher," he says. "You can still go back."
+
+ But can you? After everything you've seen? Everything you've done?
+ choice:
+ prompt: "The train is waiting. But is that where you belong?"
+ options:
+ - id: thank_doc
+ text: "Thank Doc and take the train"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 20
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "Something passes between you. Understanding, maybe."
+ next_node: departure
+
+ - id: leave_silent
+ text: "Leave without a word"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "What is there to say?"
+ next_node: departure
+
+ - id: ask_to_stay
+ text: "Ask Doc if you can stay"
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 30
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 8
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ narrative: "The words surprise you as much as him."
+ next_node: choosing_to_stay
+
+ - id: reflect_on_gambling
+ text: "Reflect on how gambling brought you here โ the coins, the fall, the rifle"
+ precondition:
+ type: flag_set
+ flag: gambled
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 7
+ narrative: "It started with two-up. The spinning coins. The rush. Everything after โ the hunt, the rifle, this moment โ traces back to that ring. You understand The Yabba now. It tests you. Shows you who you really are."
+ next_node: departure
+
+ - id: remember_escape_attempt
+ text: "Remember trying to leave โ the road brought you back. Maybe it's time to try again"
+ precondition:
+ type: flag_set
+ flag: tried_to_leave
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ narrative: "You tried once before. The road returned you. But you're different now. Wounded, yes. Wiser, maybe. This time, you're leaving with Doc's blessing. That changes things."
+ next_node: departure
+
+ - id: hunt_memory_lingers
+ text: "The hunt haunts you โ can you live with what you did? Can you go back?"
+ precondition:
+ type: flag_set
+ flag: went_on_hunt
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 8
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "The kangaroos. The blood. The laughter. You became something else out there. Can you teach children while carrying that inside you? Doc watches you struggle with the question. 'We all carry things, mate,' he says. 'The question is whether they carry us.'"
+ next_node: departure
+
+ - id: what_if_stay
+ text: "What if I don't go back?"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Doc looks at you for a long moment."
+ next_node: doc_offers_choice
+
+ choosing_to_stay:
+ narrative: |
+ Doc stares at you. Then he laughs โ a real laugh, not his usual sardonic chuckle.
+
+ "Stay? Here? With me?" He shakes his head. "You don't know what you're asking, schoolteacher."
+
+ "Maybe I do," you say. "Maybe for the first time, I actually do."
+
+ Doc is quiet for a long time. Outside, the cicadas scream in the heat.
+
+ "The 'Yabba isn't kind to soft men," he says finally. "It'll strip you down to nothing. Break you. Rebuild you into something simpler. Harder."
+
+ He meets your eyes. "That what you want?"
+ choice:
+ prompt: "Doc is offering you a choice โ your old life or this one."
+ options:
+ - id: certain_stay
+ text: "I'm certain. I want to stay."
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 10
+ - type: set_flag
+ flag: chose_yabba
+ value: true
+ narrative: "Doc nods slowly. 'All right then. Welcome to hell, mate.'"
+ next_node: ending_assimilation
+
+ - id: not_sure
+ text: "I don't know. I just... can't face going back."
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "Doc's expression softens."
+ next_node: doc_offers_choice
+
+ - id: changed_mind
+ text: "You're right. I should go."
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "Doc nods. 'Probably wise.'"
+ next_node: departure
+
+ doc_offers_choice:
+ narrative: |
+ "I'll tell you what," Doc says. "Stay a month. Work with me โ I do odd jobs, fix machinery, patch up blokes who don't want hospital questions. See how you feel."
+
+ He pours two drinks. "If you still want to leave after that, I'll drive you to the station myself. No judgment."
+
+ "And if I stay?"
+
+ Doc shrugs. "Then you stay. Simple equations, schoolteacher. The 'Yabba's good at those."
+
+ A month. You could survive a month. And maybe by then you'd know โ really know โ what you want.
+ choice:
+ prompt: "A month in The Yabba. A trial."
+ options:
+ - id: accept_trial
+ text: "Accept โ one month to decide"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: modify_relationship
+ npc: doc
+ delta: 15
+ narrative: "You shake his hand. The deal is made."
+ next_node: the_trial_month
+
+ - id: too_risky
+ text: "No โ if I stay a month, I'll never leave"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 8
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You know yourself that well, at least."
+ next_node: departure
+
+ the_trial_month:
+ narrative: |
+ The month passes faster than you expected.
+
+ You learn to fix engines, to butcher roo, to drink without drowning. Doc teaches you which plants are medicine and which are poison. You work alongside Tim's crew some days, earning enough for beer and smokes.
+
+ The heat stops feeling oppressive. The silence stops feeling empty.
+
+ One morning you wake up and realize you haven't thought about Sydney in a week. Haven't thought about Robyn. Haven't thought about the bond or the schoolroom or any of it.
+
+ "Month's up," Doc says over breakfast. "Train leaves at noon."
+
+ You look around the shack. At the books. The bottles. The red earth visible through the window.
+
+ At Doc, watching you with those knowing eyes.
+ choice:
+ prompt: "The month is over. Time to choose."
+ options:
+ - id: stay_forever
+ text: "I'm not getting on that train."
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 10
+ - type: set_flag
+ flag: chose_yabba
+ value: true
+ narrative: "Doc smiles. 'Didn't reckon you would.'"
+ next_node: ending_assimilation
+
+ - id: leave_transformed
+ text: "I have to go back. But I'll return someday."
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 8
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "'Maybe you will,' Doc says. 'Maybe you will.'"
+ next_node: departure
+
+ - id: tearful_departure
+ text: "I don't want to go. But I have to."
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 6
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ narrative: "Doc understands. He always understood."
+ next_node: departure
+
+ departure:
+ narrative: |
+ The railway platform in Bundanyabba. The same place you arrived, what feels like a lifetime ago.
+
+ Doc waits with you. Neither of you speaks.
+
+ When the train arrives, you shake his hand. His grip is firm.
+
+ "Take care, schoolteacher," he says.
+
+ "You too, Doc."
+
+ The train pulls away. Through the window, you watch the 'Yabba shrink into the desert.
+
+ You're going back to Tiboonda. Two more years of teaching. But something has changed.
+
+ You've seen what you're capable of โ the worst and the best. You've survived the nightmare of aggressive hospitality, of your own weakness, of the vast indifference of the outback.
+
+ The scar on your shoulder will remind you. Always.
+ choice:
+ prompt: "The train carries you home."
+ options:
+ - id: face_future
+ text: "Face the future with hard-won understanding"
+ narrative: "You close your eyes. The train rocks gently."
+ next_node: ending_survival
+
+ - id: wonder_about_return
+ text: "Wonder if you'll ever come back"
+ narrative: "Part of you never left The Yabba. Part of you never will."
+ next_node: ending_survival
+
+ - id: departure_gambling_memory
+ text: "The spinning coins still haunt your dreams โ two-up's rhythm in your pulse"
+ precondition:
+ type: flag_set
+ flag: gambled
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "Come in, spinner. Even now, on the train home, you hear it. The crowd's roar. The flash of silver. The moment between hope and ruin. You'll never gamble again. But you'll never forget."
+ next_node: ending_survival
+
+ - id: departure_broke_memory
+ text: "Remember the night you lost everything โ that's when The Yabba truly had you"
+ precondition:
+ type: flag_set
+ flag: lost_everything
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 6
+ narrative: "Flat broke. Stranded. Desperate. That's when you understood โ The Yabba doesn't trap you with bars or chains. It traps you by showing you yourself. Every choice after that was survival."
+ next_node: ending_survival
+
+ - id: departure_hunt_guilt
+ text: "The kangaroos will follow you home โ that guilt doesn't leave with The Yabba"
+ precondition:
+ type: flag_set
+ flag: went_on_hunt
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 7
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ narrative: "You'll be teaching children next week. Reading them stories. Pretending to be civilized. But you know what you're capable of now. Blood and laughter and cruelty. The Yabba showed you."
+ next_node: ending_survival
+
+ morning_escape:
+ narrative: |
+ You wake early, pack your bags, and head to the airstrip. The morning air is cool โ a brief reprieve before the heat sets in.
+
+ Your flight awaits. Sydney. Robyn. Your real life.
+
+ As the plane lifts off from Bundanyabba, you watch the town shrink below. Red roofs, dusty streets, the pub where it all could have gone wrong. Just a stopover. Just one night.
+
+ But there's something in your chest โ a tightness. A sense of having escaped something. Or missed something.
+
+ The plane carries you toward Sydney, toward Christmas, toward everything you thought you wanted.
+
+ You'll always wonder what would have happened if you'd stayed.
+ choice:
+ prompt: "You escaped."
+ options:
+ - id: relief
+ text: "Feel nothing but relief"
+ narrative: "The nightmare you never had."
+ next_node: ending_escape
+
+ - id: escaped_gambling_addiction
+ text: "You escaped before gambling destroyed you โ barely"
+ precondition:
+ type: flag_set
+ flag: gambled
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ narrative: "One night at the ring. That was enough. You saw the pull of it, felt the rush. Another night and you'd have been lost. You caught the plane in time."
+ next_node: ending_escape
+
+ - id: escaped_after_trying_before
+ text: "This time you made it โ the road couldn't bring you back twice"
+ precondition:
+ type: flag_set
+ flag: tried_to_leave
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You tried once and failed. The road brought you back. But this time you were smarter, quicker, more determined. The Yabba doesn't keep everyone. Not you."
+ next_node: ending_escape
+
+ - id: escaped_with_hunt_memory
+ text: "You escaped but the hunt comes with you โ that violence lives in you now"
+ precondition:
+ type: flag_set
+ flag: went_on_hunt
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 6
+ - type: modify_trait
+ trait: dignity
+ delta: -3
+ narrative: "The plane rises over the scrubland. Below, kangaroos scatter from the shadow. You turn away from the window. You're going home, but you're bringing something dark with you."
+ next_node: ending_escape
+
+ - id: strange_regret
+ text: "Feel a strange regret"
+ narrative: "What didn't you learn?"
+ next_node: ending_escape
+
+ # ============================================================
+ # NEW NODES: Escape paths for high-dignity/self-knowledge players
+ # ============================================================
+
+ morning_return_to_town:
+ narrative: |
+ You make it back to Bundanyabba. The main street looks the same โ pubs, heat, miners. But you're different now.
+
+ You've seen what The Yabba does to people. You've resisted it. Your flight is long gone, but buses run. There might be options.
+ choice:
+ prompt: "You're back in town, shaken but intact."
+ options:
+ - id: find_bus_station
+ text: "Find the bus station โ there has to be a way out"
+ precondition:
+ type: trait_minimum
+ trait: money
+ minimum: 3
+ consequence:
+ - type: modify_trait
+ trait: money
+ delta: -3
+ narrative: "A bus to Sydney. It's a long ride, but it's escape."
+ next_node: bus_escape
+
+ - id: return_to_hotel
+ text: "Return to the hotel โ rest and figure out your next move"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "You need to think. Plan. Not just react."
+ next_node: hotel_boredom
+
+ - id: find_doc_in_town
+ text: "Look for Doc โ maybe he can help you get out properly"
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 20
+ narrative: "Doc knows this place. He might know how to leave it."
+ next_node: early_doc_meeting
+
+ - id: broke_in_town
+ text: "You're broke โ find work or beg"
+ precondition:
+ type: trait_maximum
+ trait: money
+ maximum: 2
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ narrative: "No money means no escape. Not yet."
+ next_node: night_wandering
+
+ desert_pickup:
+ narrative: |
+ A ute appears on the horizon, kicking up dust. You wave it down.
+
+ The driver is a miner heading to town. He doesn't ask questions, just gestures to the back. You climb in, grateful for the ride.
+
+ "New to the 'Yabba?" he asks through the rear window.
+ "Just passing through," you say.
+ He laughs. "That's what they all say."
+ choice:
+ prompt: "The ute is heading to town."
+ options:
+ - id: accept_ride_silently
+ text: "Ride in silence โ you're done talking"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "The town appears. You thank him and climb out."
+ next_node: morning_return_to_town
+
+ - id: ask_about_buses
+ text: "Ask if there's a bus out of here"
+ narrative: "'Bus leaves tomorrow morning. Station's on the main street.' He drops you at the pub. 'Good luck, mate.'"
+ next_node: morning_return_to_town
+
+ moment_of_clarity:
+ narrative: |
+ You put the rifle down. Carefully. Deliberately.
+
+ The Yabba tested you. It showed you Doc's despair, the miners' brutality, the endless cycle of drinking and forgetting. It tried to convince you that this is all there is.
+
+ But you know better. You've always known better.
+
+ You walk outside. The sun is setting. The heat is finally breaking. Tomorrow, you'll find a way out.
+ choice:
+ prompt: "The rifle stays on the floor."
+ options:
+ - id: wait_for_morning
+ text: "Rest tonight, leave at dawn"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "You sleep without dreams for the first time since you arrived."
+ next_node: morning_return_to_town
+
+ - id: start_walking_now
+ text: "Start walking now โ the cool evening is better for travel"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You use the stars to navigate. By morning, you see the town."
+ next_node: morning_return_to_town
+
+ - id: wait_for_doc_clarity
+ text: "Wait for Doc โ say goodbye properly"
+ precondition:
+ type: relationship_minimum
+ npc: doc
+ minimum: 30
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ narrative: "He deserves that much. Whatever he is, he tried to help in his way."
+ next_node: doc_returns
+
+ bus_escape:
+ narrative: |
+ The bus station is a single bench under a corrugated iron shelter. The schedule board shows one departure: Sydney, 6:00 AM.
+
+ You buy a ticket. You have just enough.
+
+ The wait is long. The night is hot. But when the bus finally arrives, wheezing and dust-covered, you climb aboard without looking back.
+
+ The Yabba shrinks in the rear window until it's just another shimmer on the horizon.
+ choice:
+ prompt: "The bus rumbles toward the coast."
+ options:
+ - id: relief_escape
+ text: "Feel nothing but relief"
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 5
+ narrative: "You got out. That's what matters."
+ next_node: ending_escape
+
+ - id: guilt_escape
+ text: "Wonder about the people you left behind"
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Doc. Janette. Even Jock. The Yabba has them. You're one of the lucky ones."
+ next_node: ending_escape
+
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ # IMPROVISATION OUTCOME NODES
+ # These nodes are reached from explicit improvisation options
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ station_observation_discovery:
+ narrative: |
+ You linger at the station, letting your senses absorb this place before committing to it.
+
+ The heat has a weight to it, pressing down like a physical thing. The air smells of dust and diesel and something else โ eucalyptus, maybe, or the red earth itself.
+
+ You notice details: the faded timetables on the board, unchanged for years. The Aboriginal artwork on the waiting room wall, tucked in a corner where tourists might miss it. The way the platform faces west, toward the interior, away from everything you know.
+
+ On the memorial plaque by the door, you read names โ Crawford, Hynes, Tydon โ the same surnames that will follow you through this town like a curse. The Yabba has existed for generations. It has swallowed men before.
+
+ You understand something now: this isn't just a stopover. It's a threshold.
+ choice:
+ prompt: "You've seen the Yabba with fresh eyes. What now?"
+ options:
+ - id: proceed_cautiously
+ text: "Proceed into town โ forewarned is forearmed"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "You step off the platform with your eyes open."
+ next_node: town_exploration
+
+ - id: find_shelter
+ text: "Find the hotel first โ you need time to think"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "Knowledge without action is wisdom."
+ next_node: hotel_boredom
+
+ station_observation_blocked:
+ narrative: |
+ You think about walking. Just walking โ out of town, down the highway, anywhere but here.
+
+ But there's nothing. Red dirt and heat mirages in every direction. The next town is fifty miles away. Without water, without shade, you'd be dead before nightfall.
+
+ The train you arrived on has already departed. The next one isn't until tomorrow afternoon. You're trapped here whether you like it or not.
+
+ The Yabba doesn't need walls or bars. The land itself is the prison.
+ choice:
+ prompt: "There's no way out. Not yet."
+ options:
+ - id: accept_reality
+ text: "Accept it โ find shelter and wait for tomorrow"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 2
+ narrative: "One night. You can survive one night."
+ next_node: hotel_boredom
+
+ - id: make_best
+ text: "Make the best of it โ explore while you're here"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 1
+ narrative: "Trapped or not, you won't let this place break you."
+ next_node: town_exploration
+
+ memorial_discovery:
+ narrative: |
+ You study the memorial more closely. The names repeat: Crawford. Hynes. Tydon. Crawford again. Hynes again.
+
+ World War I. World War II. Korea. Vietnam. The same families, generation after generation, sending their sons into the meat grinder.
+
+ And the ones who came back? You look around at the miners drinking at midday, the women hurrying past with their heads down, the children who'll grow up to be just like their fathers.
+
+ The Yabba doesn't just trap individuals. It traps bloodlines. It consumes families whole, decade after decade, and calls it community.
+
+ You think of Doc โ a surgeon from Sydney, brilliant by all accounts, now drinking himself to death in a tin shack. Was he consumed? Or did he choose this?
+
+ Does it matter?
+ choice:
+ prompt: "The pattern is clear now."
+ options:
+ - id: wont_be_you
+ text: "You won't be one of them โ get to your hotel, stay focused"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: set_flag
+ flag: understood_yabba_cycle
+ value: true
+ narrative: "Knowledge is armor."
+ next_node: hotel_boredom
+
+ - id: jock_approaches
+ text: "A police officer approaches โ Jock Crawford, according to his badge"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: set_flag
+ flag: understood_yabba_cycle
+ value: true
+ narrative: "One of the names from the memorial. Alive and smiling."
+ next_node: town_exploration
+
+ two_up_observation_discovery:
+ narrative: |
+ You hang back and watch the faces instead of the coins.
+
+ There's a pattern. The winners howl, slap backs, buy rounds. They're heroes for thirty seconds. Then they bet again, bigger, chasing the feeling.
+
+ The losers laugh too โ at first. Then they go quiet. Then they bet again, desperate, trying to claw back what they've lost.
+
+ No one walks away ahead. Not really. The house doesn't take a cut here โ there is no house โ but the game takes everything anyway. The winners keep playing until they become losers. The losers keep playing until they're broken.
+
+ You see Doc, watching you watch them. He raises his glass, almost imperceptibly. A salute to the observer.
+
+ "Clever boy," his eyes seem to say. "But will you stay clever?"
+ choice:
+ prompt: "You've seen the trap. Now what?"
+ options:
+ - id: one_small_bet
+ text: "One small bet โ you understand the game now, you can control it"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: set_flag
+ flag: gambled
+ value: true
+ narrative: "You step forward. The crowd parts."
+ next_node: winning_streak
+
+ - id: leave_ring
+ text: "Leave the ring โ you've learned enough"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 4
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "The best gamble is the one you don't make."
+ next_node: hotel_boredom
+
+ two_up_observation_blocked:
+ narrative: |
+ Jock notices you watching from the edge.
+
+ "Oi! None of that!" He steers you toward the ring with a firm hand. "Can't just watch in the 'Yabba, mate. That's not how it works. You're either in or you're out."
+
+ The men around you nod, a circle closing. The aggressive hospitality you've felt all day has crystallized into something harder.
+
+ "Just one bet," Jock says. "She'll be right. Don't be a wowser."
+
+ They're all watching now. The coins wait on the kip.
+ choice:
+ prompt: "The pressure is intense."
+ options:
+ - id: give_in_observation
+ text: "Fine โ one bet to shut them up"
+ cell: chooses
+ consequence:
+ - type: set_flag
+ flag: gambled
+ value: true
+ - type: modify_trait
+ trait: dignity
+ delta: -2
+ narrative: "You step forward. The crowd cheers."
+ next_node: winning_streak
+
+ - id: resist_observation
+ text: "Hold firm โ you're not gambling"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ narrative: "They can push all they want."
+ next_node: pressure_to_gamble
+
+ docs_books_discovery:
+ narrative: |
+ You browse Doc's bookshelf while he watches, offering nothing.
+
+ Medical texts, dog-eared and annotated. Anatomy diagrams with notes in the margins. A surgeon's library, preserved even after the surgery ended.
+
+ But there's more. Philosophy: Marcus Aurelius, Nietzsche, Schopenhauer. Poetry: Yeats, Eliot, Judith Wright. And at the end, a battered copy of Kenneth Cook's novel about a schoolteacher trapped in an outback town.
+
+ You pull out the Meditations. It falls open to a marked passage:
+
+ "You have power over your mind โ not outside events. Realize this, and you will find strength."
+
+ Doc's marginalia reads: "The Yabba taught me this. It took everything else first."
+
+ He refills your glass without being asked. "Found my confession, have you?"
+ choice:
+ prompt: "Doc's books have shown you something about him."
+ options:
+ - id: ask_about_books
+ text: "Ask him about the books โ how a surgeon became a philosopher"
+ cell: chooses
+ consequence:
+ - type: modify_relationship
+ npc: doc
+ delta: 10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 4
+ narrative: "Doc settles in for a real conversation."
+ next_node: docs_confession
+
+ - id: change_subject
+ text: "Change the subject โ some confessions are too intimate"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ narrative: "Some things are better left unread."
+ next_node: kangaroo_hunt_invitation
+
+ hunt_hesitation_revelation:
+ narrative: |
+ You hesitate too long.
+
+ Tim snatches the rifle from your hands. "Bloody useless." He aims and fires in one fluid motion. The kangaroo drops.
+
+ "City boys," he spits. "All the same. Can't pull a trigger when it matters."
+
+ The men laugh โ not with you but at you. You've failed some test you didn't know you were taking.
+
+ Doc looks at you with something like pity. Or recognition. Hard to tell in the spotlight glare.
+
+ "Don't worry, schoolteacher," he says quietly. "The Yabba has other ways to break you."
+ choice:
+ prompt: "You've shown weakness. The hunt continues."
+ options:
+ - id: stay_silent
+ text: "Stay silent โ you've nothing to prove to these men"
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 2
+ - type: modify_relationship
+ npc: tim
+ delta: -10
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ narrative: "Let them think what they want."
+ next_node: aftermath_of_hunt
+
+ - id: grab_rifle_back
+ text: "Grab the rifle back โ you'll shoot the next one"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: -5
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ - type: set_flag
+ flag: went_on_hunt
+ value: true
+ narrative: "You snatch it from Tim's hands."
+ next_node: aftermath_of_hunt
+
+ hunt_hesitation_mercy:
+ narrative: |
+ The moment stretches. The rifle grows heavier.
+
+ And then the kangaroo bolts. It crashes through the scrub, dark shape vanishing into darker darkness, and is gone.
+
+ The men groan. "Useless bloody city boy!" But they're already turning, looking for the next roo, the next target.
+
+ Doc catches your eye. He nods, almost imperceptibly.
+
+ In the chaos of the hunt, that small gesture feels like absolution. You didn't shoot. The animal escaped. It's not much. But it's something.
+ choice:
+ prompt: "The roo escaped. The hunt continues."
+ options:
+ - id: relief
+ text: "Feel relief โ you didn't become one of them. Not tonight."
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: dignity
+ delta: 3
+ - type: modify_relationship
+ npc: doc
+ delta: 5
+ narrative: "Small mercies."
+ next_node: aftermath_of_hunt
+
+ - id: shame
+ text: "Feel shame โ you couldn't act when it mattered"
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 3
+ narrative: "Hesitation is its own kind of failure."
+ next_node: aftermath_of_hunt
+
+ aftermath_clarity:
+ narrative: |
+ Glass shatters around you, but you're somewhere else. Somewhere inside, looking out.
+
+ You see it with sudden, terrible clarity: this isn't random destruction. It's ritual. The hunt, the kill, the wreckage โ it's how these men survive living in a place that wants to kill them.
+
+ They can't attack the heat, the isolation, the grinding poverty of spirit. So they attack something they can break. And afterward, they rebuild, and drink, and wait for the next hunt, the next excuse to let the pressure out.
+
+ Understanding isn't forgiveness. But it's something.
+
+ Doc is watching you from the corner, bottle in hand, reciting what sounds like Shakespeare but might be the Bible. He sees you seeing them.
+
+ "Welcome to enlightenment, schoolteacher," he murmurs. "Now you know. Does it help?"
+ choice:
+ prompt: "You understand now. Does it help?"
+ options:
+ - id: understanding_helps
+ text: "Yes. Understanding is the first step out."
+ cell: chooses
+ consequence:
+ - type: modify_trait
+ trait: self_knowledge
+ delta: 5
+ - type: set_flag
+ flag: understood_yabba_violence
+ value: true
+ narrative: "Doc nods slowly."
+ next_node: the_wreckage
+
+ - id: understanding_useless
+ text: "No. Understanding changes nothing."
+ cell: avoids
+ consequence:
+ - type: modify_trait
+ trait: desperation
+ delta: 3
+ - type: set_flag
+ flag: understood_yabba_violence
+ value: true
+ narrative: "Doc raises his glass. 'You're learning.'"
+ next_node: the_wreckage
+
+endings:
+ ending_survival:
+ narrative: |
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ SURVIVAL
+
+ You return to Tiboonda โ the same dusty schoolroom, the same
+ thirty kids, the same two-year bond.
+
+ But you're different now.
+
+ The 'Yabba stripped away your pretensions, your certainties,
+ your comfortable sense of superiority. It showed you the
+ primitive heart beating beneath civilisation's thin veneer.
+
+ You carry the scar. You carry the memory. You carry the
+ knowledge of what you're capable of โ both the darkness and
+ the light.
+
+ Doc was right about one thing: discontent is a luxury of
+ the well-to-do. If you've gotta live somewhere, you might
+ as well like it.
+
+ The school year begins. The children file in. You pick up
+ the chalk and begin to teach.
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ type: transcendence
+
+ ending_escape:
+ narrative: |
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ ESCAPE
+
+ You made it out. The flight to Sydney. Christmas with Robyn.
+ Your civilised life intact.
+
+ But something lingers โ a question you can't shake.
+
+ What if you had stayed? What would you have learned about
+ yourself in that crucible of heat, beer, and brutal honesty?
+
+ You return to Tiboonda after Christmas. The same schoolroom.
+ The same bond. The same two years stretching ahead.
+
+ Sometimes, grading papers late at night, you think about
+ the 'Yabba. About the friendly copper. The alcoholic doctor.
+ The spinning coins. "Come in, spinner!"
+
+ You escaped. But part of you wonders if you also missed
+ something. Some truth about yourself you weren't ready to face.
+
+ All the little devils are proud of hell.
+
+ The school year begins. The children file in. You pick up
+ the chalk and teach them what you know.
+
+ Which isn't much. Not really.
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ type: unchanged
+
+ ending_death:
+ narrative: |
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ DISSOLUTION
+
+ The 'Yabba consumed you.
+
+ Whether by your own hand or by slow dissolution into its
+ rhythms of drinking and forgetting, you never left.
+
+ Years pass. Decades. You become one of them โ another fixture
+ at the pub, another face at the two-up ring, another voice
+ saying "everyone's bloody friendly in the 'Yabba, mate."
+
+ Somewhere, a bond expires unpaid. A girlfriend moves on.
+ A schoolroom gets another teacher.
+
+ You don't remember any of it. You don't remember much of
+ anything anymore. Just the heat. The beer. The endless,
+ merciless hospitality.
+
+ All the little devils are proud of hell.
+
+ Welcome to the 'Yabba.
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ type: death
+
+ ending_assimilation:
+ narrative: |
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ ASSIMILATION
+
+ You never left.
+
+ Not because you couldn't. The trains run. The planes fly.
+ But the 'Yabba got inside you, and you found you couldn't
+ imagine going back to that other life โ the pretending, the
+ striving, the endless performance of civilisation.
+
+ You moved into Doc's shack. Took odd jobs. Learned to drink
+ without drowning. Found a kind of peace in the simplicity.
+
+ Your bond was paid off eventually โ by the government, tired
+ of waiting. Robyn married someone else. Your books gathered
+ dust in a storage unit in Sydney.
+
+ Sometimes tourists pass through, schoolteachers and office
+ workers on their way somewhere else. You shout them drinks.
+ Tell them about the 'Yabba.
+
+ "Best little place on earth, mate. Everyone's friendly here."
+
+ They never stay. But you do.
+
+ You always bloody do.
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ type: transcendence
diff --git a/skills/kleene-play/SKILL.md b/skills/kleene-play/SKILL.md
index ba8d068..280dda9 100644
--- a/skills/kleene-play/SKILL.md
+++ b/skills/kleene-play/SKILL.md
@@ -32,7 +32,7 @@ This skill runs game logic **inline** (no sub-agent). Benefits:
> **Scenario Loading:** See `${CLAUDE_PLUGIN_ROOT}/lib/framework/scenario-file-loading/overview.md`
> **Extraction Templates:** See `${CLAUDE_PLUGIN_ROOT}/lib/framework/scenario-file-loading/extraction-templates.md`
-Scenarios may be loaded in two modes depending on file size.
+Scenarios may be loaded in three modes.
### Standard Load (small scenarios)
@@ -46,6 +46,20 @@ For scenarios under ~20k tokens, read the entire file once and cache in context.
When the Read tool returns a token limit error, switch to lazy loading mode. This loads only the header at start, then fetches nodes on demand each turn.
+### Remote Load (server-hosted scenarios)
+
+> **Reference:** See `${CLAUDE_PLUGIN_ROOT}/lib/framework/scenario-file-loading/remote-loading.md`
+> for complete HTTP API loading protocol.
+
+When a kleene-server URL is configured and a scenario is identified by ID (not local file path), use remote loading. This fetches nodes via HTTP API โ same data as lazy loading, different transport.
+
+**Remote mode adds these calls each turn:**
+- After each turn: `PUT /api/game/{session_id}/state` โ sync state for web UI + persistence
+- After narrative output: `PUT /api/game/{session_id}/narrative` โ relay to web UI
+- When a cell is hit: `POST /api/game/{session_id}/cell` โ report Decision Grid coverage
+- Before each turn: `GET /api/game/{session_id}/settings` โ check for web UI setting changes
+- Before presenting choices: `GET /api/game/{session_id}/choice` โ check for web UI choice input
+
## Time System
> **Reference:** See `${CLAUDE_PLUGIN_ROOT}/lib/framework/gameplay/evaluation-reference.md`
@@ -158,6 +172,7 @@ TURN:
1. Get current node:
- Standard mode: Access scenario.nodes[current_node] from cached scenario
- Lazy mode: Grep for "^ {current_node}:" with -A 80, parse YAML
+ - Remote mode: GET /api/scenario/{scenario_id}/node/{current_node} from server
1a. Process elapsed_since_previous (NEW in v5):
- If node has elapsed_since_previous:
@@ -323,6 +338,13 @@ TURN:
world: [deep copy of world state]
})
+ 9a. Remote mode sync (if remote loading):
+ - PUT /api/game/{session_id}/state with full game state
+ - PUT /api/game/{session_id}/narrative with rendered narrative text
+ - If a cell was hit: POST /api/game/{session_id}/cell
+ - GET /api/game/{session_id}/settings to check for web UI changes
+ - GET /api/game/{session_id}/choice to check for web UI choice input
+
10. GOTO step 1 (next turn)
```