diff --git a/COMPOSER-NOTES.md b/COMPOSER-NOTES.md index 5e9e2b2..e7142cd 100644 --- a/COMPOSER-NOTES.md +++ b/COMPOSER-NOTES.md @@ -1,774 +1,222 @@ -# COMPOSER-NOTES.md — running notes on the world-composer conversation +# COMPOSER-NOTES.md — the world composer -> Started 2026-08-17. Small on purpose: what we decided, what's still open, what must not be -> forgotten. Not a design doc — that comes later, if it earns it. +**What this is:** the design of the system that *invents* the depths, plus every code finding and +hazard that constrains it. Engineering document. +**What this is NOT:** the game. The premise, the city, the economy, death, combat, seasons and the +community layer live in **[GDD.md](GDD.md)**. Where this file needs one of those, it states the +*constraint* and points there for the reasoning. Do not restate game design here — duplicated +documents drift, and then neither is authoritative. -## ⚠️ VOCABULARY — read this first +**History:** this file was a chronological transcript of the 2026-08-17 design conversation, ~1400 +lines, game design and generator design tangled together. It was distilled on the same day. **The full +original transcript is in git at commit `b781e3b`** — go there for the reasoning behind anything that +reads as bald assertion here. -Jahni pinned these terms on 2026-08-17 because my notes used "world" for two different things. +--- -| term | what it means | +## 1. The vision + +**The system must INVENT strates and worlds** from Jahni's tagged raw content plus a seed. + +⚠️ **Read this correction; it is the whole point.** The first reading of the vision was that Jahni +authors *strates* and the system arranges them. **Wrong.** His words: + +> *"I'm not pretty happy to make strates forever, that's actually the opposite. If I could never make +> any new strate myself and have it do it itself would be genius."* + +**The floor of authoring is RAW CONTENT + TAGS** — operators, meshes, materials, sounds, creatures, +music — tagged, with **multiple fitting tags each**. The overlaps between tags are where the system +finds combinations he never planned that still cohere; single-tag buckets cannot do that. + +Everything above that floor is **composed and measured**: strates, layouts, danger, rarity, names. + +**A strate becomes generated data, not an asset.** `UVoxelStrateDefinition` survives as a struct the +composer fills in; nobody opens it in the editor any more. + +### This is op-stack "Phase 3", and it is REOPENED + +The 2026-08-16 `OPSTACK-HANDOFF.md` says in bold *"Do not start Phase 3."* **That is superseded.** +Asked what the world was missing, Jahni described exactly Phase 3 plus a composer above it. + +It is **not** a restart of the refactor. The eight operators are live, composable, and production. What +never happened is anything *composing* them except a port of the old archetype switch. **The machine +was built and never fed.** + +### Ambition level + +The seed **invents parameters within authored ranges** — not preset-picking. Two seeds differ in +everything: not the same strates spawn, nor the same details. + +--- + +## 2. What the game requires of the generator + +Constraints only. See [GDD.md](GDD.md) for why each exists. + +| requirement | source | |---|---| -| **THE SURFACE / THE CITY** | The hand-authored **plain Unreal level** (not voxel terrain). Sky island or unclimbable mountain, so resources cannot come from up here. **Communal, identical for everyone, and it NEVER resets.** It *progresses across seasons*: camp → village → town → city → advanced city. | -| **THE DEPTHS** | Everything **VoxelForge** generates: subterranean, bounded, stacked strates below the city hole. Some strates only *mimic* being outdoors. **THIS is what is regenerated every season.** | -| **A SEASON** | One generation of **the depths** (~6–12 months), plus the settlement tier it earns. The wipe replaces the depths and leaves the city standing. | - -⚠️ **Earlier sections of this file say "the world" when they mean THE DEPTHS.** Wherever a note talks -about wiping, regenerating, seeds, strates, the manifest or the composer, read **the depths**. The city -is never any of those things — it is authored, permanent, and outside VoxelForge entirely. - -## The idea, in one line - -**Jahni authors the vocabulary; the system writes the sentences.** He makes data assets (ops, biomes, -deco sets, set-pieces, whole handmade levels/strates); the system assembles strates and worlds out of -them from the seed, *logically*. - -This is op-stack **Phase 3 + a composer layer above it**. The 2026-08-16 handoff's "do NOT start -Phase 3" is **superseded** — it closed the door on the one thing he actually wants. - -## Decided - -- **Ambition level (c):** the seed invents parameters within authored ranges. Not preset-picking. -- **Two seeds differ in everything** — not the same strates spawn, nor the details. -- **⭐ Primordial law:** *every strate has an entrance and an exit, exposed to air, findable.* -- **Handmade content plugs in** as stamps **and** as whole strates (e.g. a unique boss strate) — - entries in the pool with special placement rules. -- **Build the connections first, generate around them.** Reachability by construction, not by luck. - Precedent: the (0,0) spine is already a guaranteed vertical connector. Generate-then-check gets - brutal at 8 strates/world. -- **Validation is 3 layers:** (1) construct-by-design so the law can't break, (2) *measurable* checks - for accidents — air fraction, continuous walkable surface, feature size vs voxel scale, strate - height sanity, (3) **reject-and-resample**: fold the attempt number into the hash - (`hash(seed, strateSlot, attempt)`) so every seed still yields a valid world and peers stay in - lockstep. -- **The AI good/bad judge is second, not first.** Not a fool idea — but the cost is *labels*, and - Jahni is the only labeller. Layers 1–3 remove the numeric garbage; what's left is genuine taste, - a much smaller job. The dataset then accumulates for free from seeds he plays and reacts to. -- **F4 save/load is the LOCK on the density field, not a feature.** Everything field-changing lands - before it. The composer is the largest field-changing work that exists ⇒ far ahead of F4. -- **Acceptance criteria get stated up front and confirmed by Jahni** — never inferred. (The - bit-identity criterion was self-imposed and cost 3 weeks; peer determinism never required matching - an older build. Determinism = same build, all machines. Version stability = a gen-version byte.) - -## Open - -- What makes a **world** coherent above the strate level? (depth-driven rules? tag adjacency? an arc?) -- What is a strate *as a unit the composer picks* — pool + tags + depth bands? Fixed count per world? -- "**Findable**" ≠ "reachable". Reachable is a number; findable is placement (light, airflow, - landmark, terrain funnelling you toward it). Whose job, and how expressed? -- Where do the authored **ranges** live, and can they be context-dependent (this op's range depends - on what else is in the stack)? - -## Hazards — do not forget - -- ⚠️ **Box-verdict bounds under (c).** T1.d / `ClassifyTile` bounds are proved today against - *hand-set* params. If the seed invents params, every bound must hold across the whole authored - **range**, not a value. Wrong ⇒ a tile wrongly proved uniform ⇒ no geometry, no collision, no - error, player falls through the floor. The only silent failure mode in the system. -- ⚠️ **`ClassifyTile` proves anything below `TerrainZ` solid** and skips meshing — it will silently - delete 3D caves inside mountains unless guarded. -- ⚠️ **Validation must itself be deterministic** across every machine that runs it — including a - future headless Linux dedicated server, where float behaviour may not match the Windows client. +| **Every strate has an entrance and an exit, exposed to air, and findable.** The one invariant that may never fail. | GDD §7 | +| **Danger = depth × per-strate multiplier**, rare exceptions in both directions. Drives creature, hazard and loot selection — **never** what kind of place a strate is. | GDD §7 | +| **Danger must never scale generation parameters.** It selects content and may bias which terrain ops are drawn. If depth multiplied a density parameter, strate 80 would be geometrically broken rather than hard. | GDD §9 | +| **Anomalous strates are rare and must NOT blend** with their neighbours. `EVoxelStrateTransition::Hard` already expresses this. | GDD §7 | +| **A share of grounded, plausible strates is guaranteed** — strangeness needs ordinary to be strange against. | GDD §7 | +| **Fightable space is a generation guarantee, not a hope.** Boss arena ≈ 3–4 chunks across, ordinary fight space ≈ 2, camera headroom ≈ 1 chunk (1 chunk = 32 voxels × 25 cm = 8 m). Spacious *and vertical* is the target. | GDD §12 | +| **Essential materials need a placement DENSITY, not a location** — "somewhere in a gigantic strate" is unfindable. Optional materials may be absent. | GDD §9 | +| **The depths are bounded** ⇒ a whole strate can be examined, so the entrance/exit law can be **proved** rather than sampled. | GDD §6 | +| **No bottom** ⇒ generate a deep buffer (~30 strates) offline and extend in a later batch. | GDD §6 | +| **Boss strates every ~5 slots** ⇒ the layout needs a **periodic** fixed-slot rule; `Settings->FixedStrates` is a map of absolute indices today. | GDD §6 | +| **A season freezes generation; a wipe frees it.** The composer may ship imperfect and improve every cycle. | GDD §13 | +| **The manifest must support names, quests, fast travel, material lookup and death locations.** | GDD §7, §10, §14 | --- -## 2026-08-17 — the FEEL: magical, not logical +## 3. The composer -Jahni corrected the direction. He does **not** want a geologically reasoned world. He wants it to feel -**magical** — the game throwing countless worlds at the player, *of any kind, any size, any -peculiarity*. Examples he gave: an alien-looking surface strate; a **"dreamcore"** strate — repetitive -patterns, a strange sense of emptiness. +### 3.1 It runs ONCE PER SEASON, OFFLINE — this sets the whole budget -- **Adjacency rules: CONFIRMED** (he agrees with those). -- **Depth-as-meaning: UNDECIDED.** The old model was "deeper ⇒ weirder, stranger, spookier"; he is no - longer sure that's right. Do not assume it. -- ⚠️ My earlier pitch ("coherence makes the world *readable*; depth should mean heat/pressure/danger") - was arguing for a **different game**. Dropped. +Everyone shares one seed per season, so there is exactly **one set of depths per season, ever**. The +composer never runs on a player's machine and never at load. It runs once, on a build box. -**Reframe that survives his goal:** coherence lives **inside** a strate, not across the stack. The -strate is the unit of authorship — one complete, committed idea, thoroughly itself. Placement can be -arbitrary; the strate still feels authored. +**The problem is not "fast enough for a loading screen" — it is "runs overnight."** So it can generate a +thousand candidate strates, measure them all, keep the best, validate at any resolution, and later run +a model over the candidates at zero runtime cost. **And Jahni reviews and vetoes the season before it +publishes** — the system does the work, he keeps the veto. -Ideas on the table (none decided): -- **Rarity instead of depth.** Weird strates are *rare*, not *deep* — you could hit dreamcore at - −200 m. Descent feels like drawing cards, which serves "countless worlds" better than a gradient. -- **Strangeness needs ordinary to be strange against.** Guarantee a share of grounded strates so the - weird ones land. A rule about the **mix**, not about depth. -- **Adjacency rules become PACING rules:** no two alike in a row; a grounded strate sets up a weird - one; special pairs (flooded over lava) earn a real transition. -- **Size and count are free variables** — a 40 m crawlspace strate vs a 2 km cathedral. Cheap to - vary, huge effect on feel. "Any size" was explicit in his description. -- The **unique boss strate** implies a *destination without a gradient*. An end is enough; every step - need not escalate. +**What ships is small:** the season's seed plus the chosen strates' parameter vectors. Clients generate +terrain from those, deterministically, exactly as today. **No runtime composition anywhere.** -**Open question left hanging:** does the world have a **bottom the player knows about**? "Boss strate" -implies an end; "countless worlds" implies endless. Those pull in opposite directions. +### 3.2 Structure: how a stack gets assembled ---- +The three real builders, for reference: -## 2026-08-17 — what the GAME is (context that reshapes the above) +``` +TunnelNetwork (19) rock source -> room graph (SDF) -> SdfCarve -> 12 density modifiers -> worm source -> structural +Maze (7) rock source -> lattice corridors (SDF) -> SdfRoughness (SDF) -> SdfCarve -> structural +FloatingIslands (7) VOID source -> island blobs (SDF) -> SdfRoughness (SDF) -> SdfFill -> structural +``` -**Fantasy exploration + combat RPG.** Medieval-ish, progressing toward better / more modern -infrastructure but staying inside fantasy. Side systems: **housing and farming**. Needs **bosses**, -and **fights that don't feel "blergh."** +**⭐ Most ordering is MECHANICAL — derive it, do not author it.** Roughness sits on the *SDF* before +conversion in Maze and on *density* after conversion in TunnelNetwork; CODEMAP confirms they are two +different ops for exactly that reason. An op's legal position is fixed by **which channel it reads and +which it writes**. +⇒ **Every operator declares its channel reads/writes.** Dependencies form a DAG and **any topological +sort of that DAG is a legal stack** — no authored order, and it produces orderings nobody wrote down. -Consequences for the composer: +**⭐ Root polarity is a one-bit identity lever.** FloatingIslands is Maze's op classes rooted in a +**void** source with a **fill** instead of a rock source with a carve — and you get islands instead of +tunnels. -- **Progression needs difficulty to correlate with position — but not by killing the magic.** - Proposal: **draw strates freely (rarity, any kind, any size), then SORT the drawn set by their own - danger rating.** Identity stays random; difficulty comes out ordered by construction. Depth then - means *danger* without ever meaning *weirdness*. -- ⚠️ **Housing is the real lock on the density field — harder than F4 save/load.** A house has a - *location* in a world that must keep existing. Once players build, the generator's output for that - seed is frozen forever. Everything field-changing must land before housing ships. -- **Farming/housing need buildable space:** flat-ish, stable, safe, reachable, probably surface. A - composer that only makes dramatic terrain has nowhere to put a farm. Likely: strate 0 stays the - grounded home base. -- **F8 (ore veins) is promoted from reward-loop polish to BACKBONE.** "Progression to better - infrastructure" is a tech tree, and a tech tree needs a supply chain. Ore quality should ride the - same danger ordering. -- **Fights need fightable space.** Default caves are hostile to good combat — cramped, no sightlines, - bad camera. Good fights want verticality, cover, chokepoints, room to retreat. ⇒ the composer must - treat **arenas as a first-class output**, not hope one occurs. This is exactly why boss strates are - handmade. +**Corrective ops must be FUSED, not ordered.** `FFloorBiasMod` exists only to undo what +`FCaveRoughnessMod` did to floors. Both are density-space, so channel rules permit either order. +**An operator whose only purpose is to correct another operator is not a separate operator** — fuse them +into one op with two internal phases. That removes the invalid state instead of annotating it. Fifteen +ops that compose in any legal order beat eighteen with a footnote. (Fusing with identical order and +math leaves the field bit-identical.) -**Next open question:** is the home base fixed on the surface with the descent run as **expeditions** -(go down, come back up — cf. F14 lift/teleport anchors), or is it a **one-way descent**? Decides -whether strates are re-entered or passed through once, and shapes the entrance/exit law. +**One declaration covers the rest:** an op is **additive** (small displacement, commutes freely — shuffle +at will) or **transformative** (clamps, multiplies, gates — position matters, needs explicit placement). ---- +**The structure roll:** +`root polarity -> shape source -> conversion (follows from polarity) -> draw k modifiers legal in the +resulting channel space (k itself rolled) -> structural post appended automatically.` +With ~5 shape sources and ~15 modifiers choosing 4–8, that is **tens of thousands of structurally +distinct stacks before a single parameter is touched** — which is what carries early variety while the +corpus is small. -## 2026-08-17 — the world is a PUBLIC SHARED ARTIFACT (the biggest constraint yet) +**⇒ Rule: vary STRUCTURE aggressively, vary PARAMETERS conservatively.** Shift the ratio as the corpus +grows. -- **Home = a hand-authored city, above ground, with a big opening inside** (the descent hub). Not - generated. Ties to the primordial law: the city opening *is* the hand-placed entrance to strate 1. -- Natural buildable space where the world offers it; otherwise **terrain-editing tools** for players. - Underground building: undecided, idea is alive. -- **Strates are revisitable and permanent to the player's view** — "fetch fish at the crystalline lake - in strate X" must be a valid quest. Places are *discovered*, and **discoveries get shared online**. -- **Players share BUILDS across worlds.** Everyone's world is the same world, so a bridge one player - builds can propagate to other players' worlds if it's liked. Community feeling of exploration. - Jahni: "it all fails without players." Landmark **names** may propagate the same way. -- **Landmarks need names** — from a word pool or similar. +### 3.3 Parameters: the range problem -### What this forces +**Verified facts, not remembered:** `FStrateGenerationParams` has **73 scalar fields** (+1 bool) — not +hundreds. The `VF_STRATE_PARAM_FIELDS` X-macro covers **all of them** (`Alpha` is the `Lerp` function's +parameter, not a field). `VoxelStrateTypes.h` carries 135 `ClampMin` and 52 `ClampMax` across 216 +UPROPERTYs, so most fields have no upper bound. Paired min/max fields exist +(`MinRoomRadius`/`MaxRoomRadius`, `TunnelMin/MaxRadius`, `RoomFloorCutMin/Max`). -- ⚠️ **Version stability is now a REAL requirement, and I was too casual about it earlier.** A - gen-version byte migrates a save file. It cannot migrate a *community's shared knowledge*. Once - wikis, quests, videos and shared builds cite "the crystalline lake at (X,Y,Z)", the density field is - frozen **globally and permanently**, not per-save. ⇒ **The composer must be finished before the - world becomes public.** (This does NOT revive bit-identity to the OLD system — matching a dead - build was always pointless. It sharpens *when* the future freeze lands.) -- **The diff layer is already the correct transport for shared builds:** position + compact - modification records, deterministic to apply, "replicate diff events, never geometry". A shared - build is a diff patch with an address. -- ⭐ **The composer must emit a WORLD MANIFEST, not just a density field.** A list of everything - notable it placed — stable ID, type, position, extent, strate. Otherwise nothing can *name* the - crystalline lake, no quest can reference it, no map can mark it, no wiki can cite it, no fast travel - can target it. The composer already knows what it placed; it just has to write it down instead of - discarding it. **Cheap now, enormously expensive to retrofit.** -- **Naming = two layers.** (1) A **deterministic procedural name** from a word pool, so every landmark - has the *same* name for every player *before anyone visits it* — that is the substrate that makes - quests and wikis possible at all. (2) An optional **community name** that propagates like builds and - overrides the default. Beloved names win; nothing is ever nameless. -- **Shared-build hazards to design in, not bolt on:** two builds claiming the same place; a build that - seals an entrance/exit and **violates the primordial law**; and content moderation — auto-propagating - user-built voxel content into strangers' worlds needs a report/reject/rollback path, and shared - builds must be individually removable client-side. +**Why rolling parameters fails, stated plainly:** the coherent region of a 73-dimensional space is a +vanishingly thin sheet, and uniform sampling in a box essentially never lands on it. The problem is not +wrong ranges — **independent rolling destroys the CORRELATIONS** that make a parameter set coherent +(room spacing ↔ room radius ↔ tunnel length ↔ strate height). -**Open fork:** does a shared build travel as **raw terrain edits** (voxel diff) or as **placed -structure pieces** (a prefab/building system)? Completely different to transmit, validate and moderate. +**⭐ The design: do not roll parameters — BLEND KNOWN-GOOD ONES.** ---- - -## 2026-08-17 — shared builds: BLUEPRINTS, not terrain diffs (fork resolved) - -**Decided:** shared builds are **defined blueprint structures** (a parent/children blueprint family -made for construction), *not* raw terrain edits. Raw voxel edits are too hard to constrain. - -**Vote system (Jahni's):** votes drive a priority list. Unvoted / negatively voted ⇒ back of the -queue. Sufficiently negative ⇒ **invalidated for sharing, never pulled again.** Where two builds -compete for the same zone, higher votes ⇒ better chance to appear. - -### Why blueprints are the right call beyond moderation -- **Payload is tiny:** blueprint class ID + transform + variation params. Bytes, not a voxel diff. -- **Validatable without simulating voxels** — bounds are known, so "does it seal a passage / violate - the primordial law" is a cheap check. -- ⭐ **It survives content updates.** Change the blueprint asset and every placed instance updates. - A baked voxel diff can never be improved after the fact. - -### Open design issues on the vote loop -- ⚠️ **Cold start / rich-get-richer.** A build must be *seen* to be voted on; if appearance is ranked - by votes, new builds never appear ⇒ never get votes. Reserve a fraction of appearances for - new/unvoted builds (explore vs exploit). -- **Rank by confidence, not raw count** — otherwise an old build with 1000 votes permanently outranks - a better new one with 10. Some **time decay** too, or the top calcifies and the world stops changing. -- **"Never pulled again" is a hard delete** — needs volume thresholds, not just ratio, so a small - group can't bury a build. Keep it soft/reversible server-side even if permanent to clients. -- Blueprint construction shrinks the crude-content surface but does not remove it (pieces can still be - arranged). Report + rollback path still required. - -### ⭐ Proposal: build SLOTS, not free coordinates -The composer marks **candidate build sites** in the world manifest (type, extent, strate — a bridge -site across a chasm, a shrine site at a passage mouth, a camp site on a ledge). Shared builds then -compete for *slots*, not arbitrary positions. Placement becomes valid by construction, "the zone" gets -a concrete definition, and conflict resolution is just "who wins this slot." - -### ⚠️ Tension to resolve: canon vs roll -Earlier: the world is the *same* for everyone, so a wiki can cite it. But vote-weighted "more chances -to appear" implies each player **rolls** which builds they get. Both can't be fully true. -Proposed split: **terrain and landmarks are canon (identical for all); shared builds are a roll.** -The wiki cites natural features; builds are the variable garnish. -Also: **pin a build once a player has seen it** — the world may gain builds over time, but shouldn't -silently lose one you've come to know (or built your house next to). -**Player claims beat shared builds** — a shared build must never roll into player-owned ground. - ---- - -## 2026-08-17 — SEASONS: worlds are wiped (this dissolves the freeze problem) - -**Decided:** -- The **base generation** — everything defined at generation time — **is** the shared world, identical - for everyone. **Shared builds are a LAYER ON TOP** that adds to it. (Confirms the canon/roll split.) -- **New and unvoted builds must be rolled in deliberately**, to give new players and new builds a - chance. Confirmed. -- ⭐ **A world is STATIC once published, until it is WIPED** — perhaps every 6–12 months — to make - room for generation changes and other evolution. - -### What seasons change (important — supersedes my earlier warning) -- ⚠️ I said two messages ago that once the world goes public the density field is frozen **globally and - permanently**, so the composer had to be *finished* first. **With wipes that is false, and it's the - best news in this conversation.** Generation is frozen only **within a season**. Every wipe is a free - window to change noise, ops, bounds, archetypes, anything. -- ⇒ The composer can ship **imperfect and improve every cycle**. No need to get it right before contact - with players. Bit-identity anxiety is now dead permanently — worlds are *expected* to differ by - version, by design. -- ⇒ **A world = seed + generator version + publish date.** Stamp the generator version into the - published world; that stamp is the whole of "version stability" now. -- ⇒ Concrete unblock: **T2.a (SIMD noise)** and anything else that changes generator output was parked - in `fable-idea.md` because it shifts seeds. At a wipe boundary that cost is **zero**. Same for the - large-seed noise-collapse fix in `AUDIT-2026-07.md`. - -### ⭐ Proposal: the BLUEPRINT LIBRARY is what survives a wipe -Your house dies with the world; your **design** doesn't. If shared builds are blueprint structures, -the corpus of player creations can persist **across** seasons — so the vote/priority pool grows richer -every cycle instead of resetting, and returning players carry something forward into a fresh world. -That makes the wipe survivable for the housing/farming players, who otherwise lose the most. - -**Knowledge dying at the wipe is a FEATURE.** Re-discovery is the loop; a wiped world resets the -"everything is already solved and on the wiki" state that eventually kills exploration games. The -community re-explores together, which is the exact feeling Jahni is after. The **hand-authored city** -stays constant across seasons — the anchor of familiarity. - -### Open -- **What else survives a wipe?** Account progression / unlocks / recipes / reputation / cosmetics? - Nothing? (Pure reset is brutal on housing + farming players.) -- **One world at a time globally, or several shards on different seeds?** -- **Does singleplayer/offline get a permanent world**, or do wipes apply there too? (Many seasonal - games run a permanent realm alongside the seasonal one.) - ---- - -## 2026-08-17 — wipe scope, housing, and the vote pool - -**Decided:** -- **The wipe is world-only.** Everything else survives (account progression etc.). -- **The vote/build pool does NOT survive a seed change** — nothing would be placed correctly. It is - wiped, with **rewards for the top builders** (titles etc.), tiered by population (10/50/100/1000). -- **Housing → "instanced worlds":** custom personal worlds that are never wiped. Jahni finds this - *slightly sad* — it cuts the player out of the strates and the shared world. Underground building - still undecided. -- **Everyone shares one seed**, until the game is big enough to offer "reboots" (shards). -- **Singleplayer** was never the intent (online-first), but may become an option. - -### Refinement: placement dies, DESIGN needn't -A blueprint build has two separable parts: **where it sits** (chasm-specific, absolutely world-bound — -Jahni is right that this cannot survive) and **the design itself** (pieces in relative arrangement, -placement-independent). So: -- **Shared/competitive pool: WIPED.** And the strongest argument isn't placement — placement *could* - be re-matched to new slots by fit. It's **fairness**: carrying the corpus forward means season 2 - opens already full of season 1's voted builds, and every new player competes against an entrenched - corpus forever. A fresh pool gives each season a fresh creative economy. -- **Personal library: KEPT (private).** A player keeps their own designs, so they can rebuild a - beloved house in the new world and re-submit it to the new pool. **The loss is the location, not the - work** — that is what makes a wipe emotionally survivable. -- **Titles carrying across** is what lets *reputation* survive when the builds don't — a season ending - rather than a deletion. Optional knob: a small roll-boost for proven builders, which also softens - the cold-start problem (watch that it doesn't recreate rich-get-richer). - -### ⭐ The "slightly sad" instanced world may not be necessary -**The hand-authored city is already wipe-proof** — it isn't generated, so a generation change can't -invalidate it. Put persistent housing **there** (a district / plots / interiors, city-adjacent -authored farmland) and it survives every wipe with **no instancing at all**, keeping players inside -the shared world and visible to each other at the social hub. Instancing then shrinks to the standard, -small MMO problem of interiors/plots rather than whole private worlds. - -### ⚠️ HAZARD — a never-wiped *generated* instanced world reintroduces the freeze problem -If a personal world is generated by generator v3 and is never wiped, then v3 must be kept alive -**forever** — every old code path, every old bound, pinned per world — or that player's world breaks. -That is the "support every version forever" trap, in a side corner, undoing exactly what seasons just -bought. Avoid by making persistent personal space **non-generated** (authored plot / city district), -or by regenerating it each season and **re-placing the blueprints** (possible precisely because -blueprint builds are placement-independent). - ---- - -## 2026-08-17 — housing: bake the plot (and why a MESH is the wrong bake) - -**Constraints from Jahni:** -- **The social hub is later** — infrastructure + difficulty. Not now. -- ⚠️ **The surface is very limited, on purpose** — story-wise it is *why* players descend for space and - resources. ⇒ **housing everyone on the surface does not scale**, so the "put housing in the - hand-authored city" idea above does not hold as the general answer. -- **His proposal:** bake the player's generated instance into a **mesh**, so the plot no longer depends - on the generation system. Players can switch or destroy their house anytime; otherwise it's safe. - -**That solves the version-pinning hazard properly** — a baked region needs no generator, so no old -generator has to be kept alive. Right instinct. - -### ⭐ Refinement: snapshot the FIELD, not the mesh -Bake the sampled **density + material IDs** for the bounded plot region, not triangles: -- **A mesh can't be dug.** Housing/farming ship alongside player terrain-editing tools; a baked mesh - kills that inside the one place players care most about. -- **A mesh freezes the plot against every future mesher/material improvement** (Transvoxel, normals, - surface-class polygroups, F6 masks). That just moves the museum problem from the generator to the - mesher. A field snapshot re-meshes with whatever the current mesher is. -- Density fields **compress very well** — most of a plot is uniformly solid or uniformly air. -- ⚠️ **The plot must be BOUNDED** or storage scales with ambition. - -Architecturally this is: **a per-region override source in the generation path** — "this region has a -stored field, read it instead of evaluating the op stack." - -### ⭐ Same mechanism ships hand-authored content -A hand-made boss strate or a distributed handmade level **is** a stored field region. The override -source Jahni needs for houses is the same one he needs for authored levels/stamps. **One feature, two -uses — build it once.** - -### On the wipe, for a plot -Simplest split: **the house travels, the ground doesn't.** Blueprints are placement-independent, so -they re-place in the new world; the terrain regenerates. Avoids having to blend a baked plot into -fresh generated terrain (seams at the plot edge) — which would otherwise need a reserved "homestead -slot" plus a blend skirt. Keep that harder option in reserve, don't build it first. - ---- - -## 2026-08-17 — housing: SETTLED - -- **Personal housing = an instanced level**, not a place in the shared world. Their own tiny world; - never wiped, never re-placed. -- ⭐ **The plot's ground is a snapshot of land the player CHOSE** in the shared world and claimed. - Their home is *from* somewhere. After a wipe it is the last surviving fragment of a world that no - longer exists — the wipe becomes meaningful rather than lossy, and the descent gains a second motive - beyond resources: finding somewhere worth living. -- **One template level, populated from save data** — a stored field region + a list of blueprint - transforms. **Not** one `.umap` per player (cooked assets can't be made at runtime). -- **Invites needed** — houses can be shared/visited. -- **Time-based growth computed from elapsed real time on entry**, not ticked (the instance isn't - loaded while the player is away). - -⇒ **Concrete plugin work implied:** a **stored-field source** for the voxel world (read a saved region -instead of evaluating the op stack) **+ its save format**. Same feature ships hand-authored levels / -boss strates. Bounded region size is the one thing to fix early. - ---- - -## STILL AWAITING JAHNI'S RULING (my proposals — do NOT treat as decided) - -1. **World manifest** — the composer emits a list of everything notable it placed (stable ID, type, - position, extent, strate). Needed for landmark names, quest references, maps, fast travel, and - build slots. Biggest architectural consequence of anything proposed here. -2. **Danger rating + sort** — draw strates freely (any kind, any size, any rarity), then order the - drawn set by their own danger rating, so depth means *difficulty* without ever meaning *weirdness*. -3. **The mix rule** — guarantee a share of grounded/plausible strates so the strange ones land. - Strangeness needs ordinary to be strange against. -4. **Build slots** — the composer marks candidate build sites; shared builds compete for *slots* - rather than free coordinates. - ---- - -## 2026-08-17 — RULINGS from Jahni on the four proposals - -### 1. World manifest — ✅ ACCEPTED, "must not be expensive" -**Make it free by inverting it: the manifest is not an export, it is the PLAN.** -The composer's layout pass runs *first* — pick strates, place connections, place landmarks/rooms/ -arenas — and that decision list **is** the manifest. The density field is generated *from* it. So it -is never derived by scanning terrain (which is what would be expensive and unreliable). -- **Only explicitly placed decisions go in** — landmarks, rooms/POIs, lakes, arenas, entrances/exits, - build sites. **Not** emergent terrain features (every rock, every ledge). -- Rough size: ~8 strates × 50–200 entries × ~32–64 B ⇒ **~100 KB per world**. Computed once, identical - for every player. -- **Names are computed, not stored:** `hash(entryID, seed)` → word pool. Zero storage. -- Must be computable **without generating terrain** — it is upstream of the field, not downstream. - -### 2. Danger rating — ✅ ACCEPTED. Drives **mobs, environmental hazards, and loot**. -Refinements proposed: -- **Danger is a RANGE per strate archetype, rolled per world** ⇒ one dreamcore asset yields a gentle - dreamcore *and* a terrifying one. Multiplies variety with no extra authoring, and serves "two seeds - differ in everything." -- A strate asset therefore specifies its **theme/identity**, not its inhabitants; mob/hazard/loot pools - are selected by **danger × theme tags**. Strates become reusable across the whole curve. -- **Ore quality rides the danger tier** ⇒ F8's supply chain and the difficulty curve are one axis. -- ⚠️ **Sort with jitter, not strictly.** Perfectly monotonic escalation reads flat; an occasional - easier strate after a hard one gives relief and pacing. - -### 3. The mix rule — ✅ ACCEPTED, sharpened by Jahni: -**dreamcore should be QUITE RARE, so players are puzzled when they find it "because it doesn't have -its place there."** -⇒ The effect is *not-belonging*, which is stronger than rarity alone: -- **Anomalous strates must NOT be blended in.** Normal neighbours get a coherent transition; an - anomaly gets a **hard cut** — the wrongness lives in the seam. The adjacency system needs a flag for - "this strate deliberately does not reconcile with its neighbours." -- Rare enough that a player may go a whole season without seeing one ⇒ in a single shared world, an - anomaly becomes **community news** ("there's a dreamcore strate at −1400 this season"). Rarity plus - one shared seed turns it into an event. - -### 4. Build slots — ⚠️ "not bad but needs refining". Refinement: -- **Slots do not restrict building.** Players build anywhere valid. A slot is the **qualification for - auto-propagation**: build in a slot and your build is eligible to spread to everyone; build freely - and it is still yours, still visitable, still shareable by invite — just not auto-distributed. - Resolves freedom vs safe validation without taking anything away. -- **Slots are DERIVED from the manifest, not separately authored** — a chasm entry implies a crossing, - a passage mouth implies a marker/shrine, a ledge implies a camp. No new authoring burden. -- **A slot = position + shape constraint** (span, clearance, orientation) so blueprint fit is a - mechanical check, which is what makes propagation safe. - ---- - -## 2026-08-17 — danger: Z-MULTIPLIER, not a sort (Jahni's model — adopted, it beats mine) - -**Decided:** no sorting pass. **Danger = a function of depth (Z) × the strate's own multiplier**, with -a **rare chance of a less-difficult strate appearing deep**. - -**Why it's better than my sort:** -- **Local, not global.** Danger is computed per strate from its Z — no whole-world ordering pass, and a - strate's danger is knowable without knowing the rest of the world's strate list. -- **Fully decouples identity from placement.** Any strate can appear at any depth; depth supplies the - danger. That serves "any world, any kind, anywhere" better than sorting ever could — sorting - implicitly binds a strate's identity to a position in the curve. -- **The rare exception is a designed feature, not the fudge-factor jitter I proposed.** -- ⭐ **It makes danger ANOMALIES possible in both directions**, which sorting structurally cannot: - - **Respite strate** — unexpectedly calm, deep. Don't leave it as "lower numbers": make it a - *designed type*. Quiet, visibly calmer, fewer mobs — a sanctuary. Natural home for a safe camp, - a merchant, a landmark, a breather in the descent. - - **Danger spike near the surface** — the "something is very wrong here" beat. Players find it early, - die, and come back for it later. Pairs perfectly with the anomaly/dreamcore concept (out of place - in *difficulty*, not only in *look*), and in one shared world it becomes season-wide news. - -**Caveats to build in:** -- ⚠️ **Clamp the multiplier range so the depth term dominates on average**, or the curve stops being - reliable and progression breaks. Rare exceptions are a feature; a noisy curve is a bug. -- ⚠️ **The exception must be LEGIBLE.** Unsignalled difficulty variance reads as bad tuning, not - design. Each anomaly needs a tell. -- ⚠️ **Danger must be readable at a strate's ENTRANCE**, before the player commits. A surprise easy - strate is a gift; a surprise deadly one without warning is just an unfair death. Give the entrance a - broadcast — visual/audio/NPC warning/UI readout fed from the manifest. Warned-and-ignored is - memorable; unwarned is unfair. -- Danger becomes a **manifest property** of each strate, so quests, UI and loot tables all read it from - one place. -- The **mix rule (grounded vs anomalous) is independent of this** and still stands. - ---- - -## 2026-08-17 — WHAT THE CODE ACTUALLY SAYS (read pass, no changes made) - -### ⭐ The composer already exists in embryo: `UVoxelStrateManager::Initialize` -`Private/VoxelStrateManager.cpp:29`, roughly 100 lines. It already does the job's skeleton: -1. loads `Settings->StratePool` (soft ptrs) and **Fisher-Yates shuffles it** with - `FRandomStream(WorldSeed)`; -2. walks `TotalStrates` slots, taking `Settings->FixedStrates[i]` where pinned, else cycling the - shuffled pool (`ShuffledPool[PoolCursor % Num]`); -3. computes each slot's Z range from `Definition->StrateHeightInChunks`, leaving - `InterStrateGapChunks` of bedrock between; -4. calls `GeneratePassages` — deterministic passages between consecutive strates. - -**This runs BEFORE any terrain and its result (`StrateLayout`) is what the density field is derived -from.** That is exactly the "manifest is the plan, not an export" architecture — it already exists. -The composer is an *expansion of this function*, not a new system. - -### ⭐ The manifest can cost ZERO storage — it needs to be ENUMERABLE, not stored -`FStrateLandmark` placement (`VoxelContentManager.cpp:1263 UpdateLandmarks`) is **hash-lattice**: -cell = `SpacingChunks` chunks, position = lattice point + hash jitter -(`VoxelHash::Mix(H ^ ...)`), plus a `PassageMouth` anchor mode that enumerates the finite passage -list. The file's own comment: *"cost scales with the NUMBER of landmarks in range, not the area."* -⇒ "what notable things are in this region?" is already a **pure deterministic function**, answerable -without generating terrain and without streaming. So the manifest is an **index computed on demand**, -not a baked table. That fully answers Jahni's "it must not be expensive." - -### ✅ Already exists — do NOT redesign these -- **`EVoxelStrateTransition::Hard`** (per strate, on its lower boundary) **is** the "anomaly must not - blend" mechanism. Already authored per strate. Gradient / Hard / Interleaved. -- **Height already varies 1–256 chunks** (~8 m to ~2 km). "Any size" is supported today. -- **`GameplayTags` on `UVoxelStrateDefinition`** — the tag vocabulary for adjacency rules and - mob/hazard/loot selection is already there, and the module already depends on GameplayTags. -- **`UVoxelTerrainOpDefinition`** — ops ARE already data assets (12 types: Terrace, LayerLines, - Ribbing, Cliff, Scallop, Overhang, Arch, Column, Pit, Chimney, Dome, Pinch), referenced from the - strate's `TerrainOperations`, merged by `BuildParamsFromDefinition`. -- **Landmarks** already carry `AnchorMode`, `SpacingChunks`, `JitterFraction`, `SpawnProbability`, - `ExclusionRadiusChunks`, `Priority` — F7's placement machinery, built. **This is the seed of build - slots**: slots are landmarks of a "build site" kind. - -### ❌ Genuinely missing for the composer -- **No rarity.** The pool is a flat `TArray` shuffled uniformly. Dreamcore-should-be-rare needs a - weighted entry (`{Def, Weight, Tags, allowed depth band…}`), not a bare array. -- **Pool CYCLES** (`PoolCursor % Num`) when `TotalStrates` > pool size ⇒ repeats in a fixed order. - "Two seeds differ in everything" needs a weighted draw instead. -- **No danger concept anywhere** in the strate definition or settings. Needs the Z-multiplier model. -- **No adjacency rules** — nothing prevents two alike in a row, nothing expresses "sets up" pairs. -- ⭐ **The op COMPOSITION is hardcoded C++**: `BuildTunnelNetworkStack` / `BuildMazeStack` / - `BuildSurfaceStack` … one builder per archetype, in `VoxelDensityOpStack.cpp` (252 KB). The *ops* - are composable; the *composition* is not data. **This is exactly the Phase 3 gap — the machine was - built and never fed.** Confirmed against the code, not inferred. - -### ⚠️ Constraints discovered -- **Layout = f(seed, pool CONTENTS, pool ORDER).** `FRandomStream` + Fisher-Yates over - `Settings->StratePool` means **adding or reordering one strate in the pool changes every world**. - ⇒ new strate types can only land **at a wipe**. Consistent with the season model, but it must be - stated: mid-season content additions cannot touch the pool. -- ⚠️ **`FStrateGenerationParams` is a giant flat param bag** with an X-macro (`VF_STRATE_PARAM_FIELDS`) - driving its `Lerp` blend. Ambition level **(c)** wants **authored ranges per parameter**, and there - is no natural home for a per-field range in a flat struct of that size. **This is the main - engineering obstacle to (c)** and the next thing worth designing. - ---- - -## 2026-08-17 — the pool entry, and DERIVING distribution instead of authoring it - -Jahni's objection to the pool-entry proposal (weight / max / min per entry): that is still -**hand-authoring the world's composition**. He wants the game to produce its own worlds. -Real distinction: he authors the **vocabulary**; he does not want to author the **distribution**. - -### ⭐ The unifying move: MEASURE each assembled strate, don't declare its numbers -Reuse the coarse validation pass already required for garbage detection (low-res sample of the strate, -once at world creation: air fraction, walkable surface, feature size, connectivity). It produces -*numbers about the place* — use them for everything, not just yes/no. **One pass, three uses.** - -| Was going to be authored | Derived instead | -|---|---| -| **Weight (rarity)** | **Outlier-ness**: distance from the pool's centroid in measurement space. Dreamcore is rare *because it is unlike everything else*. Self-correcting — author five dreamcore-ish strates and they stop being outliers, which is the right answer. | -| **Danger multiplier** | **Measured**: verticality (fall distance), openness (sightlines / room to retreat), traversal difficulty (climb/swim share), darkness. Discovered from what the strate generates. | -| **Max per world** | **Weight decay on draw** — an entry's weight drops sharply each time it's picked. Rare things don't repeat; common things recur without dominating. No per-entry number. | -| **Min per world (the mix rule)** | **One GLOBAL dial**: "a world's total strangeness may not exceed X." One knob for the whole game instead of one per strate. | -| Some descriptive **tags** | Measurable too — flooded = water volume, vertical = aspect ratio, open = mean free space. | - -### Honest limits (stated to Jahni) -- **Intent can never be derived** — which strate is the boss's, which is fixed at the top. Authored, - always, and rightly. -- **A measurement can be wrong about how a place FEELS** (measures ordinary, plays unsettling). - ⇒ **derived by default, with an optional per-entry override that is normally empty.** The escape - hatch exists without being an obligation on every strate. - -⇒ Jahni's remaining authoring job: **make strates, and turn two or three global dials.** - -**Through-line worth noting:** this is the same answer as the AI-judge discussion — *measure, don't -declare* — and it reuses the same pass. The design is cohering on one mechanism. - -**Still on the table from the earlier proposal:** adjacency rules as a small tag-rule list in settings -(no two alike adjacent / `Anomaly` needs a `Grounded` neighbour above / max run length), the draw must -never fail (fallback entry), and making the draw order-independent — `hash(seed, slotIndex, attempt)` -over a pool sorted by a stable key, so reordering the asset in the editor stops changing every world. - ---- - -## 2026-08-17 — ⛔ CORRECTION: Jahni does NOT want to author strates either - -The line above — *"Jahni's remaining authoring job: make strates, and turn two or three global -dials"* — is **WRONG**. His words: *"I'm not pretty happy to make strates forever, that's actually -the opposite. If I could never make any new strate myself and have it do it itself would be genius."* - -**⇒ The system must INVENT strates, not arrange authored ones.** I had the composer one level too -shallow for this entire conversation. Re-derive from here. - -### What that means architecturally -- **A strate stops being an asset and becomes generated data.** `UVoxelStrateDefinition` survives as a - struct (the generator needs its fields) but nobody fills it in the editor — the **composer** fills - it: draws operators, rolls parameters, assigns materials/content, and hands the generator a strate - that has never existed before and won't exist after the wipe. -- The **strate pool / weights / min / max** design above is therefore obsolete as stated. Rarity, - danger and mix must be properties of *invented* strates, measured after assembly (the measurement - idea survives the correction — it is *more* necessary now, not less). - -### ⭐ The grammar for inventing a stack ALREADY EXISTS in code -`EVoxelOpRole` (in `Public/VoxelDensityOp.h`): **`FieldSource`** (what the field IS) · -**`Combiner`** (how fields merge) · **`DetailModifier`** · **`StructuralPost`** (spine → seal → -passage, appended automatically, never author-omittable). A valid stack already has a defined shape: -one source, some modifiers, structural post last. "Invent a strate" = draw a source, draw modifiers, -roll parameters, in an order the roles already constrain. **The constraint system is sitting there -unused.** - -### Where the hard part moved -- **Coherence was free when a human authored the strate. It is not free now.** A maze op + an overhang - op + a flooded op at incompatible scales = mush. This is the garbage problem at full strength, and - it is why the **AI-judge idea makes much more sense in this light** — it was never about judging - hand-made content, it is about judging *invented* content, in a space too large to eyeball. -- **Dressing must be coherent too** — palette, mood, decorations, creatures, audio. Drawn independently - you get crystal formations in a lava flow. ⇒ the composer draws a **theme** first, then pulls - operators, materials, decorations, creatures and audio carrying that theme's tags. -- ⚠️ **The RANGE problem is no longer "at some point" — it is the centre of the design.** The ranges - *are* the space the system invents inside. - -### Open question put to Jahni -**Where is the floor of authoring?** Proposed: the **raw content** — operators, meshes, materials, -sounds, creatures — plus their tags. Those are the atoms; everything above (strates, worlds, layout, -danger, rarity) is composed and measured. Every operator added then *multiplies* with every other one -instead of adding one more place. **Or does he want the system to invent operators too?** - ---- - -## 2026-08-17 — floor CONFIRMED; op invention parked - -**Floor of authoring = raw content + tags.** Operators, meshes, materials, sounds, creatures, music — -tagged. Jahni accepts this ("I'd need to be thorough and tag every enemy, music and whatnot"). -Nothing above that (strates, worlds, layout, danger, rarity) is authored. - -### Inventing operators — parked, but not impossible -- ❌ **New KINDS of operator cannot be invented** ("warp the domain by noise", "carve capsules along a - spline"). Those are ideas expressed as code ⇒ program synthesis. Bad bet, not where the value is. -- ✅ **Discovering good COMBINATIONS and promoting them into named reusable units** is real and - buildable: a sub-stack that measures well becomes one building block. From the composer's view that - *is* a new operator. **Compounds across seasons** — season 1's discoveries are season 2's vocabulary. -- ⚠️ **Do not chase it early.** ~20 ops with rolled parameters already spans a space vastly larger than - a hundred seasons could explore. **The scarce resource is judgment, not vocabulary** — an hour on - measurement/validation/the model beats an hour widening a space we use a rounding error of. - -### Why the tagging burden is smaller than it sounds -- **Per-ASSET, not per-place.** Tag a bat once ⇒ every world, every season, forever. Cost scales with - content, not with number of places. *This is the actual payoff of inventing strates rather than - authoring them.* -- **GameplayTags are hierarchical and already a plugin dependency** — `Theme.Volcanic` reaches every - volcanic context with no enumeration. -- **Some coherence needs no tags:** palette can be sampled from a material's dominant colour and - clustered. A decoration whose colours sit in the strate's family fits, unstated. -- **Lazy:** untagged content is simply never drawn. Ship with 20 enemies and 3 tracks; add more - whenever. Nothing breaks, the pool just gets richer. - -**NEXT:** the range problem (now central, not a footnote). - ---- - -## 2026-08-17 — THE RANGE PROBLEM (designed; verified against the code) - -### Verified facts (not remembered — checked) -- `FStrateGenerationParams` has **73 scalar fields** (+1 bool). **Not hundreds** — tractable. -- ✅ **The X-macro is IN SYNC.** `VF_STRATE_PARAM_FIELDS` covers 75 entries; the only struct scalar - missing from it is `Alpha`, which *is* the blend weight and is correctly excluded. CODEMAP's warned - trap (add a field, forget the macro, blends silently reset it to default) **has not bitten**. - ⇒ `FStrateGenerationParams::Lerp` already blends the whole parameter set, and is already load-bearing - at strate boundaries. **Add a test asserting macro ≡ struct so this stays true.** -- `VoxelStrateTypes.h`: **135 `ClampMin`, 52 `ClampMax`** across 216 UPROPERTYs. Most fields have no - upper bound. Paired min/max fields exist (`MinRoomRadius`/`MaxRoomRadius`, `TunnelMin/MaxRadius`, - `RoomFloorCutMin/Max`) and some fields carry relational `EditCondition`s. - -### Why rolling parameters fails (the reason to state plainly) -The coherent region of a 73-dimensional space is a vanishingly thin sheet; uniform sampling in a box -never lands on it. The problem is not wrong ranges — it is that **independent rolling destroys the -CORRELATIONS** that make a parameter set coherent (room spacing ↔ room radius ↔ tunnel length ↔ strate -height). This is very likely what Jahni was sensing when he reached for an AI judge. - -### ⭐ The design: don't roll parameters — BLEND KNOWN-GOOD ONES -1. **Corpus** = known-good parameter vectors. Seeded by Jahni's existing hand-authored strate assets. -2. **Roll** = pick 2–3 parents (weighted), **blend with random weights**, then jitter. +1. **Corpus** = known-good parameter vectors, seeded by the existing hand-authored strate assets. +2. **Roll** = pick 2–3 parents (weighted), blend with random weights, then jitter. 3. **Reuse `FStrateGenerationParams::Lerp`** — it already does exactly this, over exactly this field set, and the world working already tests it. Build nothing. -4. **Constraint satisfaction is FREE:** a convex combination preserves every relation that holds in - both parents (`Min ≤ Max` stays true). Correlations survive because they were never broken. +4. **Constraint satisfaction is FREE:** a convex combination preserves every relation that holds in both + parents (`Min <= Max` stays true). The correlations survive because they were never broken. 5. ⭐ **Ranges come from the CORPUS SPREAD** — per field, how much it varies across existing strates — - **not from authored numbers and not from the clamps.** Clamps are "don't type nonsense" limits, not - "this makes a good world" limits. *Measure, don't declare*, again. -6. **Clamps = hard safety net only.** Never the roll range. + **not from authored numbers, and not from the clamps.** Clamps are "don't type nonsense" limits, not + "this makes a good world" limits. *Measure, don't declare.* +6. **Clamps are the hard safety net only**, never the roll range. -### Escaping the convex hull -Blending alone only reaches *between* existing points. Escapes: jitter slightly past the corpus; -deliberate **extrapolation** past a parent away from the centroid (riskier ⇒ validate harder); and -⭐ **PROMOTION** — every invented strate that measures well and survives players joins the corpus. -Season 1 blends ~6 strates; season 5 blends 200 discovered ones. **The system's imagination grows from -its own history, and the game gets stranger as it ages.** (Third appearance of this mechanism today: -discovered ops, discovered strates, discovered parameter regions. It is the spine of the design.) +**Escaping the convex hull:** blending alone only reaches *between* existing points. Jitter slightly +past the corpus; extrapolate deliberately past a parent away from the centroid (riskier, validate +harder); and — the real one — **PROMOTION**: every invented strate that measures well and survives +players joins the corpus. Season 1 blends ~6 strates; season 5 blends 200 discovered ones. -### ⚠️ Honest caveats -- **UPROPERTY metadata is editor-only** — `ClampMin`/`ClampMax` do not exist in a packaged build. - ⇒ bake them at cook time via a small editor commandlet walking the struct by reflection. Small, - real, one-time work. -- **Clamp coverage is partial** (52 of ~73 have no max). The net has holes; don't lean on it. -- **A few fields are not tunables** — `StrateTopWorldZ` / `StrateBottomWorldZ` are runtime Z bounds. - Needs an explicit **exclusion list** for rolling. -- **Bools can't be blended** — roll feature toggles by probability, or inherit from the dominant parent. -- ⚠️ **The corpus is small today** (~6 strates), so blending alone will not feel like "countless - worlds" in season 1. ⇒ **early variety must come from STRUCTURE, not parameters** — *which* operators - are in the stack at all. 20 ops choose 5 is already thousands of distinct stacks. - **Rule: vary structure aggressively, vary parameters conservatively — shift the ratio as the corpus - grows.** +**⚠️ Caveats:** UPROPERTY metadata is editor-only, so the clamp net must be **baked at cook time** by a +small commandlet walking the struct by reflection. Clamp coverage is partial. A few fields are not +tunables at all (`StrateTopWorldZ` / `StrateBottomWorldZ` are runtime Z bounds) and need an explicit +exclusion list. Bools cannot be blended — roll them by probability or inherit from the dominant parent. ---- +### 3.4 The measurement pass — one grid, one flood fill, three jobs -## 2026-08-17 — theme coherence narrowed; findability first step +Sample a candidate strate into a coarse voxel grid, flood-fill the air **once**, derive everything from +that single grid. A 4–8× downsample preserves all of it. Because the depths are bounded, an entire +strate can be covered rather than sampled. -- **Thematic mismatch is mostly FINE** — Jahni: a lava flow *could* have crystal formations, it was a - bad example. ⇒ my "coherent dressing" worry was overstated. **The real garbage is GEOMETRIC**: - operators at incompatible *scales* producing mush. Smaller and more mechanical than feared. -- ⭐ **Multiple fitting tags per asset is the mechanism, not a compromise.** One tag per thing = rigid - buckets. Several tags = a graph with overlaps, and **the overlaps are where the system finds - combinations Jahni didn't plan that still hang together** — which is the "magical" effect he wants. - He describes assets; themes emerge from co-occurrence rather than being assigned. -- **Jahni's expected first big job:** create themes and tag content with multiple fitting tags. -- **Findability, first step:** a **subtle beacon glow**, initially for debugging. Note it is *not* - throwaway — the debug beacon **is** the real solution in programmer art. The placement logic is - identical; only delivery changes later (light spilling from the passage, a draft, a shift in - ambience). Build the ugly version; ship a reskin. +**Validation — is it garbage?** +air fraction · **largest connected component share** (low = swiss cheese: sealed pockets nobody reaches, +generation paid for and never seen) · walkable surface (air above, solid below, survivable slope) · +**feature scale** (median distance-transform of the air — catches "noise fog", structures smaller than a +few voxels reading as static rather than rock) · vertical clearance · **entrance→exit reachability** · +**fightable-space count** (GDD §12 dimensions). ---- +**Danger — same grid, different questions.** +fall exposure · **openness** (mean distance to solid from walkable positions — sightlines and room to +retreat, which is most of what makes a fight good or bad) · traversal mix (walk/climb/swim) · +**tortuosity** (path length ÷ straight line = maze-likeness). -## 2026-08-17 — the MEASUREMENT PASS (and how "good" ever gets judged) - -### ⭐ The reframe that sets the budget: the composer runs ONCE PER SEASON, OFFLINE -Everyone shares one seed per season ⇒ there is exactly **one world per season, ever** ⇒ the composer -never runs on a player's machine and never runs at load. It runs once, on Jahni's box or a build -machine. **The problem is not "fast enough for a loading screen", it is "runs overnight."** So it can: -- generate a thousand candidate strates, measure them all, keep the best eight; -- validate at any resolution it likes; -- later run a model over the candidates at **zero runtime cost to anyone**; -- ⭐ **let Jahni review the world before publishing the season.** The system does the work; he keeps a - veto. Costs nothing because it is all offline anyway. - -**What ships is small:** the season's seed + the chosen strates' parameter vectors. Clients generate -terrain from those exactly as today. **No runtime composition anywhere.** - -### The pass itself: ONE coarse grid + ONE flood fill → one feature vector → three jobs -Sample the candidate into a coarse voxel grid (a few boxes at hash-chosen XY within its Z band), flood -fill the air once, derive everything from that grid. A 4–8× downsample preserves all of it. - -**Validation (is it garbage?):** air fraction · **largest connected component share** (low = swiss -cheese: sealed pockets nobody reaches = generation paid for and never seen) · walkable surface (air -above, solid below, survivable slope) · **feature scale** (median distance-transform of the air — -catches "noise fog", structures smaller than a few voxels read as static not rock) · vertical -clearance · **entrance→exit reachability** (the primordial law, as a number). - -**Danger (same grid, different questions):** fall exposure (walkable surface with a long drop beside -it) · **openness** (mean distance to solid from walkable positions — sightlines and room to retreat, -which is most of what makes a fight good or "blergh") · traversal mix (walk/climb/swim) · **tortuosity** -(path length ÷ straight-line — maze-likeness). - -**Rarity:** nothing new — the vector of all the above, distance from the corpus centroid. +**Rarity — nothing new.** The vector of all the above; distance from the corpus centroid. **Outliers are +rare because they are outliers**, so a dreamcore strate is rare without anyone typing a weight. +Self-correcting: author five dreamcore-ish strates and they stop being outliers, which is the right +answer. ⚠️ **Coarse sampling can LIE about connectivity.** A wall thinner than the sample spacing vanishes and -two sealed spaces look joined ⇒ a strate could pass the primordial-law check while being impassable. -The one metric where a false pass is dangerous. **Fix: coarse to FIND the route, then re-verify that -one path at full resolution.** Cheap — only the corridor you found. +two sealed spaces look joined, so a strate could pass the primordial-law check while being impassable. +The one metric where a **false pass is dangerous**. **Fix: coarse to FIND the route, then re-verify that +one path at full resolution** — cheap, because only one corridor is verified. -**This is the same tool as F1** (`fable-idea.md`'s top-pick 2D world-preview): the measurement pass -with a visualisation on top. Build the measurement, get the preview nearly free — and it's needed -anyway for reviewing a season before publishing. +⚠️ **Sample where players actually go** — near the spine and the passage mouths. A box measured far out +describes a place nobody will stand in. Report **distributions (median + spread), not single values**. -### Judging "good" — Jahni is right to doubt, but the judge is the CORPUS, not a model -Measurements catch **broken**, not **boring**. The gap is narrower than it looks because *good* never -has to be defined — only **collected**: -- **Rate at the STRATE level, not the world level.** Twenty candidates, thumbs up/down = twenty labels - in minutes; a hundred in an evening. Approved ones join the corpus and become blend parents. - **This loop works with ZERO machine learning.** A model only makes it faster, later. -- **Player behaviour in a live season** — lingering, revisiting, rushing past. Free telemetry, but - careful: time spent can mean *fascinating* or *lost and furious*. -- ⭐ **Where players choose to SNAPSHOT THEIR HOME.** An unambiguous "I love this place", no UI, no - prompt, ungameable. The strongest quality signal in the design, and it costs nothing. +**This is also F1.** `fable-idea.md`'s top-pick 2D world-preview tool is this pass with a visualisation +on top. Build the measurement, get the preview nearly free — and it is needed anyway to review a season +before publishing. + +### 3.5 Validation in three layers, and how "good" is ever judged + +1. **Construct by design** so the law cannot break — build the connections FIRST and generate around + them. Reachability by construction, not by luck. Generate-then-check gets brutal at 8 strates each. +2. **Measure the accidents** (§3.4). Most garbage is numerically bad, not aesthetically bad. +3. **Reject and resample** — fold the attempt index into the hash (`hash(seed, slot, attempt)`) so every + seed still yields a valid world and peers stay in lockstep. + +**Judging "good" — the judge is the CORPUS, not a model.** Measurements catch *broken*, not *boring*. +But *good* never has to be defined, only **collected**: + +- **Rate at the STRATE level, not the world level.** Twenty candidates thumbs up/down is twenty labels + in minutes. Approved ones join the corpus and become blend parents. **This loop works with zero + machine learning**; a model only makes it faster, later. +- **Player behaviour in a live season** — lingering, revisiting, rushing past. Free telemetry, but time + spent somewhere can mean *fascinating* or *lost and furious*. +- ⭐ **Where players choose to snapshot their home** (GDD §11). An unambiguous "I love this place", with + no UI, no prompt, and no way to game it. The strongest quality signal in the design, and it costs + nothing to collect. ⚠️ **Known risk: convergence to a comfortable middle.** Promote only what you already liked, keep blending near the corpus, and the system slowly stops surprising you. Same failure mode as the @@ -777,785 +225,303 @@ candidates in circulation. --- -## 2026-08-17 — STACK ASSEMBLY: how the composer builds an op stack +## 4. The world manifest -### What the three real builders actually look like (read from `VoxelDensityOpStack.cpp`) -``` -TunnelNetwork (19) rock source → room graph (SDF) → SdfCarve → 12 density modifiers → worm source → structural -Maze (7) rock source → lattice corridors (SDF) → SdfRoughness (SDF) → SdfCarve → structural -FloatingIslands (7) VOID source → island blobs (SDF) → SdfRoughness (SDF) → SdfFill → structural -``` +### ⭐ It is TWO things, not one -### ⭐ 1. Most ordering is MECHANICAL, not aesthetic — derive it, don't author it -Roughness sits on the **SDF** before conversion in Maze, and on **density** after conversion in -TunnelNetwork — CODEMAP confirms they are literally two different ops for that reason. An op's legal -position is fixed by **which channel it reads and which it writes**. -⇒ **Every operator declares its channel reads/writes.** Dependencies then form a DAG and **any -topological sort of that DAG is a legal stack.** No authored order, no judgement, and it generates -orderings no human ever wrote down. +**Tier A — the SPINE. Finite, tiny, actually stored.** Strate slots and passages. ~30 strates plus a few +passages each is roughly 100 entries — kilobytes. **This already exists** as `StrateLayout` plus the +passage list, computed once in `UVoxelStrateManager::Initialize`. -### ⭐ 2. Root polarity is a one-bit identity lever -FloatingIslands is the same op classes as Maze but rooted in a **void** source with a **fill** instead -of a rock source with a carve — and you get islands instead of tunnels. One bit flips the entire -character of a strate. +**Tier B — the FIELD. Effectively unbounded in XY, therefore NEVER stored.** Landmarks, rooms, material +deposits, build sites. It is a **function**: give it a region, it enumerates what is there. +Deterministic, hash-based, **zero bytes**. -### 3. `FFloorBiasMod` — delete the problem instead of documenting it -FloorBias exists purely to undo what `FCaveRoughnessMod` did to floors. Both are density-space, so -channel rules permit either order, but semantically one must follow the other. -⇒ **An operator whose only purpose is to correct another operator is not a separate operator. FUSE -them** into one op with two internal phases. Removes the invalid state rather than annotating it, and -leaves the rest genuinely free to shuffle. *Fifteen ops that compose in any legal order beat eighteen -with a footnote.* (Fusing with identical order and math leaves the world bit-identical.) +That split is what makes "the manifest must not be expensive" true rather than aspirational. -### 4. One small declaration covers what's left -Each op is **additive** (small displacement, commutes freely with peers ⇒ shuffle at will) or -**transformative** (clamps, multiplies, gates ⇒ position matters, needs explicit placement). +### ⭐ The trick: THE ID ENCODES THE POSITION -### 5. The structure roll -`root polarity → shape source → conversion (follows from polarity) → draw k modifiers legal in the -resulting channel space (k itself rolled) → structural post appended automatically.` -With ~5 shape sources and ~15 modifiers choosing 4–8, that is **tens of thousands of structurally -distinct stacks before a single parameter is touched** — which is what carries early variety while the -corpus is small. +Resolving an ID *back* to a place — a saved quest target, a fast-travel destination, a wiki citation — +would normally need a lookup table, which is exactly what we cannot have. Landmarks are placed on a +**hash lattice**, so build the ID from **lattice cell + type + slot index within the cell**. Then +**ID to position is arithmetic**: no table, no database, no storage, resolvable offline on any machine +forever. + +**Names fall out of the same thing:** `hash(id, seed)` indexes the word pools. Every entry has a stable, +globally identical name that nobody stored and nobody generated in advance — including entries no player +will ever visit. That is the substrate that makes quests and wikis possible at all. + +### What earns an entry + +**One test: can a system need to ask "where is it?" or "what is it called?"** If yes, it is an entry. +Yes: strates, passage mouths, landmarks, notable rooms, material deposits, build sites, boss arenas, the +(0,0) spine. No: individual rocks, every tunnel segment, terrain that merely emerged from noise. + +**ADDRESSABLE vs NOTABLE.** Everything hash-placed is *addressable* for free — it has an ID by +construction. **Notability is a filter on top, and it is exactly what the measurement pass produces**: +the biggest chamber in a region, the one with a lake, the one at a passage mouth. No new placement +logic, only a threshold. + +### Discovery sits ON TOP, never inside + +A player's discovered set is a set of **IDs** — small integers, cheap to store and sync. Community +discovery is the union. **The manifest never changes; only who has seen what.** + +⚠️ **You cannot hide it anyway.** Clients generate terrain from the seed, so players hold the generator +and can enumerate the manifest offline. Not a leak to prevent but a fact to design around, as Minecraft +does with seed maps. It barely matters here: community knowledge spreading *is* the intent, and a map of +season 4 is worthless in season 5. + +### ⚠️ Two cautions + +- **The hash functions become part of the FROZEN SEASON CONTRACT.** Change how a lattice cell hashes and + every quest target in that season moves. These are not implementation details to tidy mid-season. +- **Tier B is cheap, not FREE.** Enumerating rooms in a region needs that region's placement pass. Fine + for a beacon or nearby markers; a full-map view would hurt. **LOD the query** — coarse regions return + only high-notability entries, which is what a zoomed-out map wants anyway. + +--- + +## 5. Naming + +**Failure mode to avoid: slot-filling.** *"The Crystal Cavern of Whispers."* Players learn the template +in ten minutes; after that every name is noise and, worse, names stop **distinguishing** places, which +defeats the point of having them. + +1. ⭐ **Derive names from the MEASUREMENTS, not from a bag.** A vast open chamber draws vast-open words, + a flooded one draws water words, a tight vertical shaft draws shaft words. The name then *fits* the + place, and a player who hears "the Sink" and later finds a huge drain-like chamber gets a small + satisfying click. +2. **Several competing GRAMMARS, not one template.** Compound (Blackreach, Stonefall); descriptive + definite (The Long Dark, The Hollow); possessive (Varen's Fall — somebody was here and it went + badly); functional (Shaft Nine — implies industry and records); opaque, from an invented phonology so + it reads as a language rather than keyboard mash. **Five grammars read as history. One reads as a + generator.** +3. **Name SCARCITY.** Only *notable* entries get a name; everything else stays addressable and anonymous. + Scarcity is what makes a named place feel like a place — a threshold, not a feature. +4. **Names must survive contact with a WIKI.** Short, pronounceable on sight, no apostrophes or unusual + glyphs. **A name nobody can spell cannot become community knowledge**, which is the only reason names + exist in a shared world. + +**Two layers:** the procedural name is the substrate — it exists before anyone visits. A community name +may override it and propagates like a shared build. Nothing is ever nameless, and beloved names win. + +⛔ **Parked:** varying the naming language by depth (deeper = older, stranger tongue). Liked, but it is +still a gradient, and gradients are what Jahni rejected. + +--- + +## 6. Code findings and hazards + +All verified against the source on 2026-08-17 unless marked otherwise. + +### ⚠️ 6.1 THE BLOCKER — box verdicts are only correct in the stack they ship in -### ⚠️ 6. THE BLOCKER — box verdicts are only correct *in the stack they ship in* CODEMAP, on the lattice corridor source: *"Its `EffectOverBox` answers for the source+carve **pair** (Phase 1 simplification) so it must be told the downstream `ExtraReach`."* The op interface says the -same in its own comment: a source that writes only `Sdf` doesn't touch density itself, so it answers -on behalf of the converter that follows it. +same in its own comment: a source writing only `Sdf` does not touch density itself, so it answers on +behalf of the converter that follows it. -That is fine when a human wrote the pair. It **breaks the moment the composer assembles a combination -nobody verified**, and the failure is the silent one: a tile wrongly proved uniform ⇒ no geometry, no -collision, no error, player falls through the floor. +Fine when a human wrote the pair. **It breaks the moment the composer assembles a combination nobody +verified**, and the failure is the silent one: a tile wrongly proved uniform means no geometry, no +collision, no error, and a player falls through the floor. ⇒ **Prerequisite before free composition is safe: every operator's `EffectOverBox` must be correct in ISOLATION**, not correct-given-its-neighbours. That means propagating an **SDF interval** through the -box query instead of letting the source answer for the pair — which the interface comment already -names as the Phase 3 version of the contract. Not huge, but load-bearing, and it must land first. +box query instead of letting the source answer for the pair — which the interface comment already names +as the Phase 3 version of the contract. Not huge, but load-bearing, and it must land first. -### ✏️ Correction to yesterday's note -The X-macro / struct diff said *"the only struct scalar missing from the macro is `Alpha`, which is the -blend weight and correctly excluded."* **`Alpha` is not a field at all** — it is the `float Alpha` -parameter of `FStrateGenerationParams::Lerp` (line 1033), which the regex caught by accident. -**The macro and the struct are in PERFECT sync, zero exceptions.** Now guarded by a test — see below. +### ⚠️ 6.2 Box-verdict bounds are proved against HAND-SET params + +T1.d / `ClassifyTile` bounds are proved today against the parameters Jahni typed. **If the seed invents +parameters, every bound must hold across the whole authored RANGE, not a value.** Same silent failure +mode as above. See `voxelforge-noise-bounds` — a bound must be the SUPREMUM of what `Eval` produces, not +the parameter named "max". + +### ⚠️ 6.3 `ClassifyTile` will silently delete 3D caves + +It proves anything below `TerrainZ` solid and skips meshing. Any future 3D generation inside mountains +needs a guard, or the caves vanish with no error. + +### ⚠️ 6.4 The primordial law has a real hole TODAY + +`UVoxelStrateManager::GeneratePassages` places every inter-strate passage as: `FRandomStream(CachedSeed +^ 0x50A55A6E)` → random angle around the (0,0) spine → random distance in the config range → random +reach into each strate. **Nothing consults the destination strate's cave layout.** Carving is a +structural-post invariant so the tube *is* air — but whether its lower mouth joins the lower strate's +connected space is pure luck. **A player can descend a passage and arrive in a sealed pocket.** + +**Fix — make the strate OFFER a landing site, don't let the passage gamble.** Add to the field-source +contract something like `SuggestOpenPoint(WorldX, WorldY) -> optional Z`. Every source already knows +where its own air is: `FRoomGraphSource` the nearest room centre (hash-placed, cheap to find), +`MakeSlabVoidSource` anywhere in the void band, `FIslandBlobSource` just above an island top, +`FSurfaceColumnSource` just above `TerrainZ`, `MakeLatticeCorridorSource` the nearest lattice node. +Passage placement becomes: pick XY, ask the strate below for an open Z near it, aim there. Deterministic, +cheap, and it keeps working for invented strates because any new source must answer. + +Then the measurement pass flood fill is the net: does the upper mouth's air component reach the lower +mouth's? If not, re-roll with the attempt folded into the hash. + +### 6.5 Findability is mostly DATA, not code + +`ELandmarkAnchor::PassageMouth` already exists — landmarks can anchor at passage mouths — and +`FStrateLandmark` already carries a Light-Orb block. **A glowing landmark at every passage mouth is +authorable today with no code at all.** Beyond that, cheapest first: light (a glow down a tunnel is the +strongest pull in a cave); sound (works around corners, which light does not — you hear the draft before +you see the hole); and ⭐ **make the mouth a PLACE, not a hole** — the `SuggestOpenPoint` fix already +lands the passage in a room, so make that room distinctive. Cracks in walls are forgettable; rooms are +landmarks, and that is what makes it findable on the *second* visit, which quests need. + +### ⚠️ 6.6 The layout depends on the pool's CONTENTS *and its ORDER* + +`Initialize` Fisher-Yates shuffles `Settings->StratePool` with `FRandomStream(WorldSeed)`, then cycles it +(`PoolCursor % Num`) when `TotalStrates` exceeds the pool. So **reordering the pool asset in the editor +changes every world**, and a pool smaller than the strate count repeats entries in a fixed order. + +⇒ Use `hash(seed, slotIndex[, attempt])` instead, over a pool sorted by a stable key. The pool becomes a +**set** rather than a sequence: tidying the asset stops changing worlds, and each slot's roll becomes +independent of its neighbours'. Adding or removing a strate type still changes worlds — unavoidable, and +the reason content lands at wipes. + +### ⚠️ 6.7 A bounded world needs an XY EDGE SEAL, which does not exist + +Verified: `VF_ApplyBoundarySeal(float& Density, float WorldZ, float StrateTopZ, float StrateBottomZ, +float Thickness, float BaseDensity)` is **purely VERTICAL** — it seals each strate's ceiling and floor so +nothing punches through except passages. There is no horizontal equivalent, because until the depths were +bounded the world had no sides. + +⇒ Build it as a **fourth structural invariant** alongside spine → seal → passage: a **forcing** op, +appended automatically, not author-omittable. An invented strate then cannot forget it — and because +forcing ops prove `AllSolid` in their band, **the outer shell becomes free to skip** in tile +classification. + +### 6.8 The (0,0) spine, exactly + +`VF_ApplyOriginSpine` carves a guaranteed-open vertical column at actor-local XY (0,0), through every +strate, archetype-independent, appended automatically. **It carves only the strate INTERIOR and leaves +the seals intact on purpose** — its own comment: *"so the player must still dig through to descend"*. It +provides a clean landing space, not a continuous open shaft. + +⇒ The hand-authored descent shaft is the **above-ground continuation of the spine**. Nothing new to +build. The city's hole must sit above the VoxelWorld actor's origin (see §6.9), and the shaft's diameter +should match the spine's `Radius` or the join will visibly step. + +### ✅ 6.9 Actor space — DONE (built and confirmed off-origin, 2026-08-17) + +The field is authored in **actor space**: `(0,0)` is the `AVoxelWorld` actor's origin, not Unreal's world +origin. The plugin used to be only *half* actor-relative — biome/deco/water/density-volume/debug +converted; the **streaming centre**, the **anchors**, all **six carve/fill entry points**, +`GetStrateAtPosition` and `UVoxelAtmosphereManager::UpdateForPlayer` did not. It worked only because the +actor sat at the origin. + +`WorldToLocalCm` / `WorldToLocalVoxel` / `LocalVoxelToWorld` are now **the only sanctioned boundary** +between Unreal coordinates and voxel coordinates, and the real deliverable is the invariant beside them: +**no `/ VOXEL_SIZE` applied to a parameter named `World*` outside those three functions.** A grep can +check that — and the risk was never a missed site among the ones found, it is the next one nobody +notices. ⚠️ **Translation only**: rotation or non-unit scale breaks world-axis-aligned box maths +structurally (tiles, `ClassifyBox`, the clipmap window, culling); `BeginPlay` logs an Error. + +⚠️ **The trap avoided, worth remembering:** the content managers already converted internally, so +converting `PlayerLastPos` once at the source would have **double-converted four call sites**. The fix +had to be per-boundary. Only visible after reading each consumer. + +### ✅ 6.10 The X-macro guard — DONE (built green, 2026-08-17) + +`VoxelForge.Determinism.StrateParamBlendCoverage` expands `VF_STRATE_PARAM_FIELDS` a third way, into a +name list, and diffs it against the struct's reflection. `GExemptFieldNames` is empty on purpose. Guards +the fact §3.3 depends on: `Lerp` blends the macro list, not the struct. + +### ⚠️ 6.11 Client-side meshing makes determinism a RUNTIME requirement + +The host owns collision; every client meshes its own view, which is only safe because every client +generates the same field. Two clients producing different floats means they disagree about where walls +are, and the host's collision is authoritative — so one player hits an invisible wall where another sees +a passage. ⇒ `VoxelForge.Determinism.CrossPlatformDigest` currently **reports rather than asserts**. This +architecture promotes it to **must-be-green**. + +*(Not a hazard: six players do not multiply streaming. `RegisterStreamingAnchor` defaults to a 3×3×2 box += 18 level-0 tiles per remote player, against a clipmap of hundreds across LODs. `CollisionOnly` skips +rendering only — density, marching cubes and the collision cook still run, but at 18 tiles each that is +small. What does scale with party size is **carve-driven re-meshing**, which is bursty.)* --- -## 2026-08-17 — the X-macro guard (built, green) +## 7. Build order -`Source/VoxelForge/Private/Tests/VoxelForgeStrateParamCoverageTest.cpp` → -**`VoxelForge.Determinism.StrateParamBlendCoverage`**. Built clean first try (Jahni, 2026-08-17). -Expands `VF_STRATE_PARAM_FIELDS` a **third** way (after LERP and SNAP) into a name list and diffs it -against `FStrateGenerationParams`' UObject reflection. No fixture, no world — a shape test. -`GExemptFieldNames` is **empty**: every reflected field is covered, and an exemption must be written -down as a decision. Matters more under the composer, which invents parameter sets through that same -`Lerp` — a missing field would be **constant across every invented strate** with nothing to notice it -by. CODEMAP §3.12 row added. - ---- - -## 2026-08-17 — ⚠️ THE PRIMORDIAL LAW HAS A REAL HOLE (verified in code) - -### What passages do today -`UVoxelStrateManager::GeneratePassages` places every inter-strate passage as: -`FRandomStream Rng(CachedSeed ^ 0x50A55A6E)` → **random angle** around the (0,0) spine → **random -distance** in the config range → **random reach** into each strate. Endpoints are `(PX, PY, TopZ)` -and `(PX, PY, BottomZ)`. Carving is a structural-post invariant (`VF_ApplyPassageCarving`), so the -tube itself IS air by construction. - -### ⚠️ The hole -**Nothing consults the destination strate's actual cave layout.** The tube is air, but whether its -lower mouth *joins the lower strate's connected space* is pure luck. **A player can descend a passage -and arrive in a sealed bubble** — legal geometry, air all the way, and nowhere to go. The primordial -law is currently a hope, not a guarantee. It gets worse under invented strates, where nobody has -eyeballed the layout. - -Also: that `FRandomStream` walks **sequentially across all strates**, so changing an early strate's -connection count shifts **every later passage**. Same order-dependence as the strate pool -⇒ use `hash(seed, strateIndex, connIndex)` instead. - -### ⭐ Fix 1 — don't let the passage hope; make the strate OFFER a landing site -Add to the field-source contract something like **`SuggestOpenPoint(WorldX, WorldY) → optional Z`**. -Every source already knows where its own air is: -| source | open point | -|---|---| -| `FRoomGraphSource` | nearest room centre (rooms are hash-placed ⇒ cheap to find) | -| `MakeSlabVoidSource` | anywhere in the void band | -| `FIslandBlobSource` | just above an island top | -| `FSurfaceColumnSource` | just above `TerrainZ` | -| `MakeLatticeCorridorSource` | the nearest lattice node | - -Passage placement becomes: pick XY → **ask the lower strate's source for an open Z near it** → aim -there. Deterministic, cheap, and it keeps working for **invented** strates because any new source must -answer. Nice inversion: the strate offers the landing site instead of the passage gambling. - -### Fix 2 — verification as the net (reuses the measurement pass) -The measurement pass already flood-fills the air. So: **does the upper mouth's air component reach the -lower mouth's?** If not, re-roll the passage (`hash(..., attempt)`). Coarse to find, full resolution to -confirm the one corridor — as established for the connectivity metric. - -### Fix 3 — FINDABILITY, and most of it is data, not code -✅ **`ELandmarkAnchor::PassageMouth` already exists** — landmarks can anchor at passage mouths, and -`FStrateLandmark` already carries a **Light-Orb block**. A glowing landmark at every passage mouth is -authorable **today, with no code at all.** - -But *findable* means being drawn there from a distance, not marked once you arrive. Three layers, -cheapest first: -- **Light.** A glow down a tunnel is the strongest pull in a cave. Already supported. -- **Sound.** An emitter with a long attenuation radius at the mouth. Works **around corners**, which - light does not — in a cave you hear the draft before you see the hole. (Ties to F9 audio.) -- ⭐ **Make the mouth a PLACE, not a hole.** Fix 1 already lands the passage *in a room*; make that - room distinctive — bigger, lit, decorated. Places are memorable, cracks in walls are not. This is - the one that actually makes it findable on the second visit, which is what quests need. -- *Parked (strongest, most invasive):* bias the cave network to converge toward the mouth, so simply - following tunnels tends to lead there. - ---- - -## 2026-08-17 — RULING + BUILD ORDER - -**Jahni's ruling:** guaranteeing an open landing place is **needed**. And the passage mouth should be -**a real landmark — a special place**, so it *shows* and is *less dismissed*. Not merely a lit marker. -Also: *"not all to be implemented in a row."* - -### The sequencing principle -⭐ **Every tier must be worth doing on its own, before the next one exists.** Nothing gets built purely -as machinery for a later tier. (This is the direct antidote to the 2026-07/08 refactor, where three -weeks of correct work produced a world unchanged by a single voxel.) +**The sequencing principle: every tier must be worth doing on its own, before the next one exists.** +Nothing gets built purely as machinery for a later tier. This is the direct antidote to the 2026-07/08 +refactor, where three weeks of correct work produced a world unchanged by a single voxel. ### Tier 0 — safety nets, true regardless of the composer -- ✅ **X-macro guard test** — DONE, built green 2026-08-17. -- **Order-independent placement:** replace the sequential `FRandomStream` in `Initialize` and - `GeneratePassages` with `hash(seed, slotIndex[, connIndex, attempt])`. *Standalone value:* Jahni can - reorder or tidy the strate pool asset **without changing every world**. Small, isolated. +- ✅ **X-macro guard test** — DONE, green. +- ✅ **Actor-space sweep** — DONE, confirmed off-origin. +- **Order-independent placement** (§6.6). *Standalone value:* the strate pool asset can be reordered or + tidied without changing every world. Small and isolated. ### Tier 1 — the primordial law (a BUG FIX, not composer work) -- **`SuggestOpenPoint(X, Y) → optional Z`** on field sources; aim passages at it. -- **Passage mouth becomes a room + a distinctive landmark** (per the ruling). -*Standalone value:* fixes a **live hole in the shipping game** — today a player can descend into a -sealed pocket. Worth doing **even if the composer never happens**, and it makes the current world -better immediately. +- **`SuggestOpenPoint` on field sources; passages aim at it** (§6.4). +- **The passage mouth becomes a room plus a distinctive landmark** (§6.5). -### Tier 2 — the measurement pass -Coarse sample + one flood fill + the feature vector (air fraction, largest component, walkable surface, -feature scale, clearance, reachability, fall exposure, openness, traversal mix, tortuosity). -*Standalone value:* it **is** F1, `fable-idea.md`'s top-pick world-preview tool — Jahni can finally see -what parameters do instead of tuning blind. It also verifies Tier 1 (does the mouth actually connect?). +*Standalone value:* fixes a **live hole in the shipping game** — today a player can descend into a +sealed pocket. Worth doing even if the composer never happens. + +### Tier 2 — the measurement pass (§3.4) +*Standalone value:* it **is** F1, `fable-idea.md`'s top-pick world-preview tool. Jahni can finally see +what parameters do instead of tuning blind. It also verifies Tier 1. ### Tier 3 — op-system prerequisites for free composition -- **Channel read/write declarations** on every op ⇒ ordering derives from a DAG. -- **Fuse corrective ops** (`FFloorBiasMod` into `FCaveRoughnessMod`) — an op that only repairs another - op is not an op. -- ⚠️ **`EffectOverBox` correct in ISOLATION** (SDF interval propagation instead of "the source answers - for the pair"). **This is the gate on free composition** — without it, a novel stack can wrongly - prove a tile uniform ⇒ no geometry, no collision, no error. -*Standalone value:* the fuse and the isolation fix are both correctness improvements to the code that -ships today. +- **Channel read/write declarations** on every op, so ordering derives from a DAG (§3.2). +- **Fuse corrective ops** (`FFloorBiasMod` into `FCaveRoughnessMod`). +- ⚠️ **`EffectOverBox` correct in ISOLATION** (§6.1) — the gate on everything after. +- **The XY edge seal** (§6.7). + +*Standalone value:* the fuse and the isolation fix are both correctness improvements to shipping code. ### Tier 4 — the composer proper -Structure roll (root polarity → shape source → k modifiers) · parameter roll (blend the corpus via -`Lerp`) · reject-and-resample driven by Tier 2 · the **offline season pipeline** with Jahni's review -and veto. +Structure roll (§3.2) · parameter roll (§3.3) · reject-and-resample driven by Tier 2 (§3.5) · the +**offline season pipeline** with Jahni's review and veto (§3.1). ### Tier 5 — the long game -**Promotion** (good strates rejoin the corpus) · theme/tag draws for materials, creatures, audio · -eventually the model. All of it optional, all of it compounding. +**Promotion** (good strates rejoin the corpus) · theme and tag draws for materials, creatures and audio · +eventually the model. All optional, all compounding. --- -## 2026-08-17 — WORLD SHAPE + TRAVERSAL (the two holes Codex found, now closed) +## 8. Corrections that must not be re-derived -Codex read `COMPOSER-NOTES.md` cold, in isolation, and correctly identified the project, the goal -(the **corrected** one — the system invents strates, Jahni supplies vocabulary), and the intended -feel. It found all three marked reversals and no unmarked one. Two of its five "could not determine" -items were real holes; Jahni closed them: +Kept because each cost real time or would otherwise be repeated. -### Decided -- ⭐ **The world has NO BOTTOM.** Endless descent. -- **Boss strates are INTERMEDIARY** — roughly every 5 strates. Bosses punctuate, they do not terminate. -- **Return trip, two ways:** walk back up through the tunnels (that is content, not a chore), **or** - once a strate has been visited it opens a passage onward, so transport (elevator/other) can carry you - down, **choosing which strate to travel to**. -- **Unlock is LOCAL (per player) for now.** Base scope = a **local server** fed by **Jahni's database** - for community information. **Global/world-level unlock is a possible future** — gated on strates - being large enough that finding the next passage is not "a 10 minute endeavour of searching". - -### Consequences -- ⚠️ **No bottom breaks "generate the whole world offline at season start."** Fix: pre-generate a - **deep buffer** (~30 strates) offline; extend in another offline batch if players approach its end. - A season probably never exhausts it, and the **review/veto step survives intact**. -- ⚠️ **Danger needs a ceiling.** `danger = f(depth)` with no bottom grows forever ⇒ everything eventually - unsurvivable. It must plateau or approach an asymptote. Open question that shapes the whole curve: - **does strate 40 differ from strate 30 in danger, or only in what KIND of place it is?** -- **"Every 5th slot is a boss" needs a PERIODIC fixed-slot rule.** `Settings->FixedStrates` is a - `TMap` of absolute indices today — the composer needs `slot % N` as well. -- The boss is therefore a **GATE**, which is where the transport network naturally hangs (the route past - strate 10 opens when the boss at 10 falls). Gives the descent a rhythm: five strates, then a wall. -- **"Select which strate to travel to" is a menu — and that menu is the WORLD MANIFEST** (names, danger, - discovered state). Third time the manifest has paid for itself. -- ⭐ **The global-vs-local call is MEASURABLE, not a guess.** The measurement pass already computes - entrance→exit path length and tortuosity; a live season gives the real number by telemetry. Decide it - from data later. (Same principle as everything else here: measure, don't declare.) - -### ⚠️ Architecture consequence — the community layer must be OPTIONAL AT RUNTIME -Local server + external database means a DB outage, a network hiccup or maintenance must **degrade**, -never break. The world is generated from the seed and needs no DB at all. So: **procedural names are -the fallback** when community names cannot be fetched (the deterministic name always exists by -construction), shared builds simply do not appear, votes queue locally and sync later. Cheap now, -genuinely painful to retrofit once a hundred call sites assume the fetch succeeded. - -### ⚠️ Clarification — elevator unlocks DIE WITH THE WIPE -The wipe is world-only and "everything else survives", **but the unlock network is world-scoped**: it -references this season's strates, and next season strate 7 is a different place. Carrying it forward -would also skip the new season's frontier, which is the point of wiping. **Named here before a player -discovers it the hard way.** +- ⛔ **"Do not start Phase 3" is SUPERSEDED.** `OPSTACK-HANDOFF.md` still says it; believe this file. +- ⛔ **Jahni does not want to author strates either.** The composer must invent them. An earlier reading + had him authoring strates while the system arranged them — one level too shallow, and it shaped a + whole afternoon of wrong design. +- ⛔ **Peer determinism never required matching the OLD system.** Same build, all machines — that is all + it ever meant. The self-imposed bit-identity criterion cost three weeks. Version stability across + builds is a *separate* property, and seasons make it a non-issue. +- ⛔ **Digging is a reveal-and-shape verb, not a movement verb.** An earlier pitch made it traversal + (the Deep Rock model). That is not this game. +- ⛔ **Six players do not multiply streaming.** Raised as a 6× cost; wrong (§6.11). +- ⛔ **Depth must not mean "weirder".** An early pitch argued for geological coherence and depth-driven + strangeness. That is a different game — see GDD §7. +- ⛔ **`Alpha` is not a struct field**, it is `Lerp`'s parameter. An earlier note claimed the X-macro had + one exception; it has none. +- **State acceptance criteria up front and have Jahni confirm them.** Criteria chosen alone drift + toward whatever can be self-verified, which is reliably the wrong one. --- -## 2026-08-17 — DEPTH ECONOMY + material placement (and the manifest is confirmed a NEED) - -### Decided -- **Gear stats have a CEILING (~depth 30).** Past it, loot has the *same* stats but better natural - **rolls** (Trove-style stat-quality %) ⇒ **less material to level up**. Depth buys **less grind and - unique access, never bigger numbers.** -- **Past the ceiling, going deeper is about pride, challenge, and seeing new things.** Confirmed - intent, not an accident. -- **Trading will exist** — lightly at first, better once the surface becomes a social hub. -- ⭐ **Depth-locked materials are tagged by TIER, not by strate** — e.g. a `post difficulty content` - tag — **so placement stays random.** -- ✅ **The world manifest is confirmed a NEED**, not a proposal. - -### Why the tier tag is better than a fixed depth (Jahni's call, and it is the stronger one) -A fixed depth is **solved once and stays solved forever**. A random placement inside a difficulty tier -must be **found again every wipe**, and in a one-seed shared world the answer spreads. *"Where is X this -season?"* becomes a renewable community question — exactly what the shared-knowledge design wants, at -zero extra cost. - -### Consequences -- **Guarantee what recipes depend on; leave the rest to chance.** One flag on the material: *essential* - ⇒ the composer must place it somewhere in the generated buffer; *optional* ⇒ the dice decide, and a - season lacking it gets an identity ("the one with no X"). Essential-but-absent is a dead end nobody - can fix. -- ⭐ **MANIFEST ≠ PLAYER KNOWLEDGE.** The manifest holds ground truth (the composer placed it, and - quests/validation/fast-travel need it). **Discovery state is a separate overlay**, per player. Easy - now; awkward to unpick if the client is handed the whole manifest up front. -- ⚠️ **You cannot hide the manifest anyway** — the client generates terrain from the seed, so players - hold the generator and can enumerate it offline. Not a leak to prevent; a fact to design around, as - Minecraft does with seed maps. And it barely matters here: community knowledge spreading **is** the - intent, datamining is only a faster route to it, and **the wipe is what makes it not matter** — a map - of season 4 is worthless in season 5. -- ⚠️ **The danger growth rate past the gear ceiling IS the endgame difficulty curve.** Below 30 gear and - danger climb together; above it gear is capped and danger is not, so skill/strategy/consumables are - the only levers left. That single number decides where the wall sits. Everything else is content; - that is tuning. -- **Reward converges while danger does not** — a roll-quality % asymptotes at 100. Deliberate: the deep - game is sustained by scarcity and pride, not progression. -- **Trading is a community-layer feature** ⇒ it must degrade gracefully when the database is - unreachable, like shared builds and community names. - ---- - -## 2026-08-17 — THE WORLD MANIFEST: design - -### ⭐ It is TWO things, not one -**Tier A — the SPINE. Finite, tiny, actually stored.** Strate slots + passages. ~30 strates plus a few -passages each ⇒ ~100 entries, kilobytes. **This already exists** as `StrateLayout` + the passage list, -computed once in `UVoxelStrateManager::Initialize`. It is the world's skeleton. - -**Tier B — the FIELD. Infinite in XY, therefore NEVER stored.** Landmarks, rooms, material deposits, -build sites. The world does not end sideways, so this cannot be a table — it is a **function**: give it -a region, it enumerates what is there. Deterministic, hash-based, **zero bytes**. - -⇒ That split is what makes "must not be expensive" true rather than aspirational. - -### ⭐ The trick: THE ID ENCODES THE POSITION -The hard requirement is resolving an ID **back** to a place (a saved quest target, a fast-travel -destination, a wiki citation) — normally a lookup table, which is exactly what we cannot have. -Landmarks are placed on a **hash lattice**, so build the ID from **lattice cell + type + slot index -within the cell**. Then **ID → position is ARITHMETIC**: no table, no database, no storage. A quest -stores `strate 4, cell (−128, 73), type Lake, index 2` and resolves it instantly, forever, on any -machine, offline. - -**Names fall out of the same thing:** `hash(id, seed)` → word pool. Every entry in an infinite world -has a stable, globally identical name that nobody stored and nobody generated in advance — including -entries no player will ever visit. (This is the substrate that makes quests and wikis possible; see the -two-layer naming note, community names override on top.) - -### What earns an entry -**One test: can a system need to ask "where is it?" or "what is it called?"** If yes, it is an entry. -✅ strates · passage mouths · landmarks · notable rooms · material deposits · build sites · boss arenas · -the (0,0) spine. ❌ individual rocks · every tunnel segment · terrain that merely emerged from noise. - -**Underneath that — ADDRESSABLE vs NOTABLE.** Everything hash-placed is *addressable* for free (it has -an ID by construction). **Notability is a filter on top, and it is exactly what the measurement pass -produces**: biggest chamber in a region, the one with a lake, the one at a passage mouth. "Notable -rooms" needs no new placement logic, only a threshold. - -### Discovery sits ON TOP, never inside -A player's discovered set is a set of **IDs** — small integers, cheap to store, cheap to sync. Community -discovery is the union. **The manifest never changes; only who has seen what.** (Confirms MANIFEST ≠ -PLAYER KNOWLEDGE from the depth-economy note.) - -### ⚠️ Two cautions -- **The hash functions become part of the FROZEN SEASON CONTRACT.** Change how a lattice cell hashes and - every quest target in that season moves. Fine — worlds are frozen within a season — but these - functions are **not implementation details to tidy mid-season**. -- **Tier B is cheap, not FREE.** Enumerating rooms in a region needs that region's room-placement pass. - Fine for a beacon or nearby markers; a full-continent map view would hurt. ⇒ **LOD the query**: coarse - regions return only high-notability entries — which is what a zoomed-out map wants anyway. - ---- - -## 2026-08-17 — NEAR-INFINITE STRATES: three consequences - -Jahni: some strates (surface-like especially) are **near-infinite in XY** — storage is impossible. -Confirms the Tier A / Tier B split. But it also quietly weakens three things already decided: - -### ⭐ 1. The (0,0) SPINE is what makes an infinite world navigable — state it as a law -In a bounded cave you eventually stumble onto the way down. In an infinite surface strate you never -will — the exit is a point in an endless plane. But `GeneratePassages` already places passages at a -random **angle + distance from the origin**, within a configured range, so **passages cluster near the -spine by construction.** -⇒ **The descent is anchored to the spine; infinity is OPTIONAL content.** A lost player walks back to -the spine and finds the way down. Everything outward is exploration you choose. -⚠️ This is currently an **accident of how the code happens to work**, not a stated invariant. It should -be written down as a law alongside the entrance/exit one. - -### 2. The measurement pass must sample WHERE PLAYERS GO -An infinite strate cannot be measured, only sampled — and a box a million units out measures a place -nobody will ever stand in. ⇒ **validate the neighbourhood of the spine and the passage mouths**, the -load-bearing part. A strate may be beautiful at the origin and mush ten km out; that is genuinely fine. -Also: report measurements as **distributions (median + spread), not single values** — regions really -will differ, especially once biomes vary the surface. A mean pretending to describe infinity is a lie. - -### ⚠️ 3. CORRECTION — essential materials need a DENSITY, not a location -Earlier note said the composer must place an essential material "somewhere in the buffer". **In an -infinite strate "somewhere" is unfindable** — it could be a thousand km out. The guarantee must be a -**placement density within the reachable band**: near enough to the spine, common enough to actually -meet. Otherwise a season ships a recipe nobody can complete and it reads as a bug, not as scarcity. -Conveniently this is measurable: *expected number of deposits within N of the spine* is a number the -composer can check. - ---- - -## 2026-08-17 — the CITY is a plain UE level (no seam problem after all) - -**Correction to my "authored city meets generated surface" worry:** the hand-authored city is a -**normal Unreal level**, not voxel terrain. VoxelForge sits **under the hole in the city**, or -possibly **instantiated in a whole separate level** to be free of the city's constraints. -⇒ **There is no authored/generated seam.** They are not the same kind of thing. - -### Open / to confirm -- ❓ **Is the voxel world therefore entirely SUBTERRANEAN?** If the real outdoors is the city level, - then `SurfaceWorld` is not a surface — it is a cathedral-sized underground space that *reads* as - outdoors (consistent with the existing design where surface/sky strates are enclosed, lit from - within by skylight + gem-star ceiling, never by a sun). **Asked, not assumed.** -- ⚠️ **Separate level vs sublevel is a real tradeoff, worth settling before both exist.** A distinct map - gives the voxel world its own world settings, lighting, kill-Z and streaming, free of the city's — - genuinely valuable. **The cost is that the descent becomes a LOAD rather than a walk.** For a game - whose core verb is descending, a seamless transition through the hole would feel far better, and UE - can do it with both as sublevels of one world — but then the voxel world inherits the city's world - settings, which is exactly the constraint being escaped. *"Free of constraints"* and *"you can walk - down into it"* are in tension. -- **Infinite vs bounded world is UNDECIDED.** Jahni: *"I might not make infinite world also, but for now - I might, so I prefer saying it."* - ⇒ **Keep designing for INFINITE**: it degrades gracefully to bounded, while a bounded-assuming design - (stored manifest, complete map, exhaustive validation) breaks the moment the world is not. - ⇒ But note bounded is a **lever, not merely a limit** — it would buy complete measurement instead of - sampling, a real map, and much easier findability. A live option to pull deliberately one day. - ---- - -## 2026-08-17 — BOUNDED world, SUBTERRANEAN confirmed, and the descent shaft - -### Decided -- ⭐ **The world is BOUNDED.** Still gigantic maps, but finite. -- ✅ **The voxel world is entirely SUBTERRANEAN** — everything is under the surface; some strates only - *mimic* being surface-like. (The real outdoors is the city's UE level.) -- **Transition:** likely a **master level** holding shared elements (lighting etc.), switching when the - player enters the hole, with a transition playing. **The elevator can be as long as needed** — but - Jahni intends to place the **first strate well below** so travel time covers generation naturally. - -### What BOUNDED buys -- ⭐ **The primordial law becomes PROVABLE, not sampled.** The composer can flood-fill an *entire* - strate at coarse resolution, so entrance→exit reachability is a **proof**, not a probability. Same - for largest-connected-component and walkable surface — true statistics, not estimates from boxes. - For the one law Jahni called primordial, that is the difference between a check and a guarantee. -- **Budget note:** 8 km × 8 km at 4 m sampling ≈ 200 M cells (too many); at 16 m ≈ 3 M (nothing). - Resolution scales with map size. Aliasing caveat still applies — **coarse to find the route, full - resolution to confirm the one corridor.** -- **"Explored" becomes a real number** — a finite volume means *"the community has explored 34 % of - season 5"*. A genuine collective progress bar for a shared world, free once bounded. - -### ⚠️ NEW REQUIREMENT — an XY world-edge seal, which does not exist -Verified: `VF_ApplyBoundarySeal(float& Density, float WorldZ, float StrateTopZ, float StrateBottomZ, -float Thickness, float BaseDensity)` is **purely VERTICAL** — it seals each strate's ceiling and floor -so nothing punches through except passages. There is **no horizontal equivalent**, because until now -the world had no sides. A bounded world does. -⇒ Build it as a **fourth structural invariant** alongside spine → seal → passage: a **forcing** op, -appended automatically, not author-omittable. Two reasons that shape is right: an invented strate -cannot forget it, and because forcing ops prove `AllSolid` in their band, **the world's outer shell -becomes free to skip** in tile classification. - -### The transition, and ⭐ the descent shaft as the game's front door -- **Distance gives a FIXED time budget, not an adaptive one** — fall speed × depth is the same - wall-clock on every machine, so a slow disk still lands in ungenerated space. **An elevator can - stall for a ready signal; a free fall cannot.** If it's a drop, size it for the worst machine. -- ⭐ **Whatever sits between the hole and the first strate is the highest-traffic space in the game** — - every player, every session, forever. It is currently an *accident* (just the gap streaming needed). - It should be authored deliberately, because it is already doing several jobs: - - **sets the tone** for everything below (a featureless drop reads as a loading screen; a worked - shaft with old machinery and lights going into the dark reads as "people have been here"); - - **it is the elevator lobby** ⇒ the natural home of the manifest-driven **strate-select menu**, as a - real place rather than a floating panel; - - ⭐ **it is where depth becomes legible** — wall markings, and **the community's depth record for the - season marked on the shaft**, passed on the way down. The season's collective arc made physical, in - the one place everybody looks; - - later: shops, NPCs, surface-side infrastructure. -- Costs nothing architecturally (city-level authored geometry, or the top of the spine) but turns a - technical necessity into the game's threshold. - ---- - -## 2026-08-17 — ACTOR-SPACE sweep (built, working) - -**Why:** the city's hole is not at world (0,0), so the `AVoxelWorld` actor must be placed under it. -Jahni: *"we'll have it to be fully actor relative, just in case."* - -**What was actually true before:** the plugin was **half** actor-relative. Correct already — -`GetBiomeAtWorldLocation`, `GetVoxelSurfaceHeightAt`, `UpdateDecorations`, `UpdateLandmarks`, -`UpdateWater`, `UVoxelDensityVolume::Update`, the passage debug draw, the clipmap material. **Not** -correct — the streaming centre, the streaming anchors, all six carve/fill entry points, -`GetStrateAtPosition`, and `UVoxelAtmosphereManager::UpdateForPlayer` (whose own comment *documented* -the assumption: *"actor at origin/identity"*). It only ever worked because the actor sat at the origin. - -⚠️ **The trap avoided:** the content managers already convert internally, so converting `PlayerLastPos` -at the source would have **double-converted four call sites**. The fix had to be per-boundary, not at -the top. Found only by reading each consumer — the estimate before that read was wrong in both -directions. - -**Landed:** -- `AVoxelWorld::WorldToLocalCm` / `WorldToLocalVoxel` / `LocalVoxelToWorld` (BlueprintPure) — declared - as **the only sanctioned boundary** between Unreal coordinates and voxel coordinates. -- ⭐ **A grep-checkable invariant, which is the real deliverable:** *no `/ VOXEL_SIZE` applied to a - parameter named `World*` outside those three functions.* The risk was never a missed site among the - ones we found — it is the **next** one nobody notices. -- 11 call sites converted (counted before and after; every old pattern now zero). -- ⚠️ **Translation-only guard in `BeginPlay`** — tiles, `ClassifyBox`, the clipmap window, culling and - streaming distance all assume **world-axis-aligned** boxes. A rotation or non-unit scale breaks that - *structurally*, not gracefully. Logs an Error naming the offending rotation/scale. -- CODEMAP §3.5 and §6 document the convention. - -**Nice property:** with the actor at the origin every conversion is identity, so this is a **no-op** -until the actor moves. Verify by building, confirming nothing changed, *then* moving the actor. -Jahni built it: **"seems to work fine."** Then moved the actor off the origin and confirmed again — -**that is the real test**, since the identity case proves only that nothing broke. - ---- - -## 2026-08-17 — NAMING (Jahni: "pretty clever") - -**Failure mode to avoid: slot-filling.** *"The Crystal Cavern of Whispers."* Players learn the template -in ten minutes; after that every name is noise, and worse, names stop **distinguishing** places — if -everything is `[Adj] [Noun] of [Noun]` you cannot remember which was which, which defeats the point. - -### 1. ⭐ Derive names from the MEASUREMENTS, not from a bag -The feature vector already exists per notable entry (volume, openness, verticality, water, feature -scale). Let the name generator read it: a vast open chamber draws vast-open words, a flooded one draws -water words, a tight vertical shaft draws shaft words. The name then **fits the place** — a player who -hears "the Sink" and later finds a huge drain-like chamber gets a small satisfying click. -*(Fifth use of the measurement pass.)* - -### 2. Several competing GRAMMARS, not one template -Real maps are layers of naming logics from different eras and languages, and that mess is what makes -them feel real. Pick a grammar by hash, from ~5: -| grammar | examples | implies | -|---|---|---| -| **Compound** | Blackreach, Stonefall, Grimwater | short, two morphemes, germanic-ish | -| **Descriptive definite** | The Long Dark, The Hollow, The Sink | plain, spoken | -| **Possessive** | Varen's Fall, The Mason's Mistake | somebody was here, and it went badly | -| **Functional** | Shaft Nine, The Lower Cut | industry, records, someone was organised | -| **Opaque** | invented phonology (consistent syllable inventory) | a language, not keyboard mash | -**Five grammars reads as history. One grammar reads as a generator.** - -### 3. Name SCARCITY — most things get no name at all -If every room is named, names mean nothing. Only **notable** entries get one (the threshold already -defined by addressable-vs-notable); everything else stays addressable but anonymous. Scarcity is what -makes a named place feel like a place, and it is free — a threshold, not a feature. - -### 4. Names must survive contact with a WIKI -Players will **type these into search boxes and say them aloud**. So: short, pronounceable on sight, no -apostrophes or unusual glyphs. **A name nobody can spell cannot become community knowledge** — which is -the whole reason names exist in a shared world. - -### ⛔ Parked by Jahni -Varying the **naming language by depth** (deeper = older, stranger tongue). Liked in principle but -parked "for now" — it is still a gradient, and gradients are what he rejected. - ---- - -## 2026-08-17 — DEATH: the possessed corpse - -**Jahni's original intent:** dying in the depths (most often) leaves your corpse **possessed** — you must -**fight it** to get your loot back. His worry: with distance growing **horizontally as well as -vertically**, how much frustration does that create? Floated a **bounty hunter NPC you pay to retrieve -your stuff**. - -### ⚠️ The trap that would break it: gear on the corpse = a DEATH SPIRAL -If the corpse holds your **gear**, you return *without* your equipment to fight a copy of yourself -*wearing* it. Every attempt is weaker than the last, and a bad night ends with your best gear -permanently orbiting a corpse you can no longer beat. Souls games dodge this by taking only currency, -never equipment. -⇒ **Fix: the corpse holds your CARGO; you keep your GEAR.** The fight becomes you against an equal — -exactly the intended challenge, winnable with better play. **Self-balancing with zero tuning:** a -strong player's corpse is a strong enemy, a weak one's is weak, at every depth, forever. - -### The frustration is the TRAVEL, not the fight -Nobody resents a hard fight; they resent twenty minutes of walking to reach it. **Fix travel, leave the -fight alone.** -- ✅ **The vertical half is already solved** — the elevator/unlock network. Unlocked stop at 12, died at - 13 ⇒ you travel one strate, not thirteen. An unplanned payoff, and an argument for unlock stops being - **generous**. -- ✅ **CONFIRMED by Jahni 2026-08-17.** ⭐ **The horizontal half: LET THE CORPSE WALK** — it wanders - around **landmarks near the passage**, not arbitrarily. It is possessed — why would it stay put? Move it - toward the nearest **passage mouth or landmark**. It comes partway to meet you, which collapses the - distance problem and is *better fiction* than a static body. Falls out of it: - - the death location is reported as a **NAMED PLACE**, not coordinates — "last seen near the Sink" - (**sixth** use of the manifest); - - the retrieval point is somewhere you were heading anyway; - - and it is genuinely unnerving — your own corpse, wearing your face, hunting you. - -### The bounty hunter: price it in TIME, not only gold -A pure money sink either trivialises the corpse run or goes unused. But if he retrieves your cargo and -delivers it **later** (next session / a real-world delay), then **the corpse run is the fast path and -the hunter is the "I don't have another hour tonight" path.** Both stay meaningful, it self-balances -across players with different free time, and it gives the city's NPCs a real job. - -### Open forks -- **Dying while fighting your corpse** — one corpse that absorbs the previous, or do they stack? -- **Can another player kill your corpse and take the cargo?** A genuine fork: real interaction and real - griefing in the same mechanic. - ---- - -## 2026-08-17 — DEATH, resolved: the mirror match + the retrieval guild - -### Decided -- **Only YOU can claim your loot.** No PvP looting — keeps the death mechanic out of tension with the - otherwise cooperative design. -- ⭐ **The corpse COPIES your weapons; you keep yours too.** Better than my cargo/gear split: it keeps a - true mirror fight without ever disarming you, so the death spiral cannot happen. -- **Possible later:** dead players spawn into **multiple instances of the depths**, so your corpse - becomes an encounter in other players' worlds; killing it earns **gold from a retrieving guild** (not - your loot). - -### Why the mirror is strong — and the one thing to make deliberate -It tests **your own build's weakness** (glass cannon ⇒ you fight a glass cannon that hits as hard as you -do — a "know thyself" fight), and it **self-balances at every gear tier and depth, forever, with zero -tuning**. -⚠️ But a *perfect* mirror is a **coin flip**, and you often arrive having already spent consumables -getting there. ⇒ **Give the player a small, honest edge, on purpose:** *you* have potions and -consumables and the corpse does not; *you* can retreat, heal and come back, and it cannot. Enough to -make the fight winnable through **preparation** rather than luck — which is exactly the gear-and- -strategy test intended. Do not leave this accidental. - -### ⚠️ HARD RULE — copied weapons must NOT be lootable -If the corpse spawns a copy of your legendary sword and **drops** it, you have built an **item -duplicator**, and it will be found within a week. **Copied gear vanishes with the corpse; only the cargo -drops.** Cheap to get right now, brutal to unwind after a season of duplicated items is in circulation. - -### ⭐ The guild unifies two systems into one -The bounty-hunter NPC and the player-paid retrieval are the **same mechanism with two front ends**: -| path | who | speed | certainty | -|---|---|---|---| -| **Player** | someone finds your corpse in their instance and kills it | fast | uncertain | -| **NPC** | nobody came, so the guild sends a professional | slow (next session / real delay) | guaranteed | - -Either way **the cargo returns to you** (mail, or a retrieval office in the city) and **the killer is -paid by the guild, not out of your pocket.** Nobody loses anything, the guild becomes a real institution -with a reason to exist, and it is **one system to build, not two.** - -⭐ **Your corpse becomes content for other people.** In a game about a shared world, your worst night -turns into someone else's encounter — a wandering possessed adventurer in the deep dark, wearing better -gear than theirs, worth killing. **The first mechanic here where failure ADDS to the world instead of -only costing you.** - ---- - -## 2026-08-17 — SESSION SHAPE, PARTY PLAY, and the OPENING (big lore reveal) - -### Decided -- **Run length: still unknown.** But ⭐ **a corpse run is a DETOUR, not an evening — 10 minutes at - worst.** That is now a hard constraint that propagates (see below). -- **The descent is done TOGETHER.** Today: a local server on one player's machine or a dedicated one. - Later: **instances sent per group — different groups never meet in the depths.** -- **Creatures: nothing decided yet.** Flagged as needed. -- **Corpse location NPC** (guild, or near the passage): tells you where your corpse was last seen for a - very small fee. ⇒ **Refinement: charge for PRECISION, not existence.** Free = *"last seen near the - Sink, strate 12"*; paid = exact position / live tracking. Reasons: the decision becomes real (coin vs - hunt) instead of a fee-gated UI step; **it can never trap a broke player** (death → lost cargo → can't - afford the info → can't recover is a spiral that hits hardest after a bad run); and *"last seen"* is - honest fiction, since the corpse genuinely wanders. Diegetic too — you talk to someone instead of - reading a death screen. (**Seventh** use of the manifest's names.) - -### ⭐ THE OPENING — and it solves a structural problem -Cinematic: **civilization emerged FROM the depths.** The first, **artificial, hand-made strate** holds -**crystals with humans inside**. We are un-crystallized, discover a **base camp**, and are told we could -be adventurers who brave the depths. Then down to **"strate 2" — the first GENERATED strate.** - -- ⭐ **This justifies the authored/generated seam IN FICTION.** We had accepted "strate 1 is hand-made, - the rest are composed" as a production fact players must not notice. But the hand-made strate is - **hand-made in-world too** — artificial because someone built it; the generated ones below are the - *natural* deep world. A player who senses strate 1 feels different is **correctly reading the - fiction**, not spotting the seam. Rare, and worth protecting. -- ⭐ **Descending is a RETURN, not a conquest.** You are going back to where you came from — a far better - motive than loot, and it gives the depths an implied history for free. Also seeds mystery: who - crystallized them, and why? - -### Consequences -- ⭐ **PLACE the corpse, don't walk it.** If it pathfinds toward a landmark, retrieval time depends on - where you happened to die — and dying 40 minutes out breaks the 10-minute cap. So **on death, place it - at a landmark near the nearest passage / elevator stop**, and let it wander only *within* that - neighbourhood for atmosphere. Turns the cap from a hope into a **guarantee** — the same - build-it-in-rather-than-measure-it pattern as the primordial law. -- ⚠️ **CONTRADICTION resolved by Jahni — and my proposed fix was BACKWARDS.** I assumed the settlement - resets and the arc lives inside a season. **No: the settlement PERSISTS and progresses ACROSS - seasons.** See the dedicated section below. -- **Instancing makes the city the ENTIRE social surface.** If groups never meet below, then trade, the - guild, shared builds and simply seeing another person all happen up top. Strong argument for a - generous city and for the descent-shaft lobby. The depths are shared as **knowledge, not presence** — - which is consistent: wiki, names and discoveries all still work. -- **Strate size and elevator density are now downstream of the 10-minute cap.** If a stop anchors - retrieval, a strate must be crossable in roughly that order, or dying far from a stop breaks it. -- **Party play changes danger tuning fundamentally** — a boss balanced for one is a joke for four. - Unresolved: does danger scale with party size, or are strates tuned for a nominal group? - ---- - -## 2026-08-17 — ⭐ THE CORE LOOP, CLOSED (the settlement is communal and permanent) - -### Decided -- **The surface is limited by construction** — a **sky island**, or a mountain so tall no one can - descend it. So resources **must** come from below. -- **The settlement is COMMUNAL** — the same for everyone, global. **It does NOT reset at the wipe.** -- **A SEASON is the unit that advances it:** camp → village → town → city → advanced city → … - **driven by everyone.** -- **Players gather resources and donate** (to the mayor or similar); a **global count** rises until the - next tier is reached. - -### ⭐ The loop is now closed and coherent -**Limited surface → must descend → depths give resources → resources build the settlement → the -settlement enables deeper descent.** Every system discussed today hangs off this. The sky island does -quiet work: it explains why there is no trade with anywhere else, why the depths are the only economy, -and why descending is the civilization's project rather than a hobby. - -### ⭐ The wipe gains meaning beyond freshness -The **world** is impermanent; the **civilization** persists. *"We lost our maps, but we built a town."* -Season 8 arrives in a place season 3 made. -**Survives the wipe:** the settlement and its tier · account progression · titles · personal blueprint -library · housing instances. -**Dies with the wipe:** the world · the shared build pool · the elevator unlock network. - -### ⚠️ Three requirements this creates -1. **The tier threshold MUST scale with active population.** A fixed number stalls forever with a small - playerbase and is blown through in a week with a large one — either way the promise ("a season - advances the settlement") breaks. ⇒ **Target one tier per season**, and retune the threshold at the - season boundary from observed participation. **That moment already exists** — it is the offline - review where Jahni inspects and vetoes the next world. **One process, two uses.** A quiet season then - adjusts rather than permanently stalling the city. -2. ⚠️ **The tier must grant ACCESS and CONVENIENCE, never POWER.** Services, NPCs, workshops, bank, fast - travel, aesthetics — **not** better starting gear or stat boosts. Otherwise season-8 players start - stronger than season-1 players, early strates trivialize, and the difficulty curve erodes every - season until the top half of the world is decoration. **This is the SAME rule already chosen for - depth** (depth buys access and less grind, never bigger numbers) — one consistent economic principle, - applied twice. Good sign. -3. ⭐ **Make the city a LEDGER, for late joiners.** Someone arriving in season 8 built none of it — - alienating, or the best first impression in the game, depending on whether the city **records who - built it**: plaques on the aqueduct, names in the great hall, a monument per tier listing that - season's contributors. New players then walk through evidence that people made this place, and - veterans get something permanent — pairing exactly with the titles that already survive wipes. - -### Smaller consequence -**Cargo now has two destinations** — your own progression, or the city. A real recurring choice with a -communal dimension; it gives otherwise-worthless materials a use; and it makes the corpse run hit -harder. *That ore wasn't just yours — it was the aqueduct.* - ---- - -## 2026-08-17 — THE VERBS: digging and combat - -### Digging — a REVEAL-AND-SHAPE verb, not a movement verb -⛔ **Correction: I over-designed this.** I pitched digging as traversal (the Deep Rock model). Jahni: -it is for **digging walls that hide something behind**, **uncovering half-buried ruins**, **tiny -shortcuts within the same strate**, and **terraforming for builds**. -- ⏸️ **Noise attracting monsters: parked** ("not for now"). Recorded because it is still the cheapest way - to price digging in *safety* rather than in resources, if it is ever wanted. -- Useful fact from the code, unchanged: `VF_ApplyOriginSpine` deliberately leaves the boundary **seals - intact** *"so the player must still dig through to descend"* — descending by digging is intentional - but effortful; passages and elevators stay the fast path. - -### Combat — MMORPG-like -Ranged/melee × magic/physical · stuns, roots, mutes · AoE and **targeted** AoE · DoTs. **Party of 6.** -**First person must remain possible.** **No friendly fire** — an AoE in a tunnel is simply strong, and -that is accepted. - -### ⭐ This puts HARD SPATIAL REQUIREMENTS on the composer — and they are measurable -Arena sizes, translated into the composer's own units (**1 chunk = 32 voxels × 25 cm = 8 m**): -| space | size | chunks | -|---|---|---| -| **boss arena** (6 players + adds + movement) | ~25–30 m across | **3–4** | -| **ordinary fight space** | ~15–20 m | **~2** | -| **third-person camera clearance** | ~6–8 m of headroom | **~1** | -*Starting estimates, not gospel.* Arenas are **small relative to strates** (1–256 chunks tall), so -guaranteeing a supply of them is a **placement** problem, not a size problem. - -⇒ A fightable space needs: minimum floor area · slope below a threshold · minimum clearance · clear -sightlines. **All of these the measurement pass already computes or trivially can** ⇒ a strate can be -**validated** to contain N fightable spaces, exactly as it is validated to have an entrance and an exit. -**"Fights don't feel blergh" becomes a GENERATION GUARANTEE rather than a tuning hope.** - -**What caves add that a flat MMO arena cannot: VERTICALITY.** MMO fights are overwhelmingly flat; a -fight across ledges and drops is genuinely different — and it is already in the danger metrics as fall -exposure. The composer can deliberately produce arenas that are **spacious but vertical**, which is what -makes cave combat *better* than field combat instead of a worse version of it. - -### ⭐ First person + ground telegraphs conflict — so TELEGRAPH THE SOURCE, NOT THE FLOOR -A circle painted on the ground only works if you can see the ground, which is exactly what first person -removes (you cannot see a telegraph under your feet, or behind you). If instead the **enemy visibly -winds up** (glow, raised arm, rising particles), the threat is read off the creature and you move away -from it. Works identically in FP and TP, **works when the floor is lumpy voxel rock where decals project -badly anyway**, and reading an animation is more skilful and more legible than reading a decal. -⚠️ **Supporting both perspectives means the constraints are the UNION:** full third-person camera -clearance **and** source-readable telegraphs. - -### ⛔ CORRECTION — six players do NOT break streaming; the architecture already solved it -I raised this as a 6x streaming cost. **Wrong, and Jahni was right:** the host cares only about -**collision**; mesh and everything visual is **per client**, since every client generates the same world -(ARCHITECTURE §9.3/§9.4). Verified in code: `RegisterStreamingAnchor` defaults to `XYRadiusChunks=1, -ZBelow=1, ZAbove=0` = a 3x3x2 box = **18 level-0 tiles per remote player** (~90 for five), against a -player clipmap of hundreds-to-thousands across LODs at `ViewDistanceXY=16`. A rounding error, not a -multiplier. `CollisionOnly` skips **rendering** (`MeshComp->SetVisibility(false)`: no draw, no VSM, no -culling); it still runs density + marching cubes + the collision cook, because collision needs geometry --- but at 18 tiles each that is small. - -**Two things that DO scale with six players:** -- ⚠️ **Carve-driven re-meshing, which is BURSTY, not steady-state.** Six players digging in six places = - six streams of dirty-tile work: the host re-cooks collision for each, every client re-meshes what it - can see. This is the multiplier that actually tracks party size, and it arrives in spikes. Digging is - now a *reveal* verb (walls hiding things, ruins to uncover), so a party will do a lot of it at once. -- ⚠️ **Client-side meshing makes determinism a HARD RUNTIME REQUIREMENT, not a nice property.** If two - clients generate even slightly different geometry they disagree about where the walls are -- and the - host collision is authoritative, so one player hits an invisible wall where another sees a passage. - ⇒ `VoxelForge.Determinism.CrossPlatformDigest` currently **reports rather than asserts** (informational - until someone pins the values). **This architecture promotes it to must-be-green.** Windows client + - Linux host, or two clients on different CPUs, producing different floats is now a visible gameplay - bug. - -### Other consequences -- ⚠️ **The corpse mirror breaks for SUPPORT builds.** A healer's corpse heals itself and the fight is - unwinnable for reasons unrelated to skill. ⇒ the corpse should be a **combat-shaped** version of you — - your weapons and damage, minus the abilities that make the fight impossible — not a literal copy. -- **No friendly fire** removes a whole class of party friction (important with pick-up groups). Its - consequence: the cost of a cramped fight becomes **visual clutter**, not damage. Six players' effects - in an 8 m tunnel is unreadable noise and nobody can find the telegraph in it. The arena requirement - survives — for a different reason than "nowhere to dodge". +## 9. Open — generator side only + +Game-design questions live in **[GDD.md](GDD.md) §16**. These are the ones that block generator work: + +1. **Adjacency rules between strates** — confirmed wanted, never specified. Proposed shape: a small + tag-rule list in settings (no two alike in a row; `Anomaly` needs a `Grounded` neighbour above; max + run length), with the draw falling back to a designated safe entry so it can **never fail**. +2. **How the mix rule is expressed** — a global "total strangeness" cap was proposed; not ruled on. +3. **How theme coherence is drawn** — operators, materials, decorations, creatures and audio pulled as a + compatible set. Jahni expects his first big job to be creating themes and tagging content with + multiple fitting tags. +4. **Where the corpus lives** and how promotion is recorded between seasons. +5. **Whether op discovery** (promoting good sub-stacks into named reusable units) is ever worth it. + Parked: the existing parameter space is already vastly larger than a hundred seasons could explore. + **The scarce resource is judgment, not vocabulary.**