feat(composer): lateral REGIONS -- built, measured, and gated OFF

Jahni's own idea: "archetype could even mix within the same strate at some
point, tunnel network leading to a big chamber". An archetype becomes a
spatial REGION inside a strate, not a label on it.

Built by extending the mechanism that already exists rather than inventing
one: the stack blends VERTICALLY between strates, so regions blend the same
way LATERALLY. Deterministic jittered Voronoi sites on a 256-voxel lattice,
1-3 regions per strate from (seed, strateIndex), 24-voxel symmetric blend
band, per-chunk lookup cache. Parameters are never blended -- a shaft's
ShaftSpacing means nothing to a slab -- only the resulting DENSITY is.
Structural posts stay global and unconditional, so an invented region
cannot switch off the primordial law.

  single-region path   BIT-IDENTICAL to legacy, 256 probes, native + recipe
  cross-boundary boxes 20 of 40, 199,800 voxels brute-forced, 0 VIOLATIONS
                       (every seam-spanning box correctly returned Mixed)
  blend-band cost      663 ns vs 338 ns per voxel, over 16.0% of voxels
                       ~1.15x field-weighted
  survival             16/16 valid, non-vacuous, largest share >= .50

⛔ AND IT IS GATED OFF, because the seam breaks the one law that matters:
arrival -> departure across a region boundary passes 9/16 seeds, against
11/16 same-region controls in the same harness. Density blending alone does
not guarantee a player can cross a seam. A strate that mixes archetypes and
strands you at the boundary is strictly worse than one that does not mix.

VF_LateralRegionsAreShippable() returns false and the test ASSERTS THE GATE
IS SHUT while the rate is imperfect -- so flipping it without fixing
connectivity turns the suite red by design. The rate is a warning, not an
assertion: it is the measured state of an off-by-default feature, not a
regression in a live world, and a permanently red suite stops being read,
which is how real failures get missed.

Everything that ships is still hard-guarded: single-region bit-identity,
zero cross-boundary box violations, and every shipping-path law test
untouched.

The fix is an explicit CROSS-SEAM CORRIDOR CONTRACT -- a design decision,
recorded rather than made unilaterally. Codex diagnosed this correctly and
declined to tune the threshold, which was the right call.

Build clean, 29 succeeded / 0 failed, run and verified by me.

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-05 05:06:32 +02:00
1 parent 42e1f0ec8c
commit dd38832bb6
14 files changed
+3040 -48

No files matched your search

@@ -19,6 +19,8 @@ struct FVoxelStrateComposerSlotOverride
FVoxelStrateArchetypeParams Params;
bool bUseRecipe = false;
FVoxelOpStackRecipe Recipe;
bool bUseRegions = false;
FVoxelStrateRegionManifest Regions;
};
static void VF_SetComposerRuntimeBounds(
@@ -286,7 +288,8 @@ const FVoxelStrateComposerSlotOverride* UVoxelStrateManager::FindComposerOverrid
bool UVoxelStrateManager::SetComposerOverrideForStrate(
int32 StrateIndex, int32 CandidateSeed, ECaveGeneratorType Archetype,
const FVoxelStrateArchetypeParams& Params, bool bUseRecipe,
const FVoxelOpStackRecipe* Recipe, FString& OutError)
const FVoxelOpStackRecipe* Recipe, FString& OutError,
const FVoxelStrateRegionManifest* InRegions)
{
OutError.Reset();
@@ -342,6 +345,32 @@ bool UVoxelStrateManager::SetComposerOverrideForStrate(
Override->Recipe = *Recipe;
}
if (InRegions != nullptr && InRegions->RegionCount > 1)
{
if (!InRegions->IsValid())
{
OutError = InRegions->FailureReason.IsEmpty()
? TEXT("The composer returned an invalid lateral region manifest.")
: InRegions->FailureReason;
return false;
}
Override->bUseRegions = true;
Override->Regions = *InRegions;
VF_RekeyStrateRegionManifest(Override->Regions, CandidateSeed, StrateIndex);
Override->Regions.StrateTopWorldZ = (float)(TargetSlot->TopChunkZ + 1) * CHUNK_SIZE;
Override->Regions.StrateBottomWorldZ = (float)TargetSlot->BottomChunkZ * CHUNK_SIZE;
VF_SetStrateArchetypeRuntimeBounds(Override->Params,
Override->Regions.StrateTopWorldZ,
Override->Regions.StrateBottomWorldZ);
for (FVoxelStrateRegion& Region : Override->Regions.Regions)
{
VF_SetStrateArchetypeRuntimeBounds(Region.ArchetypeParams,
Override->Regions.StrateTopWorldZ,
Override->Regions.StrateBottomWorldZ);
}
Override->Regions.bHasGlobalStructuralParams = true;
}
// Only one slot is overridden at a time. Keeping this map small also makes the worker-side
// copy-on-chunk-refetch cheap. Passage geometry is deliberately not regenerated: the layout
// and its passages are unchanged, and this is the same manager state used by the offline
@@ -381,6 +410,25 @@ bool UVoxelStrateManager::GetComposerOverrideForChunk(
return true;
}
bool UVoxelStrateManager::GetComposerRegionOverrideForChunk(
const FIntVector& ChunkCoord, FVoxelStrateRegionManifest& OutRegions) const
{
const int32 SlotIdx = FindSlotIndexForChunkZ(ChunkCoord.Z);
if (SlotIdx < 0)
{
return false;
}
const FVoxelStrateComposerSlotOverride* Override =
FindComposerOverride(StrateLayout[SlotIdx].StrateIndex);
if (Override == nullptr || !Override->bUseRegions)
{
return false;
}
OutRegions = Override->Regions;
return true;
}
bool UVoxelStrateManager::HasComposerRecipeOverride() const
{
for (const TPair<int32, TSharedPtr<FVoxelStrateComposerSlotOverride>>& Pair : ComposerOverrides)