Worldsim#

A deterministic worldbuilding generator for narrative support: cultures, faiths, laws, relics, per-culture naming languages, and centuries of macro-history, derived from a seed plus a small authored canon file. Built so flavor lore never blocks prose. Design background: proposal v2.

Quickstart#

nix run .#worldsim -- generate --seed 42 --cultures 4 --years 500 --out ./world-42
Output What it is
codex.md Human-readable codex: known facts, the Sphere Concordance, per-culture chapters, annals, dramatis personae
world.json Everything, structured (canonical JSON; the canon-store input)
timeline.html Self-contained swimlane timeline of the annals — one lane per culture, wars as washes, hover any mark for the record
world.c4 LikeC4 model of the present: cultures, faiths and their Sphere stances, states, deities, shard holdings, aggregated wars. Open with the LikeC4 VS Code extension, or nix build .#worldsim-c4-site for the interactive site of a demo world
phonology/<culture>.def Each culture’s Lexifer sound system — reusable to mint more names

Generated worlds are ignored by git (world-*/); they are reproducible from the seed.

CLI#

worldsim generate --seed N [--cultures 3] [--years 500] [--out DIR] [--canon PATH]
  • --seed — master seed. Same seed + same options = byte-identical output.
  • --cultures — rolled cultures in addition to canon-declared ones.
  • --years — length of simulated history; the final year is “the present”.
  • --canon — canon anchors YAML (defaults to the packaged sphere canon).

The canon file#

Canon anchors are authored truth the generator must respect, never re-derive. The packaged default lives at pkgs/worldsim/worldsim/data/canon/sphere-canon.yaml; pass your own with --canon.

entities:
  - id: the-sphere
    kind: monument
    mystery: true        # generator may NEVER explain it
    facts: [...]         # the only assertable truths

  - id: vampires
    kind: culture
    status: annihilated
    pinned:              # axes fixed by the author; the rest is rolled
      ethics: honor

  - id: the-princess
    kind: person
    of_culture: vampires
    name: null           # codex will offer names in the culture's language

events:
  - id: the-annihilation
    year_offset: -30     # years before the present; text used verbatim
    culture: vampires
    type: annihilation
    text: The Elder Race was annihilated by one of their own.

Key behaviors:

  • Pinning is surgical. Every decision draws from its own seed-derived RNG stream, so pinning one axis (or adding a culture) never reshuffles anything else you have already grown attached to.
  • mystery: true is enforced, not advisory. Mystery entities can carry only observable facts; every faith must roll its own contradictory stance toward the Sphere; all belief text is attributed in-world voice (“the votaries teach that…”); a validator fails generation — and CI — if any of that regresses.
  • Canon persons are never simulated. The princess’s shard exists in canon, not in the annals: history cannot spoil it.

History#

--years N simulates year 1..N: successions (with regnal numbering), wars (ideology-clash driven, including shard-creed conflicts), schisms (sects adopt a different Sphere stance), settlement foundings and sackings, and the drift of “attested” shards through hoards, shrines, thefts, and deep water. Canon events are placed relative to the present via year_offset.

Note: changing --years moves canon events (they anchor to the present), so it reshapes the timeline; determinism is per (seed, cultures, years).

Asking the canon#

nix run .#worldsim -- ask --world ./world-42 "where are the sphere shards"

Flattens the world into retrieval “cards” (cultures, faiths, sphere beliefs, states, ruler lines, relics, events, shard traces, persons) and ranks them lexically against the query — offline and deterministic, no vector store. --json emits the cards structurally; --top N bounds the count.

Loremaster#

nix run .#loremaster -- --world ./world-42 "a rival merchant house near the capital"

Grounds on worldsim ask cards, then has Ollama write in-world flavor prose under a hard-rule system prompt (never explain the Sphere; never contradict canon; attribute all belief). --dry-run prints the exact assembled prompt without calling the model — useful for inspecting grounding (and used by CI).

Model routing: OLLAMA_HOST defaults to ereshkigal’s failover proxy, whose primary upstream is the M4 (Metal) with mokou as backup — so the M4 serves whenever it is awake, no configuration needed. OLLAMA_MODEL defaults to qwen2.5:7b-instruct-q4_K_M (loaded on both upstreams, so failover is lossless). For publish-grade passes, heavier models with parity are one env var away:

OLLAMA_MODEL=gpt-oss:20b loremaster --world ./world-42 "..."
OLLAMA_MODEL=gemma2:9b   loremaster --world ./world-42 "..."

Development#

  • Package: pkgs/worldsim (buildPythonApplication); Lexifer is packaged separately at pkgs/lexifer.
  • Tests run in-derivation (nix build .#worldsim): determinism, the Sphere gag order, canon pinning, history invariants.
  • checks.worldsim-smoke re-asserts byte-determinism and Sphere invariants hermetically in the sandbox.

Roadmap#

Remaining proposal phases: a canon store + “Loremaster” retrieval CLI (grounded flavor-text queries while drafting), and an optional terrain/map layer.