test: the slab test proved less than it claimed — add the pass that varies CeilingRoughness

SlabEquivalence came back green (FlatPlain 36/60 and CrystalChamber 40/60 tiles
proved uniform, vs zero for today's ClassifyTile; 52/20000 ULP-scale diffs, 0
isosurface crossings). But both archetypes reported the SAME 52 and the same worst
delta, which pointed at the fixture: FTestWorld::Build sets only GeneratorType, so
both slots carry DEFAULT slab params.

So the two passes were the same configuration at two depths. The test claimed to
demonstrate "one op, two archetypes" while never varying CeilingRoughness — the
only field that actually distinguishes CrystalChamber. The differing tile counts
come from the slots' Z ranges, not from the archetypes.

Third pass added: CrystalChamber(tuned), CeilingRoughness 6 -> 20, rougher floor,
3x the columns. It varies what matters and doubles as the worst case for the
ClassifyBox amplitude bounds — a large CeilingRoughness widens the ceiling band and
makes the FloorSurface + 2 clamp far more likely to bind, which is precisely where
a false verdict would be a hole. The default params were too gentle to stress it.

The ULP residue is left alone: deterministic, 0 isosurface crossings, and the same
shape C10 already cost six builds to prove not worth chasing.

UNVERIFIED: the third pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 15:34:16 +02:00
parent 644339def5
commit 85993199fb
2 changed files with 116 additions and 16 deletions
+48
View File
@@ -942,3 +942,51 @@ a FlatPlain or CrystalChamber strate for the visual A/B. Expect the floor/ceilin
changed from the Z-term removal; the question is whether it still reads as the same *kind* of place.
---
## 2026-07-27 — SlabEquivalence GREEN. The §3.1 payoff is measured: 36 and 40 of 60 tiles.
```
FlatPlain box verdicts over 60 tiles: 36 proved uniform, 24 Mixed
CrystalChamber box verdicts over 60 tiles: 40 proved uniform, 20 Mixed
both: 52 of 20000 samples differ, ALL at ULP scale, 0 cross the isosurface
```
**This is the number the whole §3.1 question was about, and it is better than Maze's.** Maze proved
23 of 60; the slab archetypes prove **36 and 40** — 60-67 % of tiles, where `ClassifyTile` proves
**zero** for these two today. The reason is structural rather than lucky: a slab strate is mostly
solid rock below its floor, and now that both surfaces are XY-pure their Z bands are known exactly,
so "this tile is entirely below the floor band" is a comparison rather than a sample.
**The ULP residue is the known C10 floor** — 52/20000, 0 isosurface crossings, worst delta 2⁻¹⁸
(exactly 1 ULP at a density magnitude of ~32). **Not investigated, on purpose.** Same shape as C10:
deterministic, ULP-scale, zero consequence, and six builds were already spent proving that shape is
not worth chasing.
### The output exposed a real weakness in my own test — fixed
Both archetypes reported **the same 52 and the same worst delta**. The explanation is in the
fixture: `FTestWorld::Build` sets only `GeneratorType`, so **FlatPlain and CrystalChamber both get
DEFAULT `FSlabGenerationParams`.** They are the same configuration at two depths.
So the test's claim #2 was overstated. It demonstrated "the slab stack works at two depths", not
"one op serves two archetypes with different defaults" — **`CeilingRoughness`, the one field that
actually distinguishes CrystalChamber, was never varied.** The differing tile counts (36 vs 40)
come from the two slots' Z ranges, not from the archetypes differing.
**A third pass added: `CrystalChamber(tuned)`**`CeilingRoughness` 6 → 20, plus a rougher floor
and 3× the columns. It varies what actually matters, and it is deliberately the **worst case for
`ClassifyBox`**: a large `CeilingRoughness` widens the ceiling band and makes the
`Max(CeilZ - noise, FloorSurface + 2)` clamp far more likely to bind, which is exactly where a
false verdict — a HOLE — would appear. The default params were too gentle to stress that bound.
**UNVERIFIED:** the third pass. Its box-verdict brute force is the part that matters; expect fewer
tiles proved uniform than the gentle passes (wider bands ⇒ more Mixed), and **zero** unsound
verdicts. If `NumUnsound > 0` here, the `CeilHi` bound in `FSlabVoidSource::ClassifyBox` is the
first suspect, not the noise contract.
**Next single action:** rebuild, confirm the tuned pass is green, then the visual A/B on a FlatPlain
or CrystalChamber strate. The floor/ceiling shape WILL differ from before (the Z-term removal); the
question is whether it still reads as the same kind of place. Then `SurfaceWorld`
(OPSTACK-DECOMPOSITION §5) — biggest payoff, most care.
---
@@ -10,6 +10,11 @@
// 2. UN OPÉRATEUR, DEUX ARCHÉTYPES — la MÊME pile est vérifiée contre FlatPlain ET
// CrystalChamber. `GetSlabDensity` ne les distingue par aucun branchement ; si la pile a
// besoin d'en faire un, la fusion est fausse et ce test le dit.
// ⚠️ La fixture ne règle que `GeneratorType`, donc les deux slots portent des params PAR
// DÉFAUT : à eux seuls ils exécutent la même configuration à deux profondeurs. C'est la
// TROISIÈME passe (`CrystalChamber(tuned)`, `CeilingRoughness` 6 → 20) qui fait réellement
// varier ce qui distingue les deux archétypes — et qui sert en même temps de pire cas aux
// bornes d'amplitude de `ClassifyBox`. Voir le bloc en bas de fichier.
// 3. LE VERDICT DE BOÎTE — et c'est ici que §3.1 se paie. `ClassifyTile` prouve ZÉRO tuile pour
// FlatPlain et CrystalChamber aujourd'hui. Depuis que les deux surfaces sont XY-PURES, leurs
// bornes en Z sont connues exactement (contrat [-1,1] de FBM), donc toute tuile entièrement
@@ -76,21 +81,10 @@ bool FVoxelForgeOpStackSlabTest::RunTest(const FString& Parameters)
//=========================================================================
// Exécutée à l'identique sur FlatPlain et CrystalChamber. Si les deux passent avec la MÊME
// pile et la MÊME fabrique, la fusion des deux archétypes est démontrée plutôt qu'affirmée.
auto RunForSlot = [&](int32 SlotIndex, const TCHAR* SlotName)
auto RunBattery = [&](const FSlabGenerationParams& SlabParams,
int32 TopVoxelZ, int32 BottomVoxelZ,
int32 SlotIndex, const TCHAR* SlotName)
{
int32 TopVoxelZ = 0, BottomVoxelZ = 0;
if (!World.GetSlotVoxelZRange(SlotIndex, TopVoxelZ, BottomVoxelZ))
{
AddError(FString::Printf(
TEXT("The fixture layout has no %s slot. Check FTestWorld::Build's Archetypes[] ")
TEXT("against FTestWorld::Slot%s."), SlotName, SlotName));
return;
}
const int32 MidChunkZ = ((TopVoxelZ + BottomVoxelZ) / 2) / CHUNK_SIZE;
const FSlabGenerationParams SlabParams =
World.StrateManager->GetSlabParamsForChunk(FIntVector(0, 0, MidChunkZ));
// `GetSlabDensity` court-circuite sur une strate dégénérée (`return 1.0f`). Cette garde
// appartient à la fonction d'archétype, pas à un opérateur ; la pile suppose une strate
// valide, et `GetDensityAt` retombe sur le `switch` dans ce cas.
@@ -303,8 +297,66 @@ bool FVoxelForgeOpStackSlabTest::RunTest(const FString& Parameters)
}
};
RunForSlot(FTestWorld::SlotFlatPlain, TEXT("FlatPlain"));
RunForSlot(FTestWorld::SlotCrystalChamber, TEXT("CrystalChamber"));
//=========================================================================
// LES TROIS PASSES
//=========================================================================
auto ResolveSlot = [&](int32 SlotIndex, const TCHAR* SlotName,
FSlabGenerationParams& OutParams, int32& OutTop, int32& OutBottom) -> bool
{
if (!World.GetSlotVoxelZRange(SlotIndex, OutTop, OutBottom))
{
AddError(FString::Printf(
TEXT("The fixture layout has no %s slot. Check FTestWorld::Build's Archetypes[] ")
TEXT("against FTestWorld::Slot%s."), SlotName, SlotName));
return false;
}
const int32 MidChunkZ = ((OutTop + OutBottom) / 2) / CHUNK_SIZE;
OutParams = World.StrateManager->GetSlabParamsForChunk(FIntVector(0, 0, MidChunkZ));
return true;
};
FSlabGenerationParams FlatParams, CrystalParams;
int32 FlatTop = 0, FlatBottom = 0, CrystalTop = 0, CrystalBottom = 0;
if (ResolveSlot(FTestWorld::SlotFlatPlain, TEXT("FlatPlain"), FlatParams, FlatTop, FlatBottom))
{
RunBattery(FlatParams, FlatTop, FlatBottom, FTestWorld::SlotFlatPlain, TEXT("FlatPlain"));
}
if (ResolveSlot(FTestWorld::SlotCrystalChamber, TEXT("CrystalChamber"),
CrystalParams, CrystalTop, CrystalBottom))
{
RunBattery(CrystalParams, CrystalTop, CrystalBottom,
FTestWorld::SlotCrystalChamber, TEXT("CrystalChamber"));
//=====================================================================
// LA PASSE QUI FAIT VRAIMENT LA DÉMONSTRATION
//=====================================================================
// ⚠️ La fixture ne règle QUE `GeneratorType` : FlatPlain et CrystalChamber y reçoivent des
// `FSlabGenerationParams` PAR DÉFAUT, donc identiques. Les deux passes ci-dessus exécutent
// en réalité la même configuration à deux profondeurs — ce qui est un test utile, mais qui
// ne démontre PAS « un opérateur, deux jeux de défauts » : `CeilingRoughness`, la seule
// chose qui distingue réellement CrystalChamber, n'y varie jamais.
//
// Cette passe-ci fait varier ce qui compte, et elle est aussi le PIRE CAS pour les bornes
// d'amplitude de `ClassifyBox` : un `CeilingRoughness` élevé élargit la bande du plafond et
// rend le clamp `Max(CeilZ - bruit, FloorSurface + 2)` beaucoup plus susceptible de mordre.
// Si un verdict de boîte est faux quelque part, c'est ici qu'il apparaît.
//
// The fixture only sets GeneratorType, so both slots get DEFAULT slab params — the two
// passes above are the same configuration at two depths. This pass varies what actually
// distinguishes CrystalChamber, and is simultaneously the worst case for the ClassifyBox
// amplitude bounds: a large CeilingRoughness widens the ceiling band and makes the
// FloorSurface + 2 clamp far more likely to bind.
FSlabGenerationParams Tuned = CrystalParams;
Tuned.CeilingRoughness = 20.0f; // vs 6.0 par défaut — de vraies stalactites
Tuned.CeilingRoughnessFrequency = 0.09f;
Tuned.FloorRoughness = 9.0f;
Tuned.ColumnDensity = 0.25f; // beaucoup plus de colonnes ⇒ FillOnly plus souvent
Tuned.ColumnMaxRadius = 11.0f;
RunBattery(Tuned, CrystalTop, CrystalBottom, 64, TEXT("CrystalChamber(tuned)"));
}
return true;
}