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: trueis enforced, not advisory. Mystery entities can carry only observablefacts; 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 atpkgs/lexifer. - Tests run in-derivation (
nix build .#worldsim): determinism, the Sphere gag order, canon pinning, history invariants. checks.worldsim-smokere-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.