The "a sampler must cover at least one period" bug was found in the tunnel test (+/-32 vs RoomSpacing 80) and again in the shaft test (+/-48 vs ShaftSpacing 55). Both were fixed. The class was not: three box-verdict scans still ran the original RandRange(-6,6)*Extent at Step 1 / Cells 8, i.e. +/-48 voxels. Island +/-48 vs IslandSpacing 95 = 0.51 periods (worse than either fix) Slab +/-48 vs ColumnSpacing 60 = 0.80 periods Maze +/-48 vs CellSize 40 = 1.20 periods Verified the fixture does not override any of those spacings, so the header defaults are what these tests really ran against. This matters now specifically:7dbdf51changed the island and maze verdicts andeaa44bfchanged the slab column verdict, and these are the tests that guard them. Widening before the build is what makes that build's green mean anything. The rule the fixed tests already encoded, now explicit: Extent = 8, so SpanCells = the lattice spacing gives exactly 8 periods -- which is where the shaft test's 55 came from. Island 95, Slab 60, Maze 40, each printing the ratio computed LIVE from the params struct so a future narrowing cannot be silent. No assertion, tolerance, AddError, brute-force loop, seed, Step, Cells or tile count was touched: this changes what the measurement looks at, never what it demands. Proved counts will move (expected); NumUnsound must stay 0. Test-only -- git diff lists three files under Private/Tests/. Not built. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5.4 KiB
Codex task 005 — three box-verdict tile scans sample less than one lattice period
Owner: Codex (Model Luna, xHigh) · Orchestrator: Claude · Branch: experimental
Status: specified, not started
Kind: test coverage. Zero risk to the game — no non-test file may change.
Why this exists
This project has already found and fixed this exact defect twice:
"A sampler must cover at least one period of what it samples. The tunnel test drew tile XY from ±32 voxels with
RoomSpacing 80— it measured the spine hub and called it the world. The shaft test had the identical bug (±48 againstShaftSpacing 55)." —OPSTACK-HANDOFF.md
Both were fixed to SpanCells = 55 ⇒ ±440 voxels, and both now print their own extent in units
of the pattern's period so it cannot silently regress.
Three box-verdict tile scans were never fixed, because the fix was applied where the bug was
noticed rather than to the class. All three still use the original Rng.RandRange(-6, 6) * Extent
with Step = 1, Cells = 8 ⇒ Extent = 8 ⇒ half-extent ±48 voxels:
| test | half-extent | lattice period (default, unchanged by the fixture) | coverage |
|---|---|---|---|
VoxelForgeOpStackIslandTest.cpp ~244 |
±48 | IslandSpacing 95 |
0.51 periods ⛔ worse than either bug already fixed |
VoxelForgeOpStackSlabTest.cpp ~289 |
±48 | ColumnSpacing 60 |
0.80 periods ⛔ |
VoxelForgeOpStackMazeTest.cpp ~276 |
±48 | CellSize 40 |
1.20 periods ⚠️ marginal |
Verified: none of EnableIslandFeatures / the slab tuning / the maze setup overrides the spacing, so
the header defaults are what these tests actually run against.
Why it matters right now, specifically. Two commits just changed the box verdicts these very
tests are supposed to guard — 7dbdf51 (the ExtraReach bound, which touches islands, maze and
shafts) and eaa44bf (the radius envelope, which touches slab columns and shafts). The tests
that would catch a mistake in those changes currently sample about half a lattice cell.
The rule the two fixed tests already encode
Extent = Step * Cells = 8 voxels, so SpanVoxels = SpanCells * 8. To get 8 periods of
half-extent you set:
SpanCells= the lattice spacing (55forShaftSpacing 55— that is where the shaft test's55comes from, and it is not a coincidence).
Apply the same:
| test | SpanCells |
resulting half-extent | periods |
|---|---|---|---|
| Island | 95 |
±760 | 8.0 |
| Slab | 60 |
±480 | 8.0 |
| Maze | 40 |
±320 | 8.0 |
What to build
For each of the three tests, mirror exactly what VoxelForgeOpStackShaftTest.cpp (~212) does:
- Hoist
const int32 SpanCellsandconst int32 SpanVoxels = SpanCells * 8;outside the tile loop — the report needs them andExtentis loop-local. (That scoping slip has already happened once in this file family; the shaft test's comment records it.) - Draw
Rng.RandRange(-SpanCells, SpanCells) * Extentfor X and Y. Leave the Z draw exactly as it is — it is clamped to the strate slot and is not part of this defect. - Extend the existing report line to print
SpanVoxels, the live ratio(float)SpanVoxels / FMath::Max(<the spacing param>, 1.0f), and the spacing itself — so the extent is stated in units of the pattern's own period and a future narrowing is visible.
⚠️ Invariants
- This is NOT "widen until it passes." The comment already in the tunnel test says it best and
the same reasoning applies here: every proved tile is still brute-forced voxel by voxel below, so
a wider sampler that produced a FALSE verdict fails exactly as before. We are changing what the
measurement looks at, not what it demands. Do not touch the brute-force loop, its
tolerance, or any
AddError. - Do not adjust an assertion to accommodate a moved number. Widening will change the proved / Mixed counts — that is the point. If an existing assertion would now fail, report it and stop; do not retune it. ("Don't assert a number you want to improve" is a written lesson here.)
- No file outside
Source/VoxelForge/Private/Tests/may change.git diff --statmust list only those three test files. - Do not change
Step,Cells, the tile count (60), or the RNG seeds — a changed seed makes the before/after incomparable, and comparability is the whole point of touching this now. - Comments are French + English; match the surrounding file.
Acceptance
- Three test files changed, nothing else.
- Each of the three now prints its extent and that extent in periods of its own spacing param.
- After the build, each ratio line reads ≥ 8 periods.
- The proved counts will move. That is expected. What must NOT move:
violations/NumUnsoundstays 0 in all three. If it becomes non-zero, the wider sampler has found a genuine hole that the narrow one was hiding — which would be this task paying for itself immediately, and must be reported loudly rather than tuned away.
Notes for the reviewer (Claude)
- Confirm
SpanCells/SpanVoxelsare outside the tile loop in all three. - Confirm the Z draw is untouched.
- Confirm the ratio is computed live from the params struct, not hardcoded — a hardcoded "8.0 periods" in a format string would be a success message that asserts coverage while measuring nothing, which is a named failure mode in this project.