research: cave planner merge evidence (L150, measurement only)

Merges cleanly. UE start: S1 -9%, S3 +11%, S0/S2 ~+1%. 30 m/s: near coverage 44.2 -> 38.5%, far holes +0.06 pp, p99
+2%. L80 near-ready slower in 6/8 cells (up to +7.9%), whole view faster in 7/8. Sweeps 15/16 each (seed 2).
Source/: VoxelForgeCoreAdapter.cpp.

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-11-01 16:17:42 +01:00
1 parent b8382eed84
commit 9ac60d5d18
15 files changed
+871

No files matched your search

@@ -0,0 +1,69 @@
# Linux150 — cave planner merge evidence
**Result:** `assembler/world` merges cleanly into `wip/l134-cave-planner`. The planner HEAD passes 15/16 seeds for each cave layer; seed 2 retains its known shell/seam failure. Linux Unreal start and movement runs completed. L80 shows slower near-ready medians in six of eight cells, while whole-view medians are lower in seven of eight cells.
No generation code changed in L150. Branch HEAD was `b8382eed`; target `assembler/world` was `eb47d722`. The UE baseline project uses `c743f2a`, code-equivalent to the target. The planner source stamp is `c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f`; the `assembler/world` baseline binary stamp is `4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621`.
## Evidence table
L80 used one warm-up per binary and five interleaved measured runs per cell at W8/W12 across all four owner layers. The quiet-host gate passed at load 0.50 and 99.3% CPU idle. The L137 A/A floor is ±1%. Time deltas are planner HEAD versus `assembler/world`; positive means slower.
UE start rows are one L121 start per stratum and build, measuring start through the full LOD0–2 set. Movement rows are medians over five interleaved L127 runs at 30 m/s (12 s moving plus 5 s dwell, eight effective tile slots). Coverage and hole figures describe the moving window. For L80, S0–S3 are Surface World, Floating Islands, Tunnel Network, and Vertical Shafts.
| Measure | assembler/world | planner HEAD | Delta / result |
|---|---:|---:|---:|
| UE start → LOD0–2, S0 (512/448/448 tiles) | 2.453159 s | 2.481646 s | +1.16% |
| UE start → LOD0–2, S1 (512/448/256 tiles) | 2.424691 s | 2.206180 s | −9.02% |
| UE start → LOD0–2, S2 (513/448/384 tiles) | 2.454927 s | 2.477987 s | +0.94% |
| UE start → LOD0–2, S3 (513/448/384 tiles) | 2.216523 s | 2.459258 s | +10.95% |
| 30 m/s near-ready moving mean | 44.2432% | 38.5307% | −5.71 pp (−12.91% relative) |
| 30 m/s far-hole moving mean | 3.2596% | 3.3208% | +0.0611 pp (+1.88% relative) |
| 30 m/s game-thread tick p99 | 10.7288 ms | 10.9596 ms | +2.15% |
| 30 m/s median per-run worst tick | 13.5996 ms | 14.4242 ms | +6.06% |
| 30 m/s worst tick observed across five runs | 14.9833 ms | 14.6717 ms | −2.08% |
| L80 S0 near-ready median, W8 / W12 | 0.737599 / 0.768684 s | 0.775814 / 0.829089 s | +5.18% / +7.86% |
| L80 S0 whole-view median, W8 / W12 | 1.737 / 1.705 s | 1.711 / 1.705 s | −1.50% / 0.00% |
| L80 S1 near-ready median, W8 / W12 | 1.006966 / 0.974332 s | 1.021554 / 1.029940 s | +1.45% / +5.71% |
| L80 S1 whole-view median, W8 / W12 | 1.907 / 1.812 s | 1.803 / 1.803 s | −5.45% / −0.50% |
| L80 S2 near-ready median, W8 / W12 | 1.044179 / 0.959096 s | 1.039310 / 1.026140 s | −0.47% / +6.99% |
| L80 S2 whole-view median, W8 / W12 | 1.913 / 1.751 s | 1.821 / 1.733 s | −4.81% / −1.03% |
| L80 S3 near-ready median, W8 / W12 | 0.875773 / 0.838977 s | 0.852260 / 0.853215 s | −2.68% / +1.70% |
| L80 S3 whole-view median, W8 / W12 | 1.788 / 1.687 s | 1.660 / 1.605 s | −7.16% / −4.86% |
| Tunnel Network composed sweep | — | 15/16 | Seed 2: passages, seams, shell |
| Vertical Shafts composed sweep | — | 15/16 | Seed 2: passages, seams, shell |
| Seed-0 capture quality | 1/4 pass | 4/4 pass | All exports/renders completed; three baseline viewpoints have no visible mesh surface |
Both moving runs reached 100% near-ready coverage and 0% far holes by dwell end (3.40 s baseline, 3.78 s HEAD). The five-run maximum tick was lower on HEAD, while its median of per-run maxima was higher.
For both sweeps, every gate other than the three seed-2 checks passed, including dispatch, route, naturalness, mouth, ripple, interior air, and negative controls. The seed-2 failure is the known interface 2–3 shell/seam failure.
## Owner-world captures
Four eye-height viewpoints use the same camera coordinates for both versions. Poses come from the planner layout resolved against the owner fixture’s embedded Core plan at seed 0. The capture layout identity is `cc87ee727c483039` on planner HEAD and `0589300eb07dfe06` on the baseline. The seed sweeps use the established `--seed=0` regeneration path (layout identity `d310515572977ebf`); the captures intentionally use the owner fixture’s embedded Core plan. For the baseline, a seed override makes the older route setup fail, so the capture runner imports the saved plan without an override. The planner images pass the capture-quality gate. Three baseline images fail its image-variance threshold because the baseline mesh has no visible surface from those exact poses; export and render processes succeeded.
| View | assembler/world | planner HEAD |
|---|---|---|
| Tunnel Network — route joint | ![assembler/world route joint](captures/assembler-world-tunnel-network-route.jpg) | ![planner HEAD route joint](captures/planner-head-tunnel-network-route.jpg) |
| Tunnel Network — departure joint | ![assembler/world departure joint](captures/assembler-world-tunnel-network-departure.jpg) | ![planner HEAD departure joint](captures/planner-head-tunnel-network-departure.jpg) |
| Vertical Shafts — looking down | ![assembler/world shaft looking down](captures/assembler-world-vertical-shafts-looking-down.jpg) | ![planner HEAD shaft looking down](captures/planner-head-vertical-shafts-looking-down.jpg) |
| Vertical Shafts — looking up | ![assembler/world shaft looking up](captures/assembler-world-vertical-shafts-looking-up.jpg) | ![planner HEAD shaft looking up](captures/planner-head-vertical-shafts-looking-up.jpg) |
Capture source stamps, coordinates, and quality output are in [`captures/manifest.json`](captures/manifest.json). The runner is [`RunL150Captures.py`](RunL150Captures.py).
## Merge risks
- Seed 2 fails the passage, seam, and shell checks in both cave-layer sweeps. Other current sweep gates pass.
- Near-ready L80 time is above the ±1% floor in six of eight cells. S0 W8/W12 and S1 W12 have the largest regressions; whole-view time is lower in seven cells and unchanged in one.
- During 30 m/s movement, HEAD has 12.91% lower near-ready coverage, 1.88% more far holes, 2.15% higher tick p99, and a 6.06% higher median per-run worst tick. Both builds fill near coverage and far holes during dwell.
- Three baseline captures cannot show an exposed mesh at the corresponding underground camera positions. The planner views render and pass image quality.
- Seed sweeps and owner-world captures both use seed 0, but use the regenerated standalone plan and embedded owner Core plan respectively; the images describe the owner-export plan path.
- The merge adds one Unreal `Source/` change for the owner’s Windows compile: `Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp` (5 insertions, 3 deletions; 8 changed lines).
## Checks and artifacts
- Merge check: clean in `Saved/Linux/l150/merge-worktree`; no unmerged paths and no merge commit. Details and hashes: [`merge-check.md`](../../../../Saved/Linux/l150/merge-check.md).
- Linux `VFHostEditor` Development build passed with `-NoUBA -MaxParallelActions=4` (27 actions, 68.79 s). It emitted three existing deprecated RenderMeshComponent API warnings in `VoxelWorld.cpp`; [build log](../../../../Saved/Linux/l150/ubt-console.log).
- L80: 80 measured runs plus warm-ups; [summary](../../../../Saved/Linux/l150/timing/summary.json) and per-run logs are under `Saved/Linux/l150/timing/`.
- Linux Unreal: four L121 starts per build and five interleaved L127 movement runs per build; [summary](../../../../Saved/Linux/l150/ue/summary.json) and run logs are under `Saved/Linux/l150/ue/`.
- Tunnel Network and Vertical Shafts sweeps: 16 seeds each; [Tunnel Network summary](../../../../Saved/Linux/l150/seeds/tunnel-network/summary.json), [Vertical Shafts summary](../../../../Saved/Linux/l150/seeds/vertical-shafts/summary.json), and per-seed logs under `Saved/Linux/l150/seeds/`.
- The fast tier was not rerun in L150. L147 passed it on the same standalone source stamp (`c6b8a100…f0609f`); no generation source changed here. A Windows UE build was not run.
@@ -0,0 +1,253 @@
#!/usr/bin/env python3
"""Capture four matched eye-height owner-world views for assembler/world and planner HEAD."""
from __future__ import annotations
import json
import math
import os
from pathlib import Path
import re
import subprocess
import sys
ROOT = Path(__file__).resolve().parents[4]
DATA = ROOT / "Standalone/data/owner_world.json"
OUT = ROOT / "Saved/Linux/l150/captures"
LAYOUT = OUT / "planner-head-resolved-layout.json"
ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux150/captures"
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
VERSIONS = {
"assembler-world": {
"headless": ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
"renderer": ROOT / "Saved/Linux/l137/bin/VoxelForgeOffscreenCapture-l137-aa-a",
"stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621",
},
"planner-head": {
"headless": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l150-head-clang",
"renderer": ROOT / "Standalone/bin/linux/VoxelForgeOffscreenCapture-l150-head-clang",
"stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f",
},
}
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M)
def fmt(values) -> str:
return ",".join(f"{value:.6f}" for value in values)
def angles(camera, target) -> tuple[float, float]:
dx, dy, dz = (target[index] - camera[index] for index in range(3))
return math.degrees(math.atan2(dx, dy)), math.degrees(
math.atan2(dz, math.hypot(dx, dy)))
def point_um(values) -> tuple[float, float, float]:
return tuple(value / 1_000_000.0 for value in values)
def point_q(layout: dict, values) -> tuple[float, float, float]:
scale = layout["core_voxel_size_um"] / (256.0 * 1_000_000.0)
return tuple(value * scale for value in values)
def camera_for_mouth(layout: dict, edge: dict):
anchor = edge["start_anchor_stable_id"]
if anchor.endswith("/lower-mouth"):
side = "lower"
mouth_q = next(row["lower_mouth_q"] for row in layout["passages"]
if row["stable_id"] == anchor.removesuffix("/lower-mouth"))
elif anchor.endswith("/upper-mouth"):
side = "upper"
mouth_q = next(row["upper_mouth_q"] for row in layout["passages"]
if row["stable_id"] == anchor.removesuffix("/upper-mouth"))
else:
raise ValueError(f"mouth edge has no passage anchor: {anchor}")
passage_id = anchor.rsplit("/", 1)[0]
passage = next(row for row in layout["passages"] if row["stable_id"] == passage_id)
controls = passage["control_points"]
mouth_index = len(controls) - 1 if side == "lower" else 0
neighbor_index = mouth_index - 1 if side == "lower" else 1
a = controls[neighbor_index]["position_q"]
b = controls[mouth_index]["position_q"]
sign = 1.0 if side == "lower" else -1.0
tx, ty = sign * (b[0] - a[0]), sign * (b[1] - a[1])
length = math.hypot(tx, ty)
if length <= 1.0e-9:
raise ValueError(f"degenerate passage mouth tangent: {anchor}")
tx, ty = tx / length, ty / length
mouth = point_q(layout, mouth_q)
eye_z = mouth[2] + 1.45
camera = (mouth[0] - tx * 2.0, mouth[1] - ty * 2.0, eye_z)
target = (mouth[0] + tx * 10.0, mouth[1] + ty * 10.0, eye_z)
return camera, target, (mouth[0], mouth[1], eye_z, 16.0)
def views(layout: dict) -> list[dict]:
edges = {row["stable_id"]: row for row in layout["cavern_edges"]}
tunnel_views = []
for stable_id, name in (
("tunnel-network/strate-2/route-edge-0", "tunnel-network-route"),
("tunnel-network/strate-2/departure-edge", "tunnel-network-departure")):
camera, target, focus = camera_for_mouth(layout, edges[stable_id])
tunnel_views.append({"name": name, "layer": "Tunnel Network", "camera": camera,
"target": target, "focus": focus})
rooms = {row["stable_id"]: row for row in layout["cavern_rooms"]}
shaft = rooms["vertical-shafts/strate-3/shaft-0/primitive-0"]
access = edges["vertical-shafts/strate-3/shaft-access-0"]["polyline_um"]
center = point_um(shaft["center_um"])
half = point_um(shaft["half_extents_um"])
shaft_top = center[2] + half[2]
access_a, access_b = point_um(access[0]), point_um(access[-1])
ledge_z = shaft_top - 12 * (shaft["ledge_spacing_um"] / 1.0e6)
shaft_views = [
{"name": "vertical-shafts-looking-down", "layer": "Vertical Shafts",
"camera": (access_b[0], access_b[1], access_b[2] + 1.45),
"target": (center[0], center[1], shaft_top - 30.0),
"focus": (center[0], center[1], shaft_top - 25.0, 14.0), "fov": 35.0},
{"name": "vertical-shafts-looking-up", "layer": "Vertical Shafts",
"camera": (center[0], center[1], ledge_z + 1.45),
"target": (center[0], center[1], shaft_top - 2.0),
"focus": (center[0], center[1], shaft_top - 22.0, 20.0)},
]
return tunnel_views + shaft_views
def stamped_run(name: str, command: list[object], log: Path, expected_stamp: str,
env: dict[str, str]) -> str:
binary = Path(str(command[0]))
probe = subprocess.run([*LIMIT, "timeout", "15s", str(binary), "--print-source-stamp"],
cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, check=False, timeout=20)
match = STAMP_RE.search(probe.stdout or "")
if probe.returncode or not match or match.group(1) != expected_stamp:
raise RuntimeError(f"{name}: source stamp mismatch for {binary}")
log.parent.mkdir(parents=True, exist_ok=True)
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)],
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT,
timeout=1745, check=False)
output = log.read_text(encoding="utf-8", errors="replace")
emitted = STAMP_RE.search(output)
if result.returncode or not emitted or emitted.group(1) != expected_stamp:
raise RuntimeError(f"{name}: export or render failed; see {log}")
return output
def resolve_planner_layout(env: dict[str, str]) -> dict:
config = VERSIONS["planner-head"]
command = [config["headless"], DATA, "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=12",
"--clip-radius=6", "--max-clip-level=0", "--max-level=0",
"--force-generate", "--recipe=surface-world", "--resolved-layout-only",
f"--resolved-layout-output={LAYOUT}"]
stamped_run("planner-head-resolve-layout", list(map(str, command)),
OUT / "planner-head-resolved-layout.log", config["stamp"], env)
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
ids = {row["stable_id"] for row in layout["cavern_edges"]}
required = {"tunnel-network/strate-2/route-edge-0",
"tunnel-network/strate-2/departure-edge",
"vertical-shafts/strate-3/shaft-access-0"}
if not required.issubset(ids):
raise RuntimeError("resolved owner seed-0 layout is missing a capture anchor")
return layout
def main() -> int:
if not DATA.is_file():
raise SystemExit("missing owner fixture")
OUT.mkdir(parents=True, exist_ok=True)
ARTIFACTS.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env["LP_NUM_THREADS"] = "12"
env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
layout = resolve_planner_layout(env)
quality_tool = ROOT / "Standalone/Tools/verify/capture_quality.py"
manifest = []
failures = []
for version, config in VERSIONS.items():
for view in views(layout):
name = view["name"]
camera, target = view["camera"], view["target"]
yaw, pitch = angles(camera, target)
mesh_dir = OUT / "meshes" / version
mesh_dir.mkdir(parents=True, exist_ok=True)
mesh = mesh_dir / f"{name}.vfmesh"
capture_layout = mesh_dir / f"{name}-layout.json"
export = [config["headless"], DATA, "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=12",
"--clip-radius=6", "--max-clip-level=0", "--max-level=0",
"--force-generate", "--recipe=surface-world",
"--render-distance-chunks=0", f"--camera={fmt((*camera, yaw, pitch))}",
f"--resolved-layout-output={capture_layout}", f"--mesh-export={mesh}"]
stamped_run(f"{version}-{name}-export", list(map(str, export)),
OUT / f"{version}-{name}-export.log", config["stamp"], env)
if not mesh.is_file() or mesh.stat().st_size == 0:
raise RuntimeError(f"{version}-{name}: empty mesh export")
rendered_layout = json.loads(capture_layout.read_text(encoding="utf-8"))
png = OUT / f"{version}-{name}.png"
jpg = ARTIFACTS / f"{version}-{name}.jpg"
render = [config["renderer"], mesh, png, "--size=1280x720",
f"--fov-deg={float(view.get('fov', 78.0)):.1f}",
f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}",
f"--pitch-deg={pitch:.6f}", "--tile-skirts=off", "--shade=readable",
"--headlamp=on", "--height-tint=on", "--height-center-m=-950",
"--height-range-m=60", "--tile-edges=off",
f"--focus-region={fmt(view['focus'])}"]
stamped_run(f"{version}-{name}-render", list(map(str, render)),
OUT / f"{version}-{name}-render.log", config["stamp"], env)
subprocess.run([*LIMIT, "timeout", "120s", "ffmpeg", "-nostdin",
"-hide_banner", "-loglevel", "error", "-y", "-i", png,
"-frames:v", "1", "-q:v", "4", jpg], cwd=ROOT, env=env,
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT,
timeout=125)
check = subprocess.run([*LIMIT, "timeout", "120s", sys.executable,
quality_tool, jpg], cwd=ROOT, env=env, text=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
check=False, timeout=125)
quality_ok = check.returncode == 0 and "status=PASS" in check.stdout
if not quality_ok:
failures.append(f"{version}-{name}")
quality_line = (check.stdout.strip().splitlines()[-1]
if check.stdout.strip() else f"quality exit={check.returncode}")
manifest.append({"version": version, "view": name, "layer": view["layer"],
"file": str(jpg.relative_to(ROOT)), "camera_m": camera, "target_m": target,
"yaw_deg": yaw, "pitch_deg": pitch,
"source_stamp": config["stamp"],
"capture_layout_identity": rendered_layout.get("stable_identity"),
"pose_layout_identity": layout.get("stable_identity"), "quality_status":
"PASS" if quality_ok else "FAIL", "quality": quality_line})
print(f"[L150Capture] version={version} view={name} quality="
f"{'PASS' if quality_ok else 'FAIL'} file={jpg.relative_to(ROOT)}",
flush=True)
(ARTIFACTS / "manifest.json").write_text(
json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
(ARTIFACTS / "INDEX.md").write_text(
"# L150 matched owner-world captures\n\n"
"Seed 0, with camera poses from the planner layout resolved against the owner's "
"embedded Core plan and reused for both versions. The plan is imported without a seed "
"override. The two "
"Tunnel Network views sit at the planned route and departure joints; the Vertical "
"Shafts views look down from the access landing and up from a ledge.\n\n"
+ "\n".join(f"- {row['layer']} / {row['view']}: "
f"[{row['version']}]({Path(row['file']).name}) "
f"— {row['quality_status']}" for row in manifest) + "\n",
encoding="utf-8")
with (ARTIFACTS / "INDEX.md").open("a", encoding="utf-8") as index:
index.write("\nAll eight exports and renders completed. The capture-quality gate "
"passed for all four planner-head images and one assembler/world image. Three "
"assembler/world images have insufficient image variance because no mesh surface "
"is visible from those same viewpoints; those gate failures are recorded as the "
"baseline result.\n")
print(f"[L150CaptureSummary] views={len(manifest)//2} pairs={len(manifest)//2} "
f"quality_failures={len(failures)}", flush=True)
return 0 if not failures else 1
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception as error:
print(f"[L150CaptureFailure] {error}", file=sys.stderr, flush=True)
raise SystemExit(1)
@@ -0,0 +1,156 @@
#!/usr/bin/env python3
"""L80 comparison of the planner HEAD with the assembler/world baseline."""
from __future__ import annotations
import json
import os
import re
import statistics
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l150/timing"
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"assembler-world": (
ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
"4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"),
"planner-head": (
ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l150-head-clang",
"c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f"),
}
CAMERAS = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
PIPELINE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M)
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
NEAR = re.compile(
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
re.M)
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
binary, expected_stamp = BINARIES[version]
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
log = OUT / f"{label}.log"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(binary), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", f"--workers={workers}",
"--clip-radius=3", "--max-clip-level=8", "--max-level=8",
f"--camera={camera}", "--profile-lod-work", "--stream-scheduler-policy=3",
]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
proc = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
pipeline, near, stamp = PIPELINE.search(content), NEAR.search(content), STAMP.search(content)
if (proc.returncode or pipeline is None or near is None or stamp is None
or stamp.group(1) != expected_stamp):
raise SystemExit(f"L80 timing failed or source stamp mismatch: {log}; exit={proc.returncode}")
fields = dict(FIELDS.findall(pipeline.group(1)))
row = {
"strate": strate, "workers": workers, "version": version,
"phase": phase, "replicate": replicate, "source_stamp": stamp.group(1),
"lod02_ready_s": float(near.group(3)),
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
"whole_tiles": int(fields["whole_tiles"].split("/")[0]),
"wall_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT)),
}
print("[L150TimingRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def quiet_machine() -> tuple[float, float]:
load_1m = os.getloadavg()[0]
def totals() -> tuple[int, int]:
fields = Path("/proc/stat").read_text().splitlines()[0].split()
values = [int(value) for value in fields[1:]]
return sum(values), values[3] + values[4]
before = totals()
time.sleep(2.0)
after = totals()
idle = 100.0 * (after[1] - before[1]) / max(1, after[0] - before[0])
return load_1m, idle
def main() -> int:
if any(not path.is_file() for path in [DATA, *(entry[0] for entry in BINARIES.values())]):
raise SystemExit("missing L150 timing input")
OUT.mkdir(parents=True, exist_ok=True)
load_1m, idle = quiet_machine()
(OUT / "machine-load-before.txt").write_text(
f"1-minute load: {load_1m:.2f}\n2-second CPU idle: {idle:.1f}%\n")
print(f"[L150TimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True)
if load_1m >= 1.5 or idle < 97.0:
raise SystemExit("timing refused: machine is above the L80 quiet-load gate")
rows = []
versions = tuple(BINARIES)
for strate in range(4):
for workers in (8, 12):
for version in versions:
run(strate, workers, version, "warmup", 0)
for replicate in range(1, 6):
order = versions if replicate % 2 else tuple(reversed(versions))
for version in order:
rows.append(run(strate, workers, version, "measured", replicate))
(OUT / "runs-partial.json").write_text(
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
measured = [row for row in rows if row["phase"] == "measured"]
summary = []
for strate in range(4):
for workers in (8, 12):
group = [row for row in measured
if row["strate"] == strate and row["workers"] == workers]
medians = {
version: {
key: statistics.median(row[key] for row in group
if row["version"] == version)
for key in ("lod02_ready_s", "whole_view_ready_s")
}
for version in versions
}
entry = {"strate": strate, "workers": workers,
"replicates_per_version": 5, "medians": medians}
for key in ("lod02_ready_s", "whole_view_ready_s"):
entry[f"planner_head_vs_assembler_world_{key}_delta_pct"] = round(
100.0 * (medians["planner-head"][key]
/ medians["assembler-world"][key] - 1.0), 2)
entry[f"{key}_paired_deltas_pct"] = [
round(100.0 * (
next(row[key] for row in group
if row["version"] == "planner-head"
and row["replicate"] == replicate)
/ next(row[key] for row in group
if row["version"] == "assembler-world"
and row["replicate"] == replicate) - 1.0), 3)
for replicate in range(1, 6)]
summary.append(entry)
(OUT / "summary.json").write_text(json.dumps({
"a_a_noise_floor_pct": 1.0,
"timing_protocol": (
"one warm-up per binary, five interleaved runs per cell, W8/W12, "
"all four owner layers; L137 A/A median noise floor ±1%"),
"runs": rows,
"summary": summary,
}, indent=2) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
"""Run L147 composed gates on the actual L150 planner HEAD build."""
from __future__ import annotations
import importlib.util
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[4]
SOURCE = ROOT / "Standalone/Research/WorldAssemblerM3/Linux147/RunL147SeedSweep.py"
spec = importlib.util.spec_from_file_location("l147_seed_sweep", SOURCE)
if spec is None or spec.loader is None:
raise SystemExit("cannot load the L147 composed gate runner")
runner = importlib.util.module_from_spec(spec)
spec.loader.exec_module(runner)
runner.OUT = ROOT / "Saved/Linux/l150/seeds"
runner.BIN = ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l150-head-clang"
runner.STAMP = "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f"
if __name__ == "__main__":
raise SystemExit(runner.main())
@@ -0,0 +1,170 @@
#!/usr/bin/env python3
"""Measure planner HEAD and code-equivalent assembler/world with the L121/L127 runners."""
from __future__ import annotations
import importlib.util
import json
import os
from pathlib import Path
import statistics
import sys
ROOT = Path(__file__).resolve().parents[4]
RESEARCH = ROOT / "Standalone/Research/WorldAssemblerM3"
EDITOR = Path("/home/jahni/UnrealEngine/Engine/Binaries/Linux/UnrealEditor-Cmd")
HOSTS = {
"assembler-world": ROOT / "Saved/Linux/l127/ue-baseline-project",
"planner-head": Path("/home/jahni/VFHost"),
}
CAMERAS = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
def load(name: str, path: Path):
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise RuntimeError(f"cannot load {path}")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def configure(arrival, movement, label: str):
host = HOSTS[label].resolve()
out = ROOT / "Saved/Linux/l150/ue" / label
plugin_saved = host / "Plugins/VoxelForge/Saved/Linux/l150"
out.mkdir(parents=True, exist_ok=True)
plugin_saved.mkdir(parents=True, exist_ok=True)
os.environ["XDG_CONFIG_HOME"] = str(out / "config")
arrival.HOST = host
arrival.EDITOR = EDITOR
arrival.OUT = out / "start"
arrival.HOST_SAVED = out / "host"
arrival.HOST_PLUGIN_SAVED = plugin_saved
arrival.RUN_TIMEOUT_S = 1740
movement.HOST = host
movement.EDITOR = EDITOR
movement.OUT = out / "movement"
movement.HOST_SAVED = out / "host"
movement.HOST_OUT = plugin_saved / "movement"
movement.RUN_TIMEOUT_S = 1740
return host, out
def start_matrix(arrival, movement, label: str) -> dict:
host, out = configure(arrival, movement, label)
spawns = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
starts = []
for index, position in spawns.items():
stratum = "FI" if index == 1 else "SW"
arrival.SPAWNS_M[stratum] = position
row = arrival.run(stratum, 1, f"start-s{index}", 8, 1, 0, 1, 1, 0, 1,
False, False, 1)
row["strate_index"] = index
row["spawn_m"] = list(position)
if row.get("lod02_full_settled_t_s") is None:
raise RuntimeError(f"LOD0-2 did not settle for S{index}: {row}")
starts.append(row)
print("[L150UEStart] " + json.dumps(row, sort_keys=True), flush=True)
(out / "start-summary.json").write_text(
json.dumps(starts, indent=2, sort_keys=True) + "\n", encoding="utf-8")
row = moving_run(movement, label, 1)
(out / "movement-first.json").write_text(
json.dumps(row, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print("[L150UEMovement] " + json.dumps(row, sort_keys=True), flush=True)
return {"host": str(host), "starts": starts, "first_movement": row}
def moving_run(movement, label: str, repetition: int) -> dict:
condition = "c743f2a" if label == "assembler-world" else "planner-head"
row = movement.run_unreal(condition, repetition)
events = movement.parse_events(ROOT / row["trace"])
row["supplemental_far_applies_moving"] = sum(
int(event.get("supplemental_far_applies", 0))
for event in events
if event.get("type") == "apply_frame"
and float(event.get("speed_cm_s", 0.0)) >= 50.0)
if row.get("effective_tile_slots") != 8:
raise RuntimeError(f"expected eight effective tile slots: {row}")
return row
def main() -> int:
out = ROOT / "Saved/Linux/l150/ue"
out.mkdir(parents=True, exist_ok=True)
arrival = load("l150_arrival", RESEARCH / "Linux121/RunL121Arrival.py")
movement = load("l150_movement", RESEARCH / "Linux121/RunL121Movement.py")
summaries = {}
all_runs = {label: [] for label in HOSTS}
for label in HOSTS:
summaries[label] = start_matrix(arrival, movement, label)
all_runs[label].append(summaries[label]["first_movement"])
for repetition in range(2, 6):
order = ("planner-head", "assembler-world") if repetition % 2 == 0 \
else ("assembler-world", "planner-head")
for label in order:
configure(arrival, movement, label)
row = moving_run(movement, label, repetition)
all_runs[label].append(row)
(out / "movement-runs-partial.json").write_text(
json.dumps(all_runs, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print("[L150UEMovementRepeat] " + json.dumps({
key: row.get(key) for key in (
"condition", "repetition", "near_ready_moving_mean_pct",
"far_holes_moving_mean_pct", "game_thread_tick_p99_ms",
"game_thread_tick_max_ms", "supplemental_far_applies_moving",
"wall_s", "trace")}, sort_keys=True), flush=True)
keys = ("near_ready_moving_mean_pct", "far_holes_moving_mean_pct",
"game_thread_tick_p99_ms", "game_thread_tick_max_ms")
medians = {
label: {key: statistics.median(row[key] for row in rows) for key in keys}
for label, rows in all_runs.items()
}
delta = {
key: round(100.0 * (medians["planner-head"][key]
/ medians["assembler-world"][key] - 1.0), 2)
for key in keys
}
result = {
"baseline_commit": "c743f2a (code-equivalent to assembler/world eb47d72)",
"protocol": (
"L121 arrival and L127 movement; starts at four owner strata; five interleaved "
"30 m/s runs per build; 12 s move plus 5 s dwell; eight effective tile slots"),
"starts": summaries,
"movement_runs": all_runs,
"movement_medians": medians,
"planner_head_vs_assembler_world_delta_pct": delta,
}
(out / "summary.json").write_text(
json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print("[L150UESummary] " + json.dumps({
"movement_medians": medians,
"planner_head_vs_assembler_world_delta_pct": delta,
"starts_s": {
label: [round(row["lod02_full_settled_t_s"], 3)
for row in summary["starts"]]
for label, summary in summaries.items()
},
}, sort_keys=True), flush=True)
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception as error:
print(f"[L150UEFailure] {error}", file=sys.stderr, flush=True)
raise SystemExit(1)
@@ -0,0 +1,14 @@
# L150 matched owner-world captures
Seed 0, with camera poses from the planner layout resolved against the owner's embedded Core plan and reused for both versions. The plan is imported without a seed override. The two Tunnel Network views sit at the planned route and departure joints; the Vertical Shafts views look down from the access landing and up from a ledge.
- Tunnel Network / tunnel-network-route: [assembler-world](assembler-world-tunnel-network-route.jpg) — FAIL
- Tunnel Network / tunnel-network-departure: [assembler-world](assembler-world-tunnel-network-departure.jpg) — PASS
- Vertical Shafts / vertical-shafts-looking-down: [assembler-world](assembler-world-vertical-shafts-looking-down.jpg) — FAIL
- Vertical Shafts / vertical-shafts-looking-up: [assembler-world](assembler-world-vertical-shafts-looking-up.jpg) — FAIL
- Tunnel Network / tunnel-network-route: [planner-head](planner-head-tunnel-network-route.jpg) — PASS
- Tunnel Network / tunnel-network-departure: [planner-head](planner-head-tunnel-network-departure.jpg) — PASS
- Vertical Shafts / vertical-shafts-looking-down: [planner-head](planner-head-vertical-shafts-looking-down.jpg) — PASS
- Vertical Shafts / vertical-shafts-looking-up: [planner-head](planner-head-vertical-shafts-looking-up.jpg) — PASS
All eight exports and renders completed. The capture-quality gate passed for all four planner-head images and one assembler/world image. Three assembler/world images have insufficient image variance because no mesh surface is visible from those same viewpoints; those gate failures are recorded as the baseline result.
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,186 @@
[
{
"version": "assembler-world",
"view": "tunnel-network-route",
"layer": "Tunnel Network",
"file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-route.jpg",
"camera_m": [
17.334593752829058,
-13.396520050883792,
-751.9240234375
],
"target_m": [
28.32312498585471,
-18.218571620581038,
-751.9240234375
],
"yaw_deg": 113.69310802290845,
"pitch_deg": 0.0,
"source_stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621",
"capture_layout_identity": "resolved-layout-v3-fnv64:0589300eb07dfe06",
"pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"quality_status": "FAIL",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-route.jpg pixels=921600 mean_luma=112.000 stddev_luma=0.000 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL"
},
{
"version": "assembler-world",
"view": "tunnel-network-departure",
"layer": "Tunnel Network",
"file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-departure.jpg",
"camera_m": [
-22.06185250467782,
-5.017471635430169,
-810.9455078125
],
"target_m": [
-10.065737476610899,
-5.322798072849153,
-810.9455078125
],
"yaw_deg": 91.45798369615608,
"pitch_deg": 0.0,
"source_stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621",
"capture_layout_identity": "resolved-layout-v3-fnv64:0589300eb07dfe06",
"pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-departure.jpg pixels=921600 mean_luma=106.806 stddev_luma=3.804 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"version": "assembler-world",
"view": "vertical-shafts-looking-down",
"layer": "Vertical Shafts",
"file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-down.jpg",
"camera_m": [
-31.001772,
-7.103472,
-901.9274509999999
],
"target_m": [
-31.001772,
-8.066018,
-929.377451
],
"yaw_deg": 180.0,
"pitch_deg": -87.99172181188044,
"source_stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621",
"capture_layout_identity": "resolved-layout-v3-fnv64:0589300eb07dfe06",
"pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"quality_status": "FAIL",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-down.jpg pixels=921600 mean_luma=112.000 stddev_luma=0.000 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL"
},
{
"version": "assembler-world",
"view": "vertical-shafts-looking-up",
"layer": "Vertical Shafts",
"file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-up.jpg",
"camera_m": [
-31.001772,
-8.066018,
-945.9274509999999
],
"target_m": [
-31.001772,
-8.066018,
-901.377451
],
"yaw_deg": 0.0,
"pitch_deg": 90.0,
"source_stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621",
"capture_layout_identity": "resolved-layout-v3-fnv64:0589300eb07dfe06",
"pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"quality_status": "FAIL",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-up.jpg pixels=921600 mean_luma=111.749 stddev_luma=1.375 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL"
},
{
"version": "planner-head",
"view": "tunnel-network-route",
"layer": "Tunnel Network",
"file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-route.jpg",
"camera_m": [
17.334593752829058,
-13.396520050883792,
-751.9240234375
],
"target_m": [
28.32312498585471,
-18.218571620581038,
-751.9240234375
],
"yaw_deg": 113.69310802290845,
"pitch_deg": 0.0,
"source_stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f",
"capture_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-route.jpg pixels=921600 mean_luma=133.954 stddev_luma=13.005 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"version": "planner-head",
"view": "tunnel-network-departure",
"layer": "Tunnel Network",
"file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-departure.jpg",
"camera_m": [
-22.06185250467782,
-5.017471635430169,
-810.9455078125
],
"target_m": [
-10.065737476610899,
-5.322798072849153,
-810.9455078125
],
"yaw_deg": 91.45798369615608,
"pitch_deg": 0.0,
"source_stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f",
"capture_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-departure.jpg pixels=921600 mean_luma=115.401 stddev_luma=12.660 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"version": "planner-head",
"view": "vertical-shafts-looking-down",
"layer": "Vertical Shafts",
"file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-down.jpg",
"camera_m": [
-31.001772,
-7.103472,
-901.9274509999999
],
"target_m": [
-31.001772,
-8.066018,
-929.377451
],
"yaw_deg": 180.0,
"pitch_deg": -87.99172181188044,
"source_stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f",
"capture_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-down.jpg pixels=921600 mean_luma=112.234 stddev_luma=3.319 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"version": "planner-head",
"view": "vertical-shafts-looking-up",
"layer": "Vertical Shafts",
"file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-up.jpg",
"camera_m": [
-31.001772,
-8.066018,
-945.9274509999999
],
"target_m": [
-31.001772,
-8.066018,
-901.377451
],
"yaw_deg": 0.0,
"pitch_deg": 90.0,
"source_stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f",
"capture_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-up.jpg pixels=921600 mean_luma=111.590 stddev_luma=2.483 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB