wip(assembler): round L31 -- FI recipe column audit corrected; recipe-stamped audit runner

On the real FloatingIslands recipe, column caps sit inside anchor-island rock and native bedrock;
the old check sampled the route foot. The audit now samples each planned cap against the native
field (negative control: a cap moved into air is detected). The audit runner records requested vs
actual recipe and exits on mismatch. All 16 passage rows (4 styles x 2 recipes x 2 compilers) pass.
The readable shader uses a smooth tint. Interim: FI Gradient displacement capped at width/256
(flat floor, no contour rings); restoring real undulation is L32. Evaluator 19.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-21 14:13:18 +02:00
1 parent 103d8b471e
commit 28069f0d98
23 files changed
+1235 -19

No files matched your search

@@ -249,8 +249,10 @@ namespace VoxelForgeCore
// L'assembleur v1 encode coordonnees et scalaires en 1/256 de voxel, independamment des tuiles.
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 2;
// Version 14 adds shared passage columns; version 16 smooths owner Gradient faces.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 16;
// Version 14 adds shared passage columns; version 16 smooths owner Gradient faces;
// version 18 limits FloatingIslands Gradient face displacement to one voxel;
// version 19 reduces it to one quarter voxel to remove the residual floor contour.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 19;
enum class FieldChannel : std::uint32_t
{
+4 -1
View File
@@ -359,7 +359,10 @@ ResolvedLayoutTransitionBandsV1 EvaluateResolvedLayoutTransitionBandsV1(
return result;
}
const std::int64_t displacement = (noise * (width / 8)) / TransitionQ16;
// FloatingIslands floor faces retain the same deterministic Gradient field but
// cap displacement at one quarter voxel so the transition does not read as a contour.
const std::int64_t gradientDivisor=upper.generatorKind=="FloatingIslands"?256:8;
const std::int64_t displacement = (noise * (width / gradientDivisor)) / TransitionQ16;
const std::int64_t interfaceZ = seam + displacement;
result.upperBedrockBottomMicrometres = interfaceZ;
result.upperBedrockTopMicrometres = interfaceZ + upperThickness;
+2 -1
View File
@@ -269,7 +269,8 @@ public:
const VoxelForgeCore::ResolvedLayoutStratumV2& Lower) const
{
const std::int64_t width=std::max<std::int64_t>(0,Upper.transitionWidthMicrometres);
if(Upper.transitionMode=="Gradient")return width/8;
if(Upper.transitionMode=="Gradient")
return width/(Upper.generatorKind=="FloatingIslands"?256:8);
if(Upper.transitionMode!="Interleaved")return 0;
const std::int64_t upperThickness=std::max<std::int64_t>(0,
Upper.bedrock.topMicrometres-Upper.bedrock.bottomMicrometres);
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Combine the initial and repeat L31 interleaved cost batches."""
import csv
import statistics
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l31"
sources = (
("first", 0, OUT / "cost-first/cost-runs.csv"),
("repeat", 5, OUT / "cost-runs.csv"),
)
rows = []
for batch, offset, path in sources:
with path.open(newline="", encoding="utf-8") as stream:
for row in csv.DictReader(stream):
row["pair"] = str(int(row["pair"]) + offset)
row["source_batch"] = batch
rows.append(row)
with (OUT / "cost-combined.csv").open("w", newline="", encoding="utf-8") as stream:
writer = csv.DictWriter(stream, fieldnames=(
"recipe", "pair", "build", "whole_view_ready_seconds", "process_elapsed_seconds",
"exit_code", "status", "source_batch"))
writer.writeheader()
writer.writerows(rows)
print("recipe pairs median_l30_s median_l31_s median_change_pct paired_median_pct range_pct")
failed = False
for recipe in ("surface-world", "floating-islands"):
by_build = {
build: [float(row["whole_view_ready_seconds"]) for row in rows
if row["recipe"] == recipe and row["build"] == build]
for build in ("l30", "l31")
}
paired = []
for pair in range(1, 11):
times = {
row["build"]: float(row["whole_view_ready_seconds"])
for row in rows if row["recipe"] == recipe and int(row["pair"]) == pair
}
paired.append((times["l31"] / times["l30"] - 1.0) * 100.0)
median_l30 = statistics.median(by_build["l30"])
median_l31 = statistics.median(by_build["l31"])
median_change = (median_l31 / median_l30 - 1.0) * 100.0
print(f"{recipe} 10 {median_l30:.3f} {median_l31:.3f} {median_change:+.2f} "
f"{statistics.median(paired):+.2f} {min(paired):+.2f}..{max(paired):+.2f}")
failed |= len(by_build["l30"]) != 10 or len(by_build["l31"]) != 10
print(f"[L31CostCombined] pairs_per_recipe=10 runs={len(rows)} "
f"status={'FAIL' if failed else 'PASS'} summary={OUT / 'cost-combined.csv'}")
raise SystemExit(1 if failed or len(rows) != 40 else 0)
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
"""Compare L25 and 24 m crossing hash records for matching recipe/compiler/workers."""
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l31"
pattern = re.compile(r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
lines = []
failed = False
for recipe in ("surface-world", "floating-islands"):
for compiler in ("clang", "gcc"):
for workers in (1, 8, 12):
left = pattern.findall((OUT / f"l25-{compiler}-{recipe}-w{workers}.log").read_text())
right = pattern.findall((OUT / f"crossing24-{compiler}-{recipe}-w{workers}.log").read_text())
same = bool(left) and len(left) == 12 and left == right
failed |= not same
lines.append(f"{recipe},{compiler},W{workers},{'MATCH' if same else 'MISMATCH'}")
path = OUT / "determinism-crossing-compare.log"
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
print("\n".join(lines))
print(f"[L31CrossingHashCompare] comparisons={len(lines)} "
f"pass={sum(line.endswith('MATCH') for line in lines)} fail={sum(line.endswith('MISMATCH') for line in lines)}")
raise SystemExit(1 if failed else 0)
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""Encode the six requested L31 screenshots as small, pinned JPEGs."""
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
SOURCE = ROOT / "Saved/Linux/l31"
DEST = Path(__file__).resolve().parent / "captures"
DEST.mkdir(parents=True, exist_ok=True)
names = (
"fi-column-top-close-v19",
"fi-column-top-wide-v19",
"fi-column-base-close-v19",
"fi-column-base-wide-v19",
"fi-floor-v19-tint-off",
"fi-floor-v19-tint-on",
)
failed = False
for name in names:
source = SOURCE / f"{name}.png"
destination = DEST / f"{name.removesuffix('-v19').replace('-v19-', '-')}.jpg"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", "ffmpeg",
"-y", "-hide_banner", "-loglevel", "error", "-i", str(source),
"-frames:v", "1", "-q:v", "4", str(destination),
]
result = subprocess.run(command, check=False)
size = destination.stat().st_size if destination.exists() else 0
ok = result.returncode == 0 and size <= 300_000
failed |= not ok
print(f"[L31CaptureJPEG] source={source.name} output={destination.name} "
f"bytes={size} status={'PASS' if ok else 'FAIL'}")
raise SystemExit(1 if failed else 0)
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Measure surface heights and normals on an eastward FI floor radial line."""
import array
import math
import struct
import sys
def read_surface_vertices(path):
vertices = []
with open(path, "rb") as stream:
if stream.read(8) != b"VFMSH001":
raise SystemExit(f"{path}: invalid scene header")
version, voxel_um, count = struct.unpack("<3I", stream.read(12))
stream.read(40)
if version != 1 or count > 100000:
raise SystemExit("unsupported scene")
for _ in range(count):
key = struct.unpack("<4i", stream.read(16))
surface_count, skirt_count, vertex_count, index_count, mask_count = struct.unpack(
"<5I", stream.read(20))
positions = array.array("f")
positions.frombytes(stream.read(vertex_count * 12))
normals = array.array("f")
normals.frombytes(stream.read(vertex_count * 12))
indices = array.array("I")
indices.frombytes(stream.read(index_count * 4))
masks = stream.read(mask_count)
if sys.byteorder != "little":
positions.byteswap()
normals.byteswap()
indices.byteswap()
used = set()
for tri in range(0, index_count, 3):
if masks and masks[tri // 3]:
continue
for index in indices[tri:tri + 3]:
if index < surface_count:
used.add(index)
for index in used:
offset = index * 3
vertices.append((positions[offset], positions[offset + 1],
positions[offset + 2], normals[offset],
normals[offset + 1], normals[offset + 2]))
return voxel_um, vertices
def main():
scene = sys.argv[1]
center_x, center_y = 64.0, -32.0
voxel_um, vertices = read_surface_vertices(scene)
floor = [v for v in vertices if abs(v[2] + 119.5) < 0.05
and math.hypot(v[0] - center_x, v[1] - center_y) <= 16.5]
if not floor:
raise SystemExit("no floor surface vertices near expected FI bedrock top")
mean = tuple(sum(v[index] for v in floor) / len(floor) for index in (3, 4, 5))
normal_rms = math.sqrt(sum(sum((v[index] - mean[axis]) ** 2
for axis, index in enumerate((3, 4, 5)))
for v in floor) / len(floor))
min_z = min(v[2] for v in floor)
max_z = max(v[2] for v in floor)
print(f"[RingNormalField] voxel_um={voxel_um} center_m=({center_x:.3f},{center_y:.3f}) "
f"vertices={len(floor)} height_min_m={min_z:.6f} height_max_m={max_z:.6f} "
f"mean_normal=({mean[0]:.8f},{mean[1]:.8f},{mean[2]:.8f}) "
f"normal_rms={normal_rms:.9f}")
print("[RingNormalRadialLine] radius_m sampled_radius_m height_m nx ny nz")
for radius in range(1, 17):
x, y = center_x + radius, center_y
vertex = min(floor, key=lambda v: (v[0] - x) ** 2 + (v[1] - y) ** 2)
sampled_radius = math.hypot(vertex[0] - center_x, vertex[1] - center_y)
if math.hypot(vertex[0] - x, vertex[1] - y) > 0.5:
raise SystemExit(f"missing near vertex at radius {radius} m")
print(f"{radius:2d} {sampled_radius:.6f} {vertex[2]:.6f} "
f"{vertex[3]:.8f} {vertex[4]:.8f} {vertex[5]:.8f}")
if __name__ == "__main__":
main()
@@ -0,0 +1,241 @@
#!/usr/bin/env python3
"""Rasterize upward mesh triangles into a top-down field height map."""
import argparse
import array
import json
import math
import struct
import sys
Q16 = 1 << 16
MASK64 = (1 << 64) - 1
def trunc_div(numerator, denominator):
return numerator // denominator if numerator >= 0 else -((-numerator) // denominator)
def transition_hash(seed, interface, x, y, z, salt):
value = 14695981039346656037
for word in (seed & 0xffffffff, interface & 0xffffffff,
x & MASK64, y & MASK64, z & MASK64, salt):
for byte in range(8):
value ^= (word >> (byte * 8)) & 0xff
value = (value * 1099511628211) & MASK64
value ^= value >> 30
value = (value * 0xbf58476d1ce4e5b9) & MASK64
value ^= value >> 27
value = (value * 0x94d049bb133111eb) & MASK64
value ^= value >> 31
return ((value >> 32) >> 15) - Q16
def transition_fade(value):
value = max(0, min(Q16, value))
square = value * value // Q16
return square * (3 * Q16 - 2 * value) // Q16
def gradient_height_um(layout, x_um, y_um):
upper = next(item for item in layout["strates"] if item["order"] == 0)
lower = next(item for item in layout["strates"] if item["order"] == 1)
width = upper["transition_width_um"]
cell = max(1_000_000, layout["strate_layout_chunk_size_um"] * 4)
ix = x_um // cell
iy = y_um // cell
tx = transition_fade(((x_um - ix * cell) * Q16) // cell)
ty = transition_fade(((y_um - iy * cell) * Q16) // cell)
def at(dx, dy):
noise = transition_hash(0, upper["strate_index"], ix + dx, iy + dy, 0,
0x47524144)
return noise
def lerp(a, b, t):
return a + trunc_div((b - a) * t, Q16)
low = lerp(lerp(at(0, 0), at(1, 0), tx),
lerp(at(0, 1), at(1, 1), tx), ty)
amplitude_divisor = 256 if upper.get("generator_kind") == "FloatingIslands" else 8
displacement = trunc_div(low * (width // amplitude_divisor), Q16)
seam = upper["bedrock"]["bottom_um"]
thickness = upper["bedrock"]["top_um"] - seam
return seam + displacement + thickness
def read_scene(path):
triangles = []
with open(path, "rb") as stream:
if stream.read(8) != b"VFMSH001":
raise SystemExit(f"{path}: invalid scene header")
version, voxel_um, count = struct.unpack("<3I", stream.read(12))
stream.read(40)
if version != 1 or count > 100000:
raise SystemExit("unsupported scene")
for _ in range(count):
key = struct.unpack("<4i", stream.read(16))
surface_vertices, skirt_count, vertex_count, index_count, mask_count = struct.unpack(
"<5I", stream.read(20))
positions = array.array("f")
positions.frombytes(stream.read(vertex_count * 12))
normals = array.array("f")
normals.frombytes(stream.read(vertex_count * 12))
indices = array.array("I")
indices.frombytes(stream.read(index_count * 4))
masks = stream.read(mask_count)
if sys.byteorder != "little":
positions.byteswap()
normals.byteswap()
indices.byteswap()
for index in range(0, index_count, 3):
if masks and masks[index // 3]:
continue
a, b, c = indices[index:index + 3]
if max(a, b, c) >= surface_vertices:
continue
nz = (normals[a * 3 + 2] + normals[b * 3 + 2]
+ normals[c * 3 + 2]) / 3.0
if nz < 0.35:
continue
vertices = tuple((positions[v * 3], positions[v * 3 + 1],
positions[v * 3 + 2]) for v in (a, b, c))
triangles.append(vertices)
return voxel_um, triangles
def rasterize(triangles, bounds, width, height):
x0, x1, y0, y1 = bounds
values = [None] * (width * height)
sx, sy = width / (x1 - x0), height / (y1 - y0)
for tri in triangles:
(ax, ay, az), (bx, by, bz), (cx, cy, cz) = tri
den = (by - cy) * (ax - cx) + (cx - bx) * (ay - cy)
if abs(den) < 1.0e-12:
continue
left = max(0, int(math.floor((min(ax, bx, cx) - x0) * sx)))
right = min(width - 1, int(math.ceil((max(ax, bx, cx) - x0) * sx)))
top = max(0, int(math.floor((y1 - max(ay, by, cy)) * sy)))
bottom = min(height - 1, int(math.ceil((y1 - min(ay, by, cy)) * sy)))
for py in range(top, bottom + 1):
y = y1 - (py + 0.5) / sy
row = py * width
for px in range(left, right + 1):
x = x0 + (px + 0.5) / sx
u = ((by - cy) * (x - cx) + (cx - bx) * (y - cy)) / den
v = ((cy - ay) * (x - cx) + (ax - cx) * (y - cy)) / den
w = 1.0 - u - v
if min(u, v, w) < -1.0e-6:
continue
z = u * az + v * bz + w * cz
idx = row + px
if values[idx] is None or z > values[idx]:
values[idx] = z
return values
def main():
parser = argparse.ArgumentParser()
parser.add_argument("scene")
parser.add_argument("output_ppm")
parser.add_argument("--bounds", nargs=4, type=float, required=True,
metavar=("XMIN", "XMAX", "YMIN", "YMAX"))
parser.add_argument("--size", nargs=2, type=int, default=(640, 640))
parser.add_argument("--range", nargs=2, type=float, default=(-122.0, -118.0),
metavar=("ZMIN", "ZMAX"))
parser.add_argument("--ring-center", nargs=2, type=float,
metavar=("X", "Y"))
parser.add_argument("--probe-max-radius", type=int, default=16)
parser.add_argument("--layout")
args = parser.parse_args()
voxel_um, triangles = read_scene(args.scene)
width, height = args.size
sx, sy = width / (args.bounds[1] - args.bounds[0]), height / (args.bounds[3] - args.bounds[2])
values = rasterize(triangles, args.bounds, width, height)
finite = sorted(value for value in values if value is not None)
if not finite:
raise SystemExit("no upward-facing field samples in bounds")
low, high = args.range
if high <= low:
raise SystemExit("height range must increase")
stops = ((0.00, (31, 52, 92)), (0.24, (41, 119, 153)),
(0.50, (85, 177, 143)), (0.73, (226, 207, 105)),
(1.00, (181, 71, 49)))
with open(args.output_ppm, "wb") as output:
output.write(f"P6\n{width} {height}\n255\n".encode())
for value in values:
if value is None:
output.write(bytes((18, 22, 30)))
continue
t = max(0.0, min(1.0, (value - low) / (high - low)))
for index in range(len(stops) - 1):
if t <= stops[index + 1][0]:
ta, ca = stops[index]
tb, cb = stops[index + 1]
blend = (t - ta) / (tb - ta)
rgb = tuple(round(ca[c] + blend * (cb[c] - ca[c]))
for c in range(3))
break
else:
rgb = stops[-1][1]
output.write(bytes(rgb))
def quantile(p):
return finite[round((len(finite) - 1) * p)]
print(f"[RingFieldRaster] voxel_um={voxel_um} triangles={len(triangles)} "
f"pixels={len(finite)}/{len(values)} height_min_m={finite[0]:.6f} "
f"height_p05_m={quantile(.05):.6f} height_median_m={quantile(.5):.6f} "
f"height_p95_m={quantile(.95):.6f} height_max_m={finite[-1]:.6f} "
f"height_range_m={finite[-1]-finite[0]:.6f} "
f"map={args.output_ppm}")
if args.ring_center:
cx, cy = args.ring_center
def at_world(x, y):
fx = (x - args.bounds[0]) * sx - 0.5
fy = (args.bounds[3] - y) * sy - 0.5
ix, iy = math.floor(fx), math.floor(fy)
tx, ty = fx - ix, fy - iy
samples = []
for px, py in ((ix, iy), (ix + 1, iy), (ix, iy + 1), (ix + 1, iy + 1)):
if not (0 <= px < width and 0 <= py < height):
return None
value = values[py * width + px]
if value is None:
return None
samples.append(value)
return ((samples[0] * (1 - tx) + samples[1] * tx) * (1 - ty)
+ (samples[2] * (1 - tx) + samples[3] * tx) * ty)
print(f"[RingRadialProbe] center_m=({cx:.3f},{cy:.3f}) "
"radius_m mean_m angular_sd_m min_m max_m samples")
for radius in range(1, args.probe_max_radius + 1):
row = [at_world(cx + radius * math.cos(a * math.tau / 32),
cy + radius * math.sin(a * math.tau / 32))
for a in range(32)]
row = [value for value in row if value is not None]
if row:
mean = sum(row) / len(row)
sd = math.sqrt(sum((value - mean) ** 2 for value in row) / len(row))
print(f" {radius:>2} {mean:.6f} {sd:.6f} {min(row):.6f} "
f"{max(row):.6f} {len(row)}")
if args.layout:
layout = json.load(open(args.layout, encoding="utf-8"))
if next(item for item in layout["strates"] if item["order"] == 0)["transition_mode"] != "Gradient":
raise SystemExit("--layout must describe a Gradient owner interface")
errors = []
for index, actual in enumerate(values):
if actual is None:
continue
px, py = index % width, index // width
x = args.bounds[0] + (px + 0.5) / sx
y = args.bounds[3] - (py + 0.5) / sy
expected = gradient_height_um(layout, round(x * 1_000_000),
round(y * 1_000_000)) / 1_000_000.0
errors.append(actual - expected)
absolute = sorted(abs(error) for error in errors)
rms = math.sqrt(sum(error * error for error in errors) / len(errors))
print(f"[RingGradientMatch] samples={len(errors)} rms_m={rms:.6f} "
f"p95_abs_m={absolute[round((len(absolute)-1)*.95)]:.6f} "
f"max_abs_m={absolute[-1]:.6f} "
"expected=owner-resolved-2d-hash-value-noise")
if __name__ == "__main__":
main()
@@ -0,0 +1,107 @@
# Linux31 report
## FI column finding
The earlier L29/L30 passage table ran SurfaceWorld twice. On the real FloatingIslands recipe, the old column check under-counted connected ends because it treated support at the route foot as the column cap. The route foot is near z = −116 m; the actual lower cap is near z = −122 m, inside native bedrock whose top is z = −119.5 m. The upper caps sit inside anchor-island rock near z = −62 to −69 m. There is no floating column end or unintended gap. The open passage mouth is intentional.
The audit now samples each planned cap against the native density field. Negative density is solid rock. Every main column cap overlaps native rock within 0.25 m. The style 3 short lower cap overlaps within 0.50 m.
| FI style | Cap | Z (m) | Native overlap / samples | Minimum native density |
|---:|---|---:|---:|---:|
| 0 | Main upper / anchor island | −65.846 | 5,051 / 10,005 | −14.761719 |
| 0 | Main lower / bedrock | −122.390 | 6,149 / 12,588 | −8.000000 |
| 1 | Short upper | −61.574 | 9,725 / 11,033 | −23.464844 |
| 1 | Short lower | −75.637 | 3,915 / 11,500 | −15.554688 |
| 1 | Main upper / anchor island | −66.485 | 4,497 / 11,118 | −15.613281 |
| 1 | Main lower / bedrock | −122.416 | 6,673 / 13,735 | −8.000000 |
| 2 | Main upper / anchor island | −68.891 | 3,986 / 8,523 | −15.546875 |
| 2 | Main lower / bedrock | −122.382 | 5,396 / 10,885 | −8.000000 |
| 3 | Short upper | −65.805 | 5,095 / 10,064 | −14.671875 |
| 3 | Short lower | −80.054 | 2,498 / 10,413 | −11.910156 |
| 3 | Main upper / anchor island | −69.478 | 3,902 / 9,985 | −15.597656 |
| 3 | Main lower / bedrock | −122.436 | 6,130 / 12,453 | −8.000000 |
The negative control moves the style 0 lower cap into the air gap at z = −96.390 m: 0/3,120 native overlaps, minimum density +22.273438, and `detected=true`. The audit still catches a genuinely disconnected end.
## Recipe-stamped passage matrix
Each row is checked against the requested and resolved recipe stamp. The columns are capsule blocked/unsupported counts and mouth checks, open-air violations, naturalness, longest uncolumned span / 12.5 m limit, connected column ends, route bindings / failures, and walkability samples.
For all eight FI rows, `FloatingIslandRouteAudit` also passes: 78,064 capsule samples, 0 blocked, 0 unsupported, and 0 grade failures. The runner now asserts both capsule audits.
| Requested → actual | Style | Compiler | Capsule | Open air | Natural | Long bridge | Column ends | Bindings | Walkability |
|---|---:|---|---|---:|---:|---:|---:|---:|---:|
| surface-world → surface-world | 0 | clang | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 1 (0 failures) | 5642/5642 |
| surface-world → surface-world | 0 | gcc | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 1 (0 failures) | 5642/5642 |
| surface-world → surface-world | 1 | clang | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 1 (0 failures) | 1490/1490 |
| surface-world → surface-world | 1 | gcc | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 1 (0 failures) | 1490/1490 |
| surface-world → surface-world | 2 | clang | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 1 (0 failures) | 1545/1545 |
| surface-world → surface-world | 2 | gcc | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 1 (0 failures) | 1545/1545 |
| surface-world → surface-world | 3 | clang | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 1 (0 failures) | 1495/1495 |
| surface-world → surface-world | 3 | gcc | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 1 (0 failures) | 1495/1495 |
| floating-islands → floating-islands | 0 | clang | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 4.464/12.500 m | 2/2 | 2 (0 failures) | 4000/4000 |
| floating-islands → floating-islands | 0 | gcc | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 4.464/12.500 m | 2/2 | 2 (0 failures) | 4000/4000 |
| floating-islands → floating-islands | 1 | clang | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 3.851/12.500 m | 4/4 | 2 (0 failures) | 1047/1047 |
| floating-islands → floating-islands | 1 | gcc | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 3.851/12.500 m | 4/4 | 2 (0 failures) | 1047/1047 |
| floating-islands → floating-islands | 2 | clang | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 2 (0 failures) | 1135/1135 |
| floating-islands → floating-islands | 2 | gcc | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 0.000/12.500 m | 2/2 | 2 (0 failures) | 1135/1135 |
| floating-islands → floating-islands | 3 | clang | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 4.243/12.500 m | 4/4 | 2 (0 failures) | 1061/1061 |
| floating-islands → floating-islands | 3 | gcc | 0/0 blocked/unsupported; mouth 2/2 | 0 violations | 1/1 | 4.243/12.500 m | 4/4 | 2 (0 failures) | 1061/1061 |
All 16 rows pass. The runner rejects a recipe mismatch. Its negative control requests SurfaceWorld while the resolved layout is FloatingIslands; the log records `requested=surface-world actual=floating-islands status=FAIL` and the process exits 2. Logs and the machine-readable matrix are under `Saved/Linux/l31/`.
## FI floor lines
The L30 rings persisted with height tint off, so they were geometry. Evaluator v19 reduces FloatingIslands Gradient displacement to `transition_width / 256` (one quarter voxel at the 16 m transition width), and the layout validator uses the same bound. The v18 one-voxel cap still left a 0.251819 m radial height range and a visible contour.
On the east radial line from (64, −32), samples at radii 1–16 m all have height −119.500000 m and normal (0, 0, 1). The extended mesh raster has a 0.000479 m total height range. The readable shader now applies a cubic smooth tint (`t²(3−2t)`). Tint-off and tint-on captures show a flat floor without contour bands.
## Captures and hashes
All six JPEGs are recipe-stamped from FloatingIslands and under 300 KB.
| Capture | Size | SHA-256 |
|---|---:|---|
| [FI column top, close](captures/fi-column-top-close.jpg) | 85,696 B | `6b651655fc2127bb5b3502a1a99c024167db2edc21bfe7241f6335a858bb8575` |
| [FI column top, wide](captures/fi-column-top-wide.jpg) | 67,043 B | `926193affd463492095b97310c62416910e12fc94e75d8548a01ad1f9601b6ec` |
| [FI column bedrock base, close](captures/fi-column-base-close.jpg) | 60,491 B | `3e524e0f6c506413b4b5b29d8835792f8b5240f95787c6a08d12ebe6e46aadd5` |
| [FI column bedrock base, wide](captures/fi-column-base-wide.jpg) | 61,543 B | `27ef1a72874c7f8e257969c5e69d614f336edce84693ee80b98783b9f610a4b2` |
| [FI floor, tint off](captures/fi-floor-tint-off.jpg) | 43,405 B | `41b9180c9696c7025e23009e6efdb2e86ace5900b08c293c92371797f9a8596d` |
| [FI floor, tint on](captures/fi-floor-tint-on.jpg) | 43,405 B | `0672e2ddc93fb6a4fe4f6adfdf4cdee896bdbc90dddfb7cf65050d978f86f32b` |
Evaluator version is 19. Binary SHA-256:
- Clang: `a796d433b7e31fa05abba970dbd486db1a904d13222d9c02b9054c394375e4f7`
- GCC: `c2ed12ddf4db5ce97fcd2d9c8f32e4515a4fba6d36d4d8a4b5fbb07a8ab8ae73`
## Determinism and cost
The L25 command and 24 m crossing each passed 12 cases: Clang and GCC at W1/W8/W12 for both recipes. All 12 hash records per log were stable across compilers and worker counts. SW matches the L30 baseline; FI changes as expected with the v19 geometry. L25 and crossing hashes match for all 12 recipe/compiler/worker combinations.
| Recipe | Full-detail density / mesh / payload / topology | Whole-view density / mesh / payload / topology |
|---|---|---|
| SurfaceWorld | `ace56c8f205238ad / a9c603a1248de6b8 / ea55f1f4fcc6a894 / 5ea7bc78a1b678ae` | `0e8c5af01df96b14 / 70099e89b77811ed / dbce0fdcf6764126 / ddaf81a1228f9b4b` |
| FloatingIslands | `ed550e0c8b182695 / 0e410a8e80a3e7f7 / 0751926f0c67d8a4 / f01208bc2f7841ec` | `52e0b9f95da4291e / 7c6e9e18346278a4 / 59160fe3df02a0ae / ef7f17088fe3253e` |
Cost compared the Clang L31 binary with the L30 `linux30-speed-fade` binary: 10 interleaved pairs per recipe, 40 runs total, with zero cache hits. Combined median whole-view time was 1.772 s → 1.784 s for SW (+0.71%) and 17.468 s → 17.482 s for FI (+0.08%). The second five-pair batch was +1.20% SW and +0.01% FI; the larger early SW deltas settled as runs warmed. The paired medians across all 10 were +1.86% SW and +0.55% FI, within the observed run variation.
## Failed and retried runs
- Five low-clip floor exports (`export-fi-floor.log`, `export-fi-floor-probe.log`, `export-fi-floor-without-mesh.log`, `export-fi-floor-probe-retry.log`, `fi-floor-no-camera-probe.log`) stopped with `std::bad_alloc`. Export succeeded with `--max-clip-level=8 --max-level=0`.
- A first radial analyzer invocation omitted the second `--range` value and was rejected by argparse; it was rerun with both bounds.
- A proof/parity probe used custom camera positions and failed SW parity because the sampled tile set differed from the established default-camera audit. That probe was stopped; the default-camera proof and transition suite passed 16/16.
- The first determinism launch ended before completing the matrix. The full 24-case run passed on retry. A first cost batch had an early SW timing outlier, so five additional interleaved pairs per recipe were run and retained in the combined summary.
- Early capture framing probes cropped the FI axis or placed the camera inside rock. A v19 wide floor view exposed only the export skirt edge; the final centered column and square top-down floor views were used.
- The first GCC build attempt was interrupted at 19 objects and retried successfully. One v18 diagnostic build used the default Standalone build path; those artifacts were removed, and final v19 build outputs are under `Saved/Linux/l31/`.
- Two one-line post-processing commands had quoting/pair-index errors; the checked-in `CompareDeterminismL31.py` and `AggregateCostL31.py` helpers produced the final passing summaries.
No run exceeded 30 minutes or remained stuck for 15 minutes. No Unreal files were edited. Nothing was committed, pushed, or stashed.
## Orchestrator review (Claude)
- Merged as an intermediate step: correct FI column-cap audit (with negative control), a recipe-stamped runner that
rejects mismatches, all 16 recipe/style/compiler rows passing, smooth shader tint, determinism.
- **Not accepted as the final floor fix:** v19 caps the FI Gradient displacement at `width/256` (≈6 cm), which
removes the owner's Gradient transition rather than making it smooth; the floor is now flat (radial
height range 0.0005 m). The real fix is a smooth, non-terraced undulation at the L29 amplitude (≈4 m
peak-to-peak). L32 restores it.
@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""Run the recipe-stamped L31 passage audit matrix and reject mislabeled runs."""
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l31"
OUT.mkdir(parents=True, exist_ok=True)
BINARIES = {
"clang": OUT / "bin/VoxelForgeCoreHeadless-linux31-clang",
"gcc": OUT / "bin/VoxelForgeCoreHeadless-linux31-gcc",
}
MARKERS = (
"[ResolvedPassageAuditSummary]",
"[PassageOpenAirAuditSummary]",
"[PassageNaturalnessSummary]",
"[PassageNoLongBridgeAudit]",
"[PassageRockColumnAuditSummary]",
)
rows = []
failed = False
for recipe in ("surface-world", "floating-islands"):
for style in range(4):
data = ROOT / f"Saved/Linux/linux25-owner-{recipe}-style{style}.json"
for compiler, binary in BINARIES.items():
name = f"passage-{recipe}-style{style}-{compiler}"
logfile = OUT / f"{name}.log"
command = [
str(binary), str(data), "--workers=12", "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--force-generate",
"--clip-radius=3", "--max-clip-level=8", "--max-level=-1",
f"--recipe={recipe}", "--audit-resolved-layout-passages",
]
started = time.monotonic()
try:
with logfile.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
timeout=1800, check=False)
except subprocess.TimeoutExpired:
result = None
elapsed = time.monotonic() - started
text = logfile.read_text(encoding="utf-8", errors="replace")
stamps = re.findall(
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
text, re.M)
recipe_ok = stamps == [(recipe, recipe, "PASS")]
markers_ok = all(re.search(re.escape(marker) + r".*status=PASS", text)
for marker in MARKERS)
capsule_ok = bool(re.search(
r"^\[ResolvedPassageAudit\].* blocked=0 support_samples=\d+ "
r"unsupported=0 grade_failures=0 mouth_content=2/2 mouth_radius=2/2 "
r"walkability=PASS$", text, re.M))
island_route_ok = (recipe != "floating-islands" or bool(re.search(
r"^\[FloatingIslandRouteAudit\].* blocked=0 support_samples=\d+ "
r"unsupported=0 grade_failures=0 status=PASS$", text, re.M)))
binding_ok = bool(re.search(
r"\[ResolvedPassageAuditSummary\].*route_bindings=[1-9][0-9]* "
r"route_binding_failures=0 .*status=PASS", text))
walk_ok = "[Walkability] PASS" in text
if recipe == "floating-islands":
control_ok = "[PassageColumnDisconnectedEndControl]" in text and bool(
re.search(r"\[PassageColumnDisconnectedEndControl\].*detected=true", text))
else:
control_ok = True
ok = (result is not None and result.returncode == 0 and recipe_ok
and markers_ok and capsule_ok and island_route_ok and binding_ok
and walk_ok and control_ok)
actual_recipe = stamps[0][1] if len(stamps) == 1 else "MISSING"
rows.append((recipe, actual_recipe, style, compiler, elapsed,
result.returncode if result else "TIMEOUT", ok))
print(f"{name} elapsed={elapsed:.1f}s exit={result.returncode if result else 'TIMEOUT'} "
f"recipe={stamps[0][1] if stamps else 'MISSING'} "
f"capsule={'PASS' if capsule_ok and island_route_ok else 'FAIL'} "
f"column_control={'PASS' if control_ok else 'FAIL'} {'PASS' if ok else 'FAIL'}",
flush=True)
if not ok:
failed = True
summary = OUT / "passage-audit-matrix.csv"
summary.write_text(
"requested_recipe,actual_recipe,style,compiler,elapsed_seconds,exit_code,status\n" +
"".join(f"{requested},{actual},{style},{compiler},{elapsed:.3f},{code},"
f"{'PASS' if ok else 'FAIL'}\n"
for requested, actual, style, compiler, elapsed, code, ok in rows), encoding="utf-8")
control_log = OUT / "recipe-mismatch-control.log"
control_owner = OUT / "recipe-mismatch-control-owner.json"
control_owner_data = json.loads(
(ROOT / "Saved/Linux/linux25-owner-surface-world-style0.json").read_text(encoding="utf-8"))
top_owner_stratum = max(control_owner_data["strates"], key=lambda stratum: stratum["top_chunk_z"])
top_owner_stratum["asset"]["GeneratorType"] = 6 # ECaveGeneratorType::FloatingIslands
control_owner.write_text(json.dumps(control_owner_data, separators=(",", ":")), encoding="utf-8")
control_command = [
str(BINARIES["clang"]),
str(control_owner),
"--workers=12", "--world-assembler-prototype", "--stream-benchmark",
"--stream-pipeline", "--force-generate", "--clip-radius=3",
"--max-clip-level=8", "--max-level=-1", "--recipe=surface-world",
"--audit-resolved-layout-passages",
]
with control_log.open("w", encoding="utf-8") as stream:
control = subprocess.run(control_command, stdout=stream, stderr=subprocess.STDOUT,
timeout=1800, check=False)
control_text = control_log.read_text(encoding="utf-8", errors="replace")
control_ok = (control.returncode == 2 and
"[WorldAssemblerRecipeAudit] requested=surface-world actual=floating-islands "
"top_generator=FloatingIslands " in control_text and
"floating_islands_stratum=present status=FAIL" in control_text)
print(f"[L31RecipeMismatchControl] requested=surface-world actual=floating-islands "
f"exit={control.returncode} detected={'true' if control_ok else 'false'} "
f"{'PASS' if control_ok else 'FAIL'}", flush=True)
print(f"[L31PassageMatrix] 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 not control_ok or len(rows) != 16 else 0)
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Run five interleaved W12 whole-view pairs against the final L30 binary."""
import re
import statistics
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l31"
OUT.mkdir(parents=True, exist_ok=True)
BINARIES = {
"l30": ROOT / "Saved/Linux/l30/bin/VoxelForgeCoreHeadless-linux30-speed-fade",
"l31": OUT / "bin/VoxelForgeCoreHeadless-linux31-clang",
}
ORDERS = (("l30", "l31"), ("l31", "l30"), ("l30", "l31"),
("l31", "l30"), ("l30", "l31"))
READY = re.compile(r"\bwhole_view_ready=([0-9.]+)s")
rows = []
failed = False
for recipe in ("surface-world", "floating-islands"):
for pair, order in enumerate(ORDERS, 1):
for build in order:
name = f"cost-{recipe}-pair{pair}-{build}"
logfile = OUT / f"{name}.log"
command = [
str(BINARIES[build]), str(ROOT / "Standalone/data/owner_world.json"),
"--workers=12", "--world-assembler-prototype", "--stream-benchmark",
"--stream-pipeline", "--force-generate", "--clip-radius=3",
"--max-clip-level=8", "--max-level=-1", f"--recipe={recipe}",
]
started = time.monotonic()
try:
with logfile.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
timeout=1800, check=False)
except subprocess.TimeoutExpired:
result = None
elapsed = time.monotonic() - started
text = logfile.read_text(encoding="utf-8", errors="replace")
matches = READY.findall(text)
if build == "l31":
stamps = re.findall(
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
text, re.M)
actual_recipe = stamps[0][1] if len(stamps) == 1 else "missing"
stamp_ok = stamps == [(recipe, recipe, "PASS")]
else:
configured = re.findall(
r"^\[HeadlessConfig\].*\brecipe=([^\s]+)", text, re.M)
actual_recipe = configured[0] if len(configured) == 1 else "missing"
floating_plan = "[FloatingIslandWorldAssembly]" in text
surface_plan = "[WorldAssemblerPrototype]" in text
plan_matches = ((actual_recipe == "floating-islands" and floating_plan)
or (actual_recipe == "surface-world" and not floating_plan
and surface_plan))
marker = (f"[L31BaselineRecipeAudit] requested={recipe} actual={actual_recipe} "
f"headless_recipe={configured[0] if len(configured) == 1 else 'missing'} "
f"floating_island_plan={'present' if floating_plan else 'absent'} "
f"surface_world_plan={'present' if surface_plan else 'absent'} "
f"status={'PASS' if actual_recipe == recipe and plan_matches else 'FAIL'}\n")
with logfile.open("a", encoding="utf-8") as stream:
stream.write(marker)
text += marker
stamp_ok = (actual_recipe == recipe and plan_matches
and len(configured) == 1)
valid = (result is not None and result.returncode == 0 and len(matches) == 1
and "hash=off" in text and "cache_hits=0" in text)
if build == "l31":
valid = valid and stamp_ok and actual_recipe == recipe
else:
valid = valid and stamp_ok and "status=PASS" in marker
ready = float(matches[0]) if len(matches) == 1 else float("nan")
rows.append((recipe, pair, build, ready, elapsed,
result.returncode if result else "TIMEOUT", valid))
print(f"{recipe} pair={pair} build={build} whole_view_ready={ready:.3f}s "
f"process_elapsed={elapsed:.1f}s {'PASS' if valid else 'FAIL'}", flush=True)
failed |= not valid
summary = OUT / "cost-runs.csv"
summary.write_text(
"recipe,pair,build,whole_view_ready_seconds,process_elapsed_seconds,exit_code,status\n" +
"".join(f"{recipe},{pair},{build},{ready:.6f},{elapsed:.3f},{code},"
f"{'PASS' if ok else 'FAIL'}\n"
for recipe, pair, build, ready, elapsed, code, ok in rows), encoding="utf-8")
for recipe in ("surface-world", "floating-islands"):
by_build = {build: [row[3] for row in rows if row[0] == recipe and row[2] == build]
for build in BINARIES}
l30 = statistics.median(by_build["l30"])
l31 = statistics.median(by_build["l31"])
paired = []
for pair in range(1, 6):
values = {row[2]: row[3] for row in rows if row[0] == recipe and row[1] == pair}
paired.append((values["l31"] - values["l30"]) / values["l30"] * 100.0)
print(f"[L31CostSummary] recipe={recipe} pairs=5 median_l30_s={l30:.3f} "
f"median_l31_s={l31:.3f} median_change_pct={(l31/l30-1)*100.0:+.2f} "
f"paired_change_pct={','.join(f'{value:+.2f}' for value in paired)}", flush=True)
print(f"[L31Cost] runs={len(rows)} status={'FAIL' if failed else 'PASS'} summary={summary}", flush=True)
raise SystemExit(1 if failed or len(rows) != 20 else 0)
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Re-run the L25 hash command and 24 m crossing on the L31 compiler matrix."""
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l31"
OUT.mkdir(parents=True, exist_ok=True)
BINARIES = {
"clang": OUT / "bin/VoxelForgeCoreHeadless-linux31-clang",
"gcc": OUT / "bin/VoxelForgeCoreHeadless-linux31-gcc",
}
HASH_PATTERN = re.compile(
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
rows = []
failed = False
candidate_reference = {}
fi_hash_changes = {"l25": False, "crossing24": False}
def hashes(path):
return HASH_PATTERN.findall(path.read_text(encoding="utf-8", errors="replace"))
def run(kind, compiler, recipe, workers):
global failed
name = f"{kind}-{compiler}-{recipe}-w{workers}"
logfile = OUT / f"{name}.log"
command = [
str(BINARIES[compiler]), str(ROOT / "Standalone/data/owner_world.json"),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--stream-hash", "--force-generate", "--clip-radius=3", "--max-clip-level=8",
"--far-horizon-lod8-step=0", f"--workers={workers}", f"--recipe={recipe}",
]
if kind == "crossing24":
command.extend(("--camera-step=24,0,0", "--region-crossing"))
started = time.monotonic()
try:
with logfile.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
timeout=1800, check=False)
except subprocess.TimeoutExpired:
result = None
elapsed = time.monotonic() - started
text = logfile.read_text(encoding="utf-8", errors="replace")
stamps = re.findall(
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
text, re.M)
current = hashes(logfile)
# The normal L30 log stem is ringfix-hash; crossing uses ringfix-crossing24.
baseline = ROOT / "Saved/Linux/l30" / (
f"ringfix-crossing24-{compiler}-{recipe}-w{workers}.log" if kind == "crossing24"
else f"ringfix-hash-{compiler}-{recipe}-w{workers}.log")
reference = hashes(baseline) if baseline.is_file() else []
key = (kind, recipe)
prior = candidate_reference.get(key)
stable = prior is None or current == prior
if prior is None and current:
candidate_reference[key] = current
baseline_match = bool(reference) and current == reference
baseline_changed = bool(reference) and current != reference
if recipe == "surface-world":
baseline_ok = baseline_match
comparison = "MATCH" if baseline_match else "MISMATCH"
else:
baseline_ok = baseline_changed
comparison = "EXPECTED_CHANGE" if baseline_changed else "UNCHANGED_OR_MISSING"
fi_hash_changes[kind] |= baseline_changed
ok = (result is not None and result.returncode == 0
and stamps == [(recipe, recipe, "PASS")]
and "hash=on" in text and len(current) == 12 and stable and baseline_ok)
rows.append((kind, recipe, compiler, workers, elapsed,
result.returncode if result else "TIMEOUT", len(current), ok))
print(f"{name} elapsed={elapsed:.1f}s recipe={stamps[0][1] if stamps else 'MISSING'} "
f"hashes={len(current)} "
f"L30={comparison} candidate_stable={'yes' if stable else 'no'} "
f"{'PASS' if ok else 'FAIL'}", flush=True)
if not ok:
failed = True
for kind in ("l25", "crossing24"):
for recipe in ("surface-world", "floating-islands"):
for compiler in ("clang", "gcc"):
for workers in (1, 8, 12):
run("crossing24" if kind == "crossing24" else "l25", compiler, recipe, workers)
summary = OUT / "determinism.csv"
summary.write_text(
"kind,recipe,compiler,workers,elapsed_seconds,exit_code,hash_rows,status\n" +
"".join(f"{kind},{recipe},{compiler},{workers},{elapsed:.3f},{code},{count},"
f"{'PASS' if ok else 'FAIL'}\n"
for kind, recipe, compiler, workers, elapsed, code, count, ok in rows),
encoding="utf-8")
for kind, changed in fi_hash_changes.items():
if not changed:
failed = True
print(f"[L31ExpectedGeometryHashChange] kind={kind} detected=false FAIL", flush=True)
else:
print(f"[L31ExpectedGeometryHashChange] kind={kind} detected=true PASS", flush=True)
print(f"[L31Determinism] 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) != 24 else 0)
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Run L31 route/proof and transition invariants with recipe-stamped logs."""
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l31"
BINARIES = {
"clang": OUT / "bin/VoxelForgeCoreHeadless-linux31-clang",
"gcc": OUT / "bin/VoxelForgeCoreHeadless-linux31-gcc",
}
rows = []
failed = False
def run(name, binary, data, recipe, extra, checks):
global failed
logfile = OUT / f"{name}.log"
command = [
str(binary), str(data), "--workers=12", "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--force-generate",
"--clip-radius=3", "--max-clip-level=8", "--max-level=-1",
f"--recipe={recipe}", *extra,
]
started = time.monotonic()
try:
with logfile.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
timeout=1800, check=False)
except subprocess.TimeoutExpired:
result = None
elapsed = time.monotonic() - started
text = logfile.read_text(encoding="utf-8", errors="replace")
stamps = re.findall(
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
text, re.M)
stamp_ok = stamps == [(recipe, recipe, "PASS")]
checks_ok = all(check(text) for check in checks)
ok = result is not None and result.returncode == 0 and stamp_ok and checks_ok
rows.append((name, recipe, elapsed, result.returncode if result else "TIMEOUT", ok))
print(f"{name} elapsed={elapsed:.1f}s recipe={stamps[0][1] if stamps else 'MISSING'} "
f"stamp={'PASS' if stamp_ok else 'FAIL'} checks={'PASS' if checks_ok else 'FAIL'} "
f"{'PASS' if ok else 'FAIL'}", flush=True)
failed |= not ok
owner = ROOT / "Standalone/data/owner_world.json"
for recipe in ("surface-world", "floating-islands"):
for compiler, binary in BINARIES.items():
name = f"invariant-route-proof-{recipe}-{compiler}"
run(name, binary, owner, recipe,
["--stream-hash", "--audit-uniform-block-proofs", "--audit-route-parity"],
[lambda text: bool(re.search(
r"^\[RouteParityAudit\].*status=PASS$", text, re.M)),
lambda text: bool(re.search(
r"^\[UniformBlockProofAudit\].*violations=0(?:\s|$)", text, re.M))])
for recipe in ("surface-world", "floating-islands"):
for compiler, binary in BINARIES.items():
for mode in ("hard", "gradient", "interleaved"):
name = f"invariant-transition-{recipe}-{mode}-{compiler}"
run(name, binary, owner, recipe,
[f"--transition-mode={mode}", "--audit-strate-transitions"],
[lambda text: bool(re.search(
r"^\[StrateTransitionAuditSummary\].*hole_columns=0 air_samples=0 .*status=PASS$",
text, re.M)),
lambda text: bool(re.search(
r"^\[StrateTransitionNegativeControls\].*status=PASS$", text, re.M))])
summary = OUT / "invariant-audits.csv"
summary.write_text(
"run,recipe,elapsed_seconds,exit_code,status\n" +
"".join(f"{name},{recipe},{elapsed:.3f},{code},{'PASS' if ok else 'FAIL'}\n"
for name, recipe, elapsed, code, ok in rows),
encoding="utf-8")
print(f"[L31InvariantAudits] 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) != 16 else 0)
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Extract the validated recipe-stamped passage matrix into a compact table."""
import csv
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l31"
rows = []
for recipe in ("surface-world", "floating-islands"):
for style in range(4):
for compiler in ("clang", "gcc"):
name = f"passage-{recipe}-style{style}-{compiler}"
path = OUT / f"{name}.log"
text = path.read_text(encoding="utf-8", errors="replace")
def match(pattern):
found = re.search(pattern, text, re.M)
if not found:
raise SystemExit(f"{path}: missing metric {pattern}")
return found
stamp = match(
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=(PASS|FAIL)$")
capsule = match(
r"^\[ResolvedPassageAudit\].*capsule_samples=(\d+) blocked=(\d+) "
r"support_samples=(\d+) unsupported=(\d+) grade_failures=(\d+) "
r"mouth_content=([^ ]+) mouth_radius=([^ ]+) .*$")
air = match(r"^\[PassageOpenAirAuditSummary\].*violations=(\d+).*status=(PASS|FAIL)$")
natural = match(r"^\[PassageNaturalnessSummary\] passed=(\d+)/(\d+).*$")
bridge = match(
r"^\[PassageNoLongBridgeAudit\].*longest_uncolumned_m=([0-9.]+) "
r"threshold_m=([0-9.]+).*$")
column = match(
r"^\[PassageRockColumnAuditSummary\] columns=(\d+) connected_ends=([^ ]+) "
r"short_spans=(\d+) wall_or_support_failures=(\d+).*status=(PASS|FAIL)$")
binding = match(
r"^\[ResolvedPassageAuditSummary\].*route_bindings=(\d+) "
r"route_binding_failures=(\d+).*status=(PASS|FAIL)$")
walk = match(r"^\[Walkability\] PASS (\d+)/(\d+) passage centerline samples")
rows.append({
"requested": stamp.group(1), "actual": stamp.group(2), "stamp": stamp.group(3),
"style": style, "compiler": compiler,
"capsule_samples": int(capsule.group(1)), "blocked": int(capsule.group(2)),
"support_samples": int(capsule.group(3)),
"unsupported": int(capsule.group(4)), "grade_failures": int(capsule.group(5)),
"mouth_content": capsule.group(6), "mouth_radius": capsule.group(7),
"air_violations": int(air.group(1)), "air_status": air.group(2),
"natural_passed": natural.group(1),
"natural_total": natural.group(2),
"longest_uncolumned_m": bridge.group(1), "bridge_threshold_m": bridge.group(2),
"column_count": int(column.group(1)), "connected_ends": column.group(2),
"short_spans": int(column.group(3)), "column_failures": int(column.group(4)),
"route_bindings": int(binding.group(1)),
"binding_failures": int(binding.group(2)),
"walk_samples": int(walk.group(1)), "walk_total": int(walk.group(2)),
"log": path.name,
})
with (OUT / "passage-audit-metrics.csv").open("w", newline="", encoding="utf-8") as stream:
writer = csv.DictWriter(stream, fieldnames=list(rows[0]))
writer.writeheader()
writer.writerows(rows)
lines = [
"| Requested → actual | Style | Compiler | Capsule | Open air | Natural | Long bridge | Column ends | Bindings | Walkability |",
"|---|---:|---|---|---:|---:|---:|---:|---:|---:|",
]
for row in rows:
ok = (row["requested"] == row["actual"] and row["stamp"] == "PASS"
and row["blocked"] == row["unsupported"] == row["grade_failures"] == 0
and row["mouth_content"] == row["mouth_radius"] == "2/2"
and row["air_violations"] == 0 and row["air_status"] == "PASS"
and row["natural_passed"] == row["natural_total"] == "1"
and float(row["longest_uncolumned_m"]) <= float(row["bridge_threshold_m"])
and row["connected_ends"] == f"{2 * row['column_count']}/{2 * row['column_count']}"
and row["column_failures"] == 0 and row["binding_failures"] == 0
and row["route_bindings"] > 0 and row["walk_samples"] == row["walk_total"])
if not ok:
raise SystemExit(f"{row['log']}: metric audit failed")
cells = (
f"{row['blocked']}/{row['unsupported']} blocked/unsupported; mouth 2/2",
f"{row['air_violations']} violations",
f"{row['natural_passed']}/{row['natural_total']}",
f"{float(row['longest_uncolumned_m']):.3f}/{row['bridge_threshold_m']} m",
row["connected_ends"],
f"{row['route_bindings']} (0 failures)",
f"{row['walk_samples']}/{row['walk_total']}",
)
lines.append(f"| {row['requested']} → {row['actual']} | {row['style']} | {row['compiler']} | "
+ " | ".join(cells) + " |")
table = OUT / "passage-audit-table.md"
table.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(f"[L31PassageAuditTable] rows={len(rows)} pass={len(rows)} "
f"csv={OUT / 'passage-audit-metrics.csv'} markdown={table}")
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+23 -9
View File
@@ -290,6 +290,7 @@ in vec4 vTileData;
out vec4 outColor;
uniform int uReadableMode;
uniform int uTileEdges;
uniform int uHeightTint;
uniform float uHeightCenter;
uniform float uHeightRange;
uniform float uTileEdgeWidth;
@@ -312,7 +313,10 @@ void main()
else
{
float heightT=clamp(0.5+(vHeight-uHeightCenter)/max(uHeightRange,0.001),0.0,1.0);
vec3 heightTint=mix(vec3(0.80,0.91,1.0),vec3(1.0,0.91,0.77),heightT);
float smoothHeightT=heightT*heightT*(3.0-2.0*heightT);
vec3 heightTint=uHeightTint!=0
?mix(vec3(0.80,0.91,1.0),vec3(1.0,0.91,0.77),smoothHeightT)
:vec3(1.0);
float diffuse=max(dot(n,normalize(uLightDirection)),0.0);
float illumination=uHeadlamp!=0?0.78+0.34*diffuse:0.62+0.38*diffuse;
color=vec3(0.64,0.62,0.56)*heightTint*illumination;
@@ -376,7 +380,7 @@ void main()
}
bool Render(FScene& Scene,const std::filesystem::path& Output,int Width,int Height,
double HorizontalFovDegrees,bool ReadableMode,bool Headlamp,bool TileEdges,
double HorizontalFovDegrees,bool ReadableMode,bool HeightTint,bool Headlamp,bool TileEdges,
double TileEdgeWidthMeters,bool ClipPlaneEnabled,
const std::array<double,4>& ClipPlane)
{
@@ -472,6 +476,7 @@ void main()
glUniform1f(glGetUniformLocation(Program,"uFar"),static_cast<float>(Far));
glUniform1i(glGetUniformLocation(Program,"uReadableMode"),ReadableMode?1:0);
glUniform1i(glGetUniformLocation(Program,"uTileEdges"),TileEdges?1:0);
glUniform1i(glGetUniformLocation(Program,"uHeightTint"),HeightTint?1:0);
glUniform1f(glGetUniformLocation(Program,"uHeightCenter"),
static_cast<float>(Scene.CameraMeters[2]));
glUniform1f(glGetUniformLocation(Program,"uHeightRange"),64.0f);
@@ -499,13 +504,14 @@ void main()
const auto Bytes=std::filesystem::file_size(Output,SizeError);
std::printf("[OffscreenCapture] source_triangles=%llu drawn_triangles=%llu tiles=%u "
"skirts=%llu visible_skirt_triangles=%llu hidden_same_lod_skirt_triangles=%llu "
"shading=%s tile_edges=%s edge_width_m=%.3f png_bytes=%llu status=PASS path=%s\n",
"shading=%s height_tint=%s tile_edges=%s edge_width_m=%.3f png_bytes=%llu status=PASS path=%s\n",
static_cast<unsigned long long>(Scene.Triangles),
static_cast<unsigned long long>(Scene.DrawnTriangles),Scene.TileCount,
static_cast<unsigned long long>(Scene.SkirtTriangles),
static_cast<unsigned long long>(Scene.VisibleSkirtTriangles),
static_cast<unsigned long long>(Scene.HiddenSameLodSkirtTriangles),
ReadableMode?"readable":"normal",TileEdges?"on":"off",TileEdgeWidthMeters,
ReadableMode?"readable":"normal",HeightTint?"on":"off",
TileEdges?"on":"off",TileEdgeWidthMeters,
static_cast<unsigned long long>(SizeError?0:Bytes),Output.string().c_str());
}
glDeleteProgram(Program);glDeleteBuffers(1,&Ebo);glDeleteBuffers(1,&Vbo);
@@ -521,13 +527,13 @@ int main(int argc,char** argv)
{
if(argc<3)
{
std::fprintf(stderr,"Usage: %s scene.vfmesh output.png [--size=WIDTHxHEIGHT] [--fov-deg=90] [--camera-m=X,Y,Z] [--yaw-deg=N] [--pitch-deg=N] [--shade=readable|normal] [--headlamp=on|off] [--tile-edges=on|off] [--tile-edge-width-m=N] [--clip-plane=NX,NY,NZ,D]\n",argv[0]);
std::fprintf(stderr,"Usage: %s scene.vfmesh output.png [--size=WIDTHxHEIGHT] [--fov-deg=90] [--camera-m=X,Y,Z] [--yaw-deg=N] [--pitch-deg=N] [--shade=readable|normal] [--height-tint=on|off] [--headlamp=on|off] [--tile-edges=on|off] [--tile-edge-width-m=N] [--clip-plane=NX,NY,NZ,D]\n",argv[0]);
return 2;
}
int Width=2560,Height=1440;double Fov=90.0;
bool HasYaw=false,HasPitch=false;double Yaw=0.0,Pitch=0.0;
bool HasCamera=false;std::array<double,3> Camera{};
bool ReadableMode=true,TileEdges=false,Headlamp=false;double TileEdgeWidthMeters=0.15;
bool ReadableMode=true,HeightTint=true,TileEdges=false,Headlamp=false;double TileEdgeWidthMeters=0.15;
bool ClipPlaneEnabled=false;std::array<double,4> ClipPlane{};
for(int I=3;I<argc;++I)
{
@@ -569,6 +575,13 @@ int main(int argc,char** argv)
else if(Mode=="normal")ReadableMode=false;
else {std::fprintf(stderr,"Invalid shading mode\n");return 2;}
}
else if(Arg.starts_with("--height-tint="))
{
const std::string_view Mode=Arg.substr(14);
if(Mode=="on")HeightTint=true;
else if(Mode=="off")HeightTint=false;
else {std::fprintf(stderr,"Invalid height-tint mode\n");return 2;}
}
else if(Arg.starts_with("--headlamp="))
{
const std::string_view Mode=Arg.substr(11);
@@ -610,12 +623,13 @@ int main(int argc,char** argv)
if(HasCamera)Scene.CameraMeters=Camera;
if(HasYaw)Scene.YawDegrees=Yaw;
if(HasPitch)Scene.PitchDegrees=Pitch;
std::printf("[OffscreenScene] voxels_per_meter=%.6f camera_m=(%.3f,%.3f,%.3f) yaw=%.2f pitch=%.2f height_m=%.3f..%.3f shade=%s tile_edges=%s vertices=%zu triangles=%llu\n",
std::printf("[OffscreenScene] voxels_per_meter=%.6f camera_m=(%.3f,%.3f,%.3f) yaw=%.2f pitch=%.2f height_m=%.3f..%.3f shade=%s height_tint=%s tile_edges=%s vertices=%zu triangles=%llu\n",
1000000.0/Scene.VoxelSizeMicrons,Scene.CameraMeters[0],Scene.CameraMeters[1],Scene.CameraMeters[2],
Scene.YawDegrees,Scene.PitchDegrees,Scene.MinimumHeight,Scene.MaximumHeight,
ReadableMode?"readable":"normal",TileEdges?"on":"off",Scene.Vertices.size(),
ReadableMode?"readable":"normal",HeightTint?"on":"off",
TileEdges?"on":"off",Scene.Vertices.size(),
static_cast<unsigned long long>(Scene.Triangles));
return Render(Scene,argv[2],Width,Height,Fov,ReadableMode,Headlamp,TileEdges,
return Render(Scene,argv[2],Width,Height,Fov,ReadableMode,HeightTint,Headlamp,TileEdges,
TileEdgeWidthMeters,ClipPlaneEnabled,ClipPlane)?0:3;
}
@@ -423,6 +423,35 @@ int main(int argc,char** argv)
VoxelForgeCore::ResolvedLayoutValidationModeV3::strict,strictIssues,strictError);
std::printf("[ResolvedLayoutSnapshot] schema=%u stable_identity=%s strata=%zu bytes=%zu round_trip=PASS\n",
resolvedLayout.schemaVersion,resolvedLayout.stableIdentity.c_str(),resolvedLayout.strates.size(),layoutJson.size());
const auto requestedRecipe=options.FloatingIslandsRecipe
?std::string_view("floating-islands"):std::string_view("surface-world");
const auto topStratum=std::max_element(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
[](const auto& a,const auto& b)
{return a.interiorTopMicrometres<b.interiorTopMicrometres;});
const bool hasFloatingIslands=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
[](const auto& stratum){return stratum.generatorKind=="FloatingIslands";});
const bool hasSurfaceWorldPlan=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
[&](const auto& stratum)
{
return stratum.generatorKind=="SurfaceWorld"
&&stratum.recipeId=="SurfaceWorld.Heightfield2D"
&&std::any_of(worldAssembly.plan.strates.begin(),worldAssembly.plan.strates.end(),
[&](const auto& plan){return plan.strateIndex==stratum.strateIndex;});
});
const auto actualRecipe=hasFloatingIslands?std::string_view("floating-islands")
:hasSurfaceWorldPlan?std::string_view("surface-world")
:std::string_view("missing");
const char* topGenerator=topStratum==resolvedLayout.strates.end()
?"missing":topStratum->generatorKind.c_str();
std::printf("[WorldAssemblerRecipeAudit] requested=%.*s actual=%.*s top_generator=%s surface_world_plan=%s floating_islands_stratum=%s status=%s\n",
static_cast<int>(requestedRecipe.size()),requestedRecipe.data(),
static_cast<int>(actualRecipe.size()),actualRecipe.data(),
topGenerator,hasSurfaceWorldPlan?"present":"absent",hasFloatingIslands?"present":"absent",
actualRecipe==requestedRecipe?"PASS":"FAIL");
if(actualRecipe!=requestedRecipe)
{std::fprintf(stderr,"Requested recipe %.*s resolved to %.*s\n",
static_cast<int>(requestedRecipe.size()),requestedRecipe.data(),
static_cast<int>(actualRecipe.size()),actualRecipe.data());return 2;}
std::printf("[ResolvedLayoutCompatibility] accepted=%s issues=%zu error=%s\n",
compatibilityAccepted?"true":"false",compatibilityIssues.size(),compatibilityError.c_str());
for(const auto& issue:compatibilityIssues)
+128 -6
View File
@@ -1084,6 +1084,8 @@ namespace StandaloneStreamBenchmark
double surfaceRadiusReliefMetres=0.0;
double longestShortSpanMetres=0.0;
bool passed=true;
bool disconnectedEndControlApplicable=false;
bool disconnectedEndControlDetected=false;
};
const auto auditPassageColumns=[&](const VoxelForgeCore::World& Field,
const VoxelForgeCore::World& Native,
@@ -1095,7 +1097,7 @@ namespace StandaloneStreamBenchmark
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
constexpr std::array<std::array<double,2>,5> supportOffsets{{
{{0.0,0.0}},{{0.45,0.0}},{{-0.45,0.0}},{{0.0,0.45}},{{0.0,-0.45}}}};
const auto endpointConnected=[&](const std::array<double,4>& point)
const auto endpointSupportCount=[&](const std::array<double,4>& point)
{
const double x=point[0]*qMetres,y=point[1]*qMetres;
const double floor=point[2]*qMetres-point[3]*qMetres+0.13;
@@ -1109,7 +1111,58 @@ namespace StandaloneStreamBenchmark
{solid=true;break;}
supported+=solid;
}
return supported>=3;
return supported;
};
const auto endpointConnected=[&](const std::array<double,4>& point)
{return endpointSupportCount(point)>=3;};
struct FColumnCapProbe
{
std::uint64_t columnSamples=0,nativeOverlapSamples=0;
double firstOverlapDepthMetres=std::numeric_limits<double>::infinity();
double minimumNativeDensity=std::numeric_limits<double>::infinity();
};
const auto probeColumnCap=[&](const VoxelForgeCore::PassageVoidCrossingAuditV1& crossing,
std::size_t segment,double capZMetres,bool upperCap,double depthLimitMetres)
{
FColumnCapProbe probe;
constexpr double DepthStepMetres=0.25;
constexpr double RadialStepMetres=0.50;
constexpr std::size_t DirectionCount=16;
const double axisX=qToMetres(crossing.columnAxisSubvoxels[0]);
const double axisY=qToMetres(crossing.columnAxisSubvoxels[1]);
for(double depth=DepthStepMetres;depth<=depthLimitMetres+1.0e-6;
depth+=DepthStepMetres)
{
const double z=capZMetres+(upperCap?-depth:depth);
const double maxRadius=qToMetres(crossing.columnMaximumRadiusSubvoxels);
const std::size_t radialSteps=static_cast<std::size_t>(
std::ceil(maxRadius/RadialStepMetres));
for(std::size_t radialStep=0;radialStep<=radialSteps;++radialStep)
{
const double radius=std::min(maxRadius,
radialStep*RadialStepMetres);
for(std::size_t direction=0;direction<DirectionCount;++direction)
{
const double angle=2.0*3.14159265358979323846
*direction/DirectionCount;
const VoxelForgeCore::Vec3d sample{
(axisX+radius*std::cos(angle))/coreMetres,
(axisY+radius*std::sin(angle))/coreMetres,z/coreMetres};
if(!Field.isPassageColumnSample(passageIndex,segment,sample))continue;
++probe.columnSamples;
const double nativeDensity=Native.densityAt(sample);
probe.minimumNativeDensity=std::min(probe.minimumNativeDensity,
nativeDensity);
if(nativeDensity<0.0)
{
++probe.nativeOverlapSamples;
probe.firstOverlapDepthMetres=std::min(
probe.firstOverlapDepthMetres,depth);
}
}
}
}
return probe;
};
const auto columnSolidAt=[&](std::size_t segment,double x,double y,double z)
{
@@ -1122,6 +1175,9 @@ namespace StandaloneStreamBenchmark
{x/coreMetres,y/coreMetres,z/coreMetres});
};
const auto crossings=Field.passageVoidCrossings();
double controlLowestRouteZ=std::numeric_limits<double>::infinity();
bool disconnectedEndControlApplicable=false;
bool disconnectedEndControlDetected=false;
for(const auto& crossing:crossings)
{
if(crossing.passageIndex!=passageIndex)continue;
@@ -1157,8 +1213,63 @@ namespace StandaloneStreamBenchmark
}
if(crossing.lastSegment+1>=passage.controlPoints.size())
{audit.passed=false;continue;}
const double capTopMetres=qToMetres(crossing.columnTopZSubvoxels);
const double capBottomMetres=qToMetres(crossing.columnBottomZSubvoxels);
const auto topCap=probeColumnCap(crossing,crossing.firstSegment,
capTopMetres,true,6.0);
const auto bottomCap=probeColumnCap(crossing,crossing.lastSegment,
capBottomMetres,false,6.0);
const bool topCapConnected=topCap.nativeOverlapSamples>0;
const bool bottomCapConnected=bottomCap.nativeOverlapSamples>0;
const std::uint32_t routeStartSupport=endpointSupportCount(
crossing.startPointSubvoxels);
const std::uint32_t routeEndSupport=endpointSupportCount(
crossing.endPointSubvoxels);
std::printf("[PassageColumnRouteEndpointSupport] id=%s start=%u/5 start_z_m=%.3f end=%u/5 end_z_m=%.3f threshold=3/5\n",
passage.stableId.c_str(),routeStartSupport,
qToMetres(crossing.startPointSubvoxels[2]),routeEndSupport,
qToMetres(crossing.endPointSubvoxels[2]));
const auto printCap=[&](const char* endName,const char* side,double capZ,
const FColumnCapProbe& probe,bool connected)
{
std::printf("[PassageColumnCapAudit] id=%s end=%s side=%s cap_z_m=%.3f column_samples=%llu native_overlap_samples=%llu first_overlap_depth_m=%.3f minimum_native_density=%.6f search_depth_m=6.000 status=%s\n",
passage.stableId.c_str(),endName,side,capZ,
static_cast<unsigned long long>(probe.columnSamples),
static_cast<unsigned long long>(probe.nativeOverlapSamples),
probe.firstOverlapDepthMetres,probe.minimumNativeDensity,
connected?"PASS":"FAIL");
};
printCap("top","upper",capTopMetres,topCap,topCapConnected);
printCap("bottom","lower",capBottomMetres,bottomCap,bottomCapConnected);
const auto upper=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[&](const auto& value)
{return value.strateIndex==passage.upperStrateIndex;});
const double startRouteZ=qToMetres(crossing.startPointSubvoxels[2]);
const double endRouteZ=qToMetres(crossing.endPointSubvoxels[2]);
const double lowerRouteZ=std::min(startRouteZ,endRouteZ);
if(upper!=ResolvedLayout->strates.end()
&&upper->generatorKind=="FloatingIslands"
&&lowerRouteZ<controlLowestRouteZ)
{
controlLowestRouteZ=lowerRouteZ;
// Move the hypothetical lower cap 20 m upward into the FI air gap.
// Its first 2 m are still inside the original column bounds, but a
// truncated column there has no native substrate to join.
const double hypotheticalBottomZ=lowerRouteZ+20.0;
const auto disconnectedControl=probeColumnCap(crossing,
crossing.lastSegment,hypotheticalBottomZ,false,2.0);
disconnectedEndControlApplicable=disconnectedControl.columnSamples>0;
disconnectedEndControlDetected=disconnectedEndControlApplicable
&&disconnectedControl.nativeOverlapSamples==0;
std::printf("[PassageColumnDisconnectedEndControl] source=air_gap_truncated_lower_cap cap_z_m=%.3f column_samples=%llu native_overlap_samples=%llu minimum_native_density=%.6f detected=%s\n",
hypotheticalBottomZ,
static_cast<unsigned long long>(disconnectedControl.columnSamples),
static_cast<unsigned long long>(disconnectedControl.nativeOverlapSamples),
disconnectedControl.minimumNativeDensity,
disconnectedEndControlDetected?"true":"false");
}
++audit.columns;
audit.connectedEnds+=connectedStart+connectedEnd;
audit.connectedEnds+=topCapConnected+bottomCapConnected;
const double axisX=qToMetres(crossing.columnAxisSubvoxels[0]);
const double axisY=qToMetres(crossing.columnAxisSubvoxels[1]);
const double minimumRadius=qToMetres(crossing.columnMinimumRadiusSubvoxels);
@@ -1262,7 +1373,7 @@ namespace StandaloneStreamBenchmark
crossingSurfaceRelief);
const double measuredWall=crossingMinimumSurfaceRadius
-crossing.maximumRouteOffsetMetres-crossing.maximumBoreRadiusMetres;
const bool columnPass=connectedStart&&connectedEnd
const bool columnPass=topCapConnected&&bottomCapConnected
&&minimumWall>=1.50&&crossingRouteFailures==0
&&crossingWallFailures==0&&std::isfinite(measuredWall)
&&measuredWall>=1.50&&crossingSurfaceRelief>=0.20;
@@ -1270,13 +1381,17 @@ namespace StandaloneStreamBenchmark
std::printf("[PassageRockColumnAudit] id=%s segments=%zu..%zu span_m=%.3f axis_m=(%.3f,%.3f) connected=%d/2 route_support=%llu/%llu wall_margin_m=%.3f threshold_m=1.500 radii_m=%.3f..%.3f relief_p2p_m=%.3f nominal_radius_m=%.3f status=%s\n",
passage.stableId.c_str(),crossing.firstSegment,crossing.lastSegment,
crossing.unsupportedLengthMetres,axisX,axisY,
static_cast<int>(connectedStart)+static_cast<int>(connectedEnd),
static_cast<int>(topCapConnected)+static_cast<int>(bottomCapConnected),
static_cast<unsigned long long>(probeCount-crossingRouteFailures),
static_cast<unsigned long long>(probeCount),measuredWall,
crossingMinimumSurfaceRadius,crossingMaximumSurfaceRadius,
crossingSurfaceRelief,qToMetres(crossing.columnNominalRadiusSubvoxels),
columnPass?"PASS":"FAIL");
}
audit.disconnectedEndControlApplicable|=disconnectedEndControlApplicable;
audit.disconnectedEndControlDetected|=disconnectedEndControlDetected;
if(disconnectedEndControlApplicable&&!disconnectedEndControlDetected)
audit.passed=false;
return audit;
};
if(Assembly)
@@ -1384,6 +1499,8 @@ namespace StandaloneStreamBenchmark
double longestUncolumnedSpanMetres=0.0,longestShortSpanMetres=0.0;
double l26LegacyBridgeSpanMetres=0.0;
bool columnAuditsPassed=true,noLongBridgeAuditPassed=true;
bool columnDisconnectedEndControlApplicable=false;
bool columnDisconnectedEndControlDetected=false;
bool l26NegativeControlApplicable=false;
const auto plannedVoidCrossings=Shape.passageVoidCrossings();
for(const auto& crossing:plannedVoidCrossings)
@@ -1439,6 +1556,8 @@ namespace StandaloneStreamBenchmark
longestShortSpanMetres=std::max(longestShortSpanMetres,
columnAudit.longestShortSpanMetres);
columnAuditsPassed&=columnAudit.passed;
columnDisconnectedEndControlApplicable|=columnAudit.disconnectedEndControlApplicable;
columnDisconnectedEndControlDetected|=columnAudit.disconnectedEndControlDetected;
++openAirAudits;
const auto radialSquared=[](const std::array<std::int64_t,3>& p)
{return p[0]*p[0]+p[1]*p[1];};
@@ -1753,12 +1872,15 @@ namespace StandaloneStreamBenchmark
longestShortSpanMetres,l26NegativeControlApplicable
?(l26NoLongBridgeControlDetected?"DETECTED":"MISSED"):"NOT_APPLICABLE",
noLongBridgeAuditPassed?"PASS":"FAIL");
std::printf("[PassageRockColumnAuditSummary] columns=%llu connected_ends=%llu/%llu short_spans=%llu wall_or_support_failures=%llu status=%s\n",
std::printf("[PassageRockColumnAuditSummary] columns=%llu connected_ends=%llu/%llu short_spans=%llu wall_or_support_failures=%llu disconnected_end_control=%s status=%s\n",
static_cast<unsigned long long>(columnCrossings),
static_cast<unsigned long long>(columnConnectedEnds),
static_cast<unsigned long long>(columnCrossings*2),
static_cast<unsigned long long>(shortGroundedSpans),
static_cast<unsigned long long>(columnRouteFailures+columnWallFailures),
columnDisconnectedEndControlApplicable
?(columnDisconnectedEndControlDetected?"DETECTED":"MISSED")
:"NOT_APPLICABLE",
columnAuditsPassed?"PASS":"FAIL");
const bool l23SmoothWallDetected=!looksNatural(0.0,0.0);
std::printf("[PassageNaturalnessNegativeControl] source=L23_smooth_unrelieved_wall relief_p2p_m=0.000 threshold_m=%.3f detected=%s\n",