68cba5a51db8d642f2db70d666f6c81d865f4f13
25
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3f629f407a |
refactor(dead-code): remove functions, members and types with no caller
Each symbol below had zero references in Source/ (tests included), Tools/, Config/ and the build files after comment/string stripping. Functions: EvaluateTunnelCoreSDF, EvaluateSDF (uncached wrapper), IsTunnelSupportFloorWorldPoint, BuildTunnelSupportFloorColumn, IsTunnelSupportFloorColumnZ, GetTunnelSupportFloorColumnBand, VF_TunnelFloorAtSegment, VF_ProjectSweptTunnelFloor, FVoxelWormField EvaluateExact, ApplyPassageLandingAirMC, ApplyPassageTunnelAirMC, GetGenerationParamsFingerprint, FVoxelStrateCorpus::LoadFromSettings, GetSkippedDefinitions, VF_RollStrateParams, VF_ArePlayerFitConnectedWithSampler, VF_GetVoxelSeasonSelectionReasonName, UVoxelSeasonAsset::GetManifestContentHash, FVoxelStackSampler::IsRunning, CompareSkippedPostValue, MakeSurfaceColumnSource, WriteObjTile, ExploreEffectName, ExploreTileClassName, and the VoxelTypes.h chunk-local helpers (WorldToLocalCoord, ChunkToWorldPos, LocalToIndex, IndexToLocal, IsValidLocalCoord, GetFaceDirection, GetFaceNormal, CHUNK_SIZE_SQUARED, CHUNK_VOLUME). Members/types: write-only floor flags (FVoxelOpSample:: bProtectedStructuralFloor, StructuralFloorMinimumDensity, bTunnelCoreSupportFloor; bSupportFloor on the tunnel evaluations; FTunnelFloorSegmentProfile::NumSteps), UVoxelStrateManager::BlendChunks, unused FSummary counters in the stack sampler, two unused FVoxelSeasonCompositionSettings aliases, and a few unused locals/constants. No UPROPERTY, UFUNCTION, reflected type or enum was touched. Behaviour is unchanged: removed code was unreachable or only written, never read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU |
||
|
|
10fd0b6e77 |
fix(determinism): own sin/cos on the world path -- same world on every CPU, 3.5% faster
On Win64, FMath::Sin/Cos resolve to CRT sinf/cosf (GenericPlatformMath.h:521/530; no platform override), and the UCRT picks FMA3 or non-FMA3 implementations at startup by CPU. /fp:precise closed only the compiler half, so the same build and seed could build a slightly different world on two Windows PCs, and on Linux. - VoxelMath::DetSin/DetCos/DetSinCos (VoxelTypes.h): fixed double-precision period reduction to a quarter period, then fixed float minimax polynomials; no CRT, FMath or CPU-dependent intrinsic. Measured max error vs sinf 5.96e-8 over the used range (|arg| <= 6.19). - Every world-deciding FMath::Sin/Cos replaced (generator, op stacks, cave morphology, strate manager, content placement, strate measure); none remain in runtime code. - Pinned test VoxelForge.Determinism.DetSinCos (golden bit patterns + error bound). - Diagnostic start-up switch -voxel.CrtFma3=0 (Windows) forces the CRT's non-FMA3 path. - Parse-once console switches now initialise in StartupModule on the game thread; workers only read them (was an unsynchronised first parse inside GetDensityAt). - Corrected the CRC comment that claimed a CRC32 cannot give a false positive. Proof (Codex round; orchestrator re-checked): - Before the fix, FMA3 on vs off was identical on this Ryzen 7 5800X3D: the hole exists but was not observed in this world. After the fix, the canonical exports are byte-identical with FMA3 on and off. - The world changed once, as expected. New canonical export SHA-256 CBB8AE18...906F (geometry B9EF84AC; was B3E5F4C3...377); worms region 9242C093 (was 311CA42C), walk OBJ 20CCF7BD.... Triangle counts unchanged in every tile. Order-independent nearest-vertex distance, worms region: max 8.7 micrometres (the reported "11.4 m" was a line-order artefact of a line-by-line OBJ diff); export region max 3e-5 m. - Capability exact: player-fit 20,830, walk-reachable 10,909, connected. TilePostReachProof, sealed-proof, cross-platform and strate-crossing tests pass. - Performance, headless game path, S/M/S/M vs the committed-tip DLL: static worker 27.41 s vs 28.44 s (-3.6%), moving 59.59 s vs 61.74 s (-3.5%); LOD0 p50/p95 not worse. From Sol's code review (DESIGN-SOL-2026-09-15-CODE.md, section 4.1), branch sol/deterministic-math. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y |
||
|
|
8e42d6d807 |
feat(tunnels): floor authored at BUILD time, room clips tunnel, climb tier added
The round the owner asked for, with three corrections from Codex that are worth more
than the features.
WHAT LANDED
- The complete tunnel floor profile is authored into FCachedTunnel during
BuildChunkCache, not derived per sample. This was his point: "it should be done at
creation time, since it knows its destination."
- Room floor ownership blends/clips the tunnel floor at mouths -- his A > B boolean.
- The traversal validator gained a climb tier, structured exactly as agreed:
bWalkOnlyCanReachDeparture / bClimbCanReachDeparture kept SEPARATE, with
WalkOnly / Climb / MineRequired cell counts reported alongside.
The comment in the header states the contract: "The primordial capability contract
is walk-only... climb is reported as a separate tier and never makes the hard
connectivity verdict pass." That is the distinction we wanted: reporting tiers, not
permission.
- Floor controls exposed under Cave Morphology|Tunnels|Floor.
⚠️ CORRECTION 1 -- LARGE LEDGES ARE NOT CAPABILITY-SAFE YET, so they stay opt-in.
default (preference 0) fit 20,804 walk reach 10,884 0 ledges
preference 2 fit 20,765 walk reach 10,712 360 ledges
Ledges cost 172 walk-reachable cells. Codex kept preference 0 (the old staircase) as
the default rather than shipping the owner's preferred look at the price of walkable
space, and led its report with that. Correct call.
⚠️ CORRECTION 2 -- THE BACKSTOP STILL CANNOT GO. Support/room fallback counters were
zero, but the structural protection clamp fired on 31,017 of 31,017 authored floor
samples -- 100%. So the shape is still not correct by construction; the clamp is
doing the work. Instrumenting it was the right move: we now have a number instead of
an argument.
⚠️ CORRECTION 3 -- THE "X + 2A" CLEARANCE BUDGET IS NOT IMPLEMENTABLE AS I SPECIFIED.
Interval propagation bounds FIELD VALUES, not SPATIAL DISPLACEMENT. Deriving A needs
a proven Lipschitz/influence bound for every post-operation, which we do not have.
Both the owner and I assumed the interval work already gave us this; it does not.
The idea is still right, the prerequisite is a displacement bound.
MEASURED BY ME: build 0 errors; export 1.365 / 1.357 / 1.420 s (checkpoint was
1.321 / 1.357), flat within noise; 153,346 triangles.
ALSO MEASURED THIS ROUND
- Edit cost: 8 affected chunks, 195.5 ms total -- generation 195.471 ms, apply 0.059,
diff 0.020, queue 0.001. It is full level-0 regeneration; a dirty-mesh path needs
partial section support that is unavailable without modifying RMC.
- Streaming caps: effective generation cap 14, pending max 14, CAP HIT. Mesh applies
per frame 4 (not hit), 2 ms apply budget (not hit), unloads 6 (not hit).
⇒ The generation core cap is what binds during startup fill.
- Peak export RSS 1.70 GB (was 19.5 GB before the memory work).
- LOD4 empty outliers gone. LOD0 not solved: 4,600 empty tiles at 3 us median, but 11
mixed-classifier fallbacks over 100 ms, max 554 ms -- verdict=Mixed, then the full
mesher, producing no triangles.
⚠️ UNVERIFIED AND CONCERNING: the reported in-game meshed times are far worse than
412a7ad's -- p50 498.8 ms vs 115 ms, p95 1.227 s vs 410 ms, max 1.974 s vs 844 ms.
The samples differ (9,336 ready events vs 841 requests, different traversal, possibly
queue wait included), and the export path is flat, so I do not know whether this is a
real regression or a different measurement. I have NOT reproduced it myself. It is
the first thing the next round must settle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
|
||
|
|
e91f1c44bb |
wip(tunnels): checkpoint -- floor parameters exposed, round cut short by Codex credit limit
⚠️ THIS IS A CHECKPOINT, NOT A FINISHED CHANGE. The agent hit its usage limit
mid-round (locked out until 2026-09-15) after compacting its context, and never
filed a report or capability numbers. Committing so 1,344 lines are not left
uncommitted for five days. Verified by me only to the extent stated below.
WHAT LANDED
The tunnel floor controls are now real strate parameters under
"Cave Morphology|Tunnels|Floor", which is what the owner asked for -- hand-made
assets can tune them and the composer can roll them later:
bTunnelFloorEnabled true
bTunnelFloorTerracingEnabled true
TunnelFloorTerraceStepHeight 1.71 voxels (the existing 42.75 cm riser)
TunnelFloorMaxLedgeHeight 12.0 voxels (3 m -- climbable, not walkable)
TunnelFloorGentleSlopeThreshold tan(44 deg)
TunnelFloorLedgeCountPreference 0 (0 = old per-segment staircase;
>0 = whole-chain ledge policy)
TunnelFloorMaxLedges 4096
⭐ So the staircase-vs-ledges question the owner raised is now answerable IN THE
EDITOR without a rebuild: set LedgeCountPreference to 2-3 and the whole-chain
policy takes over from the per-segment one.
VERIFIED BY ME
build 0 errors
export, 2 runs 1.321 / 1.357 s (
|
||
|
|
e08b49a183 |
perf(gen): a thrashing one-slot cache cost 18.7x -- and the world is unchanged
TunnelNetwork density evaluation was 45x more expensive than Maze for identical work: 2,097,152 voxels, 64 tiles, same mesher, same machine. Profiling killed every hypothesis I brought to it -- box classification skipping 0/64 tiles was true but irrelevant (export bypasses classification), near-surface early-outs fired at 49.7%, the room cache built 340 entries for 2.74M samples, and the per-room overrides resolved once per sample rather than once per modifier. The cause was the wrapper, not the stack. A mesher tile samples an EXPANDED grid (-1..Cells+1), so one worker crosses several exact chunk coordinates while meshing a single tile. The generator's cache held only the last coordinate, so every boundary crossing rebuilt the resolved parameters, the whole 22-operator stack and the tunnel-core cache. On one representative tile: 3,675 refetches, 38.0s. The entire operator stack was 18.5s of a 4,272s total -- 0.43%. Replaced with a 128-slot direct-mapped thread_local cache keyed on owner, chunk, layout version, params fingerprint and generator type. Collisions force a safe rebuild, never a stale read. No shared mutable state, no order-dependent reduction. TunnelNetwork 344.5s / 164.267 us/vox -> 18.4s / 8.763 us/vox (18.7x) Maze 7.4s / 3.535 us/vox -> 1.08s / 0.513 us/vox ( 6.9x) same tile 38.0s -> 0.51s ; refetches 3675 -> 27 Output is BYTE-IDENTICAL, before vs after and across repeats, both archetypes: TunnelNetwork 50d6b4d0..f23126, Maze 364f7489..2fbe99. The permission to change the field was not needed. Also in this commit, from the two preceding tasks: - Explorer renders from the MESH instead of raymarching. It meshes the region once through the canonical UVoxelMarchingCubesMesher, then CPU-rasterises via a BVH. A viewpoint went from ~16 minutes to 0.155s; 8 views now cost 1.25s of drawing. No render pixel samples the density field. - One sampled grid and player-fit mask per run, shared by render/walk/export, instead of the same 368s walk computed twice. - A wall-clock budget (25 min default, hard 30 max) that stops cleanly and records truncated/truncated_during/completed_modes rather than overshooting. - ParallelFor across the measurement sampling; the suite was ~10% of a 16-thread machine. - -surfaceroughness= on the commandlet, and a surface-relief metric in export mode (edge lengths, dihedral distribution, face-vs-smooth normal divergence) so "does this look like rock" is a number, not a screenshot argument. - Smooth vertex normals in the rasteriser: FVoxelMeshData::Normals is the density-gradient normal the engine shades with, and the raster path was recomputing a face normal over it. - ScaleDiagnosis: baked candidate indices are now derived from the current corpus, so a legitimate field change stops breaking the test. NOT verified, and not claimed: - TunnelNetworkSpineEquivalence exceeded the 30-minute cap and was stopped. Inconclusive, not passing. The identical hashes are strong evidence the op-stack/legacy equivalence holds, but the test has not confirmed it. - CrossPlatformDigest passes but reports 2 samples within 1e-6 of the isosurface; FMath::Sin/Cos leaves Windows/Linux equivalence unproven. Pre-existing, not caused by this change. - The cache's memory footprint (128 entries x ~16 workers) is unmeasured. What the roughness sweep settled, for the record: SurfaceRoughness 0 -> 8 moves cost by 0.3% (163.2 / 163.2 / 163.7 us), so roughness was never the cost. It does reach the surface (dihedral p50 1.1 -> 2.2 -> 5.9), which kills my claim that the density-channel operator contributed nothing. TunnelNetwork's hard seams are present at roughness 0 (4.4% of edges past 40 deg vs Maze's 1.0%), so they are structural -- in how primitives are unioned -- not a roughness deficiency. More roughness makes them worse, not better. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb |
||
|
|
d97373c5a8 |
feat(gen): Jahni's playtest corrections -- rooms flattened, roads deleted
From walking a TunnelNetwork preset at (0,0). Four corrections, and two of
them were the same mistake.
2a. The (0,0) landing room is sized to host a FUTURE straight vertical
shaft from the surface, with flooring around the origin itself. The
room is the landing for a shaft the player digs later.
2b. The inter-strate descent no longer starts at the spine -- placed by
deterministic hash within a radius around (0,0). The spine room is for
the surface shaft; the descent is elsewhere.
2c. ⛔ DELETED the landing->hub connector roads from
|
||
|
|
55bf807f60 |
feat(descent): landing ROOMS and a walkable tunnel -- per Jahni's correction
Jahni corrected the design: "the connection between level is a tiny tunnel
connecting both strate, not vertical, it needs to be walkable" and "'the
spine' shouldn't be carved except optionally from the top first strate,
then it's used to have a landing room".
COMPOSER-NOTES §6.8 had said the spine "provides a clean landing space, not
a continuous open shaft" -- and the implementation carved a vertical
cylinder anyway. A vertical cylinder has no walkable floor except at its
bottom, so a carve guaranteeing open AIR guaranteed nothing STANDABLE.
That one omission is behind five of my dead hypotheses this week.
Now:
origin spine a finite rounded ROOM per strate, no vertical bore.
radius 14 -> 8 m wide, 7.5 m tall
descent a walkable tunnel, single-file by design: 3 m mouth,
2.5 m waist, gradient capped at a NAMED 15 deg
exotic styles VerticalShaft / SpiralDescent / CascadingDrops /
CrackCrevice preserved, no longer the default
⚠️ THE GEOMETRY BITES, and it is worth Jahni's eye: at 15 deg a 32 m drop
needs 119.4 m of horizontal run -- about 59.7 m per switchback leg. That is
a long walk between strates. It is the honest consequence of "walkable",
not a tuning artifact, and the alternative is a steeper number.
⭐ AND THE ROOT CAUSE GOT NAMED A THIRD TIME, by Codex mid-run: "the round
tube was hollow air but had no authored support strip, so 'open' still did
not mean 'standable'." Tunnels now carry a floor band, and the root join
terminates in the finite origin room rather than an annular shaft hub.
Measured, commandlet sweep, 8 archetypes x 8 seeds, 64/64 valid:
origin column has player-fit cells 17/64 -> 63/64
origin column reachable 0/64 -> 7/64
arrival landing probes 315/320 fit AND in the arrival component
tunnel audit 5,916/5,916 floor and capsule samples pass; zero gradient,
floor, capsule, geometry or seal failures
narrow-gap events 0 box verdicts 14 checked, 0 violations
⚠️ arrival->departure is 6/64. The landings and tunnels are now provably
walkable; what remains is the INTRA-STRATE vertical connectivity problem
found in
|
||
|
|
6b916699af |
test(explore): 81 runs in an afternoon -- every failure is a DROP, not a squeeze
First real use of the commandlet, and it paid for itself immediately: 64
sweep runs + failure renders + origin probes, which at the old 73-minutes-
per-automation-run would have been days.
⭐ FINDING 1: THE WORLD IS FRAGMENTED VERTICALLY, NOT PINCHED.
Across all 64 runs: ZERO narrow-gap events. Every failed run has separate
arrival/departure fit components with a positive gap, 12.75 m to 72.98 m.
So corridors are not too tight anywhere -- the space is severed.
And for the first time in this project we LOOKED at a failure instead of
inferring from aggregates:
FlatPlain/42 "a broad flat floor sheet with a sharp diagonal ledge
descending into black open space" -- a drop, gap 63.9 m
SurfaceWorld/7 "terrain shelves and a large black void between them.
There is no connecting floor" -- gap 51.8 m
Maze/42 "a massive solid rock wall/overhang fills the separation"
-- gap 27.7 m
I checked the images myself. FlatPlain shows two floor LEVELS 27 m apart
in Z with nothing joining them.
⇒ The missing thing is VERTICAL CONNECTION between floor levels. Ramps,
slopes, ledges, stairs. Every archetype builds floors; nothing builds a way
between them.
⭐ FINDING 2: THE ORIGIN HUB IS NOT REACHABLE, which contradicts
|
||
|
|
bd547c35c4 |
feat(explore): a commandlet -- 91x faster, and the first real PICTURE
Every experiment in this project cost a build + full-suite cycle. The
landing task took 52 of them over ~9 hours. And nobody could SEE the world:
every visual judgement all session was made on density SLICES -- diagrams,
not pictures. That is how a floor heaving 1.4 m every two metres got
described as "roughness visible but modest" while Jahni spotted it in
thirty seconds by walking it.
-run=VoxelForgeExplore, editor-only module, three modes, one build many runs:
render raymarched PNGs, fixed 0.25-voxel step + 10 bisection iterations
(NOT sphere tracing -- the op stack yields density, not a signed
distance, so a long step tunnels through walls). Three views, each
with a 1.76 m human marker in frame, because every scale mistake
here came from numbers with no body beside them.
walk an agent using the EXISTING VF player-fit stencil, never its own
movement model -- two definitions of "can a player stand here"
would drift and we could not tell which was lying.
export OBJ through the canonical UVoxelMarchingCubesMesher. No second
mesher; a forked meshing invariant is the bug this project keeps
paying for. 23.2 MB, 106,762 verts, 200,842 tris for 128^3.
⭐ THE NUMBER THAT JUSTIFIES IT: the full automation run takes 4,396 s
(73m16s). The commandlet does all three modes in 48.1 s wall, 5.9 s of
which is engine startup. ~91x faster. And I had guessed the suite cost
~90 s -- it is 73 MINUTES. The iteration loop was far more expensive than
I knew.
First real walk metrics on the Maze (seed 0, slot 4):
distance 214 m · tortuosity 4.98 · dead ends 0.935/100 m
narrow-gap events 0 · reachable fit fraction 3.01%
result NotConnectedAtThisResolution
Tortuosity ~5 is a genuinely winding route, which is right for a maze.
3% reachable is not.
Editor Development build clean. SHIPPING build clean with the receipt
containing VoxelForge and NOT VoxelForgeEditor -- the instrument cannot
reach a packaged game. Automation 32/32, 0 failed.
⚠️ Codex initially shipped this UNBUILT, and correctly: I omitted the
## Validation section from that spec, so it fell back to CLAUDE.md's
"NEVER build or compile". 100+ compile errors from three causes -- bare
TCondensedJsonPrintPolicy and TJsonWriter needing their template
arguments, and a missing UObject/Package.h. That include is the SECOND
miss this session, so CODEMAP §6 now carries the gotcha.
⚠️ Unrelated, found in passing: VoxelForge.uplugin does not declare
RealtimeMeshComponent although the runtime module uses it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8V9my1PL68pmQb2gutZZV
|
||
|
|
e4e159f0cd |
test(measure): the window was NOT the problem -- my sixth hypothesis dies
I thought the fitted mouth-AABB window was cropping an origin-routed path and producing a false NotConnected. Codex tested it three ways at step 1 on the Maze candidate Jahni walked: mouth-AABB + margin 842,996 cells NotConnected origin-inclusive 3,251,556 cells NotConnected WHOLE STRATE 31,881,528 cells NotConnected 115,578 fit cells, 2,903 components, largest 11,418 -- and the mouths sit in components of 172 and FOUR cells, 274 voxels apart. The window was never the problem. Sixth measurement hypothesis, first one that was wrong in the other direction: this time the instrument was fine and I doubted it. The window rule still improved: bIncludeOriginInCoverWindow now covers the route an origin-rooted topology actually implies (Maze, VerticalShafts, TunnelNetwork, Underwater), expansion happens BEFORE MaxCells, and an over-cap window is REFUSED honestly instead of silently cropping back to a mouth-only box. VerticalShafts refused at the 4M production cap and said so. Corrected law across eight: 0/7 measured Connected, 1 unknown (CoarseLiedBudgetExhausted), 1 refused. ⇒ FIVE CAUSES NOW ELIMINATED: size (Jahni walked it), the fit stencil (fixed, 309 pockets -> 96), placement (mouths land on legal poses), corridor connectivity (spanning tree, 0 disconnected cells over 64 seeds), and the measurement window (whole-strate says the same thing). ⭐ AND THE REMAINING CAUSE IS NAMED BY ITS OWN NUMBER: the departure mouth sits in a FOUR-CELL player-fit component. A passage tube is a bore through rock -- sloped or vertical, no floor within the walkable angle -- so the mouth point is not a standing position and nothing around it is either. You arrive and there is nowhere to stand. That is precisely COMPOSER-NOTES §6.5 / Tier 1's unbuilt item: "the passage mouth becomes a room plus a distinctive landmark". It was never started. The law cannot pass while a mouth opens into a bore instead of a landing. Build clean, 32/32 successful, 0 failed, same two known warnings. WorldRadiusVoxels=0, lateral gating, determinism, equivalence all intact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8V9my1PL68pmQb2gutZZV |
||
|
|
110ae9d0ed |
fix(measure): the fragmentation was the instrument -- 309 pockets -> 96
Jahni walked the maze end to end while the metric called it 309 disconnected pockets whose largest held 176 cells, with the arrival mouth in a THREE-CELL pocket. Direct observation beat the instrument, again. Cause, found exactly: the stencil demanded a solid cell at precisely (X,Y,Z-1). No supporting-surface search, no slope tolerance, no step height. At step 1 a floor that slopes even slightly loses its solid cell under most of its air, so a walkable corridor dissolved into specks. Now: a bounded downward search for a COHERENT SUPPORTING SURFACE within a step-height tolerance (MaxStepHeight 45 cm = 1.8 voxels), a walkable-slope normal test (WalkableFloorAngle 44 deg), then the full capsule re-tested at the resolved support height. Deliberately NOT "any solid within N cells", which would accept ledges and isolated pixels. ⭐ ON THE WALKED MAZE (seed 0, index 50, slot 4): components 309 -> 96 largest 176 -> 1401 fit cells 2801 -> 6668 mouth comps 43/81 -> 147/1401 The 300-pocket world was a measurement artifact. Fifth time this session the instrument was the suspect; fourth time it was the culprit. ⚠️ AND IT IS NOT SELF-FLATTERING -- a metric change that only ever makes numbers rise is worthless, so it ships with controls that must REJECT: vertical shaft wall 0 fit cells isolated 1-voxel ledge 0 fit cells overhang underside 0 fit cells ramp steeper than 44 deg 0 fit cells coarse step 4 refused, not guessed Open flat-floor control: 6.13% of air cells fit, ONE component of 12,168 -- sensible, since only near-floor poses qualify in a tall room. ⚠️ BUT THE ROUTE STILL DOES NOT EXIST, and that part of H3 survives. The law is 1/8 before and after -- what changed is its honesty: the one connection used to be FloatingIslands through FOUR cells, and is now FlatPlain through 2,221 with a zero gap. Seven archetypes still have both mouths in separate player-fit components 56-169 voxels (14-42 m) apart, and those components are now large and real (663-2902 cells), not specks. ⇒ So the world HAS substantial connected player-sized space. The passage MOUTHS are not landing in it. That is the Tier 1 landing problem again, one level down: VF_SuggestLandingPoint aims at open AIR, and air is not where a body fits. Next task. Codex found three errors in my baseline quoting, all mine: I wrote "0/8 connected" while also saying one connects through four cells (it was 1/8), misquoted SurfaceWorld's gap as 1101 when the saved value is 110.648, and mixed provenance on the VerticalShafts row. Build clean, 31 succeeded / 0 failed. The 2 warnings are both deliberate: the lateral-regions gate, and a test reporting that its own sample "barely asked its question". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8V9my1PL68pmQb2gutZZV |
||
|
|
eb47efdec9 |
feat(measure): a player does not fit. The law was passing impossible routes.
Jahni walked composed strates for the first time: "'maze' is way too tight tunnel radius wise, i can't enter nor anything". The candidates he could not enter were the ones our metrics ranked BEST -- Maze had the highest walkable fraction (.285) and highest connected surface (.99) of all eight. ⭐ THE HEADLINE, AND IT IS BAD NEWS WORTH HAVING: old arrival->departure law passing on 7 of 8 candidates player-fit-restricted law CONNECTED ON 0 OF 8 **The primordial law has been passing on routes no player fits through.** Every "the world is traversable" claim in this project measured air, not passage. Player-fit fraction across the eight showcase candidates ranges .0000 (FloatingIslands -- literally zero) to .0117. The season composer now returns 0 survivors for 6 required slots, and the showcase gate rejects 8 of 8. That is the correct outcome. The gate is not tuned to a threshold; it requires a resolved player-fit volume and a connected player-fit route. Capsule confirmed by Jahni as the base ACharacter: 34 cm radius, 88 cm half-height = 1.36 voxels radius, 7.04 tall. Named constant with the derivation beside it. Two traps avoided, both of which would have produced confident nonsense: - THE COARSE GRID CANNOT RESOLVE A PLAYER. SampleStep 4 makes a cell 4 voxels, ~3x the player's radius, so fit is below the resolution of every metric built so far -- which is exactly why nothing flagged an impassable maze. Fit runs on step 1 and REFUSES (bPlayerFitResolved=false + reason) when the step is too coarse. - Manhattan distance >= Euclidean, so reusing the existing transform for clearance would OVERESTIMATE free space and report fits that do not exist. Clearance uses a separate Chebyshev transform; Manhattan stays only on the legacy feature-scale metric. ⚠️ MY ROUGHNESS HYPOTHESIS WAS WRONG, and Codex disproved it rather than confirming it. Forcing SurfaceRoughness=0 on the Maze moved PlayerFit .0105 -> .0138 and the restricted law stayed NotConnected. Minimum corridor clearance is 1.00 voxel (25 cm) with or without roughness -- the corridors are simply too tight for a 34 cm capsule, full stop. Roughness IS still a real pinch risk where reach exceeds corridor radius: Underwater 2.21x, FlatPlain 2.31x, CrystalChamber 1.81x, TunnelNetwork 1.44x on corridors, VerticalShafts 1.30x on connectors. The 5.625-voxel default bound stays dangerous. Islands measured, not changed (feel is Jahni's call): 0/2 touch either seal, median clear gap 47.8 above / 9.0 below -- but effective radius spans 27-258 voxels at H=128 with ThicknessRatio .546, so the worst case has zero guaranteed separation. Recommendation recorded: radius back to 18-42, StrateHeightInChunks 4 -> 6, ThicknessRatio <= .40. Build clean, 30 succeeded / 0 failed, verified in the namespace log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU |
||
|
|
faa62d419e |
feat(runtime): the composed world can BE the game world
Jahni: "can you not implement those settings? as in, have them be live in
game?" This is that. The composer stops being a lab tool.
THE ARCHITECTURAL LINE, which is the whole design (COMPOSER-NOTES §3.1):
COMPOSITION stays editor-only, EVALUATION becomes runtime. Rolling,
measuring, preview, selection, promotion, the corpus -- none of it reaches
a player's machine. What crosses into the runtime is exactly two things:
reading a season manifest, and materialising a recipe into an op stack.
UVoxelSeasonAsset a cookable UPrimaryDataAsset holding the manifest,
so a season ships in a packaged game. The JSON
stays the reviewable, git-diffable artefact.
UVoxelSettings::Season optional. Unset => behaviour is exactly as today.
GetEffective*() season seed / spine radius / world radius / season
number override the authored values when assigned.
The test deliberately sets DIFFERENT authored
values, so "the season is authoritative" is proved
rather than coincidental.
⭐ MULTIPLAYER DETERMINISM, PROVED: two independent runtime managers built
the same season and compared 393,216 density samples BIT-IDENTICALLY. This
is the property the whole multiplayer model rests on -- replicate seed +
layout + diff, never geometry.
⭐ AND A STALE SEASON IS DETECTABLE, NOT SILENT. Initialize() now returns
bool and an assigned-but-invalid season NEVER falls back to the authored
pool. Falling back would hand a client a world that looks fine and is not
the season -- silent divergence, the worst failure mode in a shared world.
GetCurrentSeasonContentHash() is exposed BlueprintPure to compare at join.
HasComposerRecipeOverride() is gone. It force-classified every custom recipe
as Mixed, which was correct while box verdicts were only proved for
hand-written stacks -- but
|
||
|
|
088b40216d |
feat(composer): showcase of all 8 archetypes -- and "not navigable" answered
Jahni asked for one strate per archetype to look at, and said the previews looked "not navigable nor anything except maybe the mazes". Both delivered. ⭐ PART A -- HIS EYE WAS RIGHT, AND SO WAS THE METRIC. BOTH EXPLANATIONS WERE TRUE, and they split by archetype: archetype walkable FLOOR AREA largest connected surface CrystalChamber .059 .9999 1.00 FlatPlain .048 .9985 .9998 SurfaceWorld .064 .9374 .9936 Maze .285 .2629 .9905 Underwater .229 .2061 .3312 TunnelNetwork .155 .2084 .4640 VerticalShafts .108 .1138 .1449 FloatingIslands .004 .1016 .4992 1. THE METRIC WAS WRONG FOR CHAMBERS. Walkable fraction is volumetric: an air cell with solid one step below and headroom above. In a cathedral almost no air cell is near a floor, so a huge open room scores ~.06 while a tight maze scores ~.29. It was measuring CORRIDOR-NESS, not navigability -- and mazes win that by construction, which is exactly the pattern he spotted. Slab families actually have 94-100% walkable FLOOR AREA, fully connected. They were never the problem. 2. AND FOUR ARCHETYPES ARE GENUINELY FRAGMENTED, which is a real content finding, not an artifact. Largest connected walkable surface: VerticalShafts .14, Underwater .33, TunnelNetwork .46, FloatingIslands .50. In VerticalShafts, 86% of the floor is unreachable from the biggest patch. The primordial law still passes -- you CAN get from arrival to departure -- but most of the floor is not part of that. That is precisely "not navigable" and it needed a new metric to see. New metrics: WalkableFloorAreaFraction, NumWalkableSurfaceComponents, LargestWalkableSurfaceShare. A floor you cannot cross is not a floor. Web references checked, per his suggestion -- we invented every metric in this project without once testing them against reality: NPS solution-cave and Mammoth Cave morphology, Lehman Cave dimensions (Talus Room 90x376 ft, 21,511 sq ft floor, 113 ft ceiling), Deep Rock Galactic cave-design notes, Team Cherry on Metroidvania maps. They support heterogeneous room+passage space with branches and shafts -- NOT a universal floor-to-volume target. So there is no single number to optimise, which is itself worth knowing. PART B -- Saved/VoxelForge/Showcase/index.html, 8 cards, 64 PNGs, fine renders (step 1, radius 64, centred on LargestComponentPoint), 0 blanks. Each card carries the exact PIE values to type in, because navigability is judged by walking: CrystalChamber seed 7331 idx 34 slot 1 Maze seed 0 idx 58 slot 1 FlatPlain seed 0 idx 4 slot 1 SurfaceWorld seed 0 idx 11 slot 2 FloatingIslands seed 0 idx 34 slot 1 TunnelNetwork seed 0 idx 61 slot 1 Underwater seed 0 idx 46 slot 1 VerticalShafts seed 0 idx 15 slot 1 All bComposerRollStructure = false. Selection searched seeds 0/7331 x indices 0-63, applied the three hard gates, then scored floor_area + .25*clearance + .10*largest_surface. Caveats kept honest: floor area is a sampled XY proxy, projected connectivity is not a 3D traversal proof, "best" means best within the searched range. Build clean, 30 succeeded / 0 failed, verified by me. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU |
||
|
|
90c40c8d5a |
feat(preview): fine ROI renders + density contour -- the caves look like caves
Implements Codex's own recommendation from its critique, after it settled
the owner's two observations with evidence:
"hard walls" -> THE RENDERER. rg (room graph) re-rendered at step 1 is
rounded and organic. The hard edges were 4-voxel
sampling drawn nearest-neighbour. Fifth time in this
project that an alarming result was the instrument.
"like a maze" -> REAL AND CORRECT. lt (lattice) stays rectilinear at
step 1 because it IS a maze; 19 of 64 candidates use
that source.
"noisy" -> ALIASING. nr#22 speckle 7 solid / 1 air at step 4 ->
0 / 0 at step 1, broad shapes intact.
Built: an optional fine ROI pass at step 1, radius 64 (~1.8M cells, cap
2M), run only for the 42 coarse survivors, plus a deterministic density=0
CONTOUR rendered beside the filled view.
Both views are kept side by side on purpose. The filled view is honest
about what was sampled and carries the walkability evidence; the contour
is legible. Codex's line, and it is the right principle: anti-aliasing the
coarse image "would only cosmetically blur the evidence and could invent
geometry". Do not replace evidence with beauty.
Fine labels carry their own sample step and resolved window so a fine
image can never be mistaken for a coarse one, and a MaxCells refusal shows
in the page rather than looking like a failed render.
fine sampling 71.1 s · rendering 0.6 s · peak grid 9.34 MB
42 filled pairs, 42 contour pairs, 0 refusals, 424 image links, 0 missing
Codex corrected my spec twice in the critique, both sharp: a binary step-1
raster ALWAYS has one-voxel axis-aligned edges, so only persistent
LARGE-SCALE right angles prove boxy generation (my criterion would have
produced a false "it is boxy" verdict); and persistent speckle is not
necessarily a roughness modifier.
Roughness deliberately untouched -- its call to judge that only after fine
rendering exists, and it stands.
⚠️ KNOWN, next: the fine ROI is centred on (0,0), so a candidate whose
geometry sits elsewhere renders blank (candidate 003 is a flat sheet with
no surface in the ROI). FVoxelStrateMetrics already carries
LargestComponentPoint -- centre the ROI there instead.
Build clean, 25 succeeded / 0 failed, verified by me.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
|
||
|
|
993512f0bc |
feat(composer): F1 -- the candidates are VISIBLE, and detail is invisible
PART B: the preview. Jahni, asked what "good" means for ranking a season: "good is hard to describe, i believe it'll be easier once i'm able to see the strates candidates." So the ranking policy was blocked on this, not on more metrics. COMPOSER-NOTES §3.4 promised the preview comes nearly free once the measurement exists; collected. Per candidate, rendered from the grid the measurement ALREADY built (no second sampling of the world): a vertical XZ section and a plan XY slice, solid dark, air light, walkable cells highlighted, with a chunk scale bar so a corridor is distinguishable from a cathedral. Plus a self-contained index.html contact sheet of all 64 -- survivors first, outliers first within them, recipes, metrics, and every rejected candidate labelled with WHY (vacuous 3, fragmented 2, law failed 19). Saved/ComposerPreview/<runid>/index.html 64 candidates, 128 PNGs Two honesty rules are on the page itself, not buried in a comment: every number states the window that produced it (margin/step/Z span), and a 2D slice CANNOT show connectivity -- two regions that look joined in one slice may not be. Plan-Z is chosen by highest solid/air boundary-transition count, not hardcoded mid-height; a slab strate would otherwise render as a blank sheet. Codex flagged the ambiguity in my spec and picked correctly. PART A: bootstrap jitter for zero-spread fields. ±25% of a field's own magnitude when corpus spread is ~0, ±15% of measured spread otherwise, clamps last. Marked in code as a BOOTSTRAP for a starved corpus that promotion (Tier 5) should retire. 1,422 applications over 168 fields in the parameter roll; 6,848 over 107 in the structure roll. ⚠️ AND IT DID NOT BROADEN THE DISTRIBUTIONS. Feature scale 0-376 either way; walkable actually NARROWED (.389 -> .287). Codex's conclusion, and I agree: the measurement is INSENSITIVE to these fields, not merely corpus-starved. The reason is now obvious and worth writing down: the grid samples every 4 voxels, and cave DETAIL -- terraces, scallops, ribbing -- lives at 1-3 voxels. It is below the sampling resolution. A step-4 grid structurally cannot see feel; it sees size. So "vary the detail" and "score the detail" need different instruments, and the preview images inherit the same limit. Box proofs stayed sound under wider jitter: parameter roll 1,204 proved / 1,602,524 voxels / 0 violations; structure roll 659 proved / 877,129 / 0 violations. §6.2 did not reopen. fable-idea.md F1 ticked (measurement + preview). Build clean, 25 succeeded / 0 failed, verified by me. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU |
||
|
|
859a9b25c1 |
feat(composer): the structure roll -- 64 distinct stacks nobody wrote
Tier 4b. Until now the composer varied PARAMETERS within an archetype:
differently-sized caves. This rolls STRUCTURE, which is what makes a strate
a different KIND of place.
Per COMPOSER-NOTES §3.2: roll root polarity (rock+carve or void+fill) ->
a shape source legal for that polarity -> the conversion FOLLOWS from
polarity rather than being rolled -> k modifiers drawn from what the
CHANNEL DECLARATIONS say is legal, not from a hand-written list ->
structural posts appended automatically and not omittable.
64 distinct recipes of 64 (perfect structural diversity)
invalid recipes emitted: 0 (the roller cannot produce an
order the DAG validator rejects)
survival 46/64 (71.9%) non-vacuous 62, largest-share 60,
law pass 48
⭐ AND THEY ARE REAL PLACES NOBODY DESIGNED. Root polarity as a one-bit
identity lever, exactly as §3.2 predicted:
VF|r=vd/T|s=rg/T|x=fl/T TunnelNetwork's ROOM GRAPH rooted in void with
a FILL -- rooms become solid blobs hanging in
open space. air .978, feature scale 64.
VF|r=vd/M|s=lt/M|x=fl/M Maze's lattice corridors filled instead of
carved. air .938, feature scale 16.
RC|r=rk/V|s=sh/V|x=cv/V shafts carved from rock with a Maze modifier
set they never ship with.
Same parts, different worlds. That is the whole thesis of the op stack,
finally exercised.
⭐⭐ AND THE VERDICTS HOLD ON STACKS NOBODY VERIFIED:
489 proved (213 AllSolid, 276 AllAir), 2,071 mixed
650,859 voxels brute-forced
0 VIOLATIONS
This is what
|
||
|
|
4f11ce81a4 |
fix(shafts): the primordial law holds -- 0 trusted negatives across 16 seeds
Closes the VerticalShafts bug. Two remaining failures were investigated
and neither was a broken world.
PART A -- my measurement window was binding, not the world.
The window was a square centred on (0,0) with a radius, while mouths sit
up to ~200 voxels out in arbitrary directions, so a box holding both was
mostly empty space paid for at full price. Seed 6 at step 1 needed
16,515,072 cells and blew the cap; fitted to the two mouths it needs
6,553,344 and measures fine.
seed 6 step 2 NOT_CONNECTED -> CONNECTED at doubled margin
seed 6 step 1 NOT_CONNECTED -> CONNECTED at doubled margin
seed 14 step 2 CONNECTED
seed 14 step 1 CONNECTED
A fitted box can cut off a route that detours outside it, so every
negative is re-run at doubled margin. 5 of 16 were binding negatives and
all 5 flipped. TRUSTED NEGATIVES: 0. There is no evidence of a single
unescapable strate in 16 seeds.
PART B -- the drainage tree has no orphans, and a third cause existed.
0 / 10,372 unreachable shaft paths (0% orphan rate)
16 strict local minima, all caught by the direct-spine fallback
neighbour fallback fired 0 times; emergency fallback 0
91.8% of non-largest components are smaller than one shaft volume --
they are roughness bubbles, not geometry
So a mouth could land in a noise bubble that is open but unreachable --
worse than useless, exactly the trap the law forbids.
VF_SuggestLandingPoint now returns the selected shaft AXIS, which is on
the drainage tree by construction.
⚠️ Codex found a third cause neither of us listed: tree capsules could
cross ledge bands, so a guaranteed connector could be blocked by a ledge.
Connector Z is now drawn from a deterministic ledge-free interval,
mirrored in the op stack.
⚠️ And an 8th error in my spec: "connect to any neighbour shaft" as a
fallback can create CYCLES -- a loop of shafts that drains nowhere and is
disconnected from the spine. The fallback must preserve strictly
decreasing origin-distance. Implemented that way.
per-archetype law 6/6 · box-verdict violations 0 · stack equivalence
bit-identical over 20,000 samples · sweep 45.3 -> 20.4 s
cache 7.970 us rebuild, 0.073 us hot call (225-cell rolls on rebuild only)
Build clean, 21 succeeded / 0 failed, verified by me in VoxelM.log.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
|
||
|
|
44938f3f98 |
fix(measure): a blocked route is not a disconnected pair
VF_AreConnected took the shortest coarse BFS route, re-walked it at full resolution, and on hitting a wall returned false and gave up -- never looking for another way round. That is a false NEGATIVE, and it was inflating the failure count of a bug we were actively trying to fix. On refutation it now excludes that specific coarse step and re-searches, up to a bounded budget (16). Outcomes are distinguished honestly: Connected a route survived full-resolution verification NotConnectedAtThisResolution no coarse route remains after exclusions CoarseLiedBudgetExhausted UNKNOWN -- routes kept being refuted The third is deliberately NOT folded into either other verdict. Collapsing "unknown" into "disconnected" is precisely what misled us. arrival->departure 7/16 -> 14/16 All 7 former COARSE_LIED became CONNECTED. arrival->spine 15/16 connected departure->spine 15/16 connected per-archetype law: 6/6 applicable routes pass Retries needed are small -- max 3 per query, 0 budget hits -- so 16 is generous rather than load-bearing. Runtime slightly IMPROVED (45.3 -> 44.9 s on the 16-seed sweep) because a satisfied query stops early. The false-POSITIVE guard is untouched and is now stronger in meaning: every Connected still requires a fully re-walked, air-verified route, and it now says "this specific route is open" rather than "the first route I tried was open". The controlled wall still returns BudgetExhausted after exactly 16 retries, and a repeat query is bit-identical in verdict, snap flags and retry count. ⚠️ REMAINING: seeds 6 and 14 are genuinely NOT_CONNECTED. Real or a step-2 resolution limit is not yet settled -- refine before touching generation again, per COMPOSER-NOTES §13. No generation, placement or parameter changed. Verifier only. Build clean, 20 succeeded / 0 warnings / 0 failed, confirmed in VoxelM.log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU |
||
|
|
5519b323c8 |
test(measure): the spine is the backbone in 7 of 8 strates -- and not in
VerticalShafts Diagnosis only. No generation or placement changed. The hypothesis was that the (0,0) spine (OriginSpineRadius 14) is the universal connector, so "can this mouth reach the spine" would be the real primordial-law test. Measured at margin 0, step 2, radius 192: strate spine share largest? arr->spine dep->spine TunnelNetwork 0.979 YES CONNECTED not connected FlatPlain 1.000 YES CONNECTED CONNECTED CrystalChamber 1.000 YES CONNECTED CONNECTED Maze 0.997 YES coarse-lied CONNECTED SurfaceWorld 1.000 YES CONNECTED CONNECTED VerticalShafts 0.096 NO not conn. not connected FloatingIslands 1.000 YES CONNECTED CONNECTED Underwater 0.999 YES CONNECTED n/a The spine forms one uninterrupted open interior run in 8/8 strates, and holds essentially ALL the air in 7 of them. VerticalShafts is the lone anomaly: the spine punches through as an isolated column holding 9.6% of the air and is NOT the largest component. So the archetype has three mutually unreachable things -- the spine, the arrival network, the departure network. It has no lateral connectivity at all. Over 16 seeds: spine open 16/16, spine largest only 8/16, arrival->spine 1 connected, departure->spine 0 connected. Neither extreme was right. The spine is not a universal connector, but it is far from irrelevant: it is the de-facto backbone everywhere except here, which reframes the bug as an ANOMALY to bring in line rather than a missing feature to invent. Ordering hazard confirmed on the record: making passage i depend on passage i-1 would break LayoutOrderIndependence. Both candidate fixes avoid it, because the spine is a fixed deterministic anchor that two mouths can target independently. Build clean, 19 succeeded / 0 warnings / 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bp7FxoQcSoiuhxjuLr6aBM |
||
|
|
0bbfdeb7a7 |
test(measure): VerticalShafts breaks the primordial law in 15 of 16 seeds
The margin is exonerated. Every earlier run excluded 8 voxels at each strate boundary, and voxels 4-8 of those are ordinary interior, not seal -- the same window mistake that produced "FlatPlain is 99.95% void" at margin 32. So the query was re-run at margin 0, 2, 4 and 8. All four stay negative; margin 0 includes the seal itself and still reports negative. (The implementation had clamped explicit margins to >= 1, which made the margin-0 check impossible; Codex found and fixed that.) Diagnosis -- this is not a near-miss wall: arrival LowerPoint (65.8, 95.8, -1047.25) departure UpperPoint (88.4, -104.6, -991.58) XY separation 201.7 voxels (~50 m), Z delta 55.7 arrival component 22,531 cells (22.0% of air) departure component 21,621 cells (21.1% of air) nearest approach between the two components: 135-179 voxels Two large, healthy, entirely separate shaft networks half a strate apart. Nothing to breach; the archetype has no lateral connection at all. 16-seed sweep: 1 pass, 15 fail. Not one unlucky world -- structural. So: descend into a VerticalShafts strate and you probably cannot reach its exit. This is the "live hole in the shipping game" COMPOSER-NOTES Tier 1 predicted, now measured instead of assumed. Stated precisely, because it names the fix: VF_SuggestLandingPoint makes each mouth land in open space, but NOTHING makes the two mouths of one strate land in the SAME open space. New: VF_DiagnoseConnectivity returns component sizes and the spatial separation between the two mouths' components, so a near-miss and a never-meant-to-connect read differently instead of both being "false". No generation change, no threshold tuning. The fix is a design decision. Build clean, 19 succeeded / 0 warnings / 0 failed. Sweeps 3.4 s + 6.9 s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bp7FxoQcSoiuhxjuLr6aBM |
||
|
|
cd31dec869 |
test(measure): the VerticalShafts disconnection is real, not a coarse lie
Our guard was one-directional. FullResolutionPathIsAir catches the false POSITIVE -- coarse says joined, reality says walled -- which is the dangerous direction for safety, so we built it first. The opposite error was unguarded: a corridor narrower than SampleStep falls between sample points and vanishes, so connected regions read as separate. That is exactly what a network of narrow vertical shafts would fake at step 4. Refinement sweep, radius held constant for the controls: step 4 / r256 NOT_CONNECTED 57 comp largest .119 458,752 cells step 4 / r192 NOT_CONNECTED 32 comp largest .220 258,048 step 2 / r192 NOT_CONNECTED 27 comp largest .220 2,064,384 step 4 / r128 NOT_CONNECTED 20 comp largest .291 114,688 step 1 / r128 NOT_CONNECTED 11 comp largest .303 7,340,032 Both fixed-radius pairs stayed negative, so neither the step nor the radius explains it. At one-voxel sampling the arrival and departure mouths are still in different air components. VerticalShafts is a live primordial-law violation: a player descends into it and cannot reach its exit. NotConnected is renamed NotConnectedAtThisResolution and documents its own limit at the declaration, so a bare negative can never again be read as proof. CoarseLied names one direction; this names the other. Sweep split into VoxelForge.Generation.StrateConnectivityRefinement so the fast test stays fast. 4.4 s. The MaxCells refusal path was exercised and still refuses. Build clean, 19 succeeded / 0 warnings / 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bp7FxoQcSoiuhxjuLr6aBM |
||
|
|
efcc8fcb65 |
fix(measure): ask the primordial law the right question
Three defects, two of them mine. 1. The anchor was a guess. LargestComponentAnchor() returned a hardcoded point near the (0,0) spine and the comment admitted it, so NOT_CONNECTED meant "not connected to a guessed point". The flood fill already knows the answer; it now returns LargestComponentPoint (tie- broken by lowest cell index, so it stays deterministic), LargestComponentCells and NumComponentsAtLeast1Pct. 2. "Connected to the largest component" is NOT the primordial law. The law is that a player must not descend into a place they cannot leave -- a question about MOUTHS. A strate can be 57 disconnected shafts and be fine, provided the shaft you arrive in is the shaft you leave from. The test now asks arrival -> departure per strate: the LowerPoint of the passage arriving, to the UpperPoint of the passage departing. 3. A false verdict and a true one were indistinguishable. VF_AreConnected returned false both when no route existed and when the endpoint's own coarse cell sampled solid -- and a passage mouth is a carved tube whose cell centre lands in adjacent rock easily. It now returns EVoxelConnectivityResult and snaps to an adjacent air cell first, reporting the repair. RESULT: the earlier "5 of 15 endpoints disconnected" was an ARTIFACT of the guessed anchor plus the wrong question. Corrected: 13 of 15 connected, 0 StartCellSolid, 0 GoalCellSolid. One real finding survives. arrival -> departure passes for 5 of 6 applicable strates; VerticalShafts FAILS -- its arrival mouth is in the largest component, its departure mouth is not. NOT yet reported as a bug: a coarse grid can falsely SEPARATE as well as falsely join, and a connecting corridor thinner than SampleStep vanishes. Next task settles it at a finer step. Also noted: GetPassages() contains a self-referential surface entry (0 -> 0), excluded from the chain law. Build clean, 18 succeeded / 0 warnings / 0 failed, verified in VoxelM.log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bp7FxoQcSoiuhxjuLr6aBM |
||
|
|
13399529b5 |
fix(measure): measure the strate, not the band above its floor
The window excluded a whole chunk (32 voxels) at each end to avoid a 4-voxel seal band -- 8x too much. On 4-chunk strates that discarded half of every strate, floor included, so the pass was measuring the open air above the ground and calling it the strate. The margin now derives from the seal that actually exists: ceil(2 x BoundarySealThickness), clamped to [1, height/4], overridable via InteriorMarginVoxels. The fixture resolves to 8 voxels. FlatPlain/CrystalChamber walkable fraction: 0.00044 -> 0.0578, ~132x. The "99.95% empty void" reading was the window, not the world. Metrics now carry ResolvedMarginVoxels, SampledMinZ and SampledMaxZ, and the test prints them. A measurement that does not state the volume it measured is exactly how the previous defect survived review. VerticalShafts largest-component share rose only 0.074 -> 0.119 across 57 components: the window explained part of the swiss-cheese signal, not all of it. That one is at least partly real. Coarse-lie guard still load-bearing: 24 routes checked, 3 refuted at full resolution (was 21 / 2). Build clean, 18 succeeded / 0 warnings / 0 failed, confirmed in Saved/Logs/VoxelM.log rather than from the agent's summary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bp7FxoQcSoiuhxjuLr6aBM |
||
|
|
6988e5ace1 |
feat(measure): one grid, one flood fill -- the strate measurement pass
Tier 2 slice 1 from COMPOSER-NOTES.md 3.4. A read-only, deterministic, headless measurement of a built strate: sample a coarse grid once, flood-fill the air once, derive every metric from that same grid. What it answers today: air fraction, air component count and largest component share (the swiss-cheese signal), walkable fraction, median feature scale, median vertical clearance -- and whether two points are actually connected. The connectivity verdict is the point. Coarse sampling LIES about it in the dangerous direction: a wall thinner than the sample step vanishes and two sealed caverns read as joined. So VF_AreConnected recovers the actual BFS route and re-walks that one corridor at one-voxel spacing; a true verdict is never returned on coarse evidence alone. It fired: 2 of 21 routes were coarse false positives. The test carries a negative control, because a green suite that cannot fail is what hid the last defect. Two of them, in fact: a direct probe of the known-solid inter-strate gap (AirFraction 0.00042), and -- Codex's improvement on the spec -- a diff-layer box filled solid and measured through VF_MeasureStrate itself, so an inverted sign is caught end to end rather than around the function under test. Build clean, 18 succeeded / 0 warnings / 0 failed. KNOWN, fix in flight: the interior window excludes a whole chunk (32 voxels) at each end to avoid a 4-voxel seal band -- 8x too much. On the fixture's 4-chunk strates that discards the floor, so the reported FlatPlain/CrystalChamber air fractions of 0.999 describe the open band above the floor, not the strate. The engine is right; the window is not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bp7FxoQcSoiuhxjuLr6aBM |