tools(verify): fast/medium/full verification tiers with a compiled-in source stamp (L39); research R6

BuildLinux.sh hashes a sorted manifest of compiled inputs into VOXELFORGE_SOURCE_STAMP, printed first by
every executable; Standalone/Tools/verify/verify.py checks it before and after each tier and records
binary/input hashes and the resolved recipe, style map and layout. Fast (163 s) runs on the canonical owner
world only and asserts FI Style 2 / SW Style 1; medium (8 min) and full (26 min) add the style matrix,
GCC, determinism and cost. Controls: stale source, mislabelled style-0 owner, all-black capture, L30/L31,
L35/L36 and the L38 step are all rejected. No geometry change; owner hashes identical to L37.
Research R6: leak table, generator contract and a 10-step metre-native migration plan.

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-23 15:27:52 +02:00
1 parent 88c54eab44
commit 753cc9c259
22 files changed
+1831 -163

No files matched your search

@@ -30,8 +30,9 @@ DELTA_GRID_M = 0.125
SCENES = {
"owner-fi": ("owner-fi-ground-mouth.vfmesh", "layout-owner-fi.json", "FloatingIslands", "upper"),
"owner-sw": ("owner-sw-ground-mouth.vfmesh", "layout-owner-sw.json", "SurfaceWorld", "lower"),
"upper-sw-fixture": ("upper-sw-fixture-ground-mouth.vfmesh",
"layout-upper-sw-fixture.json", "SurfaceWorld", "upper"),
"style-override-upper-sw-fixture-style0": (
"style-override-upper-sw-fixture-style0-ground-mouth.vfmesh",
"layout-style-override-upper-sw-fixture-style0.json", "SurfaceWorld", "upper"),
}
@@ -456,6 +457,9 @@ def audit_scene(scene, out, baseline_out, mesh_override=None):
crop_to_center=False)
full_surface = analyze_triangles(corridor, mouth[:2], mouth[2],
crop_to_center=False, include_sides=True)
proxy_alert = fail(differential, include_planarity=False)
corner_gate = (metrics["corners"] == 0 and full_surface["corners"] == 0
and differential["corners"] == 0)
metrics["all_face_planar_area"] = full_surface["planar_area"]
metrics["all_face_straight"] = full_surface["straight"]
metrics["all_face_corners"] = full_surface["corners"]
@@ -484,10 +488,13 @@ def audit_scene(scene, out, baseline_out, mesh_override=None):
f"patch_bounds={metrics['bounds']} plane_normal={metrics['normal']} "
f"limits=(plane<{PLANE_LIMIT_M2:.2f}m2 informational,line<={STRAIGHT_LIMIT_M:.2f}m,"
f"corner_turn<{CORNER_TURN_LIMIT_DEG:.0f}deg) "
f"straight_or_planar_proxy={'ALERT' if proxy_alert else 'PASS'} "
f"delta_threshold_m={DELTA_THRESHOLD_M:.2f} status="
f"{'FAIL' if fail(differential, include_planarity=False) else 'PASS'}")
f"{'PASS' if corner_gate else 'FAIL'}")
metrics["differential"] = differential
metrics["matched_no_support"] = baseline_metrics
metrics["corner_gate"] = corner_gate
metrics["proxy_alert"] = proxy_alert
return metrics
@@ -519,13 +526,14 @@ def main():
f"{control['planar_area']:.4f} straight_run_m={control['straight']:.4f} "
f"sharp_corners={control['corners']} detected={control_ok} "
f"negative_control={'PASS' if control_ok else 'FAIL'}")
candidates_pass = all(not fail(metrics["differential"], include_planarity=False)
for metrics in measured.values())
candidates_pass = all(metrics["corner_gate"] for metrics in measured.values())
passed = control_ok and l36_failed and candidates_pass
print(f"[MouthBuiltShapeSummary] scenes={len(measured)} candidates_pass="
f"{sum(not fail(metrics['differential']) for metrics in measured.values())}/{len(measured)} "
f"{sum(metrics['corner_gate'] for metrics in measured.values())}/{len(measured)} "
f"L36_control_fail={'PASS' if l36_failed else 'FAIL'} "
f"box_control_fail={'PASS' if control_ok else 'FAIL'} "
f"corner_gate={'PASS' if candidates_pass else 'FAIL'} "
f"straight_or_planar_proxy_alerts={sum(metrics['proxy_alert'] for metrics in measured.values())} "
f"planar_gate=informational_owner_accepted "
f"status={'PASS' if passed else 'FAIL'}")
raise SystemExit(0 if passed else 1)
@@ -3,13 +3,15 @@
import math
import os
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = Path(os.environ.get("VOXELFORGE_L37_OUT", ROOT / "Saved/Linux/l37"))
OUT = Path(os.environ.get("VOXELFORGE_L37_OUT",
os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l37")))
CAPTURES = Path(os.environ.get("VOXELFORGE_L37_CAPTURE_DIR",
Path(__file__).resolve().parent / "captures"))
OUT / "captures"))
MESH = Path(os.environ.get("VOXELFORGE_L37_MESH",
OUT / "owner-fi-ground-mouth.vfmesh"))
RENDERER = Path(os.environ.get("VOXELFORGE_L37_RENDERER",
@@ -20,6 +22,8 @@ MOUTH = (5.600, 0.445, -55.156)
CAMERA = (MOUTH[0] - 9.75, MOUTH[1] - 11.4, MOUTH[2] + 8.0)
TARGET = (6.298233, 1.137233, -55.468264)
FOV = 80.0
sys.path.insert(0, str(ROOT / "Standalone/Tools/verify"))
from runner import current_stamp, first_stamp, bounded_run
def normalize(vector):
@@ -42,6 +46,11 @@ def run(name, command):
log = OUT / f"capture-overview-{name}.log"
wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1800s", *map(str, command)]
expected = current_stamp() if Path(command[0]) == RENDERER else None
if expected is not None:
probe = bounded_run([str(RENDERER), "--print-source-stamp"], timeout=15)
if probe is None or probe.returncode != 0 or first_stamp(probe.stdout or "") != expected:
raise SystemExit(f"{name} refused an unstamped or stale renderer: {RENDERER}")
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(wrapped, cwd=ROOT, stdout=stream,
@@ -49,6 +58,10 @@ def run(name, command):
elapsed = time.monotonic() - started
print(f"[L37OverviewCaptureRun] step={name} elapsed={elapsed:.1f}s "
f"exit={result.returncode} log={log}", flush=True)
if expected is not None:
output = log.read_text(encoding="utf-8", errors="replace")
if first_stamp(output) != expected or current_stamp() != expected:
raise SystemExit(f"{name} source stamp mismatch; see {log}")
if result.returncode != 0:
raise SystemExit(f"{name} failed; see {log}")
@@ -67,6 +80,7 @@ def main():
"--tile-edges=off", f"--focus-region={fmt((*MOUTH, 20.0))}"])
run("jpeg", ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error",
"-y", "-i", PNG, "-frames:v", "1", "-q:v", "4", JPEG])
run("quality", [sys.executable, ROOT / "Standalone/Tools/verify/capture_quality.py", JPEG])
size = JPEG.stat().st_size if JPEG.exists() else 0
distance = math.dist(CAMERA, MOUTH)
print(f"[L37OverviewCapture] file={JPEG.relative_to(ROOT)} camera_to_mouth_m="
@@ -7,26 +7,29 @@ import math
import os
import re
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = Path(os.environ.get("VOXELFORGE_L37_OUT", ROOT / "Saved/Linux/l37"))
OUT = Path(os.environ.get("VOXELFORGE_L37_OUT",
os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l37")))
BIN = OUT / "bin"
CAPTURES = Path(os.environ.get("VOXELFORGE_L37_CAPTURE_DIR",
Path(__file__).resolve().parent / "captures"))
OUT / "captures"))
HEADLESS = Path(os.environ.get("VOXELFORGE_L37_HEADLESS",
BIN / "VoxelForgeCoreHeadless-l37-final-clang"))
RENDERER = Path(os.environ.get("VOXELFORGE_L37_RENDERER",
BIN / "VoxelForgeOffscreenCapture-l37-final-clang"))
PNG_DIR = OUT / "capture-png"
RECIPE_INPUTS = (
("owner-fi", ROOT / "Saved/Linux/linux25-owner-floating-islands-style0.json",
("owner-fi", ROOT / "Standalone/data/owner_world.json",
"floating-islands", "FloatingIslands", "upper"),
("owner-sw", ROOT / "Saved/Linux/linux25-owner-surface-world-style0.json",
("owner-sw", ROOT / "Standalone/data/owner_world.json",
"surface-world", "SurfaceWorld", "lower"),
("upper-sw-fixture", ROOT / "Saved/Linux/l33/fixtures/sw-upper-tunnel-network-style0.json",
("style-override-upper-sw-fixture-style0",
OUT / "fixtures/style-override-upper-sw-fixture-style0.json",
"floating-islands", "SurfaceWorld", "upper"),
)
SUBVOXELS_PER_VOXEL = 256.0
@@ -34,6 +37,8 @@ RING_AUDIT = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerra
spec = importlib.util.spec_from_file_location("ring_profile_l32", RING_AUDIT)
ring_profile = importlib.util.module_from_spec(spec)
spec.loader.exec_module(ring_profile)
sys.path.insert(0, str(ROOT / "Standalone/Tools/verify"))
from runner import current_stamp, first_stamp, bounded_run
for directory in (OUT, BIN, PNG_DIR, CAPTURES):
directory.mkdir(parents=True, exist_ok=True)
@@ -51,12 +56,23 @@ def run(name, command):
wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1800s", *map(str, command)]
started = time.monotonic()
binary = Path(command[0]) if command else None
is_stamped_executable = binary in (HEADLESS, RENDERER)
expected = current_stamp() if is_stamped_executable else None
if is_stamped_executable:
probe = bounded_run([str(binary), "--print-source-stamp"], timeout=15)
if probe is None or probe.returncode != 0 or first_stamp(probe.stdout or "") != expected:
raise SystemExit(f"{name} refused an unstamped or stale executable: {binary}")
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(wrapped, cwd=ROOT, env=ENV, stdout=stream,
stderr=subprocess.STDOUT, timeout=1800, check=False)
elapsed = time.monotonic() - started
print(f"[L37CaptureRun] step={name} elapsed={elapsed:.1f}s exit={result.returncode} log={log}",
flush=True)
if is_stamped_executable:
output = log.read_text(encoding="utf-8", errors="replace")
if first_stamp(output) != expected or current_stamp() != expected:
raise SystemExit(f"{name} source stamp mismatch; see {log}")
if result.returncode != 0:
raise SystemExit(f"{name} failed; see {log}")
return log
@@ -163,7 +179,32 @@ def load_layout(name, source, recipe):
"--stream-benchmark", "--stream-pipeline", "--force-generate", "--clip-radius=2",
"--max-clip-level=0", "--max-level=0", f"--recipe={recipe}",
f"--resolved-layout-output={path}", "--resolved-layout-only"])
return json.loads(path.read_text(encoding="utf-8"))
layout = json.loads(path.read_text(encoding="utf-8"))
passage_styles = sorted({int(passage["style"]) for passage in layout.get("passages", [])})
resolved_styles = {str(index): int(style) for index, style in
layout.get("resolved_strate_passage_styles", {}).items()}
if name.startswith("owner-"):
expected_styles = {"0": 2, "1": 1}
if resolved_styles != expected_styles or passage_styles != [2]:
raise SystemExit(f"{name} resolved styles={resolved_styles} passage_styles={passage_styles}; "
f"expected strata={expected_styles} passage_styles=[2]")
expected_style = 2 if name == "owner-fi" else 1
print(f"[L37CaptureResolvedOwner] scene={name} recipe={recipe} "
f"generator_style={expected_style} strata_styles={resolved_styles} "
f"passage_style={passage_styles} layout_id={layout.get('stable_identity')} input={source}",
flush=True)
return layout
expected_style = 0
matching_styles = sorted({resolved_styles[str(stratum["strate_index"])]
for stratum in layout.get("strates", [])
if stratum.get("generator_kind") == "SurfaceWorld"
and str(stratum["strate_index"]) in resolved_styles})
if expected_style not in passage_styles or matching_styles != [expected_style]:
raise SystemExit(f"{name} resolved style={matching_styles} passage styles={passage_styles}; "
f"expected [{expected_style}]")
print(f"[L37CaptureResolvedOwner] scene={name} recipe={recipe} style={matching_styles} "
f"layout_id={layout.get('stable_identity')} input={source}", flush=True)
return layout
def select_passage(layout, mouth_generator, preferred_side):
@@ -327,7 +368,8 @@ for scene_name, source, recipe, mouth_generator, preferred_side in RECIPE_INPUTS
jpg = CAPTURES / f"{stem}.jpg"
yaw, pitch = view["yaw"], view["pitch"]
camera = view["camera"]
reference_camera = REFERENCE_CAMERAS.get(stem)
reference_camera = (REFERENCE_CAMERAS.get(stem)
if scene_name.startswith("style-override-") else None)
if reference_camera is not None:
camera, target, reference_clip = reference_camera
view["camera"], view["target"], view["clip"] = camera, target, reference_clip
@@ -397,6 +439,8 @@ for scene_name, source, recipe, mouth_generator, preferred_side in RECIPE_INPUTS
run(f"{stem}-render", render)
run(f"{stem}-jpeg", ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error",
"-y", "-i", png, "-frames:v", "1", "-q:v", "4", jpg])
run(f"{stem}-quality", [sys.executable,
ROOT / "Standalone/Tools/verify/capture_quality.py", jpg])
size = jpg.stat().st_size if jpg.exists() else 0
print(f"[L37CaptureJPEG] file={jpg.relative_to(ROOT)} bytes={size} "
f"status={'PASS' if 0 < size <= 300_000 else 'FAIL'}", flush=True)
@@ -1,69 +0,0 @@
#!/usr/bin/env python3
"""Run the L13 route parity and L9 uniform-block proof audits on L37."""
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l37"
BINARIES = {
"clang": OUT / "bin/VoxelForgeCoreHeadless-l37-final-clang",
"gcc": OUT / "bin/VoxelForgeCoreHeadless-l37-final-gcc",
}
RECIPES = ("floating-islands", "surface-world")
rows = []
failed = False
for recipe in RECIPES:
for compiler, binary in BINARIES.items():
name = f"legacy-route-proof-{recipe}-{compiler}"
logfile = OUT / f"{name}.log"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "900s", str(binary), str(ROOT / "Standalone/data/owner_world.json"),
"--workers=12", "--world-assembler-prototype", "--stream-benchmark",
"--stream-pipeline", "--stream-hash", "--force-generate", "--clip-radius=3",
"--max-clip-level=8", "--max-level=-1",
f"--recipe={recipe}", "--audit-uniform-block-proofs", "--audit-route-parity",
]
started = time.monotonic()
try:
with logfile.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, timeout=900, check=False)
except subprocess.TimeoutExpired:
result = None
elapsed = time.monotonic() - started
output = logfile.read_text(encoding="utf-8", errors="replace")
stamp = re.findall(
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
output, re.M)
route = re.findall(r"^\[RouteParityAudit\].*$", output, re.M)
proofs = re.findall(r"^\[UniformBlockProofAudit\].*$", output, re.M)
stamp_ok = stamp == [(recipe, recipe, "PASS")]
route_ok = len(route) == 1 and route[0].endswith("status=PASS")
proof_ok = len(proofs) == 1 and bool(
re.search(r"violations=0(?:\s|$)", proofs[0]))
ok = (result is not None and result.returncode == 0 and stamp_ok
and route_ok and proof_ok)
code = result.returncode if result is not None else "TIMEOUT"
rows.append((name, recipe, compiler, elapsed, code, route_ok, bool(proof_ok), ok))
failed |= not ok
print(f"[L37LegacyAuditRun] case={name} elapsed={elapsed:.1f}s exit={code} "
f"recipe_stamp={'PASS' if stamp_ok else 'FAIL'} "
f"L13={'PASS' if route_ok else 'FAIL'} L9={'PASS' if proof_ok else 'FAIL'} "
f"{'PASS' if ok else 'FAIL'}", flush=True)
summary = OUT / "legacy-audits.csv"
summary.write_text(
"case,recipe,compiler,elapsed_seconds,exit_code,l13,l9,status\n" +
"".join(f"{name},{recipe},{compiler},{elapsed:.3f},{code},"
f"{'PASS' if l13 else 'FAIL'},{'PASS' if l9 else 'FAIL'},"
f"{'PASS' if ok else 'FAIL'}\n"
for name, recipe, compiler, elapsed, code, l13, l9, ok in rows),
encoding="utf-8")
print(f"[L37LegacyAuditSummary] runs={len(rows)} pass={sum(row[-1] for row in rows)} "
f"fail={sum(not row[-1] for row in rows)} summary={summary}", flush=True)
raise SystemExit(1 if failed or len(rows) != 4 else 0)
@@ -4,6 +4,7 @@ import ast
import contextlib
import importlib.util
import io
import json
import math
import re
import argparse
@@ -15,10 +16,13 @@ SPEC = importlib.util.spec_from_file_location("mouth_fragment_l37", BASE_PATH)
BASE = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(BASE)
# Frozen before the L37 mouth edit: 20 degrees over the audit's 0.125 m samples is
# the visible-crease limit. At the 6.016 m standard camera and 80-degree horizontal
# FOV, each sample gap spans multiple pixels at 2560x1440.
BASE.NORMAL_LIMIT_DEG = 20.0
# Keep the near limit frozen at 20 degrees. The saved L37 owner overview measures
# 25.542 degrees, so the overview regression limit is 26 degrees: it admits that
# baseline while still rejecting the saved L36 washboard (27.133 degrees).
NEAR_NORMAL_LIMIT_DEG = 20.0
OVERVIEW_NORMAL_LIMIT_DEG = 26.0
MIN_VISIBLE_PAIRS = 20
MIN_PROFILE_COVERAGE = 0.05
CAMERA = (1.691820, -3.424982, -54.224198)
TARGET = (6.298233, 1.137233, -55.468264)
FOV_DEG = 80.0
@@ -42,11 +46,14 @@ def view_basis(camera, target):
return forward, right, up
def visible_crease(path, camera, target):
cx, cy = BASE.CENTER
bounds = (cx - BASE.OUTER, cx + BASE.OUTER, cy - BASE.OUTER, cy + BASE.OUTER)
voxel_um, triangles, _ = BASE.RING.read_scene(
str(path), 0, BASE.HEIGHT_MIN, BASE.HEIGHT_MAX, bounds)
def visible_crease(scene_data, camera, target, center=None, preferred=None,
inner=None, outer=None):
cx, cy = center or BASE.CENTER
inner = BASE.INNER if inner is None else inner
outer = BASE.OUTER if outer is None else outer
bounds = (cx - outer, cx + outer, cy - outer, cy + outer)
voxel_um, triangles, _ = scene_data
preferred = BASE.PREFERRED if preferred is None else preferred
cell = max(2.0, 16.0 * voxel_um / 1_000_000.0)
buckets = BASE.RING.build_buckets(triangles, True, bounds, cell)
forward, right, up = view_basis(camera, target)
@@ -54,11 +61,11 @@ def visible_crease(path, camera, target):
tan_v = math.tan(math.atan(tan_h * HEIGHT_1440P / WIDTH_1440P))
focal = WIDTH_1440P / (2.0 * tan_h)
lines = []
count = round((BASE.OUTER - BASE.INNER) / BASE.STEP)
count = round((outer - inner) / BASE.STEP)
for degrees in (135.0, 180.0, 225.0, 270.0, 315.0):
angle = math.radians(degrees)
lines.append([(cx + radius * math.cos(angle), cy + radius * math.sin(angle))
for radius in (BASE.INNER + index * BASE.STEP
for radius in (inner + index * BASE.STEP
for index in range(count + 1))])
tangent_count = round(8.0 / BASE.STEP)
for degrees in (225.0, 315.0):
@@ -70,6 +77,7 @@ def visible_crease(path, camera, target):
for index in range(tangent_count + 1)])
maximum, largest_pixels, worst_depth, visible_pairs = 0.0, 0.0, 0.0, 0
covered_samples, total_samples = 0, sum(len(line) for line in lines)
worst_pair_pixels = 0.0
worst_pair = None
@@ -81,8 +89,9 @@ def visible_crease(path, camera, target):
return depth, x, y
for points in lines:
samples = [BASE.RING.sample_surface(buckets, x, y, True, cell, BASE.PREFERRED)
samples = [BASE.RING.sample_surface(buckets, x, y, True, cell, preferred)
for x, y in points]
covered_samples += sum(sample is not None for sample in samples)
for index in range(1, len(samples)):
before, current = samples[index - 1], samples[index]
if before is None or current is None:
@@ -105,38 +114,74 @@ def visible_crease(path, camera, target):
if angle > maximum:
maximum, worst_pair, worst_depth = angle, (p0, p1), depth
worst_pair_pixels = pixels
return maximum, worst_depth, largest_pixels, visible_pairs, worst_pair, worst_pair_pixels
coverage = covered_samples / total_samples if total_samples else 0.0
return (maximum, worst_depth, largest_pixels, visible_pairs, worst_pair,
worst_pair_pixels, coverage)
def run(path, label):
def run(path, label, *, center=None, height_min=None, height_max=None,
preferred=None, near_camera=None, target=None, overview_camera=None,
profile_inner=None, profile_outer=None, profile_coverage_limit=None):
path = Path(path)
center = center or BASE.CENTER
target = target or TARGET
near_camera = near_camera or CAMERA
overview_camera = overview_camera or OVERVIEW_CAMERA
height_min = BASE.HEIGHT_MIN if height_min is None else height_min
height_max = BASE.HEIGHT_MAX if height_max is None else height_max
profile_inner = BASE.INNER if profile_inner is None else profile_inner
profile_outer = BASE.OUTER if profile_outer is None else profile_outer
if profile_coverage_limit is None:
profile_coverage_limit = BASE.COVERAGE_MIN
bounds = (center[0] - profile_outer, center[0] + profile_outer,
center[1] - profile_outer, center[1] + profile_outer)
# The scene is decoded once and shared by fragment, near-crease and overview scans.
scene_data = BASE.RING.read_scene(str(path), 0, height_min, height_max, bounds)
stream = io.StringIO()
with contextlib.redirect_stdout(stream):
passed, maximum = BASE.audit(path)
passed, maximum = BASE.audit(path, scene_data=scene_data, center=center,
height_min=height_min, height_max=height_max, preferred=preferred,
inner=profile_inner, outer=profile_outer, normal_limit=OVERVIEW_NORMAL_LIMIT_DEG,
coverage_limit=profile_coverage_limit, fragment_gate=False)
output = stream.getvalue()
print(output, end="")
match = re.search(r"worst_pair=(\([^\n]*\)) status=", output)
worst = ast.literal_eval(match.group(1)) if match else None
path = Path(path)
near = visible_crease(path, CAMERA, TARGET)
far = visible_crease(path, OVERVIEW_CAMERA, TARGET)
near = visible_crease(scene_data, near_camera, target, center, preferred,
profile_inner, profile_outer)
far = visible_crease(scene_data, overview_camera, target, center, preferred,
profile_inner, profile_outer)
print(f"[MouthCreasePixels] build={label} sample_step_m={BASE.STEP:.3f} "
f"horizontal_fov_deg={FOV_DEG:.1f} resolution=2560x1440 "
f"near_camera_to_mouth_m=6.016 near_visible_pairs={near[3]} "
f"near_max_visible_deg={near[0]:.6f} near_depth_m={near[1]:.3f} "
f"near_sample_spacing_px={near[2]:.2f} "
f"near_camera_to_mouth_m={math.dist(near_camera, (*center, target[2])):.3f} "
f"near_visible_pairs={near[3]} near_max_visible_deg={near[0]:.6f} "
f"near_profile_coverage={near[6]:.3f} "
f"near_depth_m={near[1]:.3f} near_sample_spacing_px={near[2]:.2f} "
f"near_worst_pair_spacing_px={near[5]:.2f} "
f"overview_camera_to_mouth_m={math.dist(OVERVIEW_CAMERA, MOUTH):.3f} "
f"overview_camera_to_mouth_m={math.dist(overview_camera, (*center, target[2])):.3f} "
f"overview_visible_pairs={far[3]} overview_max_visible_deg={far[0]:.6f} "
f"overview_profile_coverage={far[6]:.3f} "
f"overview_depth_m={far[1]:.3f} overview_sample_spacing_px={far[2]:.2f} "
f"overview_worst_pair_spacing_px={far[5]:.2f} "
f"overall_max_deg={maximum:.6f}")
return passed, maximum
f"overview_worst_pair_spacing_px={far[5]:.2f} overall_max_deg={maximum:.6f}")
near_ok = (near[3] >= MIN_VISIBLE_PAIRS and near[6] >= MIN_PROFILE_COVERAGE
and near[0] <= NEAR_NORMAL_LIMIT_DEG)
overview_ok = (far[3] >= MIN_VISIBLE_PAIRS and far[6] >= MIN_PROFILE_COVERAGE
and far[0] <= OVERVIEW_NORMAL_LIMIT_DEG)
views_pass = near_ok and overview_ok
print(f"[MouthCreaseViewGate] build={label} near_limit_deg={NEAR_NORMAL_LIMIT_DEG:.1f} "
f"overview_limit_deg={OVERVIEW_NORMAL_LIMIT_DEG:.1f} "
f"min_visible_pairs={MIN_VISIBLE_PAIRS} "
f"min_profile_coverage={MIN_PROFILE_COVERAGE:.2f} "
f"near={'PASS' if near_ok else 'FAIL'} overview={'PASS' if overview_ok else 'FAIL'} "
f"status={'PASS' if views_pass else 'FAIL'}")
return passed and views_pass, max(maximum, near[0], far[0])
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--candidate", type=Path,
default=ROOT / "Saved/Linux/l37/owner-fi-ground-mouth.vfmesh")
parser.add_argument("--candidate-sw", type=Path)
parser.add_argument("--layout-sw", type=Path)
parser.add_argument("--capture-log-sw", type=Path)
args = parser.parse_args()
paths = {
"L35": ROOT / "Saved/Linux/l35/owner-fi-ground-mouth.vfmesh",
@@ -146,12 +191,38 @@ def main():
}
results = {label: run(path, label) for label, path in paths.items()}
controls = (not results["L35"][0] and not results["L36"][0]
and results["L35"][1] > 20.0 and results["L36"][1] > 20.0)
final = results["L37"][0] and results["L37"][1] <= 20.0
print(f"[MouthCreaseGate] normal_limit_deg=20 frozen_before_fix=true "
and results["L35"][1] > OVERVIEW_NORMAL_LIMIT_DEG
and results["L36"][1] > OVERVIEW_NORMAL_LIMIT_DEG)
final = (results["L37"][0]
and results["L37"][1] <= OVERVIEW_NORMAL_LIMIT_DEG)
if args.candidate_sw:
if not args.layout_sw or not args.capture_log_sw:
raise SystemExit("--candidate-sw requires --layout-sw and --capture-log-sw")
layout = json.loads(args.layout_sw.read_text(encoding="utf-8"))
passage = layout["passages"][0]
q_to_m = layout["core_voxel_size_um"] / (256.0 * 1_000_000.0)
mouth = tuple(value * q_to_m for value in passage["lower_mouth_q"])
capture = args.capture_log_sw.read_text(encoding="utf-8", errors="replace")
camera_match = re.search(r"\[L37CaptureCamera\] view=owner-sw-ground-mouth .*?"
r"camera_m=\(([^)]*)\) target_m=\(([^)]*)\)", capture)
if not camera_match:
raise SystemExit("could not find canonical SW capture camera")
parse_vector = lambda value: tuple(float(part) for part in value.split(","))
near_camera, target = map(parse_vector, camera_match.groups())
overview_camera = (mouth[0] + 2.5 * (near_camera[0] - mouth[0]),
mouth[1] + 2.5 * (near_camera[1] - mouth[1]),
near_camera[2] + 7.0)
sw_final, sw_max = run(args.candidate_sw, "owner-sw-style1",
center=mouth[:2], height_min=mouth[2] - 24.0,
height_max=mouth[2] + 16.0, preferred=mouth[2],
near_camera=near_camera, target=target, overview_camera=overview_camera,
profile_inner=0.5, profile_outer=4.5, profile_coverage_limit=0.0)
final = final and sw_final and sw_max <= OVERVIEW_NORMAL_LIMIT_DEG
print(f"[MouthCreaseGate] near_limit_deg={NEAR_NORMAL_LIMIT_DEG:.1f} "
f"overview_limit_deg={OVERVIEW_NORMAL_LIMIT_DEG:.1f} "
f"L35_42deg_fail_control={'PASS' if controls else 'FAIL'} "
f"L36_27deg_fail_control={'PASS' if controls else 'FAIL'} "
f"L37_candidate={'PASS' if final else 'FAIL'} "
f"real_owner_candidates={'PASS' if final else 'FAIL'} "
f"status={'PASS' if controls and final else 'FAIL'}")
raise SystemExit(0 if controls and final else 1)
@@ -25,7 +25,7 @@ FRAGMENT_MAX_AREA_M2 = 0.05
FRAGMENT_MAX_DISTANCE_M = 12.0
def small_components(triangles):
def small_components(triangles, center=None):
parent = list(range(len(triangles)))
sizes = [1] * len(triangles)
owners = {}
@@ -67,7 +67,7 @@ def small_components(triangles):
ab[0] * ac[1] - ab[1] * ac[0])
item["area"] += 0.5 * math.sqrt(sum(value * value for value in cross))
cx, cy = CENTER
cx, cy = center or CENTER
small = []
below_floor = []
for item in components.values():
@@ -81,12 +81,21 @@ def small_components(triangles):
return len(components), small, below_floor
def audit(path):
cx, cy = CENTER
bounds = (cx - OUTER, cx + OUTER, cy - OUTER, cy + OUTER)
voxel_um, triangles, tiles = RING.read_scene(
str(path), 0, HEIGHT_MIN, HEIGHT_MAX, bounds)
component_count, fragments, below_floor = small_components(triangles)
def audit(path, scene_data=None, center=None, height_min=None, height_max=None,
preferred=None, inner=None, outer=None, normal_limit=None,
coverage_limit=None, fragment_gate=True):
cx, cy = center or CENTER
inner = INNER if inner is None else inner
outer = OUTER if outer is None else outer
normal_limit = NORMAL_LIMIT_DEG if normal_limit is None else normal_limit
coverage_limit = COVERAGE_MIN if coverage_limit is None else coverage_limit
bounds = (cx - outer, cx + outer, cy - outer, cy + outer)
if scene_data is None:
scene_data = RING.read_scene(str(path), 0,
HEIGHT_MIN if height_min is None else height_min,
HEIGHT_MAX if height_max is None else height_max, bounds)
voxel_um, triangles, tiles = scene_data
component_count, fragments, below_floor = small_components(triangles, (cx, cy))
print(f"[MouthFragmentComponents] mesh={path.name} components={component_count} "
f"small_near_mouth={len(fragments)} subthreshold_near_mouth={len(below_floor)} "
f"limits=(triangles<={FRAGMENT_MAX_TRIANGLES},"
@@ -94,16 +103,18 @@ def audit(path):
f"distance<={FRAGMENT_MAX_DISTANCE_M:.1f}m) "
f"first={fragments[0] if fragments else None} "
f"subthreshold={below_floor[0] if below_floor else None} "
f"status={'PASS' if not fragments else 'FAIL'}")
f"gate={'on' if fragment_gate else 'diagnostic'} "
f"status={'PASS' if not fragments or not fragment_gate else 'FAIL'}")
preferred = PREFERRED if preferred is None else preferred
cell = max(2.0, 16.0 * voxel_um / 1_000_000.0)
buckets = RING.build_buckets(triangles, True, bounds, cell)
lines = []
count = round((OUTER - INNER) / STEP)
count = round((outer - inner) / STEP)
for degrees in (135.0, 180.0, 225.0, 270.0, 315.0):
angle = math.radians(degrees)
lines.append((f"radial-{degrees:.0f}", [
(cx + radius * math.cos(angle), cy + radius * math.sin(angle))
for radius in (INNER + i * STEP for i in range(count + 1))]))
for radius in (inner + i * STEP for i in range(count + 1))]))
tangent_count = round(8.0 / STEP)
for degrees in (225.0, 315.0):
angle = math.radians(degrees)
@@ -118,7 +129,7 @@ def audit(path):
max_pair = None
min_coverage = 1.0
for name, points in lines:
samples = [RING.sample_surface(buckets, x, y, True, cell, PREFERRED)
samples = [RING.sample_surface(buckets, x, y, True, cell, preferred)
for x, y in points]
coverage = sum(value is not None for value in samples) / len(samples)
min_coverage = min(min_coverage, coverage)
@@ -136,11 +147,11 @@ def audit(path):
max_pair = (name, points[index - 1], prior[0], points[index], current[0])
print(f"[MouthFadeLine] mesh={path.name} line={name} coverage={coverage:.3f} "
f"max_normal_jump_deg={line_max:.6f}")
normal_pass = max_angle <= NORMAL_LIMIT_DEG and min_coverage >= COVERAGE_MIN
passed = normal_pass and not fragments
normal_pass = max_angle <= normal_limit and min_coverage >= coverage_limit
passed = normal_pass and (not fragment_gate or not fragments)
print(f"[MouthFadeAudit] mesh={path.name} tiles={tiles} lines={len(lines)} "
f"coverage_min={min_coverage:.3f} coverage_limit={COVERAGE_MIN:.2f} "
f"max_normal_jump_deg={max_angle:.6f} normal_limit_deg={NORMAL_LIMIT_DEG:.1f} "
f"coverage_min={min_coverage:.3f} coverage_limit={coverage_limit:.2f} "
f"max_normal_jump_deg={max_angle:.6f} normal_limit_deg={normal_limit:.1f} "
f"fragments={len(fragments)} "
f"worst_pair={max_pair} status={'PASS' if passed else 'FAIL'}")
return passed, max_angle
@@ -1,7 +1,8 @@
#!/usr/bin/env python3
"""Run recipe-stamped passage/profile audits for both compilers and both owner recipes."""
"""Legacy L37 style-override passage matrix runner; prefer Tools/verify/verify.py."""
import re
import os
import shutil
import subprocess
import time
from pathlib import Path
@@ -19,12 +20,19 @@ for recipe, filename in (
("surface-world", "surface-world"),
):
for style in range(4):
RUNS.append((f"owner-{filename}-style{style}",
ROOT / f"Saved/Linux/linux25-owner-{filename}-style{style}.json", recipe))
source = ROOT / f"Saved/Linux/linux25-owner-{filename}-style{style}.json"
name = f"style-override-{filename}-style{style}"
destination = OUT / "fixtures" / f"{name}.json"
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(source, destination)
RUNS.append((name, destination, recipe))
for style in range(4):
RUNS.append((f"upper-sw-fixture-style{style}",
OUT.parent / "l33/fixtures" / f"sw-upper-tunnel-network-style{style}.json",
"floating-islands"))
source = OUT.parent / "l33/fixtures" / f"sw-upper-tunnel-network-style{style}.json"
name = f"style-override-upper-sw-fixture-style{style}"
destination = OUT / "fixtures" / f"{name}.json"
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(source, destination)
RUNS.append((name, destination, "floating-islands"))
RECIPE = re.compile(r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=PASS$", re.M)
PASSAGE = re.compile(r"^\[ResolvedPassageAudit\].*walkability=PASS$", re.M)
@@ -1,14 +1,18 @@
#!/usr/bin/env python3
"""Export v24 ring meshes at matching LOD windows and retain old controls."""
import csv
import argparse
import re
import subprocess
import sys
import time
import os
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l37"
BIN = OUT / "bin/VoxelForgeCoreHeadless-l37-final-clang"
OUT = Path(os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l37"))
BIN = Path(os.environ.get("VOXELFORGE_VERIFY_HEADLESS",
OUT / "bin/VoxelForgeCoreHeadless-l37-final-clang"))
OWNER = ROOT / "Standalone/data/owner_world.json"
TERRACE = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py"
RING_SET = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingSetL32.py"
@@ -19,6 +23,13 @@ TERRACE_RESULT = re.compile(r"^\[RingTerraceAudit\].*status=(PASS|FAIL)$", re.M)
SET_RESULT = re.compile(r"^\[RingSetAudit\].*status=(PASS|FAIL)$", re.M)
rows = []
failed = False
PARSER = argparse.ArgumentParser(description=__doc__)
PARSER.add_argument("--lod0-only", action="store_true")
PARSER.add_argument("--scene", choices=("fi-floor", "sw-ceiling", "all"), default="all")
PARSER.add_argument("--skip-controls", action="store_true")
OPTIONS = PARSER.parse_args()
sys.path.insert(0, str(ROOT / "Standalone/Tools/verify"))
from runner import current_stamp, first_stamp, bounded_run
FI_CENTERS = ((56, -72), (72, -72), (56, -88), (40, -72), (24, 24),
(104, 24), (72, -88), (120, 24), (104, 8), (24, -24))
@@ -42,6 +53,13 @@ def run(name, command):
log = OUT / f"ring-{name}.log"
wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1800s", *map(str, command)]
stamped = bool(command and Path(command[0]) == BIN)
expected_stamp = current_stamp() if stamped else None
if stamped:
probe = bounded_run([str(BIN), "--print-source-stamp"], timeout=15)
if (probe is None or probe.returncode != 0
or first_stamp(probe.stdout or "") != expected_stamp):
raise SystemExit(f"{name} refused an unstamped or stale executable: {BIN}")
started = time.monotonic()
try:
with log.open("w", encoding="utf-8") as stream:
@@ -51,6 +69,9 @@ def run(name, command):
result = None
elapsed = time.monotonic() - started
output = log.read_text(encoding="utf-8", errors="replace")
if stamped and (first_stamp(output) != expected_stamp
or current_stamp() != expected_stamp):
raise SystemExit(f"{name} source stamp mismatch; see {log}")
code = result.returncode if result is not None else "TIMEOUT"
print(f"[L37RingStep] name={name} elapsed={elapsed:.1f}s exit={code} log={log}",
flush=True)
@@ -59,18 +80,32 @@ def run(name, command):
def export(label, recipe, max_level, camera):
mesh = OUT / f"ring-owner-{label}-v24.vfmesh"
layout = OUT / "layouts" / f"ring-layout-{label}.json"
layout.parent.mkdir(parents=True, exist_ok=True)
command = [BIN, OWNER, "--workers=12",
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--force-generate", "--clip-radius=3", "--max-clip-level=8",
f"--max-level={max_level}", f"--recipe={recipe}"]
if camera:
command.append(f"--camera={camera}")
command.append(f"--mesh-export={mesh}")
command.extend((f"--mesh-export={mesh}", f"--resolved-layout-output={layout}"))
result, output, elapsed = run(f"export-{label}", command)
stamps = STAMP.findall(output)
ok = (result is not None and result.returncode == 0
and stamps == [(recipe, recipe)] and EXPORT.search(output) and mesh.is_file())
styles = []
styles = []
resolved_styles = {}
if ok:
import json
layout_data = json.loads(layout.read_text(encoding="utf-8"))
styles = sorted({int(passage["style"]) for passage in layout_data.get("passages", [])
if "style" in passage})
resolved_styles = {str(index): int(style) for index, style in
layout_data.get("resolved_strate_passage_styles", {}).items()}
ok = styles == [2] and resolved_styles == {"0": 2, "1": 1}
print(f"[L37RingExport] scene={label} recipe={recipe} stamp={stamps} "
f"passage_style={styles if mesh.exists() else []} resolved_strate_styles={resolved_styles} "
f"bytes={mesh.stat().st_size if mesh.exists() else 0} "
f"status={'PASS' if ok else 'FAIL'}", flush=True)
return mesh, elapsed, ok
@@ -108,32 +143,36 @@ def lod1_set(label, recipe, surface, mesh, centers, height_min, height_max):
return ok
for label, recipe, surface, window_min, window_max, camera0, camera1, centers in (
SCENES = (
("fi-floor", "floating-islands", "floor", -122.0, -117.0,
"64,-32,-115,0,-45", "64,-32,-115,0,-45", FI_CENTERS),
("sw-ceiling", "surface-world", "ceiling", -132.0, -124.0,
"64,-32,-136,0,45", "-96,128,-136,0,45", SW_CENTERS)):
"64,-32,-136,0,45", "-96,128,-136,0,45", SW_CENTERS))
selected_scenes = (SCENES if OPTIONS.scene == "all" else
tuple(scene for scene in SCENES if scene[0] == OPTIONS.scene))
for label, recipe, surface, window_min, window_max, camera0, camera1, centers in selected_scenes:
low_mesh, _, low_ok = export(f"{label}-lod0", recipe, 0, camera0)
failed |= not low_ok
if low_ok:
failed |= not terrace(label, recipe, surface, low_mesh, 0, window_min, window_max)
detail_mesh, _, detail_ok = export(f"{label}-lod1", recipe, 1, camera1)
failed |= not detail_ok
if detail_ok:
set_min, set_max = ((-121.625, -117.375) if label == "fi-floor"
else (-128.375, -124.125))
failed |= not lod1_set(label, recipe, surface, detail_mesh, centers,
set_min, set_max)
if not OPTIONS.lod0_only:
detail_mesh, _, detail_ok = export(f"{label}-lod1", recipe, 1, camera1)
failed |= not detail_ok
if detail_ok:
set_min, set_max = ((-121.625, -117.375) if label == "fi-floor"
else (-128.375, -124.125))
failed |= not lod1_set(label, recipe, surface, detail_mesh, centers,
set_min, set_max)
full_mesh, _, full_ok = export(f"{label}-lod2-8", recipe, 8, None)
failed |= not full_ok
if full_ok:
for lod in range(2, 9):
center = LOD_CENTERS[label][lod]
radius = 4 * (1 << lod)
failed |= not terrace(label, recipe, surface, full_mesh, lod,
window_min, window_max, center, radius)
full_mesh, _, full_ok = export(f"{label}-lod2-8", recipe, 8, None)
failed |= not full_ok
if full_ok:
for lod in range(2, 9):
center = LOD_CENTERS[label][lod]
radius = 4 * (1 << lod)
failed |= not terrace(label, recipe, surface, full_mesh, lod,
window_min, window_max, center, radius)
# Keep the established L30 terrace and L31 flat-surface negative controls live.
controls = (
@@ -141,7 +180,7 @@ controls = (
-130.0, -110.0),
("l31-flat", ROOT / "Saved/Linux/l31/fi-floor-v19.vfmesh", -122.0, -117.0),
)
for name, mesh, height_min, height_max in controls:
for name, mesh, height_min, height_max in (() if OPTIONS.skip_controls else controls):
result, output, _ = run(f"control-{name}", ["python3", TERRACE, mesh,
"--surface=floor", "--center", "64", "-32", "--radius=32", "--lod=0",
f"--height-min-m={height_min}", f"--height-max-m={height_max}"])
@@ -151,14 +190,18 @@ for name, mesh, height_min, height_max in controls:
print(f"[L37RingControl] name={name} detected={detected} "
f"status={'PASS' if detected else 'FAIL'}", flush=True)
summary = OUT / "ring-audits-l37.csv"
scene_tag = OPTIONS.scene
lod_tag = "lod0" if OPTIONS.lod0_only else "lod0-8"
summary = OUT / f"ring-audits-{scene_tag}-{lod_tag}.csv"
with summary.open("w", newline="", encoding="utf-8") as stream:
writer = csv.writer(stream)
writer.writerow(("scene", "recipe", "surface", "lod", "elapsed_seconds", "status"))
writer.writerows((label, recipe, surface, lod, f"{elapsed:.3f}",
"PASS" if ok else "FAIL")
for label, recipe, surface, lod, elapsed, ok in rows)
expected_runs = (1 if OPTIONS.lod0_only else 9) * len(selected_scenes)
expected_controls = 0 if OPTIONS.skip_controls else len(controls)
print(f"[L37RingSummary] runs={len(rows)} pass={sum(row[-1] for row in rows)} "
f"fail={sum(not row[-1] for row in rows)} controls=2 "
f"fail={sum(not row[-1] for row in rows)} controls={expected_controls} "
f"status={'FAIL' if failed else 'PASS'} summary={summary}", flush=True)
raise SystemExit(1 if failed or len(rows) != 18 else 0)
raise SystemExit(1 if failed or len(rows) != expected_runs else 0)
@@ -0,0 +1,54 @@
# Linux39 verification report
## Tier results
| Tier | Result | Wall time | Coverage |
|---|---:|---:|---|
| Fast | PASS | 161.0 s | Canonical owner only; resolved FI Style 2 / SW Style 1, passage ripple and coverage, adjacent-profile step, W1/W12 hashes, LOD0 rings and controls. |
| Medium | PASS | 472.9 s | Clang style overrides, transitions, L13/L9 proofs, LOD0–8 rings, owner shape/crease/fragments and captures. |
| Full | PASS | 1573.7 s (26m 14s) | Revalidated medium evidence, GCC supplement, 30 determinism cells, and five interleaved cost pairs per recipe. |
Fast is below the five-minute limit. The five-pair median cost change was +4.12% for Floating Islands (17.053 s L36, 17.756 s L39) and +0.94% for SurfaceWorld (1.800 s L36, 1.817 s L39). All commands used the CPU 0–11 / 6 GiB limits; the full tier had a 29-minute timeout.
## Source stamp and owner identity
`BuildLinux.sh` hashes a sorted manifest covering Core inputs, standalone pipeline sources and generated wrappers, build files, and the source-stamp helper. It includes the Unreal adapter when that adapter is enabled for compilation. The manifest SHA-256 is compiled as `VOXELFORGE_SOURCE_STAMP`; headless and capture executables print it as their first runtime line. The runner compares the expected stamp before and after execution, probes every available binary at both ends, and stops on a missing or mismatched stamp. Summaries record the stamp, binary SHA-256, input JSON SHA-256, resolved recipe, style and layout ID.
Current stamp: `644cd7ed510034b1855f249cc1dba338da2336c4071069792663c88810b59b90`. The canonical owner JSON SHA-256 is `7b095f9bcc6b90133d5a463f99f0b571133b2c549ce1ecf2d64fe9175e41e183`, unchanged from L37. FI and SW each produced 12 matching W1/W12 hashes, and both signatures match the saved L37 controls. No Core geometry source changed; C++ edits add build stamps and resolved-layout evidence.
The instrumented L36 cost baseline was exported from commit `8be1c4d` and carries its own recorded stamp: `28c74aac57643360cb563e07bae278e2e14eceda0b63a3a50cf5c9876003e765`.
## Negative controls
| Control | Evidence |
|---|---|
| Core edit after build | Runner refused the stale binary; original source bytes were restored. |
| Style-0 fixture named `owner_world.json` | Resolved snapshot exposed FI Style 0; the owner assertion requiring Style 2 rejected it. |
| All-black JPEG | Rejected at stddev 0 and coverage 0 (floors: 2.0 and 0.001). |
| L30 terrace rings | Rejected: 3.125 m flat run followed by a 0.243 m step. |
| L31 flattened floor | Rejected: amplitude range 0.000252 m. |
| L35 wall ribs / L36 floor washboard | Detected amplitudes 0.0918 m / 0.0943 m against 0.035 m / 0.015 m limits. |
| L38 owner step | Detected 0.2443 m against the 0.100 m adjacent-profile limit. Fitted period 7.875 m is diagnostic only. |
## Cleanup and remaining diagnostic
The duplicate L13/L9 runner was removed. The crease/fragment audit now decodes each owner mesh once for fragment, near-crease and overview scans. Style-override artifacts use `style-override-*`; `owner` runs use the canonical input with asserted resolved styles. Capture quality decodes pixels and enforces variance and coverage floors, including on JPEGs.
The current FI owner has six small fragments. That count remains diagnostic because a strict zero-fragment gate fails on the L37 owner; a baseline-backed fragment threshold remains open. The merged audit still reports the count alongside near and overview crease results.
## Failed attempts
- `full-final` stopped after determinism with a missing `VerificationRunner.bounded_run` method reference; switched to the shared bounded runner.
- `full-final2` could not find the historical temp tree's `Tools/verify` directory; created it before copying the stamp helper and computed the stamp from the temp repository root.
- `full-final3` was interrupted during an expected long FI W1 cell after the log went quiet; rerun allowed the cell to finish.
- `full-final4` passed all 30 determinism cells, then exposed an expected-name typo for the L36 binary (`...-stamped-clang` vs `...-stamped`). The corrected full run passed the baseline build and all cost pairs.
How-to instructions are in [`Standalone/Tools/verify/README.md`](../../../Tools/verify/README.md). Logs and JSON summaries are in `Saved/Linux/l39/`.
## Orchestrator review (Claude)
Accepted. I re-ran `verify.py fast` myself on the final tree: PASS in 162.7 s, the stamp matched, owner hashes match
L37 for both recipes at W1/W12, and the resolved style map asserted {FI: 2, SW: 1}. Note for readers: the
`style=[2]` on SurfaceWorld rows is the style of the single FI→SW passage, which belongs to the upper (FI)
strate. The owner world has no SW-owned passage, so "SW Style 1" never shapes a tunnel today. Earlier rounds'
"owner-sw Style 1" captures show the lower end of the Style 2 passage.
@@ -0,0 +1,62 @@
# Research R6 — director, generators, and mesher in metres
Read-only review of the current checkout and L20/L21/L22/L29, R2, and R5 notes. No builds or tests were run.
**R2’s layout-chunk split is already resolved in the checked source.** `AttachValidatedWorldAssembly` computes one physical `LayoutChunkWidthSubvoxels` from `StrateLayoutChunkSizeMeters`, passes it to both `BuildSurfaceStratumPlan` and `BuildFloatingIslandStratumPlan`, and writes the same manager/dispatch width into the resolved snapshot (L22). Do not apply the FI owner/Core ratio to layout bounds again. FI still uses that ratio to convert its *generator parameters* and passage dimensions from owner voxels; that separate authoring-unit leak remains.
## 1. Current leaks
The closest current match to the three layers is: director work in `VoxelStrateManager.cpp::Initialize` and `VoxelForgeCoreAdapter.cpp::AttachValidatedWorldAssembly`; field evaluators in `Heightfield2D::point`, `FloatingIslandFieldV1::densitySubvoxels`, and legacy `UVoxelGenerator::GetDensityAt`; shared composition in `FStandaloneShapeWorld::ApplyResolvedStrateComposition`; extraction in `World::generateTile` and `UVoxelMarchingCubesMesher::GenerateMesh`. `VoxelWorld::BuildDesiredTiles` is the scheduling side of the director. The table lists where these boundaries still cross.
| Leak | Current location and consequence |
|---|---|
| Director lookup inside field evaluation | `Standalone/Core/src/ShapeWorldInternal.h::FStandaloneShapeWorld::DensityAt`, `NativeSurfaceDensityAt`, `NativeFloatingIslandDensityAt`, and `ClassifyBoxDetailed` query `UVoxelStrateManager` and select native SurfaceWorld, native FloatingIslands, or legacy evaluation while workers sample. `Source/VoxelForge/Private/VoxelGenerator.cpp::UVoxelGenerator::GetDensityAt` also routes each sample through manager type/params. The resolved layout exists, but is not yet the sole dispatch input. |
| No common generator API/format | `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h::Heightfield2D` and `FloatingIslandFieldV1` have distinct recipe/evaluator APIs; the legacy path in `VoxelGenerator.cpp`, operator stacks, and morphology is a third. `FStandaloneShapeWorld` is their adapter and shared composition path at once. A new generator cannot currently implement one common contract. |
| Units cross boundaries repeatedly | `VoxelStrateDefinition.h::StrateHeightInChunks`, `VoxelSettings.h::InterStrateGapChunks`, and many `VoxelStrateTypes.h` shape/passage values are authored in chunks or voxels. `VoxelStrateManager.cpp::Initialize` converts metre chunk width to owner voxels; `VoxelForgeCoreAdapter.cpp::WorldAssemblyFixed` packs values as 1/256 Core voxel; `BuildFloatingIslandStratumPlan` and `AttachValidatedWorldAssembly` use owner/Core ratios for FI dimensions and passages. Snapshot bounds are micrometres, but recipe anchors are still labelled 1/256 Core voxel, and `voxelSizeMicrometres` is currently filled with the same Core size for every strate. `VoxelStrateDefinition.h` still documents 16 chunks as 64 m at 4 m/chunk although the current 8 m setting makes that 128 m. |
| Generator policy remains in shared composition | `ShapeWorldInternal.h::ApplyResolvedStrateBoundaryBand` branches on generator-kind strings for FI air-gap and surface/landing behavior; the density/classification functions above branch on generator types. These exceptions should become explicit resolved interface or generator capabilities, not string tests in the common evaluator. |
| Route and passage logic is only partly shared | `VoxelForgeCoreAdapter.cpp::BuildFiniteSurfaceRoute` plans the SurfaceWorld route. `BuildFloatingIslandStratumPlan` embeds passage/spiral dimensions into the FI anchor root. `ResolvedLayoutInternal.h::PlanResolvedLayoutPassagesV1` still tests SurfaceWorld and FloatingIslands to place mouths and handle the FI root; `ShapeWorldInternal.h::ApplyResolvedStratePassageCarves` applies the shared carve but also contains route/support policy. Legacy passage behavior remains in `UVoxelGenerator::GetDensityAt`. A new generator should not need a route implementation. |
| Tile builder knows generator recipes | `Standalone/Core/src/VoxelForgeCore.cpp::World::generateTile` scans SurfaceWorld feature IDs 2/4, refines their resolution, limits a native SurfaceWorld density band, and selects/skips far-horizon repair based on SurfaceWorld/FloatingIslands plan presence. The generic mesher should receive bounds, surface hints, and a sampling request, not recipe IDs. |
The shared ceiling/content/bedrock records and Hard/Gradient/Interleaved evaluation are real foundations (`ResolvedLayoutSnapshotV3`, `ApplyResolvedStrateBoundaryBand`, L29). Remaining work is to make their resolved physical records authoritative and remove generator-name exceptions. The mesh-side boundary floor/ceiling patches in `VoxelForgeCore.cpp`/`FarShellsInternal.h` still duplicate part of that surface policy.
## 2. Generator contract
The director resolves an immutable world snapshot before jobs are queued. A generator is constructed once for a resolved strate and receives:
- Stable world seed, strate ID, generator/recipe/schema/evaluator versions, and that strate’s immutable parameters.
- Physical half-open bounds `[bottom, top)` and lattice origin in integer micrometres; the resolved ceiling/content/bedrock and adjacent-interface records that touch the strate.
- Its own `voxelSizeMicrometres` (25 cm by default), independent of the 8 m layout chunk and of the tile key. The request gives the physical query bounds plus the mesher’s exact halo. LOD may guide work, but the same physical coordinate and recipe must return the same field value at every LOD.
- The shared deterministic library: versioned noise, fixed-point conversion, common distance/shape helpers, and conservative bounds utilities. Resolved passage descriptors and style are consumed by shared composition; the generator need not plan or carve the inter-strate route.
Concrete evaluator shape: `densityAt(xUm, yUm, zUm) -> signed field value`, with one documented convention (recommended: negative is solid, zero is the surface, positive is air). Keep magnitude units stable and documented; metre-scaled signed distance is the target for shared band composition. Provide a conservative support/influence AABB or “unbounded/unknown”; an interval/classification for a requested AABB (`solid`, `air`, `mixed`, or `unknown`); and an optional detail envelope (smallest safe sample spacing). A SurfaceWorld-like generator may also return semantic ground/cap surfaces in metres. Caches and feature tile lists may accelerate these queries, but are not part of output identity.
**A generator must never** query the manager, UObject state, chunk index, season, or a neighbour while evaluating; choose strate bounds, gaps, transitions, bedrock/ceiling ownership, or route geometry; mutate input state; generate triangles; or vary a field by tile order, worker, or LOD. Unknown proof means evaluate, never skip. The director selects the evaluator once; shared composition applies the interface bands, passages and edits; the mesher consumes the composed field and generic hints. This is enough for TunnelNetwork or a cavern generator to be added without edits to the director’s sampling loop or mesher.
## 3. Ordered migration
“No” means existing-world density/mesh/output hashes stay identical. A new schema may change manifest identity only when deliberately versioned; do not hide such a change behind a claimed pure refactor. Size estimates are one engineer’s rough implementation effort: S 1–2 days, M 3–5 days, L 1–2 weeks.
| Step | Change and verification tier | Changes output? | Main risk | Size |
|---:|---|---|---|---|
| 1 | Freeze the current source stamp, canonical owner FI Style 2/SW Style 1 resolved snapshots, and existing hashes as the comparison set. Use R5 **fast** to establish the baseline; medium/full remain promotion gates. | No | Incorrect/stale binaries or mislabeled style invalidate all comparisons. | XS |
| 2 | Add a common immutable field interface and wrap the current SurfaceWorld, FloatingIslands, and legacy evaluators without changing their arithmetic. Bind the evaluator once per resolved strate/tile, not via a manager lookup per density sample. Run **fast** hash parity; **medium** before merge. | No; hashes must match. | Virtual/function dispatch cost; accidentally changing float/fixed-point call order. | M |
| 3 | Make the resolved metre layout the sole runtime authority: resolve height, gap, transition width, band bounds, seed, and passage settings before scheduling. Keep chunk indices only as a compatibility projection. Convert the existing 8 m chunk and L29 bands exactly. R2’s FI/SW split is handled here by the already-present common layout width; retain owner/Core ratios only in the legacy FI parameter adapter. Run **fast + medium** (layout, transition, passage checks). | No for equivalent existing layouts; hashes must match. | Negative-Z floor/rounding, old season snapshots, adapter and manager disagreeing about interval edges. | M |
| 4 | Introduce metre-valued authored height/gap/transition/passage dimensions and a per-strate `voxelSizeMeters` default of 0.25. Add a versioned conversion from old chunk/voxel assets based on each legacy route’s established unit; do not assume the source unit is recorded on the asset or reinterpret old numeric values as metres. Keep 8 m layout chunk independent. **Fast + medium** for old assets; full when enabling non-default resolutions. | No for converted old assets. Yes for changed metre values or voxel size; bump layout/recipe/evaluator identity. | Asset migration and resolution-dependent detail; changing 25 cm to 12.5 cm can mean about 8× samples per physical volume. | L |
| 5 | Route all new and existing passage requests through resolved per-strate configs and a shared route plan. First copy existing SW/FI route anchors exactly; replace FI-root and SurfaceWorld placement branches with explicit resolved anchor/surface capabilities in follow-up slices. Run **fast + medium** passage matrix and route parity. | No for exact route replay; hashes must match. New geometry/style semantics are Yes and need a generator/layout version bump. | Walkability/support, deterministic placement, route planning cost. | M |
| 6 | Move remaining boundary exceptions into explicit interface records/capabilities. Keep ceiling → content → breakable bedrock per strate and the upper strate’s Hard/Gradient/Interleaved rule. Preserve L29 samples and passage-mouth fade exactly. Run **fast + medium** transition, hole, and passage gates. | No; hashes must match. | Interface edge ownership and a false solid/air shortcut can open a hole. | M |
| 7 | Standardize culling outputs: conservative support bounds, interval certificates, unknown fallback, and detail envelope. Adapt existing field proofs first; replace type switches only when the new proof matches brute-force/classification controls. Run **fast**, then **medium** proof negatives and cost pairs. | No; hashes must match. | Unsound certificates skip density work; per-voxel bounds calls hurt performance. | M/L |
| 8 | Make `World::generateTile` and the mesher consume generic feature boxes, semantic surfaces, field bounds, and sample-spacing hints. Move current SurfaceWorld refinements behind capabilities while keeping their algorithms byte-for-byte. Run **fast + medium** ring/shape checks. | No for dispatch-only refactor; hashes must match. | Cross-LOD seams, tile halos, mixed-resolution tile boundaries, Unreal mesher assumptions. | L |
| 9 | Add an opt-in metre-native TunnelNetwork/cavern recipe through the contract and generic mesher interface. Run **medium** on the new recipe and R5 **full** before enabling it in owner layouts. | Yes for worlds selecting it; bump that generator’s recipe/evaluator version. | New density cost and output identity. | M/L |
| 10 | Implement the L20 shared, LOD-stable surface/transition representation to remove measured ring ledges. Run **medium** ring/shape checks and R5 **full** on the final tree. | Yes; bump the mesher/layout version and establish new golden hashes. | Surface topology and cross-LOD transitions change; coarse sampling may erase thin bands. | L |
R5’s draft timings are about **3–4 min fast**, **8–10 min additional medium**, and **32–34 min full**, excluding builds. Run fast for every step: output-neutral slices must match existing hashes; output-changing slices must match their new versioned golden hashes. Medium is required before promoting layout, route, culling, or mesher changes. Full is final-source evidence after output-changing versions and golden hashes are reviewed. Include the actual owner configs and assert their resolved styles, as R5 requires; do not treat style-override fixtures as owner evidence.
### Cross-cutting risks
- **Determinism:** convert metres to integer micrometres once; use a specified tie rule and mathematical floor for negative coordinates; hash resolved parameters, voxel size, generator versions, and interface records in stable order. Keep floating-point contraction/platform checks for the compatibility evaluator.
- **Per-voxel cost:** eliminate manager/recipe selection from the hot call, pre-resolve field objects and tile candidates, and keep unknown proofs conservative. Benchmark actual FI2 and SW1 with R5 cost pairs at medium/full. A finer voxel size is cubic work growth, not a cheap resolution toggle.
- **Unreal adapter:** `VoxelForgeCoreAdapter.cpp` currently serializes UObject parameters into Core fixed units; new reflected metre fields need versioned asset migration and UHT/UBT validation. R2 also recorded that recent adapter changes had no Unreal build evidence. Static research cannot establish that compile boundary.
## Open question for the owner
Should existing voxel-authored assets retain their current physical dimensions through a per-generator compatibility conversion? Recommended: yes, then author new recipes in metres. Reinterpreting old values at 25 cm would rescale worlds and require broad output-version bumps.
@@ -0,0 +1,14 @@
# Shared notes (researcher R6 <-> implementer L39), newest at bottom
## R5 read-only architecture findings (2026-10-23)
Inspected `Standalone/BuildLinux.sh`, `Build.ps1`, `Core/core_sources.txt`, `Source/StreamBenchmarkHeadless.cpp`, `Source/LinuxOffscreenCapture.cpp`, `data/owner_world.json`, the L37 passage/invariant/legacy/ring/capture/crease/fragment runners, `Saved/Linux/l37/mouth-curves-run/layout-owner-fi.json`, and the R5/L37 reports. No source was changed and no binary was run.
- `BuildLinux.sh` reads `Core/core_sources.txt`, generates wrapper translation units under `Standalone/Build`, compiles the Core and compatibility objects, then links `VoxelForgeCoreHeadless-$VARIANT`. It separately compiles `LinuxOffscreenCapture.cpp` into `VoxelForgeOffscreenCapture-$VARIANT`. The generated wrappers/objects are build outputs; the stamp manifest should hash the listed original source paths, `core_sources.txt`, both standalone executable sources, and the build scripts. No stamp exists today. The script already enforces `taskset -c 0-11` plus `prlimit --as=6442450944` and rejects `--jobs` above 12.
- There are two executable entry points to stamp. `StreamBenchmarkHeadless.cpp` parses options before its first current log (`[HeadlessConfig]`); `LinuxOffscreenCapture.cpp` checks `argc` before logging. Print the source stamp at the start of both `main`s, before parse/validation. A cheap stamp-only option on each binary would let the shared runner reject stale binaries before expensive work.
- The canonical JSON contains FI `PassageConfig.Style=2` and SW `PassageConfig.Style=1`. The checked-in L37 capture and passage runners label `linux25-owner-*-style0.json` as `owner-*`; those are style overrides. In an existing resolved snapshot, each `strates[]` entry has `generator_kind`, `recipe_id`, and IDs, while `passages[]` has geometry, but neither exposes the effective style. The requested style assertion cannot be made from the current snapshot: add the resolved passage style per strate to its serialization, then assert FI=2/SW=1. `stable_identity` is the existing layout ID (`resolved-layout-v3-fnv64:…`); the recipe audit log reports recipe but not style.
- The L37 runners each create their own subprocess wrappers and summaries. A shared helper can centralize the constrained invocation, stamp-only preflight, expected-stamp recomputation, binary/input SHA-256, resolved-layout extraction, and a common summary row (stamp, binary hash, input hash, actual recipe/style/layout ID, elapsed time). Recompute the manifest after the tier. Resolve the canonical layout once per owner case and use that same snapshot for all gates/captures.
- `CapturePassageStandardSetL37.py` and `CaptureMouthOverviewL37.py` only check JPEG size. An all-black JPEG therefore passes. Add decoded-pixel variance/coverage validation (without PIL, which the earlier research could not import) and include a black-image negative control.
- `RunMouthCreaseAuditL37.py` imports the fragment audit. For each candidate it reads the mesh once in `BASE.audit`, then `visible_crease` reads it again for the near view and again for overview. A combined analyzer can decode once, then share triangles/buckets for fragment, crease, and both views. Existing overview check matters: L37 passed near at 4.40° but measured 25.54° against 20° at overview.
- `RunRingAuditsL37.py` already keeps the L30 terrace and L31 flat meshes as live controls; the full LOD0–8 work is separate from targeted LOD0 checks. The R5 split can reuse `AuditRingTerraceL32.py` directly for fast LOD0 plus those controls.