Move closed audits, task briefs, design rounds, perf reports, the OPSTACK plan/progress logs and WORK-NEXT.md into Docs/archive/ (git mv, history kept). Whitelist Docs/ in the strict .gitignore, ignore Tools/**/__pycache__/, and remove the tracked .pyc. Repoint references in the kept docs and in non-test source comments to the new paths.
21 KiB
AUDIT-COLD-2026-09-14: read-only examination, with the orchestrator's verification notes
Produced by Codex (gpt-5.6-luna, max effort, read-only sandbox) on 2026-09-14 at HEAD 7a972f5,
briefed with no project background. It did read the plugin-root docs, so it is not context-free,
but its findings are code-level. Findings are verbatim below. These notes record what the
orchestrator checked against the code afterwards.
Orchestrator verification notes
- #7 CONFIRMED, a determinism bug introduced by the tile-cache work (
2de603c/cfad3d1).VoxelGenerator.cpp:2968movesCP_OpStackinto the cache entry and resetsGTunnelCoreCache. A later request for the sameChunkCoordunder the otherbTileCacheContextmisses the key (:2660), but the refetch guard (:2723-2725) skips because chunk, owner and version are unchanged, so evaluation uses the moved-from (empty) stack and core cache. The hit path setsCP_Chunkto the shared key(0,0,Z)(:2698), so the spawn columnX=Y=0reaches this state after an ordinary hit, not only in a narrow timing window. Coarse tiles (LOD >= 3) covering the origin can be evaluated wrongly depending on the order work reaches a worker. - #3 and #8 are real but narrower than stated. In multiplayer, the server and client worlds share
seed and layout by design, so the cached values are identical. Contamination needs worlds that
genuinely differ under the same key: editor edits that do not bump the version, or two diff
layers with different contents at the same
ModsVersion. Cheap to fix (add owner/layer identity). - #19 is not a defect under C++20 (UE 5.7), where left-shifting negative signed values is defined. Overflow for extreme coordinates remains theoretical.
- #21 matters again if the gap/seal AllSolid proof (in progress) calls
HasAnyModInChunkRangeper LOD0 tile. - The others (#1, #2, #4-6, #9-18, #20, #22) were not independently re-verified. Verify each before fixing it.
Read-only audit report
I made no file changes and did not build or run the plugin. “VERIFIED” means the failure follows directly from the source path; “SUSPECT” means a runtime/compiler-dependent hazard that was not executed.
Highest impact
-
The density-volume worker races live generator mutation — P1 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—FScopedGenerationPause(203–222),AVoxelWorld::RebuildStrates(551–582),AVoxelWorld::ChangeSeed(5569–5623).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelDensityVolume.cpp—UVoxelDensityVolume::Reset(101–120),ProcessOneFill(603–637).FScopedGenerationPausewaits for ordinary generation tasks and decoration tasks, but not the dedicated density-volume fill thread. That thread reads the rawGeneratorpointer and callsGetDensityAtwhileRebuildStratesorChangeSeedmutates the generator and strata manager.A fill in progress during a PIE edit or seed change can therefore read partially rebuilt arrays or mixed old/new settings. This is an actual data race with crash/garbled-density potential.
Resetalso clearsPendingFillsbut not the already queuedFillQueue, causing stale work to continue after reset. -
Decoration tasks can use a destroyed content manager — P1 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelContentManager.cpp—UVoxelContentManager::BeginDestroy(58–62),NotifyShutdown(65–75),WaitForDecorationTasks(78–85),LaunchDecoTasks(327–415).Tasks capture raw
thisand later accessbShuttingDownandDecoResults. Shutdown waits only three seconds, ignores the timeout result, andBeginDestroyimmediately callsSuper::BeginDestroy.Any decoration task exceeding the timeout can later enqueue into the destroyed manager. The global task counter also includes tasks belonging to unrelated worlds, so one world can time out because another world is busy.
-
The diff snapshot TLS cache is not keyed by its owning diff layer — P1 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp—UVoxelGenerator::GetDensityAt(3069–3097).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Public\VoxelDiffLayer.h—GetModsVersionandModsVersion(241, 289).thread_local FDiffSlot DiffSlots[64]considers only chunk coordinates andModsVersion. It does not include theUVoxelDiffLayer*.Two PIE worlds commonly have the same chunk and the same per-layer version—for example, both reach version 2 after their first edit. If a worker samples world A first, world B can hit A’s cached modification snapshot and apply the wrong edits. This produces cross-world terrain contamination dependent on worker history.
-
Fixed-only strata configurations are silently disabled — P1 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—AVoxelWorld::BeginPlay(1294–1305).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelStrateManager.cpp—UVoxelStrateManager::Initialize(1060–1244).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp— fallback generation inGetDensityAt(3030–3039).BeginPlaycreates a strata manager only when a season is assigned orStratePool.Num() > 0. It does not testFixedStrates.Num().A settings asset with valid fixed strata but no season and no pool therefore receives no strata manager. The generator takes its generic fallback path, ignoring the authored fixed definitions.
-
Settings rebuilds leave dependent systems with inconsistent snapshots — P1/P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—RebuildStrates(551–582),PostEditChangeProperty(885–895),OnObjectModifiedInEditor(898–985).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp—InitializeSettings(2079–2088).RebuildStratesreinitializes the strata manager but does not reinitialize generator settings.PostEditChangePropertyonly regenerates chunks. Asset-change handling recognizes strata and terrain-op definitions, but not general settings or biome assets.Changing seed, world radius, edge sealing, the settings object reference, or related configuration can leave the manager using new values while the generator, content manager, or density volume still use old snapshots.
ChangeSeedhas a separate correct reinitialization path, but the other rebuild paths do not. This also contradicts theRebuildStratesdocumentation inE:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Public\VoxelWorld.h(572–575), which promises to re-read all settings. -
Invalid strata initialization destroys the previous valid layout — P1/P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelStrateManager.cpp—UVoxelStrateManager::Initialize(1060–1205).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—RebuildStrates(562–576),OnObjectModifiedInEditor(975–985).InitializeemptiesStrateLayout,SeasonStrates, and related state before all season/layout validation has succeeded. If an assigned season is missing, malformed, or contains invalid bounds, initialization returns false after the old layout has already been discarded.RebuildStratesthen leaves existing meshes visible while future generation queries see an empty or partial manager. The editor asset-modification path ignores the initialization return value and regenerates anyway, making a transient invalid asset capable of replacing the working world with fallback/empty terrain.
Material correctness and determinism issues
-
The operator-stack cache is stale when tile-cache context changes — P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp—UVoxelGenerator::GetDensityAt(2218–2230, 2255–2261, 2320–2324, 2393–2407, 2551–2573, 2600–2672).The tunnel cache key includes whether tile-cache mode is active, but the rebuild guard does not. A fine-LOD request can build and move the operator stack into the cache, leaving the worker-local
CP_OpStackempty. A later request for the same chunk under a different tile-cache context misses the cache key but does not rebuild the stack because chunk, manager, and layout version are unchanged.The empty or mismatched stack is then used for density evaluation. LOD request order can therefore change terrain results.
-
Several surface and biome TLS caches omit generator/context identity — P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp—FSurfaceColumnCache::Acquire(274–328),GetDensityAt(2692–2705),GetSurfaceHeightAt(5149–5166),ClassifyTile(6014–6023),GetBiomeMaterialAt(7019–7034).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Public\VoxelBiomeTypes.h—FChunkBiomeCache::Contains(240–245).These worker-local caches use combinations of chunk, seed, strata key, and layout version, but not the owning generator, strata manager, or biome context. Two worlds can legitimately share all those scalar keys while having different terrain or biome parameters.
When a pooled worker handles both worlds, the second world can receive cached surface columns, biome classification, or material data from the first. Direct asset changes that do not advance the relevant version have the same stale-cache problem.
-
Native shaft/island topology caches omit parameters used to build them — P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp—GetVerticalShaftDensity(7090–7106, 7211–7219, 7392–7413),GetFloatingIslandDensity(7518–7554).The shaft cache key omits
LedgeSpacingandLedgeDepth, although both affect connector and ledge placement. The floating-island cache key omitsBoundarySealThickness, although it affects cached vertical spread.Changing these values—or using two contexts with identical keyed values but different omitted values—reuses old topology on the same worker.
-
Completed pre-edit decoration results can resurrect removed objects — P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—ApplyModification(5319–5375).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelContentManager.cpp—LaunchDecoTasks(381–404),ProcessDecoResults(769–807),MergeCellResult(815–862),RemoveDecorationsInSphere(1249–1260).Applying a live diff removes currently applied decorations but does not invalidate queued/in-flight decoration builds or associate them with a diff version.
BuildIdonly identifies decoration-region rebuilds.Sequence: a task computes old grass/props, the edit removes current instances, then the old result is drained and accepted. The old decorations reappear over the edited terrain until another rebuild occurs.
-
Landmark conditions are evaluated without the actual biome context — P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelContentManager.cpp—UpdateDecorations(153–263),UpdateLandmarks(1263–1310),SpawnFromProfile(1101–1137).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp—EvaluateTerrainConditions(6682–6717).Normal decoration updates populate
FDecoContext::BiomeCtx; landmark updates construct a context but never setBiomeCtx. A landmark profile using biome, moisture, relief, or biome-border conditions therefore evaluates with the default/invalid biome map.On a non-default biome map, landmarks can spawn or fail to spawn differently from ordinary decorations.
-
Decoration cleanup uses local coordinates as world coordinates — P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—ApplyModification(5369–5375);WorldToLocalVoxelandLocalVoxelToWorld(2314–2321).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelContentManager.cpp—RemoveInstancesInContent(1231–1242).Modification.Centeris a local voxel coordinate, but cleanup computesCenter * VOXEL_SIZEand passes it to a world-space overlap query. For a translated actor, the query is centered at the wrong location.The same cleanup also approximates boxes and capsules with only
Modification.Radius, which does not cover a long capsule or a box’s full extent. Existing decorations can remain over an edit or unrelated origin decorations can be removed. -
Decoration application order is timing-dependent — P2 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelContentManager.cpp—LaunchDecoTasks(327–415),ProcessDecoResults(769–810),ApplyRegion(885–952),RebuildDesiredCells(281–324).Tasks enqueue results in completion order. Results append actor spawns and HISM transforms in that order; mesh buckets are iterated through a
TMap. Equal-distance pending regions also have no deterministic tie-breaker.The same candidate set can therefore produce different actor spawn order, NetGUID/side-effect order, HISM instance indices, and internal clustering across runs or machines.
-
The passage shortlist version is not advanced when passages are cleared — P2/P3 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelStrateManager.cpp—GeneratePassages(1611–1620, 2477),EvaluateModifierSDF(2488–2524),VF_GetNearbyPassages(294–338).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Public\VoxelStrateManager.h—GetLayoutVersionandPassagesVersion(250, 515, 519).GeneratePassagesemptiesPassagesand returns early for an empty layout without incrementingPassagesVersion. Existing worker TLS nearby-passage caches still match the manager lifetime, chunk, and unchanged version.After a previously valid layout becomes empty,
EvaluateModifierSDFcan reuse indices into the now-empty array and dereference invalid passage data. This is an edge configuration, but it is reachable through the invalid-layout paths above.
Input-contract and performance issues
-
Diff modifications accept nonfinite and invalid geometry — P2/P3 — VERIFIED for malformed input
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelDiffLayer.cpp—CanModify(20–44),ApplyModification(63–96),EvaluateMods(191–248).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—CarveAtPosition(5301–5307).There is no finite-value validation for strength, radius, centers, endpoints, or extents. A NaN strength survives
FMath::Abs, then propagates throughTotalOffsetand density evaluation. Infinite strength produces infinite density. Negative or zero radii can consume budget while producing no meaningful spherical modification.This is reachable from C++ or malformed Blueprint/runtime data and permanently poisons the diff layer until cleared.
-
The public diff API contradicts itself about coordinate units — P2/P3 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Public\VoxelDiffLayer.h—FVoxelModification::Center(64),CapsuleEnd(86).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Public\VoxelWorld.h—ApplyModificationdocumentation (334–340).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelDiffLayer.cpp—GetWorldBounds(88–96).The modification struct comments describe the center as world-space, while the low-level world API and implementation interpret it as voxel-space. The helper functions explicitly convert world centimeters into voxel coordinates.
A caller following the struct comment and supplying centimeters gets an edit displaced by a factor of
VOXEL_SIZE. -
Diff budget accounting underestimates boxes and long capsules — P3 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—CarveBox/FillBox(5379–5398), capsule helpers (5401 onward).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelDiffLayer.cpp—CanModify(34–39).The budget charges every shape as a sphere with volume based on
Modification.Radius. Boxes use the maximum half-extent as a proxy; capsules use the tube radius even when endpoints are very far apart.A capsule with radius 3 and endpoints 1000 voxels apart is charged like a tiny sphere despite affecting a long volume and many chunks. Large edits can therefore bypass the intended modification budget and cause excessive remeshing.
-
Decoration radius/spacing changes are ignored while the player is stationary — P3 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelContentManager.cpp—UpdateDecorations(199–256),RebuildDesiredCells(265–325).Radius and spacing are refreshed every update, but desired-cell rebuilding occurs only when
PlayerCell != LastDecoCell.Shrinking the radius leaves distant regions loaded; expanding it does not enqueue new regions; changing spacing leaves existing regions at the old grid layout. Movement eventually repairs the state, but a stationary player sees stale decoration coverage.
-
Signed left shifts of negative chunk coordinates are undefined — P3 — SUSPECT
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelWorld.cpp—BuildDesiredTiles(3498–3548).Streaming computes coordinates with expressions such as
T.Z << L. Ordinary underground tiles have negativeT.Z, so this left-shifts a negative signed integer. Large positive coordinates can also overflow.The exact manifestation is compiler/optimization-dependent, so I classify this as SUSPECT rather than a fully end-to-end verified runtime failure.
-
Interleaved transitions can divide by zero — P3 — VERIFIED math path
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Public\VoxelStrateDefinition.h—TransitionBlendChunks(99–101).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelStrateManager.cpp—GetGenerationParams(5673, 5728–5753, 5826).The property metadata clamps the value in the editor, but runtime/cooked data can still contain zero. The interleaved transition path divides warped distance by
EffectiveBlendwithout a zero guard.A zero-width transition can produce Inf/NaN blend values and invalid generation parameters.
-
Tile classification scans the entire modification map — P3 — VERIFIED
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp—ClassifyTileUncached(5785–5790).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelDiffLayer.cpp—HasAnyModInChunkRange(170–188).Classification calls a routine that walks every
ChunkModskey under the read lock instead of using the existing per-chunk grouping to narrow the query.After many edits, each tile classification becomes O(number of modified chunks), increasing lock hold time and making edit-heavy worlds disproportionately expensive.
-
Operator documentation is internally stale — P3 — VERIFIED documentation contradiction
Location:
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Public\VoxelDensityOp.h(1–3, 86–92).
E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Source\VoxelForge\Private\VoxelGenerator.cpp—UVoxelGenerator::GetDensityAt(2393–2600).The header says the density-op system is not wired into
GetDensityAt, while later comments say all eight operators now have the stack path. The generator does in fact execute the operator stack.This is not itself a runtime defect, but it is misleading maintenance documentation around a central generation path.
The most urgent issues are the uncoordinated density-volume thread, decoration-task lifetime, cross-world TLS diff cache, and incomplete settings/layout rebuilds.