The two biome checks added last commit printed nothing on success, so a passing run
was indistinguishable from a block that never executed — the exact flaw I flagged
twice this session and then wrote myself. Both now report their coverage.
Step 2c closes SurfaceWorld:
- FSurfaceColumnSource takes per-biome params and an OWNED IVoxelBiomeField. Empty
params leaves the original path bit-for-bit unchanged.
- The field is owned by the stack rather than borrowed: the adapter points at
GetDensityAt's thread_local biome context and cache, and the stack is itself
thread_local rebuilt in the same refetch block, so all three live and die together.
Structural ownership beats a convention the next reader has to infer.
- The overhang amp blends across biomes — Lerp(Amp(PD), Amp(PN), W) with slope and
threshold from the dominant only, as ComputeSurfaceColumn does. Interpolating the
slope would be meaningless; it measures the terrain rather than configuring it.
- FGeneratorBiomeField lives in VoxelGenerator.cpp, on the side that knows the
generator. The op sees a capability, never an owner — which is what lets it become
an asset in Phase 3.
- The no-biome guard is removed from UsesOperatorStackForChunk.
Also: the two constructors now delegate to one body with one id counter. The first
draft had two competing counters, one tagged with a high bit to avoid collision,
which is a smell rather than a design.
5 of 8 archetypes ported: Maze, FlatPlain, CrystalChamber, SurfaceWorld.
UNVERIFIED: not compiled.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>