docs: new acceptance bar (peer agreement, not fidelity) — and C1's documented fix is wrong
Jahni: "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, since everyone rebuilds it on multiplayer." Recorded as OPSTACK-PLAN 2.6.1, superseding 2.6's "recognisably the same place". Consequences, each recorded where it will be found: - C10 closed permanently rather than parked: it measures old-path vs new-path agreement and the two never coexist in a shipped world. - The equivalence tests keep their value as PORT-CORRECTNESS checks, not fidelity checks. Isosurface hard-fail stays; ULP grading is diagnostic only. - C9 promoted to top open risk. "Two people share a seed" is exactly what /fp:fast weakens across toolchains, and a Linux dedicated server against Windows clients compiles the density path under opposite float models. FPSemantics = Precise is the fix and the IWYU debt now blocks something that matters. - C1 unblocked: it was deferred only because it re-rolls the world's noise. Then, doing C1's arithmetic before applying its documented one-liner: THE FIX IS WRONG. It bounds SeedF but keeps the * 97.7 multiplier, so the coordinate term still reaches 1.6e6 where the ULP is 0.19 — 9.5x the ~0.02/voxel step. It would have left the bug live for mid-range seeds while closing the ticket. The real fix deletes the multipliers: they only decorrelate the ~40 noise sites, which is a hashing job. VoxelHash::SeedOffset(Seed, Site) gives a site-salted, bounded, final-units offset. Bounding SeedF alone would also funnel every seed through 16384 offsets shared by all sites; per-site salting requires a collision at all ~40 sites instead. The op stack has already inherited the bug via FSlabVoidSource, so it must land in both paths at once — and every further port copies it again. Docs only; the C1 fix is not written. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+68
-2
@@ -75,6 +75,48 @@ Applied consistently at all `SeedF` definitions (`:682`, `:1756`, `:2066`, `:220
|
||||
|
||||
---
|
||||
|
||||
#### ⛔ CORRECTION 2026-07-27 — **the fix above is WRONG. Do not apply it.**
|
||||
|
||||
It bounds `SeedF` but leaves the **multiplier** in place, and the multiplier is where the magnitude
|
||||
comes from. `SeedF · 97.7` with `SeedF ≤ 16383` still reaches **1.6e6**, where the float ULP is:
|
||||
|
||||
| | max coord term | ULP there | vs the ~0.02/voxel coordinate step |
|
||||
|---|---|---|---|
|
||||
| `Seed = 1000` today | 9.8e4 | 0.012 | 0.6× — fine |
|
||||
| **audit's proposed fix, worst case** | **1.6e6** | **0.19** | **9.5× — still lattice-snaps** |
|
||||
|
||||
So the "fix" would have made the bug *less catastrophic* while leaving it live, and — worse —
|
||||
**closed the ticket.** It would have cost a build and a world re-roll to discover that mid-range
|
||||
seeds still degrade. (Found by doing the arithmetic before applying it, not after.)
|
||||
|
||||
**The actual fix: bound the OFFSET, not the seed — i.e. delete the multipliers.**
|
||||
|
||||
The `· 7.3f … · 97.7f` multipliers exist only to decorrelate the ~40 noise call sites from each
|
||||
other. That is a *hashing* job, and hashing does it better: give each site its own offset drawn from
|
||||
the full seed entropy, already in final units, with no multiplier to re-inflate it.
|
||||
|
||||
```cpp
|
||||
// VoxelHash — one definition, so the bound cannot drift per site.
|
||||
// Site-salted, final units, bounded: ULP at 16384 is 0.002 = 10% of a voxel step.
|
||||
FORCEINLINE float SeedOffset(int32 Seed, uint32 Site)
|
||||
{
|
||||
return (float)(Mix((uint32)Seed ^ (Site * 2654435761u)) & 0x3FFF);
|
||||
}
|
||||
```
|
||||
`WorldX * SF + SeedF * 83.1f` → `WorldX * SF + VoxelHash::SeedOffset(Seed, kSiteScallopX)`.
|
||||
|
||||
**Why this is strictly better, not just smaller:** bounding `SeedF` alone would squeeze all seeds
|
||||
through 16384 distinct noise offsets *shared across every site*, so two colliding seeds would give
|
||||
identical noise everywhere. Per-site salting means two seeds must collide at **all** ~40 sites to
|
||||
produce the same world — which is never.
|
||||
|
||||
**Scope:** ~40 call sites, mechanical, plus the same bug now inherited by the operator stack
|
||||
(`FSlabVoidSource::SeedF`, `VoxelDensityOpStack.cpp`). Must land in **both** paths in one change, or
|
||||
the equivalence tests will (correctly) scream. **Cost of delay: every archetype ported copies it
|
||||
again.**
|
||||
|
||||
---
|
||||
|
||||
### C2 — Per-chunk parameter caches have no layout key (stale after live-edit) ⚠️ **real, reproducible**
|
||||
|
||||
`VoxelGenerator.cpp:503-524`:
|
||||
@@ -247,7 +289,22 @@ reachable only with a very large origin room *and* short tunnels. Flagging it be
|
||||
|
||||
---
|
||||
|
||||
### C9 — The build uses `/fp:fast`, and the MP plan assumes bit-reproducible terrain ⚠️ **added 2026-07-27, measured not assumed**
|
||||
### C9 — The build uses `/fp:fast`, and the MP plan assumes bit-reproducible terrain 🔴 **NOW THE TOP OPEN RISK — promoted 2026-07-27**
|
||||
|
||||
> **Why this outranks everything else in this document now.** Jahni stated the actual acceptance bar:
|
||||
> *"only having it 99.99% at worst reproducible if two people share the same seed, since everyone
|
||||
> rebuilds it on multiplayer."* That is **precisely and only** the guarantee this entry says
|
||||
> `/fp:fast` weakens. Fidelity to the old world was dropped as a requirement (`§C10`); peer agreement
|
||||
> replaced it as the single bar. **This entry is now load-bearing.**
|
||||
>
|
||||
> **The concrete case to decide:** a **Linux dedicated server** generating collision geometry and nav
|
||||
> against **Windows clients** compiles the same density code under *opposite* float models
|
||||
> (`ClangToolChain` → precise, `VCToolChain` → `/fp:fast`). That is not a theoretical divergence.
|
||||
> Same-platform peers on the same build are fine.
|
||||
>
|
||||
> **The fix is known and blocked:** `FPSemantics = Precise` on this module, which needs the IWYU debt
|
||||
> cleared first (seven headers compile only via the shared PCH). That debt is now worth clearing on
|
||||
> *this* justification rather than on tidiness.
|
||||
|
||||
Found while chasing a 1-ULP difference between `GetMazeDensity` and its operator-stack port. The
|
||||
difference survived a full bisect down to code that is character-for-character identical, which
|
||||
@@ -332,7 +389,16 @@ risk is real. That is one build, and it settles it.
|
||||
|
||||
---
|
||||
|
||||
### C10 — The op-stack ULP residue: PARKED, with the evidence, 2026-07-27
|
||||
### 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.
|
||||
>
|
||||
> 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.
|
||||
|
||||
**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
|
||||
|
||||
@@ -220,6 +220,38 @@ SIMD noise switch required.
|
||||
|
||||
---
|
||||
|
||||
### 2.6.1 ⚠️ RELAXED FURTHER, 2026-07-27 — resemblance to the old world is NOT a requirement at all
|
||||
|
||||
**Jahni, verbatim:** *"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, since
|
||||
everyone rebuilds it on multiplayer."*
|
||||
|
||||
**This replaces the "recognisably the same place" bar above.** The requirement is not fidelity to the
|
||||
past — it is **agreement between peers in the present**. Restated as the only two properties that
|
||||
now matter:
|
||||
|
||||
| Property | Required? | Enforced by |
|
||||
|---|---|---|
|
||||
| **Same seed ⇒ same world, on every peer** | **YES — this is the whole bar** | `DensityPurity` within a binary; **`§C9`** across binaries/platforms |
|
||||
| Resemblance to the pre-refactor world | **NO** | nothing; freely re-tunable |
|
||||
| Bit-identity with the archetype `switch` | **NO** | nothing; never compare them |
|
||||
|
||||
**What this changes, concretely:**
|
||||
|
||||
1. **`§C10` is closed, not parked.** It measures old-path vs new-path agreement, and the two paths
|
||||
will never both exist in a shipped world. The residue cannot affect anything Jahni requires.
|
||||
2. **The equivalence tests keep their value, but for a different reason.** They are no longer
|
||||
*fidelity* checks; they are **port-correctness** checks — a transcription slip is still a real
|
||||
bug, and comparing against the old function is the cheapest way to catch one. Read them that way.
|
||||
The hard-fail (isosurface crossing) stays; the ULP grading is now diagnostic only.
|
||||
3. **`§C9` is promoted from a footnote to THE risk.** "Two people share a seed" is exactly the
|
||||
guarantee `/fp:fast` weakens across toolchains, and a Linux dedicated server generating collision
|
||||
geometry against Windows clients is the concrete case.
|
||||
4. **Changes that re-roll the world's noise are no longer expensive.** `§C1` in particular was
|
||||
deferred *only* because it forces a re-tune. That objection is gone.
|
||||
|
||||
---
|
||||
|
||||
## 3. The contract
|
||||
|
||||
```cpp
|
||||
|
||||
@@ -1059,3 +1059,63 @@ sub-ULP-of-scale deltas. Then `SurfaceWorld` (§5) — biggest payoff, most care
|
||||
and the exact-lattice `ClassifyTile` bound must both survive the port.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-27 — the acceptance bar changed, and it invalidated the audit's own fix for C1.
|
||||
|
||||
**Jahni:** *"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, since everyone rebuilds it
|
||||
on multiplayer."*
|
||||
|
||||
The bar is **peer agreement in the present**, not fidelity to the past. Recorded as
|
||||
`OPSTACK-PLAN §2.6.1`, which supersedes §2.6's "recognisably the same place".
|
||||
|
||||
**Four consequences, all recorded where they will be found:**
|
||||
|
||||
1. **`§C10` closed permanently, not parked.** It measures old-path vs new-path agreement, and the
|
||||
two paths never coexist in a shipped world. No requirement depends on it.
|
||||
2. **The equivalence tests keep their value for a different reason** — they are **port-correctness**
|
||||
checks (a transcription slip is a real bug), not fidelity checks. Hard-fail on isosurface
|
||||
crossings stays; the ULP grading is now diagnostic only.
|
||||
3. **`§C9` promoted to the top open risk.** "Two people share a seed" is exactly what `/fp:fast`
|
||||
weakens across toolchains, and a **Linux dedicated server** generating collision/nav against
|
||||
Windows clients compiles the same density code under *opposite* float models. The fix
|
||||
(`FPSemantics = Precise`) is blocked behind the IWYU debt, which now has a real justification
|
||||
rather than a tidiness one.
|
||||
4. **`§C1` is unblocked** — it was deferred *only* because it re-rolls the world's noise.
|
||||
|
||||
### ⛔ And then C1's documented fix turned out to be wrong
|
||||
|
||||
Before applying the one-liner the audit has carried since it was written, I did its arithmetic:
|
||||
|
||||
```
|
||||
proposed: SeedF bounded to 16383, multiplier * 97.7 KEPT
|
||||
max coord term = 1.6e6 -> ULP = 0.19 -> 9.5x the ~0.02/voxel step
|
||||
```
|
||||
|
||||
**It bounds the seed but not the offset, and the multiplier is where the magnitude comes from.** The
|
||||
fix would have made the bug less catastrophic while leaving it live for mid-range seeds — and, worse,
|
||||
**closed the ticket**. Cost to discover after applying: one build plus a world re-roll.
|
||||
|
||||
**The real fix is to delete the multipliers.** The `· 7.3f … · 97.7f` factors exist only to
|
||||
decorrelate the ~40 noise sites from each other; that is a hashing job, and hashing does it better:
|
||||
`VoxelHash::SeedOffset(Seed, Site)` returns a **site-salted, bounded, final-units** offset. Bounding
|
||||
`SeedF` alone would also funnel every seed through 16384 offsets *shared by all sites*, so two
|
||||
colliding seeds would give identical noise everywhere; per-site salting means two seeds must collide
|
||||
at **all ~40 sites**, which is never.
|
||||
|
||||
**And the op stack has already inherited the bug** — `FSlabVoidSource::SeedF` is a faithful
|
||||
transcription of `const float SeedF = (float)Seed`. It must be fixed in **both** paths in one change
|
||||
or the equivalence tests will correctly scream. **Every further archetype port copies it again**,
|
||||
which is the argument for doing it before `SurfaceWorld` rather than after.
|
||||
|
||||
Corrected in `AUDIT §C1` with the arithmetic, so the wrong fix cannot be applied later from the doc.
|
||||
|
||||
**UNVERIFIED:** nothing new in code this entry — docs only. The C1 fix itself is NOT written; it
|
||||
needs Jahni's go-ahead on scope (~40 sites, re-rolls every world, one build).
|
||||
|
||||
**Next single action — Jahni's call between two:**
|
||||
- **`§C1`** (recommended first): ~40 mechanical sites + the op stack, one build, kills the
|
||||
highest-severity latent bug before more ports copy it.
|
||||
- **`SurfaceWorld`** (`OPSTACK-DECOMPOSITION §5`): the biggest op-stack payoff, most care.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user