feat: height-space operator family — SurfaceWorld step 1, and C10 is solved

Maze and Slab now report BIT-IDENTICAL: FPSemantics = Precise, set for
cross-platform play, dissolved the ULP residue. Hypothesis 3 had the right
mechanism all along — under /fp:fast the compiler transforms by surrounding
context with no isolable axis, which is exactly why five one-variable experiments
all came back negative. Removing the permission removed the difference. Nobody
solved C10; C9 got fixed for an unrelated reason and C10 fell out of it.

SurfaceWorld step 1 forced an architectural decision. DECOMPOSITION section 5 notes
the height ops operate on Z values rather than density, then lists them as children
of FHeightfieldSource. Writing them made the consequence unavoidable: they do not
fit IVoxelDensityOp. No input Z (they produce one), XY-pure per column rather than
per voxel, and they write neither channel. Forcing them in would need a per-voxel
channel for a column property, or one opaque op — section 2.5's failure mode.

So height space gets its own contract: VoxelHeightOp.h (FVoxelHeightSample with
Height + Relief, IVoxelHeightOp, FVoxelHeightStack) and five ops. Relief is the
original's M — produced by the structural source, consumed by the terrace gate.
Section 0.1 found density needed a second channel; this found terrain needs a
second space.

The type system now forbids for free what AUDIT 6.3 warns about: a height stack
cannot hold Z-dependent data because there is no Z in the signature.

Deliberately staged — this touches nothing on the density path. If height space had
not decomposed cleanly, it shows up here for one test rather than after building the
adapter, the column cache integration and the dispatch on top.

The test runs twice; the second pass is load-bearing because the F20 terrain ops are
off by default, so a defaults-only run leaves all four modifiers untested. It also
brute-forces MaxDisplacement, since a false bound would later be a hole.

ComputeSurfaceTerrainZ moved private -> public for the test, same justification as
GetSlabDensity. Old declaration removed.

UNVERIFIED: not compiled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 16:06:50 +02:00
parent 25df4fceff
commit 921a9fb666
10 changed files with 990 additions and 13 deletions
+26 -7
View File
@@ -391,14 +391,33 @@ risk is real. That is one build, and it settles it.
### C10 — The op-stack ULP residue: ~~PARKED~~ **CLOSED PERMANENTLY**, 2026-07-27
> **⛔ CLOSED, and now provably irrelevant — not merely parked.** Jahni, 2026-07-27: *"I do not need
> your work to be identical or near identical to what I had before, only having it 99.99% at worst
> reproducible if two people share the same seed."* This entry measures **old path vs new path**
> agreement, and the two paths will never both exist in a shipped world. **No requirement anywhere
> in the project depends on this number.** Do not spend another minute on it.
> # ✅ SOLVED, 2026-07-27 — and the cause was `/fp:fast`, exactly as hypothesis 3 said.
>
> What survives is the *method* lesson at the bottom of this entry, and the redirect: the
> reproducibility that IS required is **`§C9`** — across binaries and platforms, not across refactors.
> **Setting `FPSemantics = Precise` (see `§C9`) made both `MazeEquivalence` and `SlabEquivalence`
> report BIT-IDENTICAL.** The residue is gone — not tolerated, *gone*.
>
> **So hypothesis 3 had the right mechanism and every isolation experiment built on it was doomed.**
> Under `/fp:fast` the compiler may reassociate and contract *based on surrounding context*, with
> **no single isolable axis** — which is precisely why five carefully-designed one-variable tests
> (FVector round-trip, transcription, cross-TU, inlining, const-vs-runtime) all came back negative
> while the difference stayed. There was no variable to find. Removing the *permission* removed the
> difference.
>
> ### The lesson, and it is not the one I expected
>
> **The answer arrived for free, from work done for an unrelated reason.** Nobody solved C10; C9 got
> fixed because Jahni wants Linux/Windows cross-play, and C10 fell out of it. Six more builds spent
> hunting would have found nothing, because the thing that resolved it was a *build setting nobody
> was looking at* while I was busy bisecting source code.
>
> **The decision to stop was therefore right on its own terms and right in hindsight** — the
> information was not obtainable by continuing along that path at any price. Park a question whose
> every consequence is measured and benign; it may well be answered later by something else.
>
> **What this changes going forward:** ports can now aim for and *achieve* bit-identity, so the
> equivalence tests are much sharper instruments than they were — any diff at all is now a real
> finding rather than noise to be graded. The ULP-grading machinery in both tests is kept, because it
> is what would tell us if this ever regresses.
**Status: accepted and closed by decision (Jahni), not by explanation.** Do not reopen this without
reading the whole entry — five hypotheses have already been measured and refuted, and re-deriving