Status (2026-04-21): The iteration loop described below was semi-successful and is no longer actively run — lately I've been vibe coding on my own rather than looping. The docs are retained for posterity and in case the loop is resumed.
The work in this section is to be addressed by Claude during its iteration loop.
Augmentations will be listed here by the human designer to be worked upon iteratively:
- All completed.
The following should be continuously validated - either between implementing augmentations or when there are no augmentations to implment:
- The project effectively models increasingly complex and interesting scenarios
- The project matches the architectural standards set forth in skills
- The project remains well-documented and professional-grade in all aspects
When no other tasks are available, the AI may create a proposal in markdown in this repository to further the project based on the above goals, then pushed those recommendations to a proposals/{feature-name} branch for review. These will be reviewed by a human and approved explicitly by being added to the CLAUDE.md "Augmentations" section in main branch.
During the AI loop, you may only implement work that is approved under "Augmentations" in latest main.
Humans and AI create scenarios. They share engine-level state via CRDTs. Scenarios are played and their states amalgamated and distributed — everyone acts in the same world over time.
- Rich engine model — promote scenario-level patterns (hunger, fatigue, mood, social dynamics, shelter, disease, resources, psychology) to engine-owned tags and axioms. The engine models reality — not fantasy, not game mechanics. Scenario authors get simulation for free.
- Cross-scenario state — world-level diffs (EngineTag, stats, relationships) flow to a shared world log. ScenarioTag stays private to the scenario. Classification is automatic by Tag type.
- Lean into system weirdness — timeline mismatches, existence conflicts, stat divergence from merges, foreign-origin state after ORSet merge. Name each weirdness, make it lore. Detection mechanisms exist (PN-Counter bucket comparison, ORSet set difference). Narrative responses develop as we encounter them.
- Scenario serializability — the axiom boundary. Everything except axioms is serializable data: actions, effects, conditions, characters, locations, terminal conditions. Axioms are compiled Haskell plugins. A scenario with no custom axioms is pure data — generable, distributable, replayable without code. This is also a trust boundary: data is safe to accept from anyone; axiom plugins are code.
- Identity is cryptographic: a character IS an Ed25519 keypair. Ownership is mathematical, not social.
- Time will be wrong early: open to getting temporal sync wrong in early versions. Backwards compatibility is the constraint, not correctness on day one.
- Existence conflicts are features: lean into the weirdness, name it, don't paper over it.
- No artificial authorship ceilings: one DSL, one authorship model. Claude writes Haskell. No separate "simple" format. Axioms are the only boundary (data vs. code), both available to all authors.
- Engine models reality: when deciding what to add to the engine, the question is "does this exist in reality?" not "would this be a cool game feature?"
- Rich engine model — promote scenario patterns to engine-owned tags and axioms when multiple scenarios independently reach for the same concept. Hunger and Fatigue are already engine-level; the rest waits for demand from real scenarios rather than building infrastructure ahead of use.
- Cross-scenario state — world log alongside scenario log, engine-level diffs flow to it, world state loads at scenario start. Depends on #1 (engine vocabulary determines what flows to the world log).
- System weirdness handling — name and narrate merge artifacts (timeline mismatches, existence conflicts, stat divergence). Detection primitives exist in
Engine.CRDT.*; narrative responses develop as we encounter them.
Scenario serializability shipped 2026-05-02: the GameWorld -> wrapper is gone, the Snapshot handoff package round-trips through JSON, and mergeActions/mergeRules/mergeMergeRules are wired into the runtime. A few scenario code axioms remain — the PN-Counter "set absolute" semantics they rely on are CRDT-incompatible, which is a modeling problem rather than a serialization one.
A text-based narrative engine written in Haskell. It is a learning project for the language, but the design goals are serious: this is meant to be a storytelling engine, not a game engine in the traditional sense.
The player navigates situations through choices, and the world responds through effects on characters, relationships, and internal states. There is no combat loop, no inventory management, no XP bar.
This is not a standard RPG. Avoid suggesting patterns from that tradition unless explicitly asked.
Specifically:
- No default health points. A
Strengthdrop is not a hit point system. Something likehealth--isn't forbidden, but it should emerge from specific causes (fight-or-flight overload, prolonged cognitive stress) — not be the default reach. Blunt mechanical shortcuts are valid as emergent outcomes of precise causes, not as primary levers. - No combat stats as primary purpose. Strength is not for hitting things. Intelligence is not for spell damage. These stats model capacity — cognitive, physical, social — across their full range.
- No loot, levels, or progression in the gamey sense. Character change is state, not score.
- No HUD. Don't render stats directly. Effects surface through prose via
Engine.Narrative.
- Engine builder (
src/). Primitives —EffectBody,Condition,EngineTag, monad stack, execution semantics. Changes here affect all scenarios. - Scenario author (
app/Scenarios/). Composes primitives to build a specific story. The author's job is declaring what happens, not managing sequencing. - Player (runtime). Sees only prose. Stats, tags, and effects surface through
Engine.Narrativeand authored beats.
A Condition guard can check any world state: a stat threshold, a tag, a location, a trust value, a time of day, a prior event. HasWorldTag, AtLocation, and RelationAbove are structurally equivalent.
Trust and other relational stats live in RelationshipGraph as directed edges, using the same StatType and RelationAbove conditions as capacity stats.
SDL2 system libraries are required before stack build:
- Ubuntu/Debian/WSL:
sudo apt-get install libsdl2-dev libsdl2-ttf-dev pkg-config - macOS:
brew install sdl2 sdl2_ttf pkg-config
- Use
stack buildandstack run— notcabal. - This is a learning project. Prefer readable, idiomatic Haskell over clever one-liners.
- Use
whereclauses freely for local helpers. - Pattern match exhaustively; GHC is configured with
-Werror=incomplete-patterns. - Prefer
Map.lookup+Maybechaining over partial functions. - Don't add type annotations or comments to code that wasn't changed.
- Dialogue (
Say): cyan speaker name, grey colon, normal text - Narrator beats (
Narrate): green with>prefix - Internal thoughts (
Think): dim with~prefix - UI chrome (prompts, action list numbers): grey or dim
- Warnings/errors: yellow or red respectively
- No raw
showon game types in player-facing output
Run tests with stack test.
When writing code, don't update tests until you know why they're failing, and only update tests expected to fail. Those unexpected to fail need further consideration — the source change may be wrong, not the test.
When reasoning about interactions between multiple engine primitives (effects, conditions, chains, axioms), stop thinking and write a test. Express the desired behaviour as a concrete assertion, run it, and iterate.
Engine functions should be testable without the full game loop:
- Pure functions (
checkCondition,diffWorlds, etc.) take no monad — test them directly Appfunctions (executeBody, etc.) should be runnable viarunAppwith a minimalEnvfromTestFixtures.mkEnv- Avoid baking I/O concerns into logic that should be pure
Architecture, design decisions, and narrative direction are the developer's. Claude is used as a Haskell tutor and pair programmer — translating intent into idiomatic code, catching type errors, and suggesting patterns. When in doubt, ask before refactoring.
Custom agent definitions live in .claude/agents/. These define focused roles with scoped responsibilities:
- engine-coder —
src/Engine/,src/GameTypes/,src/MonadStack.hs - terminal-coder —
src/Terminal/,src/Engine/Layout.hs - scenario-coder —
app/,test/, config - qa — runs tests, fixes only expected failures, pushes unexpected breaks back
The flow: route to the right coder agent, coder makes changes and runs stack build, route to QA with predicted failures, QA runs stack test and fixes only predicted failures, repeat if unexpected failures.
Don't plan large changes before writing any code. Write a small, working piece first, build it, then extend. The loop is: write something small -> build -> iterate.
Detailed reference material has been extracted into skills that auto-load when relevant. You can also invoke them manually.
- engine-architecture — Module structure, App monad, effects/conditions, tags, layer ownership, stats/truth/perception, performance. Auto-loads when working in
src/. - design-checklist — Implementation checklist, compose-before-inventing rules, ScenarioTag-first policy. Invoke with
/design-checklist [feature]. - current-work — Terminal visual effects status (done/TODO). Auto-loads when working in
Terminal.*. - sync-design — Local-first remote state: identity, sessions, snapshots, merge. Auto-loads when working on
Engine.SyncorEngine.Runtime.