perf(mesher): shared proof-driven sampling skips certified-uniform blocks (L52); FI 7.1 -> 5.6 s; R21

The shared marching-cubes mesher applies its 8-cell block proofs at step 2 for standalone shape contexts
and skips density-grid fills outside certified-uniform blocks (plus a one-sample normal halo); mixed and
unknown blocks still sample exactly. FI certified-air boxes 1,959 -> 14,119; FI LOD1 samples -63%. FI whole
view 7.08 -> 5.61 s (-21%), moving p50 about -33%; SW unchanged. Geometry hashes identical (no version
bump); proof audit 36.9 M samples, 0 violations, Clang and GCC. Source stamp now covers the standalone
mesher copy. Research R21 (deepened): native Core cave generator design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-24 23:53:43 +02:00
1 parent c42c785e2c
commit 408c14baff
5 files changed
+290 -10

No files matched your search

@@ -0,0 +1,86 @@
# Linux 52 — shared proof-driven meshing
Date: 2026-10-24. Branch: `assembler/world`.
## Critical path and profile
Profiles used `--profile-lod-work --mesh-coverage` with 12 workers. In the FI profile, shape setup took 0.887 s, first tile was ready at 1.186 s, the near ring at 1.351 s, and the whole view at 5.194 s. After the ring, the remaining tile drain took 3.843 s. LOD0/1 meshing remains the largest work pool. LOD7/8 together still cost about 7.66 worker-seconds, but those jobs overlap with other workers and are not a 7.66 s wall-time stage.
SW took 0.437 s for shape setup, 0.510 s to first tile, 0.524 s to the near ring, and 1.572 s for the whole view. Its post-ring tail is 1.048 s. LOD6 connector evaluation and LOD8 meshing have the largest remaining worker sums. The trace shows the serial shape prefix followed by the outstanding-tile drain; it does not include queue-wait timestamps, so it cannot separate scheduler saturation from far-tile ordering.
Shape-stage timings (before → candidate, ms):
| Recipe | Local shapes | Intervals/signs | Surface band | Far parents | Total |
| --- | ---: | ---: | ---: | ---: | ---: |
| FI | 35.1 → 37.3 | 84.8 → 85.8 | 689.2 → 706.6 | 50.4 → 49.9 | 866.5 → 886.9 |
| SW | 34.4 → 36.5 | 82.7 → 78.1 | 261.1 → 259.2 | 52.3 → 53.8 | 438.2 → 436.2 |
W12 worker sums per LOD (seconds; before → candidate):
| LOD | FI | SW |
| ---: | ---: | ---: |
| 0 | 30.214 → 28.633 | 1.994 → 2.032 |
| 1 | 17.079 → 2.829 | 1.034 → 1.075 |
| 2 | 1.977 → 2.092 | 1.014 → 1.078 |
| 3 | 1.647 → 1.683 | 0.729 → 0.783 |
| 4 | 1.952 → 2.004 | 1.299 → 1.368 |
| 5 | 0.777 → 0.792 | 0.502 → 0.531 |
| 6 | 2.677 → 2.650 | 1.817 → 1.896 |
| 7 | 2.045 → 1.972 | 1.046 → 1.098 |
| 8 | 5.665 → 5.684 | 2.723 → 2.884 |
FI LOD0 density samples fell 18.951 M → 17.883 M (−5.6%); LOD1 fell 12.948 M → 4.802 M (−62.9%). SW LOD0/1 sample counts were unchanged at 21.952 M and 19.208 M.
## Change and gain
The shared mesher now applies its existing 8-cell block proofs at Step 2 for standalone shape contexts. It omits density-grid fills outside certified uniform blocks and their one-sample normal halo. Mixed and unknown blocks still use exact sampling. FI certified-air boxes rose from 1,959 to 14,119; mixed boxes stayed at 18,857 and unknown boxes at 240. FI meshing worker time fell 46.44 s → 30.60 s, while proof classification rose 6.53 s → 22.47 s. These are overlapping worker sums, not wall time.
The full-tier five-pair medians show FI whole-view readiness 7.080 s → 5.614 s (−20.7%), with first tile 1.273 s → 1.264 s and near ring 1.448 s → 1.441 s. SW measured 1.720 s → 1.718 s (−0.1%), first tile 0.535 s → 0.533 s, near ring 0.552 s → 0.549 s. A separate direct five-pair set measured FI 6.800 s → 5.430 s and SW 1.642 s → 1.649 s.
Moving submit-to-completion p50/p95 (ms; one before/after paired sweep per recipe and distance):
| Recipe / step | Before | Candidate |
| --- | ---: | ---: |
| FI / 8 m | 418.3 / 631.7 | 279.4 / 463.7 |
| FI / 24 m | 790.1 / 1,406.4 | 535.1 / 898.8 |
| SW / 8 m | 24.8 / 45.9 | 23.3 / 46.5 |
| SW / 24 m | 54.9 / 99.2 | 57.1 / 101.4 |
The moving percentiles use 144 entering tiles at 8 m and 314 at 24 m. The direct static timings ran five pairs per recipe; the full verifier also ran its own five cost pairs per recipe. Moving measurements were not repeated five times.
An earlier broad Core experiment classified every fine tile. All 62,400 SW boxes were unknown; it added 11.7 classifier worker-seconds and about 20 ms of wall time, so that change was reverted. The retained shared mesher path benefits FI with negligible SW classifier work and no SW sample-count change. The source-stamp helper now hashes nested `Standalone/Source/VoxelForge` sources, fixing the previous blind spot that let a mesher rebuild carry an L51 stamp.
## Proofs, hashes, version, and tiers
The brute-force uniform-block audit checked 27,727 blocks and 36,904,637 samples under both Clang and GCC: zero fallbacks, unknowns, or violations. Clang/GCC W1/W8/W12, random-camera sweeps, and the 30-cell matrix passed. Fast, medium, and full also passed capture, walkability, passage, transition, hole, and ring gates.
Owner full-detail signatures match L51 for both recipes (density / mesh / payload / topology):
- FI: `1feaa7bd5d80b60d / a818654be89790ac / 370ff80451105a39 / 06e507557aa70173`
- SW: `ace56c8f205238ad / a9c603a1248de6b8 / ea55f1f4fcc6a894 / 5ea7bc78a1b678ae`
No geometry hash changed, so `farHorizonVersion` remains **5**; no version bump was needed. Final source stamp and manifest SHA-256: `5c2acf01030d994d5983bc6390664f049b9945a95316da747e131b126c51b041`. Mesher SHA-256: `927e41985b13941fe538da7fc81ae136dabf970218a4bf76a022a81a71e17024`. Stamp-helper SHA-256: `205a4c0fa4d19679f5f2e201e40efcb9aee0b1320991030404d54d781aea31f4`. Final headless binary SHA-256: Clang `cd10303927bdc64179e8c395089ffc86be75d03844170c6be8e1b2ac39b6e152`; GCC `5838c8192900a2930adf162ac360863578509b117ef6f5d5b5b1dbad8b8d5824`. Both builds used FP contraction off.
| Tier | Result | Elapsed |
| --- | --- | ---: |
| Fast | PASS, 136 checks | 150.7 s |
| Medium | PASS, 484 checks | 634.3 s |
| Full | PASS, 1,084 checks; 30/30 cells | 1,421.5 s |
## Failed runs and remaining work
The first full run with GCC's default FP contraction failed SW W1 LOD2 hash stability. Rebuilding GCC with FP contraction off made the Clang/GCC peer matrix pass; the final tier summaries now use identical binary hashes. An initial timing attempt had no timing directory and launched no process. The first mesher build also exposed the source-stamp blind spot; it was discarded and rebuilt after fixing the manifest.
FI remains 5.61 s whole-view with a 1.44 s near ring; shape setup alone is 0.89 s and LOD0 still costs 28.63 worker-seconds. SW remains 1.72 s with a 0.55 s near ring. Reaching about 1 s needs a much cheaper or overlapped shape stage, less LOD0 mixed-surface work, and scheduler/LOD-drain tracing to improve far-tile completion without slowing the near ring.
## Orchestrator review (Claude)
Accepted. The stamp matches the final Clang and GCC builds (`5c2acf01…`). FI whole view 7.08 → 5.61 s (−21%); FI
moving p50 is about −33% at 8 m and 24 m; SW is unchanged. Geometry hashes are identical, so there's no version bump.
The proof audit covered 36.9 M samples with 0 violations. Two process fixes come with it:
- the stamp now covers the standalone mesher copy (`Standalone/Source/VoxelForge`), which had let a mesher rebuild
carry the old stamp;
- GCC must build with FP contraction off, or SW W1 LOD2 diverges. That's the L2 lesson, enforced again.
**Cumulative across L51 and L52: FI 16.74 → 5.61 s.** Next measured bottlenecks: shape setup (0.89 s, serial), LOD0
mixed-surface work, and far-tile drain ordering.
@@ -0,0 +1,129 @@
# VoxelForge R21 — native Core cave generator
**Recommendation:** add `caves` as a versioned, metre-native Core field. Plan its required arrival-to-departure spine before optional rooms and branches. Build room and tunnel floors into the same rounded void fields as their walls, then use conservative intervals from the first slice.
**Scope:** read-only research. No source changes, scripts, tests, builds, or binaries were run.
## 1. Legacy cave generation
`UVoxelGenerator::GetDensityWithParams` is the scalar `TunnelNetwork` field. It starts with solid density, scales Z, warps the room/tunnel query, carves the room graph and shafts, then applies roughness and terrain details, worms, boundary seals, passages, and the XY edge seal. Its internal convention is positive rock; it negates for Marching Cubes. The canonical stack, `BuildTunnelNetworkStack`, has the same stages and selects a fused evaluator; fusion removes operator-dispatch overhead, not the feature and noise work. [GetDensityWithParams](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4041>) · [BuildTunnelNetworkStack](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelDensityOpStack.cpp:9422>)
| Piece | What it computes and per-sample work | Range and route notes |
|---|---|---|
| **Room and tunnel graph** — `BuildChunkCache`, `EvaluateSDFCached` | `BuildChunkCache` hashes rooms and bakes topology, floors, and tunnel chains once per cache window, not once per voxel. Room-neighbor selection is O(N²) in that build, followed by connection, player-fit, and chain work. At a sample, each active room evaluates one primitive SDF; each eligible tunnel can evaluate up to 32 tapered-capsule segments. Each room-floor join adds one capsule SDF. Cache bounds use a wide collect region to keep graph decisions stable across windows. [BuildChunkCache](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelCaveMorphology.cpp:7203>) · [hash-placed rooms](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelCaveMorphology.cpp:7430>) · [EvaluateSDFCached](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelCaveMorphology.cpp:8990>) | Each graph feature has finite bounds when parameters are finite; smooth-union reach must be included. Per-room floor relief costs up to two noise octaves on a relief-column cache miss. Room and feature candidates are distance-culled, but the LOD0 path does not use the spatial index, so per-sample work can still scale with traversed candidates. |
| **Primitive SDFs and joins** | Sphere: `|p-c|-r`; ellipsoid: `(sqrt(Σ((p-c)/r)²)-1)·mean(r)`; capsule: distance to the clamped segment minus radius; rounded box: `length(max(q,0))+min(max(q),0)-round`. The ellipsoid is a pseudo-distance: its zero set is useful, but values are not exact Euclidean distances. Smooth union is `min(a,b)-h³k/6`, `h=max(k-|a-b|,0)/k`; smooth intersection is the dual. [VoxelCaveMorphology.h](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelCaveMorphology.h:78>) · [smooth min/max](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelCaveMorphology.h:166>) | These primitives admit conservative box bounds. The smooth-min correction is bounded by `k/6`; include that margin in feature bounds and intervals. |
| **Cave warp** | Three independent Perlin channels are evaluated per sample by `Perlin3D_x3`. They perturb the room/tunnel query coordinates only; pits, details, and hash-placed features use the unwarped frame. The code comment says “two octaves,” but the implementation is three channels, not a two-octave stack. [warp](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4129>) | Perlin is proven within ±1.5. With `VOXEL_NOISE_SCALE=1.25`, the per-axis displacement bound is `1.875 × abs(CaveWarpStrength)` old voxels. Default strength 16 is a 2 m authored value at the old 0.125 m voxel, but requires up to 3.75 m of conservative margin per axis. |
| **Pits and chimneys, then SDF carve** | Each active shaft uses one XY square root and a finite tapered vertical profile; it is smooth-minned into the graph. The carve then converts the graph SDF to density: within the blend band, `f=smoothstep(clamp((k-SDF)/max(2k,1),0,1))`, and `Density -= 2·BaseDensity·f`. This is a density field, not a metre-valued signed distance. [pit/chimney SDFs](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4427>) · [carve conversion](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4499>) | Shafts have finite support and bounded blend correction. Density amplitude is bounded for finite parameters, but the conversion does not preserve metric SDF magnitude. |
| **Wall roughness** | Runs near the surface. At LOD0, fBM or ridged uses 3 main plus 2 fine Perlin octaves; Mixed doubles those to 10. Optional domain warp adds 3 Perlin evaluations. Cellular roughness makes two Cellular3D calls, each probing a 3×3×3 cell neighborhood. Roughness is faded within `2×SurfaceRoughness`; in known cave air it is clamped so it cannot fill rock back in. [roughness](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4558>) | Finite band and amplitude for valid parameters; use proven Perlin/Cellular bounds. The effective octave counts vary with LOD, so the legacy field is not LOD-invariant. |
| **Terrace, lines, ribs, overhang, cliff, scallop** | Terrace adds **two extra SDF evaluations** at Z±1 and optional two-octave noise. Layer lines and ribs use deterministic sine, with no noise or extra SDF. Overhang uses two Perlin octaves. Cliff uses one Perlin call; despite the comment describing finite differences, the implementation uses a noise proxy. Scallop uses one Cellular3D call. [terraces](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4729>) · [lines and ribs](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4826>) · [overhang](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4885>) · [cliff](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4921>) · [scallop](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:4965>) | All have finite support fades and bounded noise for finite parameters. Per-room overrides need per-feature bounds; the generic classifier conservatively gives up in some override cases. |
| **Arches, columns, domes, pinches, floor bias** | Up to 3 capsule SDFs for arches; one XY square root per active column; up to 2 ellipsoid pseudo-SDFs for domes; up to 3 quadratic pinch tests and one square root per active pinch. Floor bias adds a quadratic density offset without noise or SDF work. [arches](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:5001>) · [columns](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:5030>) · [domes](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:5063>) · [pinches](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:5128>) · [floor bias](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:5162>) | Finite if each placement and influence envelope is included in the bounds. These stamps can still change final walkability. |
| **Worm tunnels** | `N1=abs(Perlin(p)×1.25)`. If `N1` is below threshold, evaluate `N2=abs(Perlin(p+(137,259,431))×1.25)`; carve when `W=N1+N2<T` by `Density -= (1-W/T)·WormStrength·NetworkMask`. Thus the common cost is 1 Perlin, rising to 2 for eligible samples. [worm field](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelWormField.cpp:12>) · [worm mask and carve](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:5216>) | Carve amplitude is bounded by `WormStrength`. With `WormNetworkRange>0`, it is confined to a network-local fade, if the network distance bound is conservative. Range 0 gives global support and can create disconnected “confetti”; it is unsuitable for tight tile proofs or route guarantees. |
| **Boundary, passages, edge seal** | Structural operations run after worm carving. Their cost is parameter- and feature-dependent; they add no cave SDF or Perlin work, but are part of the final field and proof. [structural tail](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:5278>) | Their bands, passage mouths, and supports must be included in any native classification proof. |
The visual strengths are the contrast between chambers and corridors, tapered winding edges, soft room/tunnel joins, floor relief, and localized wall detail. The largest legacy costs are variable room/edge candidate walks, repeated square roots and capsule distances, per-sample noise, plus the two extra SDF probes for terraces. Cache construction also has nontrivial graph and player-fit work. I found no unseeded RNG in these cave paths: room and edge choices use deterministic hashes, and the cache key includes seed, parameter fingerprint, layout, and manager lifetime. However, LOD-dependent noise changes the field, and deterministic geometry is not automatically a safe route.
`bTunnelsFlowTowardOrigin` can create a tree toward the origin, but that origin is `(0,0)`—not the actual surface or inter-strate mouth. The code explicitly notes there is no radial road from those mouths. Length limits and origin connection caps can leave disconnected components; when flow is disabled, disconnected clusters are expected. Local player-fit-point selection is not a final capsule flood-fill from arrival to departure. Tunnel floors are wound for some critical steep edges and may receive broad ledges on eligible noncritical edges, but the default 44° “gentle” threshold exceeds the current 15° route gate. [origin and graph construction](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelCaveMorphology.cpp:7531>) · [local room fit](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelCaveMorphology.cpp:7833>) · [tunnel winding](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelCaveMorphology.cpp:8150>)
### `FStrateGenerationParams` fields
The relevant legacy defaults are: `BaseDensity=8`, `VerticalScale=1`; worm frequency `.015`, horizontal bias `3`, threshold `.06`, strength `10`, and network range `64`; room spacing `128`, density `.35`, radii `16–40`, height ratio `.85`, floor cut `.70–.90`, relief strength `4` at frequency `.015`, mouth rise `0` with blend `8`, origin-room radius `48`; tunnel radii `6–8`, density `.4`, max length `360`, warp strength `24`, horizontal bias `.5`, endpoint Z offset `.25`; SDF blend `4`, cave warp `16` at frequency `.015`, roughness `2` at frequency `.1` using FBM, domain warp `0` at `.03`, and floor bias `4`. Tunnel floor terrace step `1.71` is retained for compatibility but ignored by the active evaluator. See the fields in [FStrateGenerationParams](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:372>).
## 2. Native Core field and route
Core already has resolved graph records, but not a native cave field contract or graph planner. `ResolveResolvedCavernGraphV1` validates and copies supplied records; it does not generate the spine. Current validation requires at least one room and edge and checks local identities, parameters, and bounds, but not global graph connectivity or an arrival-to-departure capsule route. [graph validation](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ResolvedLayoutInternal.h:1145>) · [graph resolver](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ResolvedLayoutInternal.h:1294>)
Use an immutable, versioned `FCaveFieldAdapter` bound once per resolved stratum. Evaluate in metres with Core’s sign convention: **negative solid, positive air**. Store feature coordinates and IDs in the resolved snapshot; key all randomness by world seed, strate ID, recipe version, and stable feature ID. Keep the field value identical at the same physical coordinate across LODs. R6’s bound-field contract also requires finite influence bounds and conservative `solid`, `air`, `mixed`, or `unknown` classification. [R6 contract](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research6/REPORT.md:22>) · [current adapter kinds](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:136>)
Let `Dhost` be the native stratum field, with negative rock. Define each room or edge’s void field as negative inside its carved space:
```text
d_ellipsoid(p) = (sqrt(Σ((p_i - c_i)/r_i)^2) - 1) · mean(r)
d_floor(p) = F(x,y) - z
d_room_void = smax(d_ellipsoid, d_floor, w_floor)
d_tube(p) = min over baked segments j of (distance(p, segment_j) - r_j(t))
d_edge_void = smax(d_tube, d_floor, w_floor)
d_cave_void = smin over room and edge voids i of d_i, using w_union
D0 = max(Dhost, -d_cave_void)
```
For a sloped floor, use a grade-normalized half-space locally, `(F-z)/sqrt(1+|∇F|²)`, and keep the profile continuous through room/edge portals. Bake curves into deterministic physical-error-bounded polylines during resolve; the current Core demo searches a cubic Bezier with 17 XY samples and up to 5 Newton iterations per query, and repeats a similar search for the floor cap. [current graph evaluation](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:6189>)
Build the cave graph into the stratum field first. Then apply L42’s boundary bands, shared passage carving, and floor-aware stamps in that order. Give stamps and relief route keepouts, and include them in final route and interval checks. Remove the current trailing hard floor cap: it runs after boundary composition, passages, and stamps, reintroducing a hard intersection at the floor. [current composition order](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:1788>) · [L42 boundary rules](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux42/REPORT.md:1>)
### Bounds and feature queries
For every box, evaluate conservative intervals for the full composed field, outward-rounded:
- **Ellipsoid:** per axis, let `δmin` be distance from the box interval to the center and `δmax=max(|lo-c|,|hi-c|)`. Then `qmin/max=sqrt(Σ(δmin/max/r)²)` and `d` lies in `[(qmin-1)·mean(r), (qmax-1)·mean(r)]`.
- **Rounded room box:** apply interval arithmetic to the existing rounded-box formula, including round radius. A coarse result may be `mixed`; never claim more than the interval proves.
- **Swept edge:** bound each baked segment’s distance-to-box and its radius range. Its signed range is `[distance_min-r_max, distance_max-r_min]`; combine segment ranges with `min` for the tube. Subdivide long or highly curved edges at resolve time to tighten bounds.
- **Floor:** for `F∈[Fmin,Fmax]`, relief `δ∈[-A,A]`, and `z∈[zmin,zmax]`, the unnormalized numerator lies in `[Fmin-A-zmax, Fmax+A-zmin]`; multiply by an outward interval of the positive grade-normalization factor. Subdivide boxes at floor-profile spans.
- **Roughness/warp:** expand the SDF interval by proven displacement amplitude. For cave warp, expand the query box by `1.875·strength` on each axis at legacy scale, or use a separately bounded native warp.
- **Smooth CSG:** use the actual monotone smooth-min/max kernel at the interval endpoints (with outward rounding). The smooth correction is at most `width/6`; include all widths in bounds.
A BVH should be built once from the immutable resolved graph: a top-level tree for rooms, edges, shafts, columns, and bounded detail features, plus a segment tree for long edges. Inflate bounds for blend width, relief, warp, support, and the sample halo. Use deterministic median splits on the longest AABB axis, stable-ID tie breaks, and small leaves; sort candidates by stable ID before CSG folding. Reuse the same feature bounds for sample candidate lists, box intervals, and shape-free checks. Today `BuildCavernFeatureTileList` scans the whole graph per tile; the classifier returns mixed/unknown when graph bounds overlap rather than proving the cave field. [feature tile list](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:3740>) · [box classifier](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:4935>)
**`isTileShapeFree` and interval classification prove different things.** The shape-free gate decides whether a tile may use native-only evaluation at all; its conservative bounds must cover every shape dependency, including route, cave records, floor support, passage supports, stamps, and edits. The final-field interval then determines whether a padded tile is wholly air or solid. Any unsupported contributor or nonfinite bound must yield false/unknown and force evaluation. L44’s six LOD0 mismatches came from omitting support-column envelopes from `isTileShapeFree`; the fix bounded the anchor, route center, bore, wall, flare, and vertical span. [current shape-free check](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:1410>) · [L44 finding](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux44/REPORT.md:3>)
### Walkable backbone
Follow R12/R18: resolve actual arrival and departure mouths first, then plan a grade-limited spine between those mouths before optional room placement. Put at least one room on the spine, with matching edge portals and floor heights. Route through the usable content band; use broad switchbacks when direct grade is too high. Add Poisson rooms, connect accepted Delaunay neighbors with a deterministic spanning tree, then add only feasible loops and branches. Give each decision a stable feature-keyed seed.
Graph connectivity is only the planning constraint. After adding relief, stamps, passages, and boundary bands, run a capsule-swept reachability proof through the final field from arrival to departure, checking support, headroom, and local grade. Use the current gate as a minimum: 0.34 m radius, 1.76 m capsule height, at least 0.30 m overhead, and grade ≤15°. [R18 planner](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research18/REPORT.md:1>) · [L46 reachability evidence](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux46/REPORT.md:1>)
Keep the legacy look selectively: varied ellipsoid/rounded-box rooms, tapered winding corridors, smooth junctions, low-amplitude floor relief, and bounded roughness, ribs, scallops, domes, and shafts. Replace the raw global worm field with bounded hash-seeded spline side branches attached to the spine. Keep noisy detail out of the route clearance band. Avoid global implicit noise with unbounded support.
## 3. Proof coverage and cost
For default sparse rooms—about 16 m spacing, 2–5 m room radii, and 0.75–1 m corridor radius after applying the R8 old-voxel conversion—most padded 8 m tiles in a typical underground view should be solid host rock. **Planning estimate, not measured:** about 75% `AllSolid`, 5% `AllAir`, and 20% mixed, with plausible ranges of 65–85%, 0–10%, and 15–30%. These are tile fractions, not volume fractions; the view direction, graph density, and tile halo can shift them. Target under 1% unknown, with zero false proofs.
The L51 comparison sets the scale: FloatingIslands whole-view time improved from 16.736 s to 7.355 s, while SurfaceWorld measured 1.770 s to 1.756 s. Before the fix, 1,075 of 2,370 nonempty FI tiles (45.36%) were all-air, yet loose bounds yielded no whole-tile skips. After interval changes **along with** X8 reuse and far-shell bounds, the run reported 1,959 air boxes, 18,857 mixed, 240 unknown, and 216 whole-tile skips. A separate audit checked 21,504 uniform blocks and 28.6 million samples with zero violations. The 7.36 s result is not an interval-only speedup. [L51 report](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux51/REPORT.md:1>)
A tight BVH and interval-aware field should make sparse caves a **medium-cost** generator: each mixed sample still evaluates local SDF features, but most distant host-rock boxes can skip cave work. A first implementation might land around **1.5–3 s** for the same whole-view scope; about 1 s is a reasonable optimization target, not a demonstrated estimate. The current Core graph demo is too expensive to serve as evidence: it performs repeated Bezier closest-point searches per edge per density sample, scans feature bounds linearly per tile, and does not yet interval-classify graph interiors. Measure whole-view time, first playable ring, graph-build time, candidate counts, feature evaluations, proof verdicts, and memory separately.
## 4. Floor/wall junction
R16’s construction is the right target: make the bore and floor **one void field**, apply its geometric fillet, carve once, then apply only floor-aware stamps. Do not intersect a bore and floor as separate hard fields and repair the seam afterward. Keep native rock beneath the walk plane; any fallback support should be narrow and included in bounds.
At 25 cm LOD0 spacing (`h`), R16 recommends geometric fillet radius `R≥2h=0.5 m`, preferably `R≈3h=0.75 m`. For this cubic smooth-max kernel, the local radius for two perpendicular unit half-spaces is approximately `R≈w/(2√2)`. That means `w≈1.41 m` for the minimum and `w≈2.12 m` for the preferred fillet. L46’s 0.30 m smooth width corresponds to only about 0.106 m, or 0.42 voxel, in that simplified cross-section. [R16 sizing and composition](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research16/REPORT.md:1>) · [smooth-max kernel](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelCaveMorphology.h:177>) · [L46 demo dimensions](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux46/REPORT.md:1>)
This construction removes the hard, off-grid crease that produced the L47–L48 wall-foot teeth and gives Marching Cubes a smoother field across the junction. It does **not** guarantee alias-free output at every grid phase: R16 explicitly treats `2h–3h` as a practical sampling rule, not a proof. The field must stay LOD-invariant, so do not grow the fillet with LOD; use feature-aware sampling/filtering and audit phase shifts and transitions. L49’s one-void-before-stamps idea is a proposal; there is no validated L49 report in this checkout, and current Core code still runs a trailing floor cap. [L47 wall-foot defect](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux47/REPORT.md:1>) · [L48 whole-surface failure](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux48/REPORT.md:1>) · [current cap order](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:1788>)
## 5. Prior art
- **Minecraft cheese, spaghetti, noodle caves:** Mojang describes large cheese chambers, long winding spaghetti tunnels, and thinner, more claustrophobic noodles. The versioned data is a density expression, not a route graph. For example, cheese combines `4·L² + clamp(.27+C,-1,1) + clamp(1.5-.64·S,0,.5)` from cave-layer, cave-cheese, and sloped-cheese noise. The 1.20.3 spaghetti field uses rarity-scaled noise: `clamp(max(weirdScaled(R,N1), weirdScaled(R,N2))-.0765-.0115·T,-1,1)`. Noodles gate on a vertical range and combine a noise threshold with thickness and ridge terms. These formulas create strong scale families but provide no explicit arrival-to-exit walkability proof. [Mojang’s cave overview](https://www.minecraft.net/de-de/article/caves---cliffs--part-ii-out-today-java) · [1.21.1 overworld density](https://mcasset.cloud/1.21.1/data/minecraft/worldgen/noise_settings/overworld.json) · [1.20.3 cave density functions](https://mcasset.cloud/1.20.3-pre2/data/minecraft/worldgen/density_function/overworld/caves/_all.json)
- **Deep Rock Galactic:** 119 hand-built room templates and 10 biomes are assembled as rooms connected by narrow, winding tunnels; templates are warped, mirrored, or overlapped and receive biome-specific surface noise and debris. The cave seed is quick, but the developers say the full cave is assembled during the Drop Pod descent. Designers play through randomized rooms with different classes and reject layouts that prove unfun or unplayable. That is useful human traversal testing, not a formal capsule proof or a published full-build latency. [Ghost Ship Games, “Below Decks: Cave generation”](https://store.steampowered.com/news/posts/?appgroupname=Deep+Rock+Galactic&appids=548430&enddate=1730296516&feed=steam_community_announcements)
- **No Man’s Sky:** the Foundation update says caves are generated at lower terrain LODs to reduce visual differences at lower detail, alongside smaller features nearer the player and terrain cost/priority changes. This informs LOD consistency and streaming; the notes provide no route proof or directly comparable timing. [Foundation Update](https://www.nomanssky.com/foundation-update/)
- **Astroneer:** layered noise generates terrain and objects on the fly without needing neighboring planet parts or whole-planet load-time generation; cave layers are separate authoring layers. That supports local evaluation but provides no explicit walkability guarantee or comparable cave-generation time. [Unreal Engine interview](https://www.unrealengine.com/spotlights/how-system-era-softworks-leveraged-ue4-to-create-astroneer-s-wonderful-universe)
- **Graph and tunnel papers:** Santamaría-Ibirika et al. use POIs, Voronoi/Delaunay neighborhoods, relationship constraints, and path search; unreachable branches are discarded. Their 2014 evaluation on an Intel i7-950 reports roughly 10.7–11.6 s total, dominated by diagram construction, with the cave-generation phase itself at 52–877 ms. It is useful topology evidence, not a Core performance target. Mark et al. use an L-system room/tunnel skeleton, connect some ends with curl-noise paths, and carve noise-warped metaballs; point spacing no greater than the minimum metaball diameter ensures tunnel connection, but also prevents very tight cracks. Their paper claims a real-time GPU pipeline without a directly comparable latency figure. Paris et al. derive a graph-like karst skeleton from anisotropic shortest paths and geological fields, then build tunnel SDFs with blending and warping; shortest-path plus skeleton construction is reported under a second, nearest-neighbor synthesis up to 3 s, and construction-tree generation a few seconds in the worst case. None of these reported methods supplies the final VoxelForge capsule-clearance proof; that comparison is an inference from the described algorithms. [Santamaría-Ibirika et al.](https://santosgrueiro.com/papers/2014/2014-Santam-Cave.pdf) · [Mark et al.](https://lucris.lub.lu.se/ws/files/6067634/5464988.pdf) · [Paris et al.](https://diglib.cgv.tugraz.at/items/a4e7985c-af52-4b55-bd1a-eeeff6c47422)
## 6. Migration and legacy assets
R8 identifies the legacy owner voxel as 0.125 m; Core defaults to 0.25 m. Convert old spatial values with `metres=n×0.125`; convert frequencies with `cycles/m=oldFrequency/0.125`. Thus the old runtime defaults become: room spacing 16 m, room radii 2–5 m, origin radius 6 m, tunnel radius 0.75–1 m, max tunnel length 45 m, and SDF blend 0.5 m. Worm network range 64 becomes 8 m; roughness 2 becomes 0.25 m. Cave warp strength 16 is 2 m authored, but its proven displacement envelope is larger as described above. [R8 unit rules](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research8/REPORT.md:1>)
Do not scale density amplitudes such as `BaseDensity`, `WormStrength`, layer depth, or floor bias as lengths. Split old mixed density and spatial parameters into explicit native fields; map only semantics that survive. Preserve `VerticalScale`, probabilities, angles, and noise thresholds as dimensionless settings. Some header comments conflict with the runtime units—for example, the 128-voxel spacing and 360-voxel max length are described there as 32 m and 90 m. Use each world’s captured legacy resolution and layout width, not those comments or current CDO defaults.
Keep old assets working through a versioned `LegacyCaveParamsV1` path with their old reflected values, defaults, evaluator, seed, and captured `CoreVoxelSizeCm`/layout width. Add custom asset-version conversion and redirects/deprecated holding properties; convert DataTable and external CSV/JSON sources too. Keep those assets on the legacy evaluator unless explicitly migrated to the new `caves` recipe. Store recipe/evaluator version in the resolved snapshot and version any new geometry identity. This preserves existing owner assets while making native caves an opt-in migration.
## 7. Verification slices
`verify.py` currently covers SurfaceWorld and FloatingIslands, not `caves`; its L48 whole-surface audit is not wired into the tiers and assumes exactly one room and one edge. The current reachability gate proves arrival-to-chamber only. Generalize the audit and route gate before promoting the graph. [verify.py tiers](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Tools/verify/verify.py:1035>) · [single-room/edge audit](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux48/AuditWholeSurfaceL48.py:143>) · [current reachability gate](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Tools/verify/verify.py:181>)
Estimates below are **post-build** planning estimates. Existing tier baselines are L51: fast 152 s, medium 471 s, full 1,062 s; the L48 whole-surface audit took 162 s. [L51 timings](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux51/REPORT.md:1>) · [L48 timings](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux48/REPORT.md:1>)
| Slice | Exact verification to add | Estimated pass time |
|---|---|---:|
| **1. One mandatory spine, one room, primitive fields and intervals** | Extend `verify.py fast`: retain Clang owner passages, reachability, regression/hash/ore controls, partial LOD0 ring, and random-camera sweep (seed `0x4C3434`, 3 cameras, W1/W8/W12, settings `(3,0,0)`); add the cave recipe, typed graph validation, interval-oracle containment and false-proof controls, and a deliberately broken-route expected failure. Run the generalized L48 whole-surface audit on the corridor, chamber, and full wall-foot at base alignment and ±h/2 grid phases. Capsule-flood-fill actual composed arrival-to-departure path, with buried/blocked negative control. | **7–10 min** |
| **2. Multiple rooms, attached branches, bounded wall detail** | Extend `verify.py medium`: all current Clang passages, reachability, regression and style matrices, transitions/invariants, LOD0–8 rings, capture/mesh audits, ore controls, and 5-camera random sweep with settings `(3,0,0)`, `(2,1,1)`, `(3,2,1)`, W1/W8/W12. Add at least 5 cave seeds, all routes, BVH-vs-brute-force candidate checks, interval negative controls, every room/edge wall-foot and floor phase in the whole-surface audit, and LOD transition samples. | **13–18 min** |
| **3. Loops, BVH tuning, legacy opt-in and promotion** | Extend `verify.py full`: passed medium, GCC supplement, 5-camera/3-setting sweep, 30-cell determinism, and five interleaved whole-view cost pairs. Add Clang/GCC W1/W8/W12 and reordered-input hash parity; route and interval audits across seed matrix; whole-surface and LOD-transition gates for every cave feature. Load/save old owner cave assets and compare non-opted-in output against legacy parity; benchmark the opted-in native recipe in five matched pairs against the previous build, with fixed tiles and seeds. | **25–40 min** |
Current `verify.py` random cameras use seed `0x4C3434`; fast runs 3 cameras at `(3,0,0)`, medium/full run 5 cameras at `(3,0,0)`, `(2,1,1)`, and `(3,2,1)` across W1/W8/W12. Whole-surface phase sweeps and departure reachability are new gates, not existing ones. No verification tier was run for this report.
**Owner question:** should existing `TunnelNetwork` assets remain on the legacy evaluator until explicitly opted into the versioned native `caves` recipe? My recommendation is yes.
@@ -0,0 +1,53 @@
# R21 — Native Core Caves
**Finding:** Core already stores and evaluates resolved cavern room and edge records, but it has no native `Caves` stratum kind or graph planner. Those records currently carve over another generator’s field. A native cave needs its own bound field, resolved capabilities and interval classifier. [Core field binding](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:292>) · [cavern records](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h:764>) · [R20 inventory](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research20/REPORT.md:5>)
## Legacy inventory
| Path | What it does |
|---|---|
| [`UVoxelGenerator::VF_BuildOpStackForChunk`, `GetDensityAt`, `GetDensityWithParams`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:2586>) | Routes TunnelNetwork and Underwater into the cave stack. The legacy field starts as rock, carves rooms and tunnels, then applies boundary and passage work. `GetDensityAt` retains manager routing and thread-local chunk caches; it is not an immutable bound Core field. Its density sign convention is opposite Core’s. |
| [`BuildTunnelNetworkStack`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelDensityOpStack.cpp:9422>) | Builds the constant-rock source, cached room graph and SDF carve; then optional near-wall roughness, terraces, layer lines, ribbing, overhangs, cliffs, scallops, arches, columns, domes, pinch and floor bias; then worm carving and structural seals. The production route also has a fused evaluator. |
| [`BuildChunkCache`, `EvaluateSDFCached`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelCaveMorphology.cpp:7203>) | Resolves seeded rooms, corridors, floor profiles and morphology features into a per-window cache, then evaluates candidate room/tunnel SDFs. Room shapes are pre-baked; local relief and smooth room/tunnel joins provide much of the natural look. |
| [`VoxelWormField::Evaluate`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelWormField.cpp:77>) | Uses two offset Perlin fields; their intersecting low-value sheets create winding worm cuts and organic braids. |
The legacy defaults combine room spacing and density, variable room radii, curved corridors, vertical bias, cave warp, floor relief and roughness. For example, the authored `RoomSpacing=128` is 16 m at the 12.5 cm owner-voxel scale; some header comments describe it as 32 m. Convert from the recorded legacy units, not those comments. [Params](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:372>) · [R8 unit audit](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research8/REPORT.md:7>)
The cave field is deliberately seeded: room placement and graph choices use hashes, and worm noise uses seed offsets. I found no unseeded random stream in these evaluators. The remaining determinism risk is unproven bitwise parity across compilers and floating-point paths, not an observed global-random dependency. The bigger gameplay gap is route semantics: the legacy graph is rooted around the origin, not planned between resolved arrival and departure mouths. Frontier rooms may fail to bridge back to the origin; optional worm cuts can also create space outside the required route. Its default 44° floor threshold and climbable ledges do not establish the project’s 15° capsule-swept route guarantee. [Legacy graph rules](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelCaveMorphology.cpp:7532>) · [Core planner proposal](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research18/REPORT.md:7>)
The archived 128³ legacy TunnelNetwork profile measured about **591 ms** to fill 2.25 million density samples; its separate classifier route averaged **2.06 s**, with 479,872 boxes classified wholly Mixed and none wholly Air. This is historical Unreal-path evidence, not a Core benchmark. It shows why cave intervals must be designed alongside the field. [Profiler results](</home/jahni/VoxelM/Plugins/VoxelForge/Docs/archive/PERF-ATTRIBUTION.md:45>)
## Native design
Add a native cave evaluator to the L40 bound-field table and resolve cave floor and passage behavior as L42 capabilities. Bind immutable, metre-authored graph records once; keep manager lookups and route selection out of sample evaluation. The existing graph resolver derives seeds and bounds for supplied records but does not place rooms or find routes. Plan after passages and stamps resolve, before layout finalization. [R12 field and capability design](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research12/REPORT.md:21>) · [current resolver](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ResolvedLayoutInternal.h:1581>) · [Paris et al., cave skeletons and SDF construction](https://doi.org/10.1111/cgf.14420)
Build a mandatory spine first between the **resolved arrival and departure mouths**, with ≤15° maximum local grade, capsule clearance, headroom and support checked on the final composed field. Put at least one room on that route. If the resolved bounds and mouth heights do not admit a feasible route, reject that plan. Graph connectivity alone is not a walkability proof. [R18 route plan](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research18/REPORT.md:9>)
Keep legacy room motifs, curved passages, bounded wall roughness and floor relief. Preserve the worm look through seeded, bounded meandering side edges attached to the graph. Do not use unrestricted intersecting noise sheets as the backbone. Add optional pits, chimneys, columns and other morphology only as bounded features after the required route passes.
For floors, make the bore, sloped walk plane and rounded wall-foot one void shape. Avoid a hard cap after passages and stamps; reserve route keepouts and validate the complete composition so later features cannot re-block it. L47–L48 show that floor rails missed wall-foot teeth and the rounded-cap trial still failed its whole-surface audit. Use a geometric fillet of at least `2h`, preferably `3h`, with coarse-LOD filtering or feature-aware handling. [L47–L49 evidence and proposal](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux48/REPORT.md:18>) · [R16 fillet guidance](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research16/REPORT.md:3>)
## Performance and proof plan
Build a stable-ID BVH over expanded room bounds and subdivided Bezier-edge hulls. Include blend width, roughness reach, floor support, junctions and any detail displacement in every bound. The current graph tile list scans feature bounds linearly, and any box touching a cavern record is classified Mixed. A BVH will reduce candidates, but only conservative intervals can prove air or solid. [Graph candidate scan](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:3740>) · [current graph classification](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:5012>)
Provide outward-rounded intervals from day one: analytic ranges for room shapes and floor profiles; conservative subdivided bounds for curves; explicit amplitude budgets for smooth blends and noise. Evaluate roughness only in a bounded band around cave surfaces. Outside finite cave bounds, prove solid; prove uniform air or solid where the whole box permits it; leave boxes Mixed when the field interval crosses zero. Cave-field audits should have no unknown boxes or unsound skips. Extend the `isTileShapeFree` influence envelope so every structural feature is represented or forces fallback; L44 found that omitting passage support columns caused worker-count-dependent output. [L44 finding](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux44/REPORT.md:5>) · [L51 interval lesson](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux51/REPORT.md:39>)
Current whole-view references are **1.756 s for SurfaceWorld** and **7.355 s for FloatingIslands**. A sparse, bounded cave graph is a medium-cost target: likely above SurfaceWorld because it has volumetric interior surfaces, but below FloatingIslands if intervals prune uniform rock and detail stays narrow-band. Dense topology or weak proofs could approach or exceed FI cost. **About one second is plausible but unmeasured.** The L51 timings are headless whole-view results, not VR frame rates. [L51 timings](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux51/REPORT.md:9>)
## Migration
Map legacy lengths and displacements in `FStrateGenerationParams` using **0.125 m per owner voxel**; convert inverse-owner-voxel frequencies to cycles per metre by multiplying by eight. Keep ratios, probabilities and degrees unitless. Preserve `TunnelFloorTerraceStepHeight` as deprecated compatibility data; it is ignored. Convert to integer micrometres once at the adapter boundary. This preserves physical scale, not legacy topology or output hashes. [R8 mapping](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research8/REPORT.md:15>)
Keep existing assets on the legacy route by default. Add native caves as explicit opt-in data, preferably an optional per-strate `CoreGeometry` block with stable IDs and metre fields, and version the serialized asset conversion. The existing strate asset already has a custom-version hook; keep old fields loadable through migration and only change generator identity for opted-in assets. [R17 asset and adapter flow](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research17/REPORT.md:5>) · [current automatic Core plans](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp:874>)
## Slices and verification
1. **Bound field and fixed spine:** one room and one edge joined to resolved mouths; add cave intervals, BVH and final-field route checks. Generalize the L48 whole-surface audit and wire it into `verify.py`; currently the verifier’s cavern fixture expects one room and one edge, and L48 says its audit is not wired into the tiers. [Verifier fixture](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Tools/verify/verify.py:181>) · [L48 audit status](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux48/REPORT.md:54>)
2. **Planner:** multiple rooms and a deterministic mouth-to-mouth backbone; audit graph connectivity, capsule fit, support, headroom, grade, seals and full-surface defects across a seed matrix.
3. **Optional morphology:** bounded worm-like side branches, loops, roughness and floor styles; keep the same whole-surface gate and check LOD transitions.
4. **Asset and runtime integration:** versioned asset migration, Unreal preview/collision/cook, worker/compiler determinism, and paired graph-off/on timings. `verify.py` can gate Core fixtures; the Unreal adapter also needs its UHT/UBT and owner-asset checks.
**Owner question:** Should converted legacy settings preserve broad style and scale while the native graph topology is versioned as new? That is the recommended default; close visual parity would need a separate target.
Read-only research only. No scripts, tests, builds, binaries or tree changes were made.