feat(composer): the structure roll -- 64 distinct stacks nobody wrote

Tier 4b. Until now the composer varied PARAMETERS within an archetype:
differently-sized caves. This rolls STRUCTURE, which is what makes a strate
a different KIND of place.

Per COMPOSER-NOTES §3.2: roll root polarity (rock+carve or void+fill) ->
a shape source legal for that polarity -> the conversion FOLLOWS from
polarity rather than being rolled -> k modifiers drawn from what the
CHANNEL DECLARATIONS say is legal, not from a hand-written list ->
structural posts appended automatically and not omittable.

  64 distinct recipes of 64          (perfect structural diversity)
  invalid recipes emitted: 0         (the roller cannot produce an
                                      order the DAG validator rejects)
  survival 46/64 (71.9%)             non-vacuous 62, largest-share 60,
                                      law pass 48

⭐ AND THEY ARE REAL PLACES NOBODY DESIGNED. Root polarity as a one-bit
identity lever, exactly as §3.2 predicted:

  VF|r=vd/T|s=rg/T|x=fl/T   TunnelNetwork's ROOM GRAPH rooted in void with
                            a FILL -- rooms become solid blobs hanging in
                            open space. air .978, feature scale 64.
  VF|r=vd/M|s=lt/M|x=fl/M   Maze's lattice corridors filled instead of
                            carved. air .938, feature scale 16.
  RC|r=rk/V|s=sh/V|x=cv/V   shafts carved from rock with a Maze modifier
                            set they never ship with.

Same parts, different worlds. That is the whole thesis of the op stack,
finally exercised.

⭐⭐ AND THE VERDICTS HOLD ON STACKS NOBODY VERIFIED:
  489 proved (213 AllSolid, 276 AllAir), 2,071 mixed
  650,859 voxels brute-forced
  0 VIOLATIONS

This is what 8e346a3 was for. A false uniform verdict on an invented stack
means no geometry, no collision, and a player falls through the floor --
and it is the failure that cannot be seen without brute force. Zero.

Survival is 71.9% against 92.2% for the parameter roll, and that is the
EXPECTED direction, not a regression. Rolling structure aggressively is
supposed to fail sometimes; §3.5's reject-and-resample is what that is for.
A composer that only emits safe stacks is not exploring. Deliberately not
tuned.

Recipes are serialisable (op ids + param-block bindings), so a season
manifest can ship one -- which is also what will let an archetype become a
spatial REGION inside a strate rather than a label on it.

Build clean, 24 succeeded / 0 failed, verified by me in VoxelM.log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-04 15:21:27 +02:00
1 parent 190ff381c0
commit 859a9b25c1
10 files changed
+2194 -55

No files matched your search

+41 -4
View File
@@ -160,6 +160,40 @@ corpus is small.
**⇒ Rule: vary STRUCTURE aggressively, vary PARAMETERS conservatively.** Shift the ratio as the corpus
grows.
**Tier 4b implementation (2026-09-04):** `FVoxelOpStackRecipe` is the shipped-manifest shape: it
stores the one-bit `RootPolarity`, root/source/conversion `EVoxelStrateOpClass` IDs, an ordered list
of modifier IDs, and an `EVoxelStrateParamBlock` on every entry. The four structural posts are
deliberately not a field in the recipe. `VF_BuildStackFromRecipe` appends spine, vertical seal,
passage carving, and XY edge seal in that fixed order, so an invented strate cannot omit primordial
law. The conversion ID is checked against polarity rather than rolled independently.
The roller's modifier catalogue is a deterministic array of declared ops, not a compatibility list.
It asks each op for its channel reads/writes, additive contract, required resources, and provided
resources, then draws 4–8 unique entries from the legal remainder. The channel declarations exposed
a real missing dependency: `FCaveTerraceMod` and the other room-relative cave modifiers use the
`FRoomGraphSource` room cache through a non-owning pointer, even though their channel masks only said
`Density`. `FShaftLedgeMod` similarly consumes shaft state, and `FOverhangShelfMod` consumes the
surface-column state. `RequiredResources` / `ProvidedResources` now declare and validate those
dependencies (RoomGeometry, ShaftGeometry, SurfaceColumn); the roller cannot place such an op before
its provider. `FCaveRoughnessMod` and `FWormFieldSource` were checked and do not require room state:
they read only their declared channels and parameters.
The parameter decision is deliberately visible: structure and parameter rolls are separate. Each
candidate rolls the six native family blocks independently from the exact-archetype corpus using the
Tier 4a conservative blend/jitter procedure. An op consumes the block named on its recipe entry;
structural posts consume the shape source's family block. There is no unsafe 73-field cross-family
blend. The current `SurfaceWorld` block is still rolled and carried even when a structure has no
surface-column op, so the recipe remains a complete family-parameter record.
The focused 64-candidate run produced 64 distinct recipes, 0 invalid recipes, and 0 box-verdict
violations. It checked 650,859 voxels across 489 proved boxes. Survival was 46/64 (71.9%) under the
same non-vacuous, largest-share ≥ 0.50, exact `Connected` criteria as Tier 4a; that lower rate is an
expected consequence of aggressive structural exploration, not a tuning target. The test also
rerolls every recipe and rebuilt stack, checks `WorldRadiusVoxels == 0`, and feeds the novel stack
through the offline Tier 2 sampler; no runtime generation path calls this API. The final focused
test took **76.466 s**. The complete `VoxelForge` namespace then passed **24/24 tests (0 failed,
0 not run)** in **339.797 s**.
### 3.3 Parameters: the range problem
**Verified facts, not remembered:** `FStrateGenerationParams` has **73 scalar fields** (+1 bool) — not
@@ -300,8 +334,8 @@ produced **59/64 survivors (92.2%)**. There were 60 non-vacuous candidates, 60 w
component share ≥ 0.50, 63 exact unsnapped arrival→departure law passes, and no roll failures.
The complete 64-row table is emitted by `VoxelForge.Composer.ParameterRoll`; each row reports
archetype, weighted parents, air fraction, largest share, walkable fraction, feature scale, and
arrival→departure verdict. The final full-suite run took **112.574 s** total (**0.015 s** corpus
load, **112.556 s** roll/measurement).
arrival→departure verdict. The current run took **114.845 s** total (**0.014 s** corpus load,
**114.827 s** roll/measurement).
The §6.2 check is no longer vacuous: **1,224 boxes proved**, **1,629,144 voxels checked**,
**0 violations**. Its verdict mix was 1,336 Mixed, 549 AllSolid, and 675 AllAir; the law check
@@ -650,8 +684,11 @@ roughness and should stay late in the stack.
- ✅ **Parameter roll (§3.3, Tier 4a)** — corpus spread + deterministic blend/jitter implementation
and the 64-candidate measurement pass are built; the first run exposed a one-vector settings corpus
and a vacuous cave box-verdict scan, both recorded above.
- ⬜ Structure roll (§3.2) · reject-and-resample driven by Tier 2 (§3.5) · the **offline season
pipeline** with Jahni's review and veto (§3.1).
- ✅ **Structure roll (§3.2, Tier 4b)** — serialisable recipes, root polarity, declaration-derived
resource-safe modifier legality, mandatory posts, 64-candidate measurement, and direct box-verdict
brute force are built; focused result is 46/64 survival with 0 invalid recipes and 0 violations.
- ⬜ 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 and tag draws for materials, creatures and audio ·