The "starting Phase 0.5" entry said the ClassifyTile test self-skips when the
fixture fails to build. It does not -- all four tests fail loudly with a message
identifying it as a fixture failure. Corrected by appending, since the log is
append-only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Phase 0.5 and the Phase 1 skeleton marked WRITTEN / NOT COMPILED (not "done" --
the gates are not met until the tests actually run). Section 8 independent fixes
2, 5 and 6 ticked. Section 9 "Resume here" now says BUILD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Q3: fable-idea F20 phases 1/2 and F18, REVIEW_FINDINGS perf pass 2 and batch 3,
and ARCHITECTURE's biome full-param redesign were all still carrying
"CODE-COMPLETE, PENDING BUILD" markers dated 07-04/-06/-08. Jahni confirmed on
2026-07-26 that everything is built and working (AUDIT-2026-07.md §0), so the
documents were misreporting project state. Ticked with the date they were
ticked, not just the date they were built.
Deliberately NOT ticked: ARCHITECTURE's F6 master material graph. Its C++ half
is built, but the material graph itself is editor-side work that is genuinely
still open, and ticking it would recreate the problem this queue item fixes.
CODEMAP discipline for this batch: new §3.2b (the VoxelDensityOp contract), new
§3.12 (Private/Tests), the EVoxelTileClass move into §3.2, and
FChunkBiomeCache::Invalidate under the biome types row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
All 8 archetypes read line by line and broken into field source / combiners /
detail modifiers / structural post, with every FStrateGenerationParams field
traced to the op that will own it.
Three findings that change the sequencing:
- The op contract needs an SDF channel alongside density. Rooms, pits and
chimneys are SmoothMin'd in SDF space before a single carve, and three of the
four SDF archetypes add roughness to the SDF rather than to density. A
single-channel Eval can only overwrite, which is also why cross-source
SmoothUnion -- 'a maze inside a mountain that looks like it belongs' -- is not
expressible without it. Recommended before the Maze port; not applied, it is
Jahni's call.
- Worm tunnels are why TunnelNetwork can never skip a tile. A fielded 3D-noise
carve with no bounds forces CarveOnly everywhere, killing AllSolid for the
most-used archetype. But its amplitude is trivially bounded by WormStrength,
so a scalar cap recovers deep-rock skipping -- suggests one numeric bound
belongs in Phase 2, not Phase 3 as the plan has it.
- Disturbances already carry lattice bounds that ClassifyTile discards (it only
tests ChasmDensity > 0 strate-wide). Making them ops with real Identity tests
is a tile-skipping win for SurfaceWorld available independently of everything
else.
Q2 param audit: every field claimed except WaterLevelRelative, which is a
render/water property misfiled in the density struct and Lerp'd across strate
boundaries. Also flags three op names that mean different things in the cave and
surface structs and will collide the moment ops become assets.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
VoxelForge.Determinism.LiveEditInvalidation: sample a SurfaceWorld column,
triple the heightfield params, Initialize() again, and require the density to
have MOVED at the same point on the same thread.
The edit is chosen so it does NOT move the strate — StrateBottomWorldZ, and
therefore StrateKey, the seed and every chunk coord stay identical. The layout
version is the only thing that changes, so the test fails on the pre-fix code
and can only pass because the version is now part of the key.
Then re-checks purity on the edited world: a half-warm cache after invalidation
would show up as order dependence.
UNVERIFIED: not compiled, not run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Five caches under the density path were keyed on ChunkCoord (or an XY box)
alone. After RebuildStrates or an editor live edit, StrateManager rebuilds the
layout and bumps PassagesVersion, but a pooled worker whose cache is still warm
for the chunk it is asked to regenerate skips the refetch and generates with the
OLD params. RegenerateAllChunks reloads the same tile coords, often on the same
workers, so this is likely rather than exotic. Symptom: "I tweaked the strate
asset, regenerated, and one patch kept the old shape."
Fixed:
- CP_* in GetDensityAt (the archetype params + biome context)
- OC_* in GetSurfaceHeightAt (the height oracle)
- BM_* in GetBiomeMaterialAt (per-vertex palette)
- TC_BiomeCache in ClassifyTile (survives across calls)
- GSurfColCache boxes (see below)
Two things beyond what the audit listed:
1. GSurfColCache. Its key is (XY box, StrateKey, Seed) where StrateKey is
round(StrateBottomWorldZ). A live edit that changes terrain params WITHOUT
moving the strate — noise frequency, mountain strength, a biome — leaves that
key identical and serves stale columns down the whole vertical stack. This is
the most visible form of the bug, so LayoutVersion joins the box key.
2. FChunkBiomeCache validity is a world-XY box, which says nothing about the
FBiomeContext its cells were classified against. Refetching the context
without invalidating the grid would leave the fix half-done, so the four
caches call the new FChunkBiomeCache::Invalidate() on a version change.
No behavioural change at a static layout: the version only moves on Initialize.
UNVERIFIED: not compiled, not run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Public/VoxelDensityOp.h: IVoxelDensityOp (PrepareChunk / Eval / EffectOverBox /
ClassifyBox / IsXYPure), EVoxelOpEffect, EVoxelOpRole (the four roles),
EVoxelOpCombine, FVoxelOpContext, and the box-verdict fold.
Nothing is wired in: GetDensityAt is untouched, the archetype switch is intact,
no operator exists yet. This is the contract plus the reasoning behind it.
Two things worth flagging beyond OPSTACK-PLAN §3:
- ClassifyBox is NOT source-only. ApplyBoundarySeal does Max(D, SealFactor*Base)
inside its band, i.e. it FORCES solid regardless of input. Pure direction
(FillOnly) cannot express that: over a box that sits entirely in the top seal
band above the terrain the source says AllAir, FillOnly then kills AllAir, both
hypotheses die and the tile becomes Mixed — whereas ClassifyTile returns
AllSolid there today. Not a hole, but a silent loss of exactly the trivial
tiles T1.d exists to skip. So forcing ops override the fold, and ops after them
still apply (a passage crossing that box takes the verdict back, as today).
- The fold reproduces the current hand-written ClassifyTile line for line; the
mapping is written out in the header. That correspondence is the evidence the
abstraction fits this codebase rather than being imposed on it.
Also moves EVoxelTileClass from VoxelGenerator.h to VoxelTypes.h (CODEMAP 3.2:
foundational, no UClass, everyone includes it) so the op header needs no UCLASS
dependency. All existing users reach it through VoxelTypes.h transitively.
Types are plain C++ on purpose — no UHT, no .generated.h. They become
UENUM/USTRUCT in Phase 3 when ops turn into data assets.
UNVERIFIED: not compiled.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The plugin had zero tests, and the docs make dozens of "bit-identical" and
"conservative verdict" claims that nothing machine-checks. OPSTACK-PLAN §4
Phase 0.5 asks for these before any op-stack work is built on top.
- VoxelForgeTestFixture.h — headless world (transient strate definitions ->
UVoxelSettings -> a real UVoxelStrateManager::Initialize), so the tests hit
UVoxelGenerator::GetDensityAt where the ~30 thread_local caches actually live.
One strate per archetype, pinned via FixedStrates so slot index -> archetype
is stable across seeds.
- DensityPurity — 10k points re-sampled in shuffled order on the same thread and
on N worker threads, asserting BIT equality. AVoxelWorld::ValidateDeterminism
is game-thread only and structurally cannot see worker-cache divergence, which
is how AUDIT C2 stayed hidden. Includes a flat-field canary so a collapsed
noise field (AUDIT C1) can't make the test pass vacuously, and a diff-layer
pass that exercises the direct-mapped DiffSlots cache.
- ClassifyTileSoundness — scans tiles for a non-Mixed verdict, then brute-forces
the exact mesher lattice (g in [-1, Cells+1], margin included) and asserts every
sample is on the claimed side of IsoLevel 0. A false AllSolid/AllAir is an
invisible collisionless hole; T1.d v1 was reverted for exactly that in June.
Errors out rather than passing if no tile yielded a verdict to check.
- DiffLayerContention — N reader threads running the worker call mix while the
game thread writes and Clear()s, asserting survival and a monotonic ModsVersion.
UNVERIFIED: never compiled — this module has never pulled in AutomationTest.h
and Private/Tests/ is new. See OPSTACK-PROGRESS.md for the likely error spots.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AUDIT P1: every markdown design doc except CODEMAP.md was untracked, so
ARCHITECTURE / AUDIT / OPSTACK-PLAN / fable-idea / REVIEW_FINDINGS lived
only on disk. Replaces the single !CODEMAP.md exception with !*.md.
Also makes OPSTACK-PROGRESS.md commits actually record something, which
the unattended crash-safety discipline depends on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>