fix(core): smooth tunnel entrance floor; verifier keeps baselines out of /tmp (L47); research R14

The demo tunnel's hard floor cap used a vertical Z - floorZ on a sloped Bezier floor, which is not a signed
distance, so marching cubes produced a staircase through the first 7 m (40-51 mm ripple, 82 mm steps). The
cap now uses the grade-normalised signed distance with a floor-adjacent bore re-application fading over the
entrance; mesh tile Z bounds include cavern bounds. New three-rail entrance audit: L46 fails, L47 passes.
verify: the cost baseline exports only build inputs into Saved/Linux/<out>/baseline-<commit> and removes it;
/tmp unchanged after a full tier. Demo off: owner hashes identical to L46. fast/medium/full PASS.
Known: sawtooth at the tunnel wall foot (next round). Research R14: Windows/MSVC readiness review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-24 12:54:39 +02:00
1 parent e016f6a20f
commit c091e4434f
13 files changed
+839 -27

No files matched your search

@@ -0,0 +1,401 @@
#!/usr/bin/env python3
"""Capture L47 cavern entrance views and audit the exported floor profile."""
from __future__ import annotations
import argparse
import importlib.util
import json
import math
import os
from pathlib import Path
import subprocess
import sys
import time
ROOT = Path(__file__).resolve().parents[4]
VERIFY = ROOT / "Standalone/Tools/verify"
LINUX37 = ROOT / "Standalone/Research/WorldAssemblerM3/Linux37"
sys.path.insert(0, str(VERIFY))
from runner import bounded_run, current_stamp, first_stamp
def load_module(name: str, path: Path):
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise RuntimeError(f"cannot import {path}")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
PROFILE = load_module("l45_ring_profile", ROOT /
"Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py")
SHAPE = load_module("l45_shape_audit", LINUX37 / "AuditMouthShapeL37.py")
PARSER = argparse.ArgumentParser(description=__doc__)
PARSER.add_argument("--layout", type=Path, required=True)
PARSER.add_argument("--artifact-dir", type=Path, required=True)
PARSER.add_argument("--capture-dir", type=Path, required=True)
PARSER.add_argument("--data", type=Path, default=ROOT / "Standalone/data/owner_world.json")
OPTIONS = PARSER.parse_args()
ARTIFACT_DIR = OPTIONS.artifact_dir.resolve()
CAPTURE_DIR = OPTIONS.capture_dir.resolve()
LAYOUT_PATH = OPTIONS.layout.resolve()
ARTIFACT_DIR.mkdir(parents=True, exist_ok=True)
CAPTURE_DIR.mkdir(parents=True, exist_ok=True)
PNG_DIR = ARTIFACT_DIR / "capture-png"
PNG_DIR.mkdir(parents=True, exist_ok=True)
HEADLESS = Path(os.environ["VOXELFORGE_L37_HEADLESS"])
RENDERER = Path(os.environ["VOXELFORGE_L37_RENDERER"])
EXPECTED_STAMP = current_stamp()
def run_logged(name: str, command: list[str], *, capture: bool = False) -> str:
log = ARTIFACT_DIR / f"{name}.log"
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = bounded_run(command, timeout=1800, stdout=stream)
elapsed = time.monotonic() - started
output = log.read_text(encoding="utf-8", errors="replace")
if result is None:
raise RuntimeError(f"{name} timed out; see {log}")
print(f"[L47CaptureRun] step={name} elapsed={elapsed:.1f}s "
f"exit={result.returncode} log={log}", flush=True)
if result.returncode != 0:
raise RuntimeError(f"{name} failed; see {log}")
if capture and (first_stamp(output) != EXPECTED_STAMP
or first_stamp(bounded_run(
[str(command[0]), "--print-source-stamp"], timeout=15).stdout or "")
!= EXPECTED_STAMP):
raise RuntimeError(f"{name} executable source stamp mismatch")
return output
def stamp_by_id(layout: dict, stable_id: str) -> dict:
for stamp in layout.get("stamps", []):
if stamp.get("stable_id") == stable_id:
return stamp
raise RuntimeError(f"missing stamp {stable_id}")
def metres(stamp: dict, key: str = "position_um") -> tuple[float, float, float]:
return tuple(value / 1_000_000.0 for value in stamp[key])
def camera_angles(camera, target):
dx, dy, dz = (target[index] - camera[index] for index in range(3))
return (math.degrees(math.atan2(dx, dy)),
math.degrees(math.atan2(dz, math.hypot(dx, dy))))
def fmt(values) -> str:
return ",".join(f"{value:.6f}" for value in values)
def camera_spec(camera, target):
yaw, pitch = camera_angles(camera, target)
return (*camera, yaw, pitch)
def edge_point(edge: dict, t: float) -> tuple[float, float, float]:
points = edge["control_points_um"]
one = 1.0 - t
weights = (one ** 3, 3 * one ** 2 * t, 3 * one * t ** 2, t ** 3)
return tuple(sum(weights[index] * points[index][axis]
for index in range(4)) / 1_000_000.0
for axis in range(3))
def cavern_features(layout: dict):
if (layout.get("cavern_graph_vocabulary_version") != 1
or len(layout.get("cavern_rooms", [])) != 1
or len(layout.get("cavern_edges", [])) != 1):
raise RuntimeError("L47 capture needs one resolved cavern room and edge")
room = layout["cavern_rooms"][0]
edge = layout["cavern_edges"][0]
return room, edge
layout = json.loads(LAYOUT_PATH.read_text(encoding="utf-8"))
room, edge = cavern_features(layout)
portal_stamp = stamp_by_id(layout, "landmark/sw-arrival-chamber-mouth-approach-v1")
floor_stamp = stamp_by_id(layout, "landmark/sw-arrival-chamber-v1-floor")
dais_stamp = stamp_by_id(layout, "landmark/sw-arrival-chamber-v1-dais")
entrance = edge_point(edge, 0.0)
next_ramp = edge_point(edge, 0.08)
portal = metres(portal_stamp)
chamber = metres(room, "center_um")
floor = metres(floor_stamp)
floor_top = floor[2] + floor_stamp["half_extents_um"][2] / 1_000_000.0
dais = metres(dais_stamp)
dais_top = dais[2] + dais_stamp["half_extents_um"][2] / 1_000_000.0
ground_z = entrance[2]
direction_length = math.hypot(next_ramp[0] - entrance[0], next_ramp[1] - entrance[1])
direction = ((next_ramp[0] - entrance[0]) / direction_length,
(next_ramp[1] - entrance[1]) / direction_length)
outward_length = math.hypot(entrance[0] - chamber[0], entrance[1] - chamber[1])
outward = ((entrance[0] - chamber[0]) / outward_length,
(entrance[1] - chamber[1]) / outward_length)
mouth_normal = (-direction[1], direction[0])
mesh = ARTIFACT_DIR / "landmark-l47.vfmesh"
export_anchor = edge_point(edge, 0.25)
export_camera = (export_anchor[0], export_anchor[1], export_anchor[2] + 1.4)
export_target_floor = edge_point(edge, 0.42)
export_target = (export_target_floor[0], export_target_floor[1],
export_target_floor[2] + 1.2)
run_logged("landmark-export", [str(HEADLESS), str(OPTIONS.data), "--workers=12",
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--force-generate", "--clip-radius=4", "--max-clip-level=0", "--max-level=0",
"--render-distance-chunks=0", "--recipe=surface-world", "--landmark-chamber-demo",
f"--camera={fmt(camera_spec(export_camera, export_target))}",
f"--mesh-export={mesh}", f"--resolved-layout-output={LAYOUT_PATH}"], capture=True)
layout = json.loads(LAYOUT_PATH.read_text(encoding="utf-8"))
room, edge = cavern_features(layout)
portal_stamp = stamp_by_id(layout, "landmark/sw-arrival-chamber-mouth-approach-v1")
floor_stamp = stamp_by_id(layout, "landmark/sw-arrival-chamber-v1-floor")
dais_stamp = stamp_by_id(layout, "landmark/sw-arrival-chamber-v1-dais")
entrance = edge_point(edge, 0.0)
next_ramp = edge_point(edge, 0.08)
portal = metres(portal_stamp)
chamber = metres(room, "center_um")
floor = metres(floor_stamp)
floor_top = floor[2] + floor_stamp["half_extents_um"][2] / 1_000_000.0
dais = metres(dais_stamp)
dais_top = dais[2] + dais_stamp["half_extents_um"][2] / 1_000_000.0
ground_z = entrance[2]
direction_length = math.hypot(next_ramp[0] - entrance[0], next_ramp[1] - entrance[1])
direction = ((next_ramp[0] - entrance[0]) / direction_length,
(next_ramp[1] - entrance[1]) / direction_length)
outward_length = math.hypot(entrance[0] - chamber[0], entrance[1] - chamber[1])
outward = ((entrance[0] - chamber[0]) / outward_length,
(entrance[1] - chamber[1]) / outward_length)
if not mesh.is_file() or mesh.stat().st_size == 0:
raise RuntimeError(f"missing mesh export {mesh}")
entrance_audit = ROOT / "Standalone/Research/WorldAssemblerM3/Linux47/MeasureEntranceL47.py"
run_logged("entrance-floor-control-l46", [sys.executable, str(entrance_audit),
"--mesh", str(ROOT / "Saved/Linux/l46/landmark-l46.vfmesh"),
"--layout", str(ROOT / "Saved/Linux/l46/layouts/landmark-reachability-clang.json"),
"--out", str(ARTIFACT_DIR / "entrance-profile-control-l46.json"), "--expect=fail"])
run_logged("entrance-floor-audit", [sys.executable, str(entrance_audit),
"--mesh", str(mesh), "--layout", str(LAYOUT_PATH),
"--out", str(ARTIFACT_DIR / "entrance-profile-l47.json"), "--expect=pass"])
voxel_um, triangles, tile_count = PROFILE.read_scene(str(mesh), 0,
floor_top - 8.0, entrance[2] + 9.0,
(min(entrance[0], chamber[0]) - 20.0, max(entrance[0], chamber[0]) + 20.0,
min(entrance[1], chamber[1]) - 20.0, max(entrance[1], chamber[1]) + 20.0))
bounds = (min(entrance[0], chamber[0]) - 20.0, max(entrance[0], chamber[0]) + 20.0,
min(entrance[1], chamber[1]) - 20.0, max(entrance[1], chamber[1]) + 20.0)
buckets = PROFILE.build_buckets(triangles, True, bounds, cell_size=1.0)
ground_candidates = []
for ix in range(-20, 21):
for iy in range(-20, 21):
x, y = portal[0] + ix * 0.5, portal[1] + iy * 0.5
dx, dy = x - portal[0], y - portal[1]
distance = math.hypot(dx, dy)
if not 6.0 <= distance <= 10.0:
continue
hit = PROFILE.sample_surface(buckets, x, y, True, 1.0,
preferred_height=ground_z)
if hit is None or abs(hit[0] - ground_z) > 3.0 or hit[1][2] < 0.85:
continue
alignment = max(-1.0, min(1.0,
(dx * mouth_normal[0] + dy * mouth_normal[1]) / distance))
angle = math.acos(alignment)
score = abs(distance - 7.0) + angle * 4.0
ground_candidates.append((score, x, y, hit[0]))
if not ground_candidates:
raise RuntimeError("no walkable mesh ground lies 6 to 10 m from the cave mouth")
_, ground_x, ground_y, outside_ground = min(ground_candidates)
inside_floor = PROFILE.sample_surface(buckets, chamber[0], chamber[1], True, 1.0,
preferred_height=floor_top)
if inside_floor is None or abs(inside_floor[0] - floor_top) > 1.5:
raise RuntimeError(f"chamber floor mesh hit {inside_floor} does not match designed floor {floor_top:.3f} m")
floor_top = inside_floor[0]
ground_camera = (ground_x, ground_y, outside_ground + 1.6)
entry_ground = PROFILE.sample_surface(buckets, entrance[0], entrance[1], True, 1.0,
preferred_height=ground_z)
if entry_ground is None:
raise RuntimeError("cave mouth has no walkable ground surface in the exported mesh")
entry_camera = (entrance[0] + mouth_normal[0] * 3.8,
entrance[1] + mouth_normal[1] * 3.8, ground_z + 1.6)
entry_target_floor = edge_point(edge, 0.30)
entry_target = (entry_target_floor[0], entry_target_floor[1], entry_target_floor[2] + 1.4)
low_camera = (entrance[0] + outward[0] * 1.0,
entrance[1] + outward[1] * 1.0, ground_z + 0.65)
low_target_floor = edge_point(edge, 0.24)
low_target = (low_target_floor[0], low_target_floor[1], low_target_floor[2] + 0.15)
to_dais = (dais[0] - chamber[0], dais[1] - chamber[1])
to_dais_length = math.hypot(*to_dais)
opposite_dais = (-to_dais[0] / to_dais_length, -to_dais[1] / to_dais_length)
inside_x = chamber[0]
inside_y = chamber[1]
inside_floor = PROFILE.sample_surface(buckets, inside_x, inside_y, True, 1.0,
preferred_height=floor_top)
inside_eye_z = inside_floor[0] + 1.6
chamber_camera = (chamber[0] + opposite_dais[0] * 5.5,
chamber[1] + opposite_dais[1] * 5.5, inside_eye_z)
chamber_target = (dais[0], dais[1], dais_top - 0.25)
overview_camera = (entrance[0] + outward[0] * 18.0,
entrance[1] + outward[1] * 18.0, outside_ground + 15.0)
overview_target = ((entrance[0] + chamber[0]) * 0.5,
(entrance[1] + chamber[1]) * 0.5,
(ground_z + floor_top) * 0.5)
inward = (-outward[0], -outward[1])
clip_point = (entrance[0] + outward[0] * 0.5,
entrance[1] + outward[1] * 0.5)
overview_clip = (inward[0], inward[1], 0.0,
-(inward[0] * clip_point[0] + inward[1] * clip_point[1]))
views = (
("ground-entrance", ground_camera, (portal[0], portal[1], portal[2]),
(portal[0], portal[1], ground_z, 20.0), -509.0),
("entrance-looking-in", entry_camera, entry_target, (entrance[0], entrance[1], entrance[2], 16.0), -507.0),
("entrance-floor-low", low_camera, low_target,
(entrance[0], entrance[1], entrance[2], 12.0), ground_z),
("inside-to-dais", chamber_camera, chamber_target,
(chamber[0], chamber[1], chamber[2], 15.0), chamber[2]),
("overview-entrance-and-chamber", overview_camera, overview_target,
((entrance[0] + chamber[0]) * 0.5,
(entrance[1] + chamber[1]) * 0.5,
(ground_z + floor_top) * 0.5, 34.0), -509.0),
)
for name, camera, target, focus, height_center in views:
png = PNG_DIR / f"{name}.png"
jpg = CAPTURE_DIR / f"{name}.jpg"
yaw, pitch = camera_angles(camera, target)
fov = (80.0 if name == "overview-entrance-and-chamber" else
70.0 if name in ("inside-to-dais", "entrance-looking-in") else
58.0 if name == "entrance-floor-low" else 65.0
if name == "ground-entrance" else 60.0)
if name == "ground-entrance":
fov = 55.0
print(f"[L47CaptureCamera] view={name} camera_m=({fmt(camera)}) "
f"target_m=({fmt(target)}) yaw_deg={yaw:.3f} pitch_deg={pitch:.3f} "
f"fov_deg={fov:.1f} entrance_distance_m={math.dist(camera, portal):.3f}", flush=True)
render = [str(RENDERER), str(mesh), str(png), "--size=1600x900", f"--fov-deg={fov:.1f}",
f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}", f"--pitch-deg={pitch:.6f}",
"--shade=readable", "--headlamp=on", "--height-tint=on",
"--light-direction=0.3,-0.4,0.85",
f"--height-center-m={(floor_top if name == 'inside-to-dais' else height_center):.6f}",
f"--height-range-m={4 if name == 'inside-to-dais' else 6 if name == 'entrance-floor-low' else 16}",
"--tile-skirts=off", "--tile-edges=off",
f"--focus-region={fmt(focus)}"]
if name == "overview-entrance-and-chamber":
render.append(f"--clip-plane={fmt(overview_clip)}")
run_logged(f"{name}-render", render, capture=True)
for quality in (4, 6, 8):
run_logged(f"{name}-jpeg-q{quality}", ["ffmpeg", "-nostdin", "-hide_banner",
"-loglevel", "error", "-y", "-i", str(png), "-frames:v", "1",
"-q:v", str(quality), str(jpg)])
if jpg.is_file() and 0 < jpg.stat().st_size <= 300_000:
break
if not jpg.is_file() or not 0 < jpg.stat().st_size <= 300_000:
raise RuntimeError(f"{jpg.name} exceeds the 300 KB capture limit")
quality_log = run_logged(f"{name}-quality", [sys.executable,
str(VERIFY / "capture_quality.py"), str(jpg)])
print(f"[L47CaptureJPEG] file={jpg} bytes={jpg.stat().st_size} "
f"quality_log={quality_log.strip().replace(chr(10), ' ')} status=PASS", flush=True)
def region_triangles(center, radius_xy, z_min, z_max):
selected = []
for triangle in triangles:
points = tuple(vertex[0] for vertex in triangle[2])
centroid = tuple(sum(point[axis] for point in points) / 3.0 for axis in range(3))
if (math.hypot(centroid[0] - center[0], centroid[1] - center[1]) <= radius_xy
and z_min <= centroid[2] <= z_max):
selected.append(triangle)
return selected
def tunnel_wall_triangles(edge, t, height_above_floor):
center = edge_point(edge, t)
before = edge_point(edge, max(0.0, t - 0.01))
after = edge_point(edge, min(1.0, t + 0.01))
tx, ty = after[0] - before[0], after[1] - before[1]
tangent_length = math.hypot(tx, ty)
tx, ty = tx / tangent_length, ty / tangent_length
expected_radius = edge["bore_radius_um"] / 1_000_000.0 * math.sqrt(
1.0 - (height_above_floor / (edge["bore_height_um"] / 1_000_000.0)) ** 2)
selected = []
for triangle in triangles:
points = tuple(vertex[0] for vertex in triangle[2])
centroid = tuple(sum(point[axis] for point in points) / 3.0 for axis in range(3))
dx, dy = centroid[0] - center[0], centroid[1] - center[1]
longitudinal = dx * tx + dy * ty
lateral = abs(-dy * tx + dx * ty)
vertical = centroid[2] - center[2]
if (abs(longitudinal) <= 1.5
and abs(lateral - expected_radius) <= 0.7
and abs(vertical - height_above_floor) <= 0.75):
selected.append(triangle)
print(f"[L47TunnelWallWindow] t={t:.3f} center_m=({center[0]:.3f},"
f"{center[1]:.3f},{center[2]:.3f}) expected_radius_m={expected_radius:.3f} "
f"longitudinal_half_width_m=1.500 vertical_half_width_m=0.750 "
f"triangles={len(selected)}", flush=True)
return selected
def audit_shape(name, region, center):
metrics = SHAPE.analyze_triangles(region, center[:2], center[2],
crop_to_center=False, include_sides=True)
passed = (metrics["triangles"] > 0
and metrics["planar_area"] < SHAPE.PLANE_LIMIT_M2
and metrics["straight"] <= SHAPE.STRAIGHT_LIMIT_M
and metrics["corners"] == 0)
print(f"[LandmarkBuiltShape] region={name} triangles={metrics['triangles']} "
f"planar_patch_m2={metrics['planar_area']:.4f} "
f"straight_run_m={metrics['straight']:.4f} sharp_corners={metrics['corners']} "
f"corner_turn_max_deg={metrics['corner_turn_max']:.2f} "
f"thresholds=({SHAPE.PLANE_LIMIT_M2:.2f}m2,{SHAPE.STRAIGHT_LIMIT_M:.2f}m,"
f"{SHAPE.CORNER_TURN_LIMIT_DEG:.0f}deg) status={'PASS' if passed else 'FAIL'}",
flush=True)
if not passed:
raise RuntimeError(f"L37 built-shape thresholds failed in {name}")
return metrics
entrance_metrics = audit_shape("cave-mouth", region_triangles(
portal, 5.0, ground_z + 0.8, ground_z + 4.5),
(portal[0], portal[1], ground_z))
tunnel_mid = edge_point(edge, 0.64)
tunnel_metrics = audit_shape("tunnel-wall-edge", tunnel_wall_triangles(
edge, 0.64, 1.2),
(tunnel_mid[0], tunnel_mid[1], tunnel_mid[2] + 1.2))
dais_surface = PROFILE.sample_surface(buckets, dais[0], dais[1], True, 1.0,
preferred_height=dais_top - 0.4)
if dais_surface is None:
raise RuntimeError("dais stamp has no upward-facing surface in the exported mesh")
dais_rise = dais_surface[0] - floor_top
print(f"[LandmarkDaisSurface] center_m=({dais[0]:.3f},{dais[1]:.3f}) "
f"floor_z_m={floor_top:.3f} top_z_m={dais_surface[0]:.3f} "
f"rise_m={dais_rise:.3f} minimum_m=1.000 "
f"status={'PASS' if dais_rise >= 1.0 else 'FAIL'}", flush=True)
if dais_rise < 1.0:
raise RuntimeError(f"dais rises only {dais_rise:.3f} m above the chamber floor")
dais_metrics = audit_shape("dais-platform", region_triangles(
dais, 1.7, floor_top + 0.6, dais_top + 0.4),
(dais[0], dais[1], dais_surface[0]))
metrics_path = ARTIFACT_DIR / "landmark-shape-metrics.json"
metrics_path.write_text(json.dumps({
"graph_vocabulary_version": layout["cavern_graph_vocabulary_version"],
"entry_m": entrance, "portal_m": portal, "entry_ground_m": ground_z,
"chamber_center_m": chamber, "chamber_floor_m": floor_top,
"tunnel_midpoint_m": tunnel_mid,
"dais_center_m": dais, "dais_top_m": dais_top,
"dais_surface_m": dais_surface[0], "dais_rise_m": dais_rise,
"l37_thresholds": {"planar_patch_m2_lt": SHAPE.PLANE_LIMIT_M2,
"straight_run_m_lte": SHAPE.STRAIGHT_LIMIT_M,
"corner_turn_deg_lt": SHAPE.CORNER_TURN_LIMIT_DEG},
"entrance_arch": entrance_metrics, "tunnel_wall": tunnel_metrics,
"dais_platform": dais_metrics,
"mesh_tiles": tile_count, "mesh_voxel_um": voxel_um,
}, indent=2) + "\n", encoding="utf-8")
print(f"[L47CaptureShapeMetrics] file={metrics_path} status=PASS", flush=True)
@@ -0,0 +1,208 @@
#!/usr/bin/env python3
"""Measure the L46 entrance floor from open terrain through its first ten metres."""
from __future__ import annotations
import importlib.util
import argparse
import json
import math
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[4]
PROFILE_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py"
spec = importlib.util.spec_from_file_location("ring_profile", PROFILE_PATH)
if spec is None or spec.loader is None:
raise RuntimeError(f"cannot load {PROFILE_PATH}")
PROFILE = importlib.util.module_from_spec(spec)
spec.loader.exec_module(PROFILE)
ARTIFACTS = ROOT / "Saved/Linux/l47"
PARSER = argparse.ArgumentParser(description=__doc__)
PARSER.add_argument("--layout", type=Path,
default=ROOT / "Saved/Linux/l46/layouts/landmark-reachability-clang.json")
PARSER.add_argument("--mesh", type=Path,
default=ROOT / "Saved/Linux/l46/landmark-l46.vfmesh")
PARSER.add_argument("--out", type=Path, default=ARTIFACTS / "entrance-profile-before.json")
PARSER.add_argument("--expect", choices=("pass", "fail", "measure"), default="measure")
PARSER.add_argument("--lod", default="0", help="LOD level, or 'all' to include every exported level")
OPTIONS = PARSER.parse_args()
LAYOUT = OPTIONS.layout.resolve()
MESH = OPTIONS.mesh.resolve()
OUT = OPTIONS.out.resolve()
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
edge = layout["cavern_edges"][0]
controls = [[value / 1_000_000.0 for value in point]
for point in edge["control_points_um"]]
def bezier(t: float) -> tuple[float, float, float]:
u = 1.0 - t
weights = (u ** 3, 3.0 * u * u * t, 3.0 * u * t * t, t ** 3)
return tuple(sum(weights[i] * controls[i][axis] for i in range(4))
for axis in range(3))
route = [bezier(i / 4096.0) for i in range(4097)]
arc = [0.0]
for a, b in zip(route, route[1:]):
arc.append(arc[-1] + math.hypot(b[0] - a[0], b[1] - a[1]))
def route_at(distance: float) -> tuple[float, float, float]:
if distance < 0.0:
start = route[0]
dx = route[1][0] - start[0]
dy = route[1][1] - start[1]
dz = route[1][2] - start[2]
horizontal = math.hypot(dx, dy)
scale = distance / (arc[1] - arc[0])
return start[0] + dx * scale, start[1] + dy * scale, start[2] + dz * scale
index = min(len(arc) - 2, max(0, next(
(i for i, value in enumerate(arc) if value >= distance), len(arc) - 1) - 1))
span = arc[index + 1] - arc[index]
t = 0.0 if span <= 0.0 else (distance - arc[index]) / span
return tuple(route[index][axis] + (route[index + 1][axis] - route[index][axis]) * t
for axis in range(3))
entry = route[0]
tangent = route[1]
tx, ty = tangent[0] - entry[0], tangent[1] - entry[1]
length = math.hypot(tx, ty)
tx, ty = tx / length, ty / length
nx, ny = -ty, tx
requested_lod = None if OPTIONS.lod == "all" else int(OPTIONS.lod)
voxel_um, triangles, tile_count = PROFILE.read_scene(
str(MESH), requested_lod, entry[2] - 7.0, entry[2] + 5.0,
(entry[0] - 14.0, entry[0] + 14.0, entry[1] - 14.0, entry[1] + 14.0))
buckets = PROFILE.build_buckets(
triangles, True,
(entry[0] - 14.0, entry[0] + 14.0, entry[1] - 14.0, entry[1] + 14.0),
cell_size=1.0)
down_buckets = PROFILE.build_buckets(
triangles, False,
(entry[0] - 14.0, entry[0] + 14.0, entry[1] - 14.0, entry[1] + 14.0),
cell_size=1.0)
mesh_points = [vertex[0] for triangle in triangles for vertex in triangle[2]]
print(f"[EntranceProfileGeometry] entry_m=({entry[0]:.3f},{entry[1]:.3f},{entry[2]:.3f}) "
f"point10_m=({route_at(10.0)[0]:.3f},{route_at(10.0)[1]:.3f},"
f"{route_at(10.0)[2]:.3f}) mesh_xy=({min(p[0] for p in mesh_points):.3f},"
f"{max(p[0] for p in mesh_points):.3f},{min(p[1] for p in mesh_points):.3f},"
f"{max(p[1] for p in mesh_points):.3f}) mesh_z=({min(p[2] for p in mesh_points):.3f},"
f"{max(p[2] for p in mesh_points):.3f})")
for sample_distance in range(11):
sx, sy, expected = route_at(float(sample_distance))
bucket = buckets.get((math.floor(sx), math.floor(sy)), ())
hit = PROFILE.sample_surface(buckets, sx, sy, True, 1.0,
preferred_height=expected)
down_hit = PROFILE.sample_surface(down_buckets, sx, sy, False, 1.0,
preferred_height=expected)
print(f"[EntranceProfileBucket] distance_m={sample_distance:.1f} "
f"xy_m=({sx:.3f},{sy:.3f}) triangles={len(bucket)} "
f"hit_z_m={None if hit is None else round(hit[0], 4)} "
f"down_hit_z_m={None if down_hit is None else round(down_hit[0], 4)} "
f"expected_z_m={expected:.4f}")
rows = []
for rail, lateral in (("left", -1.5), ("center", 0.0), ("right", 1.5)):
distance = -5.0
while distance <= 10.000001:
x, y, expected = route_at(distance)
x += nx * lateral
y += ny * lateral
hit = PROFILE.sample_surface(buckets, x, y, True, 1.0,
preferred_height=expected)
rows.append({
"rail": rail,
"distance_m": round(distance, 3),
"x_m": x,
"y_m": y,
"expected_floor_m": expected,
"surface_z_m": None if hit is None else hit[0],
"normal_z": None if hit is None else hit[1][2],
"residual_m": None if hit is None else hit[0] - expected,
})
distance += 0.125
results = {}
for rail in ("left", "center", "right"):
profile = [row for row in rows if row["rail"] == rail and row["surface_z_m"] is not None
and 0.0 <= row["distance_m"] <= 10.0]
jumps = []
for previous, current in zip(profile, profile[1:]):
expected_delta = current["expected_floor_m"] - previous["expected_floor_m"]
residual_delta = current["residual_m"] - previous["residual_m"]
if abs(residual_delta) >= 0.025:
jumps.append({"from_m": previous["distance_m"], "to_m": current["distance_m"],
"delta_m": current["surface_z_m"] - previous["surface_z_m"],
"residual_delta_m": residual_delta,
"expected_delta_m": expected_delta})
residuals = [row["residual_m"] for row in profile]
ripple_residuals = []
for row in profile:
nearby = [other["residual_m"] for other in profile
if abs(other["distance_m"] - row["distance_m"]) <= 1.0]
ripple_residuals.append((row["distance_m"], row["residual_m"]
- sum(nearby) / max(1, len(nearby))))
amplitude, period = 0.0, 0.0
for candidate in (0.375 + index * 0.125 for index in range(30)):
cosine = sum(value * math.cos(2.0 * math.pi * distance / candidate)
for distance, value in ripple_residuals)
sine = sum(value * math.sin(2.0 * math.pi * distance / candidate)
for distance, value in ripple_residuals)
current_amplitude = 2.0 * math.hypot(cosine, sine) / max(1, len(ripple_residuals))
if current_amplitude > amplitude:
amplitude, period = current_amplitude, candidate
step_error = max((abs(item["residual_delta_m"]) for item in jumps), default=0.0)
coverage = len(profile) / 81.0
rail_passed = (coverage >= 0.85 and amplitude <= 0.015 and step_error <= 0.05)
results[rail] = {
"coverage": coverage,
"max_adjacent_step_m": max((abs(item["delta_m"]) for item in jumps), default=0.0),
"max_step_residual_m": step_error,
"ripple_amplitude_m": amplitude,
"ripple_period_m": period,
"passed": rail_passed,
"jumps_over_0_04m": jumps,
"residual_peak_to_peak_m": max(residuals) - min(residuals) if residuals else None,
}
payload = {
"layout": str(LAYOUT.relative_to(ROOT)),
"mesh": str(MESH.relative_to(ROOT)),
"voxel_um": voxel_um,
"selected_tiles": tile_count,
"sample_spacing_m": 0.125,
"distance_range_m": [-5.0, 10.0],
"lateral_offsets_m": {"left": -1.5, "center": 0.0, "right": 1.5},
"rails": results,
"samples": rows,
}
all_passed = all(result["passed"] for result in results.values())
expected_ok = (OPTIONS.expect == "measure"
or (OPTIONS.expect == "pass" and all_passed)
or (OPTIONS.expect == "fail" and not all_passed))
OUT.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
print(f"[EntranceProfileBefore] voxel_um={voxel_um} tiles={tile_count} "
f"lod={OPTIONS.lod} spacing_m=0.125 distance_m=-5.000..10.000 status=MEASURED")
for rail, summary in results.items():
print(f"[EntranceFloorRailAudit] rail={rail} coverage={summary['coverage']:.3f} "
f"ripple_amplitude_m={summary['ripple_amplitude_m']:.4f} "
f"ripple_period_m={summary['ripple_period_m']:.3f} "
f"max_step_residual_m={summary['max_step_residual_m']:.4f} "
f"status={'PASS' if summary['passed'] else 'FAIL'}")
for jump in summary["jumps_over_0_04m"]:
print(f"[EntranceProfileJump] rail={rail} from_m={jump['from_m']:.3f} "
f"to_m={jump['to_m']:.3f} delta_m={jump['delta_m']:+.4f} "
f"expected_delta_m={jump['expected_delta_m']:+.4f} "
f"residual_delta_m={jump['residual_delta_m']:+.4f}")
failed_rails = sum(not result["passed"] for result in results.values())
print(f"[EntranceFloorMeshAudit] samples_per_rail=81 distance_m=0.000..10.000 "
f"rails=3 failed_rails={failed_rails} observed={'PASS' if all_passed else 'FAIL'} "
f"expected={OPTIONS.expect.upper()} status={'PASS' if expected_ok else 'FAIL'}")
print(f"[EntranceProfileJSON] file={OUT} status={'PASS' if expected_ok else 'FAIL'}")
if not expected_ok:
raise SystemExit(1)
@@ -0,0 +1,38 @@
# Linux47 report
## Entrance floor
The L46 mesh has a repeating staircase through the first 7 m: 39.9–51.4 mm ripple and 81.8–82.9 mm largest residual step. The cause was the final hard floor cap, applied after the mouth and terrain stamps, using vertical `Z-floorZ` on a sloped Bezier floor. That field is not a signed distance to the ramp, so Marching Cubes interpolated a distorted gradient; stamp fill beside the cap made the ridges visible. Voxel height quantisation was not the main cause.
The cap now uses the grade-normalised signed distance. The tunnel SDF is reapplied in a floor-adjacent band, with the wider correction fading over the first 10% of the entrance; the narrow floor correction continues along the ramp. This removes the ridges without changing the tunnel wall profile. Mesh tile Z bounds now include resolved cavern bounds too; without that, the exported ramp lost triangles past about 6.6 m.
The mesh audit samples 0–10 m at 0.125 m on three rails (−1.5, 0, +1.5 m), requiring ≥85% coverage, ≤15 mm ripple and ≤50 mm adjacent residual step. L46 fails all rails: coverage 0.63–0.68, ripple 39.9–51.4 mm, step 81.8–82.9 mm. L47 passes all rails: 100% coverage, ripple 0.4–1.2 mm, no measured residual step. The Core audit also passes 243 samples with 0.3 mm maximum step and 0.0 mm reported ripple. Buried L43 and segmented-ramp L45 controls still fail as expected.
## Verifier, hashes and cost
The cost baseline now exports only Core, Source, Compat, and the build/helper scripts to `Saved/Linux/l47/baseline-e016f6a20fcfac785c9870b3d1133fd5df47a6d4`; an exit handler removes the export. It does not copy the repository to `/tmp`. `/tmp` was **28K before** and **28K after** the full tier; the saved top-level-entry comparison is identical and the baseline export was removed.
Source stamp / source manifest: `68328b0c2a154e261eba94a067eb7d879f37ef987a54758275b4d14e3e9746d3`. Demo-off owner hashes match L46 exactly for both recipes, with W1=W12. Whole-view hashes (density/mesh/payload/topology): floating-islands `598e9daaa1744ccb / 3114e0f4f6dbf734 / c49c4e48bff74089 / cadd4edd10d4d8ef`; surface-world `7dc75e2c09302258 / 0cb34c40ceec4ce3 / 9d23e8c46b515cf1 / 2962c9464991eedd`. Demo-on determinism passed 30/30 cells.
Five-pair median cost: floating-islands 16.390→16.435 s (+0.27%); surface-world 1.722→1.717 s (−0.29%). The baseline build took 40.2 s.
## Tiers, captures and failed runs
Fast **PASS** (195.9 s), medium **PASS** (567.8 s), full **PASS** (1606.5 s); zero failed checks. All used source stamp above. Final captures: [ground entrance](captures/ground-entrance.jpg), [looking in](captures/entrance-looking-in.jpg), [overview](captures/overview-entrance-and-chamber.jpg), [inside to dais](captures/inside-to-dais.jpg), and [low entrance floor](captures/entrance-floor-low.jpg).
Exploration failures: the first entrance gate caught a 171 mm step; stamp-only re-carving restored about 40 mm ripple and a 100 mm step; full-width re-carving failed the wall-shape check at t=0.58. The final mouth-local fade passed the wall check at t=0.64. The first fast invocation also stopped because the executable had not yet been built; the rebuilt fast tier passed. L46/L43/L45 negative controls are intentional failures and passed their expected-fail checks. Work stayed within the 15-trial budget.
## Orchestrator review (Claude)
Accepted, with one new defect logged. The stamp matches the final Clang and GCC builds (`68328b0c…`); demo-off hashes
are identical to L46; `/tmp` is unchanged after the full tier (1.8 MB used). The entrance floor is smooth now, and
the cause, a vertical cap distance on a sloped floor, was measured before the fix.
**New defect:** a row of **jagged sawtooth teeth along the wall foot,** where the floor-adjacent band correction meets
the bore wall. It shows in `entrance-looking-in.jpg` and along the edge in the overview. The three floor rails (−1.5,
0, +1.5 m) don't reach the 2.6 m wall foot. **This is the third time** a fix has moved an artifact to where the
audit isn't looking (L46 inside-only, L47 rails only). L48 adds a whole-surface artifact audit.
R14: no likely MSVC or UHT blocker. The Unreal adapter does not resolve stamps or cavern graphs, so the L43–L47 demos
are standalone-only until the adapter supplies those records. The legacy chunk callers agree with the micrometre
layout only at the default 8 m width.
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

@@ -0,0 +1,31 @@
# VoxelForge R14 — Windows build review
**Verdict:** I found no likely MSVC or UHT compile blocker in the requested diff. This is a source review, not a build result. Runtime behavior has one important condition: the legacy chunk callers align with the new physical layout only at the default 8 m width.
## Compile and UHT review
The diff adds no designated initializers, `__int128`, GNU builtins, `[[likely]]`, or `std::visit` use. The `std::variant` code has `<variant>` available; Core uses C++20, and the `std::llround` calls have `<cmath>` available. The new `FStrateSlot` metadata is plain non-`UPROPERTY` data.
| File:line | Issue | Surety | One-line fix |
|---|---|---|---|
| [CoreNamespacePreamble.h:60](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/CoreNamespacePreamble.h:60), [VoxelForgeCore.Build.cs:41](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForgeCore/VoxelForgeCore.Build.cs:41) | Toolchain check: the Core build uses C++20 and MSVC’s `fp_contract` pragma. Core pins C++20 and precise FP; confirm the owner’s UBT-selected MSVC version and flags. | check | Use the UE-supported VS2022 toolset and confirm `/std:c++20` and `/fp:precise` in the build log. |
Epic documents C++20 as UE’s default and minimum, and UBT’s module settings come from `.Build.cs` files ([C++ standard](https://dev.epicgames.com/documentation/unreal-engine/epic-cplusplus-coding-standard-for-unreal-engine), [module properties](https://dev.epicgames.com/documentation/unreal-engine/module-properties-in-unreal-engine)). Microsoft documents that `/fp:precise` disables contraction by default starting with Visual Studio 2022; earlier versions could contract, while the Core preamble explicitly requests `fp_contract(off)` ([`/fp`](https://learn.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior?view=msvc-170), [`fp_contract`](https://learn.microsoft.com/en-us/cpp/preprocessor/fp-contract?view=msvc-160)).
## Behaviour risks
- **Chunk coordinates versus micrometre bounds:** [VoxelStrateManager.cpp:1201](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelStrateManager.cpp:1201) resolves physical layout width in micrometres, but [GetStrateForChunk/GetStrateChunkZBounds](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelStrateManager.cpp:6298) and [IsGapChunk](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelStrateManager.cpp:6398) still treat `ChunkCoord.Z` as a layout-chunk index. Normal world chunk coordinates use `CHUNK_SIZE` voxels ([VoxelTypes.h:285](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelTypes.h:285)). Those units match at the default 8 m width with 25 cm voxels; the setting also allows 0.25–64 m. At other widths, generator selection, gap detection, and transition distances can disagree with the physical slot bounds. Enforce the matching width or convert these chunk APIs using the actual streaming-chunk width.
- **Stamp and cavern graph records:** The Unreal adapter builds strata and passages, then finalizes its layout JSON ([VoxelForgeCoreAdapter.cpp:1078](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp:1078), [1260](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp:1260)); it does not call either stamp or graph resolver. The Core world is active in Unreal, but the adapter currently supplies it with no L43–L46 stamp or graph records. The current headless demo calls those resolvers directly ([StreamBenchmarkHeadless.cpp:933](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamBenchmarkHeadless.cpp:933)). To get that geometry in Unreal, the adapter needs to build and serialize those records.
- **Bezier FP contraction:** The new Newton solve is in [ShapeWorldInternal.h:6090](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:6090). Core’s compile preamble disables contraction for MSVC, and `World::compile` runs a contraction self-check before compiling the shape. The solve itself has no graph input from the current Unreal adapter, so it should not execute in the current plugin path. Confirm the owner’s MSVC toolset; Microsoft documents version-dependent `/fp:precise` defaults as above.
## First-build checklist
1. Confirm the UE minor version and UBT-selected VS2022/MSVC toolset.
2. Build the Win64 Development Editor target through UBT so it runs UHT and compiles both plugin modules; UBT takes module settings from `.Build.cs`.
3. Check the first UHT or MSVC diagnostic, then confirm the Core compile flags include C++20 and precise FP.
4. Launch the editor with the default 8 m layout and confirm Core world initialization succeeds, including its FP self-check.
5. If using a non-default layout width, resolve the chunk-coordinate mismatch before relying on streamed generator and gap behavior. Do not expect the stamp/graph demo geometry in Unreal until the adapter supplies those records.
I left the tree untouched. `git status` showed `Standalone/Research/WorldAssemblerM3/Linux18/exports/` as pre-existing untracked content.