diff --git a/CURRENT-STATE.md b/CURRENT-STATE.md index a1344f3..30685fc 100644 --- a/CURRENT-STATE.md +++ b/CURRENT-STATE.md @@ -1,11 +1,11 @@ # VoxelForge current state -As of 2026-10-31. The L130 baseline is on `assembler/world` as `044cd6d`; this L131 work branch starts at `cbbfd46`. Current-state claims below point to their code or measured report. +As of 2026-10-31. L131 is on `assembler/world` as `8107196`; this L132 worktree is on `wip/l132-dispatch-truth`, starting at `ebae71b`. Current-state claims below point to their code or measured report. ## What ships today - **Core:** evaluator version **227**. Unreal's `AVoxelWorld::bUseVoxelForgeCore` is opt-in and defaults to `false`; the legacy generator remains the default runtime path. [Core version](Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h), [runtime switch](Source/VoxelForge/Public/VoxelWorld.h), [L130 report](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md). -- **Generator dispatch when Core is enabled:** `SurfaceWorld` uses the Core surface adapter; `FloatingIslands` uses its Core adapter. `Caves` uses the Core cave field only with `Core.Caves.PrimitiveField` v1. `Maze` and `VerticalShafts` use the Core lattice field only with `Core.Lattice.PrimitiveField` v1. Other generator/recipe combinations fall through to `FLegacyGeneratorFieldAdapter`. [Dispatch](Standalone/Core/src/ShapeWorldInternal.h), [recipe checks](Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h). +- **Verified owner4 generator dispatch (L132):** SW binds `SurfaceWorld-native` because the owner export contains a matching `SurfaceWorld.Heightfield2D` v2 plan. FI binds `compiled-analytic/FloatingIslands` because its `FloatingIslands.ClusterField` v0 layout has no matching native island plan. TN and VS bind `compiled-analytic/TunnelNetwork` and `compiled-analytic/VerticalShafts`; their legacy recipe IDs provide neither a `NativeCaves`/`NativeLattice` graph recipe nor native field capability. The L132 fast-tier report prints the binding and generated tile-center share at each owner strate. [Dispatch](Standalone/Core/src/ShapeWorldInternal.h), [adapter recipe checks](Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h), [L132 report](Standalone/Research/WorldAssemblerM3/Linux132/REPORT.md). - **Passages:** continuous smooth swept tubes with a continuous floor profile. L125 stamps conservative route slabs and outside sentinels into the tile cache; exact route geometry is still evaluated. All 21,425 route samples passed the open-air walk check; the nine-station floor probe's maximum root offset was 7.884 mm. L130 sets the shared influence reach to **1.0533 m** (0.75 m shell + 0.21 m relief + 0.50 m smooth-max support / 6 + 0.01 m margin), converted once from metres to Core voxels for the proof, feature, fallback, and off-grid bounds. [L125](Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md), [L130](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md). - **Streaming:** arrival uses a bounded startup apply burst: `voxel.StreamingStartupApplyBoost=1` by default, up to 24 visible meshes or 14 ms per frame for at most 30 seconds, until the initial desired set and walkable ring are ready. `voxel.StreamingFarApplySupplement=1` is also default-on: after the normal visible apply cap, at most one far result applies when under half the apply-time budget; it is inactive during the startup burst. Dynamic near reserve defaults on; coarse-first remains off; the legacy pending-result cap defaults off. The far-apply lane is Unreal-only. [CVar source](Source/VoxelForge/Private/VoxelWorld.cpp), [L121 report](Standalone/Research/WorldAssemblerM3/Linux121/REPORT.md), [architecture](ARCHITECTURE.md). - **Determinism:** proven on Linux with Clang and GCC at W1/W8/W12. L131's unchanged-output comparison covers owner4's SW/FI/TN/VS views in 24 compiler/worker cases; the canonical owner4/owner2 recipe matrix also passes. Windows determinism is unverified. [L131 checks](Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md), [v227 canonical hashes](Standalone/Tools/verify/baselines/owner-hash/canonical/). @@ -34,8 +34,8 @@ The latest recorded Linux Unreal start-to-LOD0–2 values are L127's one run per ## How to verify -- Build standalone Clang or GCC with [`Standalone/BuildLinux.sh`](Standalone/BuildLinux.sh), for example `bash Standalone/BuildLinux.sh --compiler=clang++-19 --variant=l131-clang --fp-contract=off --jobs=4`. The script pins CPUs 0–11 and applies `prlimit --as=6442450944` itself. -- Fast tier: `python3 Standalone/Tools/verify/verify.py fast --out Saved/Linux/l131 --overall-timeout 1740`. It runs the walkability and current owner checks; per-run logs and the summary stay under `Saved/Linux/l131`. +- Build standalone Clang or GCC with [`Standalone/BuildLinux.sh`](Standalone/BuildLinux.sh), for example `bash Standalone/BuildLinux.sh --compiler=clang++-19 --variant=l132-clang --fp-contract=off --jobs=4`. The script pins CPUs 0–11 and applies `prlimit --as=6442450944` itself. +- Fast tier: `python3 Standalone/Tools/verify/verify.py fast --out Saved/Linux/l132 --overall-timeout 1740`. It includes the L132 four-pose owner density-dispatch report; per-run logs and the summary stay under `Saved/Linux/l132`. - Canonical output baselines are the v227 files in `Standalone/Tools/verify/baselines/owner-hash/canonical/`. L131 also provides reproducible pre-cleanup identity matrices for owner4 tile hashes and owner4/owner2 canonical hashes under `Standalone/Research/WorldAssemblerM3/Linux131/`. - Timing follows L80: quiet machine, no overlapping build, one warm-up per version, five interleaved runs per stratum and worker count, W8/W12. Each headless run uses `taskset -c 0-11`, `prlimit --as=6442450944`, and a 1,740 s timeout. See [`RunL131Timing.py`](Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py). - Linux host build: `/home/jahni/UnrealEngine/Engine/Build/BatchFiles/Linux/Build.sh VFHostEditor Linux Development -Project=/home/jahni/VFHost/VFHost.uproject -NoUBA -MaxParallelActions=4 -Log=/home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l131/ubt.log`. Run Unreal without `prlimit`. diff --git a/Standalone/Research/WorldAssemblerM3/Linux132/PrepareOwnerNativeFI.py b/Standalone/Research/WorldAssemblerM3/Linux132/PrepareOwnerNativeFI.py new file mode 100644 index 0000000..bddedb3 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux132/PrepareOwnerNativeFI.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Create the isolated L132 owner snapshot with a native plan for existing FI strate 1.""" +from __future__ import annotations + +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +SOURCE = ROOT / "Standalone/data/owner_world.json" +MANIFEST = ROOT / "Saved/Linux/l132/fixtures/owner-native-fi.world-assembly.json" +LAYOUT = ROOT / "Saved/Linux/l132/fixtures/owner-native-fi.resolved-layout.json" +OUTPUT = ROOT / "Saved/Linux/l132/fixtures/owner_world_native_fi.json" + + +def main() -> int: + owner = json.loads(SOURCE.read_text(encoding="utf-8")) + manifest_text = MANIFEST.read_text(encoding="utf-8").strip() + layout_text = LAYOUT.read_text(encoding="utf-8").strip() + manifest = json.loads(manifest_text) + layout = json.loads(layout_text) + islands = manifest.get("floating_islands", []) + native = [row for row in islands if row.get("strate_index") == 1] + resolved = [row for row in layout.get("strates", []) + if row.get("strate_index") == 1] + if len(native) != 1 or len(resolved) != 1: + raise SystemExit("native FI snapshot must contain exactly one strate-1 island plan/layout") + if (resolved[0].get("generator_kind") != "FloatingIslands" + or resolved[0].get("recipe_id") != "FloatingIslands.ClusterField" + or resolved[0].get("recipe_version") != 8): + raise SystemExit("resolved strate 1 is not FloatingIslands.ClusterField v8") + owner["world_assembly"] = manifest + owner["core_world_assembly_json"] = manifest_text + owner["resolved_layout_json"] = layout_text + OUTPUT.parent.mkdir(parents=True, exist_ok=True) + OUTPUT.write_text(json.dumps(owner, separators=(",", ":")) + "\n", encoding="utf-8") + print(f"[L132NativeFISnapshot] path={OUTPUT.relative_to(ROOT)} " + f"islands={len(native[0].get('manifest', {}).get('plan', {}).get('islands', []))} " + "strate=1 recipe=FloatingIslands.ClusterField/v8 status=PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux132/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux132/REPORT.md new file mode 100644 index 0000000..06096a1 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux132/REPORT.md @@ -0,0 +1,41 @@ +# L132 — owner density dispatch + +## Result + +With Core enabled in the owner headless pipeline, the four strates bind to four paths. Unreal’s `bUseVoxelForgeCore` remains opt-in and defaults off. The fast-tier diagnostic reports the Core bindings at the four L80 owner poses and attributes hashed tiles by tile-center height within the resolved stratum bounds. + +| Strate | Owner recipe | Bound path | Tile centers / share | +|---|---|---|---:| +| SW (0) | `SurfaceWorld.Heightfield2D` v2 | `SurfaceWorld-native` | 2,640 / 100% | +| FI (1) | `FloatingIslands.ClusterField` v0 | `compiled-analytic/FloatingIslands` | 2,128 / 100% | +| TN (2) | `legacy.TunnelNetwork` v0 | `compiled-analytic/TunnelNetwork` | 2,208 / 100% | +| VS (3) | `legacy.VerticalShafts` v0 | `compiled-analytic/VerticalShafts` | 1,392 / 100% | + +SW has a matching strate/bounds `SurfaceWorld` plan, and its `Heightfield2D` evaluator is valid. FI has a legacy v0 layout record but no matching `FloatingIslandPlan`, so `FloatingIslandFieldV1` cannot bind. The Core owner manifest has zero island plans. TN and VS have legacy recipe IDs, not `Core.Caves.PrimitiveField` v1 or `Core.Lattice.PrimitiveField` v1. Their resolved layouts therefore lack the matching graph recipe and native-field capability. All three fall through to `FLegacyGeneratorFieldAdapter`. + +The dispatch check is in the fast tier and can also be requested from the headless binary with `--density-dispatch-report`. It prints the per-strate bound adapter/path and hashed tile-center path shares. Its tile counts aggregate the four standard owner poses; tiles whose centers fall outside an interior interval are excluded, and a tile spanning a boundary is assigned by its center. Per-view logs are `Saved/Linux/l132/owner-density-dispatch-s*-clang.log`; the printed shares are in `Saved/Linux/l132/logs/verify-fast.log` and `Saved/Linux/l132/fast-summary.json`. + +Across the 8,368 classified tile centers, path shares are 31.5% SurfaceWorld-native, 25.4% compiled-analytic/FloatingIslands, 26.4% compiled-analytic/TunnelNetwork, and 16.6% compiled-analytic/VerticalShafts. + +## Native FI decision + +The native evaluator is present. `BuildFloatingIslandStratumPlan` can convert the owner’s legacy island parameters to a v8 plan, but the owner’s imported manifest does not contain that plan. The existing automatic-plan branch is guarded by `bImportedOverride` inside the generated-assembly branch, so it does not fill this owner’s missing imported plan. + +I built a candidate v8 plan for the existing owner FI strate without changing the default owner data. Its raw island-route audit passed: 29 route points, 78,336 capsule samples, zero blocked samples, zero unsupported probes, and zero grade failures. The composed owner audit did not pass: + +- Existing compiled-analytic owner: all 3 passage interfaces pass; shell floor violations 0; ripple checks 3/3. +- Native FI candidate: only 2/3 interfaces pass; the added FI route is not walkable (1/2 strate routes walkable); shell floor violations 13; ripple checks 2/3. +- Passage tile seams passed for both bindings: 4 pairs, 4,900 shared samples, zero mismatches. + +Native FI is a quality regression on the owner’s current passages, despite its standalone route plan passing. I did not switch the owner. The conditional captures, W8/W12 timing matrix, and Clang/GCC W1/W8/W12 matrix were not run because the candidate failed the composed walk/shell gate. No Core version or output baseline changed; the default owner output remains on its existing bindings. No Linux UE host build was run because there was no qualifying native change or timing matrix. + +## TN/VS graph translation size + +The active TN `GenerationParams` block has 47 fields; VS `VerticalShaftParams` has 11. Each also has 15 passage settings. Both have `CoreGeometry.GeneratorRecipe=0` and empty `Rooms`, `Tunnels`, and `Stamps` arrays. Native cave/lattice authoring requires at least one explicit room and one edge, with geometry, links, and route data. There is no adapter translating those legacy generator blocks into graph records. This is a substantial procedural migration, not a small recipe mapping, so I did not build it this round. + +## Verification + +- Fast tier: PASS, 304.0 s, 84 runs and 273 checks; includes four-pose dispatch output. +- Passage tile seams: compiled-analytic and native FI candidate both PASS at the FI owner pose. +- Owner walk/shell comparison: default PASS; native candidate FAIL as detailed above. +- Build stamp: `766b94ca7638475975b0345e5907eebb96158360c384f5d916c78eccbb1aa815`. diff --git a/Standalone/Source/StreamBenchmarkHeadless.cpp b/Standalone/Source/StreamBenchmarkHeadless.cpp index bb39a9a..a0cb0a7 100644 --- a/Standalone/Source/StreamBenchmarkHeadless.cpp +++ b/Standalone/Source/StreamBenchmarkHeadless.cpp @@ -70,6 +70,8 @@ namespace bool HasRenderDistanceOverride=false; int RenderDistanceOverride=0; bool FloatingIslandsRecipe=false; + bool NativeOwnerFloatingIslandsRecipe=false; + bool DensityDispatchReport=false; bool CavesRecipe=false; bool LatticeRecipe=false; int LatticeStrateIndex=-1; @@ -1432,6 +1434,8 @@ namespace " --core-snapshot=FILE (benchmark the exact resolved Core snapshot from Unreal)\n" " --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n" " --seed=N --recipe=surface-world|floating-islands|caves|lattice\n" + " --native-owner-fi (build the existing owner FI plan; leave other strates intact)\n" + " --density-dispatch-report (print bound path per strate and tile hashes)\n" " --lattice-strate=N (opt one Maze or VerticalShafts strate into Core.Lattice)\n" " --landmark-chamber-demo (opt-in SW cavern room and swept tunnel)\n" " --ore-veins-demo (opt-in material-only SW capsule veins)\n" @@ -1490,6 +1494,9 @@ namespace else if(arg=="--resolved-layout-only")options.ResolvedLayoutOnly=true; else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=true; else if(arg=="--audit-resolved-layout-passages")options.AuditResolvedLayoutPassages=true; + else if(arg=="--native-owner-fi")options.NativeOwnerFloatingIslandsRecipe=true; + else if(arg=="--density-dispatch-report") + {options.DensityDispatchReport=true;options.StreamHash=true;options.StreamHashTiles=true;} else if(arg=="--audit-landmark-reachability")options.AuditLandmarkReachability=true; else if(arg=="--audit-native-caves")options.AuditNativeCaves=true; else if(arg=="--audit-native-lattice")options.AuditNativeLattice=true; @@ -1819,7 +1826,9 @@ int main(int argc,char** argv) else if(!StandaloneWorldAssemblyPrototype::AttachRound1WorldAssemblyPrototype( *ownerWorld,ownerJsonRoot,error,{},&worldAssembly)) {std::fprintf(stderr,"World assembler prototype setup failed: %s\n",error.c_str());return 2;} - if(!useExportedCorePlan&&options.FloatingIslandsRecipe + if(((!useExportedCorePlan&&options.FloatingIslandsRecipe) + ||(options.NativeOwnerFloatingIslandsRecipe + &&worldAssembly.floatingIslands.empty())) &&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly( *ownerWorld,ownerJsonRoot,worldAssembly,error)) {std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;} @@ -1852,15 +1861,17 @@ int main(int argc,char** argv) const std::string variant=options.LatticeRecipe ?"lattice/"+std::to_string(options.LatticeStrateIndex) :options.CavesRecipe?"caves" - :options.FloatingIslandsRecipe?"floating-islands":"surface-world"; + :(options.FloatingIslandsRecipe||options.NativeOwnerFloatingIslandsRecipe) + ?"floating-islands":"surface-world"; const bool layoutResolved=useExportedCorePlan + &&!options.NativeOwnerFloatingIslandsRecipe ?StandaloneWorldAssemblyPrototype::ImportExportedResolvedLayout( ownerJsonRoot,resolvedLayout,error) :StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot( *ownerWorld,worldAssembly,variant,resolvedLayout,error); if(!layoutResolved) {std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;} - if(useExportedCorePlan + if(useExportedCorePlan&&!options.NativeOwnerFloatingIslandsRecipe &&!StandaloneWorldAssemblyPrototype::ValidateExportedCorePlanMatchesOwner( *ownerWorld,worldAssembly,resolvedLayout,error)) {std::fprintf(stderr,"Unreal-resolved Core plan/owner slot validation failed: %s\n",error.c_str());return 2;} @@ -2113,6 +2124,65 @@ int main(int argc,char** argv) controls.controlsRejectedAsExpected,controls.controlsRun,controls.passed?"PASS":"FAIL"); if(!controls.passed) {for(const auto& failure:controls.failures)std::printf("[ResolvedLayoutValidatorControlFailure] %s %s\n",failure.code.c_str(),failure.detail.c_str());return 2;} + if(options.DensityDispatchReport) + { + const auto& slots=ownerWorld->Manager->GetLayout(); + for(const auto& stratum:resolvedLayout.strates) + { + const auto slot=std::find_if(slots.begin(),slots.end(),[&](const auto& candidate) + {return candidate.StrateIndex==stratum.strateIndex;}); + bool native=false; + std::string path,adapter; + if(slot!=slots.end()&&stratum.generatorKind=="SurfaceWorld") + { + const auto plan=std::find_if(worldAssembly.plan.strates.begin(), + worldAssembly.plan.strates.end(),[&](const auto& candidate) + {return candidate.strateIndex==stratum.strateIndex + &&candidate.bottomChunkZ==slot->BottomChunkZ + &&candidate.topChunkZ==slot->TopChunkZ;}); + if(plan!=worldAssembly.plan.strates.end()) + { + const VoxelForgeCore::Heightfield2D field(worldAssembly.recipe,*plan); + native=field.isValid(); + } + if(native){path="SurfaceWorld-native";adapter="FSurfaceWorldFieldAdapter";} + } + else if(slot!=slots.end()&&stratum.generatorKind=="FloatingIslands") + { + const auto plan=std::find_if(worldAssembly.floatingIslands.begin(), + worldAssembly.floatingIslands.end(),[&](const auto& candidate) + {return candidate.strateIndex==stratum.strateIndex + &&candidate.bottomChunkZ==slot->BottomChunkZ + &&candidate.topChunkZ==slot->TopChunkZ;}); + if(plan!=worldAssembly.floatingIslands.end()) + { + const VoxelForgeCore::FloatingIslandFieldV1 field( + plan->manifest.recipe,plan->manifest.plan); + native=field.isValid(); + } + if(native){path="FloatingIslands-native";adapter="FFloatingIslandFieldAdapter";} + } + else if(VoxelForgeCore::HasNativeGraphField(stratum) + &&(VoxelForgeCore::IsNativeCaveFieldStratum(stratum) + ?stratum.passageCapabilities.hasNativeCaveField + :stratum.passageCapabilities.hasNativeLatticeField)) + { + native=true; + path=VoxelForgeCore::IsNativeLatticeFieldStratum(stratum) + ?"Lattice-native":"Caves-native"; + adapter="FCavesFieldAdapter"; + } + if(!native) + { + path=stratum.generatorKind=="SurfaceWorld" + ?"SurfaceWorld-legacy":"compiled-analytic/"+stratum.generatorKind; + adapter="FLegacyGeneratorFieldAdapter"; + } + std::printf("[DensityDispatch] strate=%d generator=%s recipe=%s version=%u bound_path=%s adapter=%s\n", + stratum.strateIndex,stratum.generatorKind.c_str(),stratum.recipeId.c_str(), + stratum.recipeVersion,path.c_str(),adapter.c_str()); + } + } if(!options.ResolvedLayoutOutputPath.empty()) std::printf("[ResolvedLayoutSnapshotOutput] path=%s\n",options.ResolvedLayoutOutputPath.c_str()); if(auto* root=std::get_if(&ownerJsonRoot.Data)) diff --git a/Standalone/Tools/verify/verify.py b/Standalone/Tools/verify/verify.py index b369932..167da39 100644 --- a/Standalone/Tools/verify/verify.py +++ b/Standalone/Tools/verify/verify.py @@ -89,6 +89,9 @@ TILE_HASH_RE = re.compile( r"^\[PipelineTileHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) " r"density=(0x[0-9a-f]+) mesh=(0x[0-9a-f]+) " r"payload=(0x[0-9a-f]+) topology=(0x[0-9a-f]+)$", re.M) +DENSITY_DISPATCH_RE = re.compile( + r"^\[DensityDispatch\] strate=(\d+) generator=([^ ]+) recipe=([^ ]+) " + r"version=(\d+) bound_path=([^ ]+) adapter=([^ ]+)$", re.M) TILE_MATERIAL_HASH_RE = re.compile( r"^\[PipelineTileMaterialHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) " r"material=(0x[0-9a-f]+)$", re.M) @@ -493,6 +496,117 @@ def run_new_owner_plan_stage(runner: VerificationRunner, binary: Path) -> None: + ", ".join(deferred_failures)) +def run_density_dispatch_report(runner: VerificationRunner, binary: Path) -> None: + """Report selected owner density paths and generated tile shares by path.""" + 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), + } + aggregate: dict[int, dict[str, int]] = {} + bound_paths: dict[int, tuple[str, str, str, int, str]] = {} + failures: list[str] = [] + for focus_strate, position in cameras.items(): + name = f"owner-density-dispatch-s{focus_strate}-clang" + camera = "--camera=" + ",".join( + f"{value:.12f}" for value in (*position, 0.0, 0.0)) + layout_path = OUT / "layouts" / f"{name}.json" + try: + row, output = runner.run_cpp(name, binary, OWNER_NEW, "surface-world", + ["--density-dispatch-report", "--render-distance-chunks=0", camera], + expected_style=1, workers=8, max_level=8, clip_radius=3, + layout_path=layout_path) + except VerificationFailure as error: + failures.append(f"{name}: {error}") + continue + + dispatch_rows = DENSITY_DISPATCH_RE.findall(output) + dispatch = {int(index): (path, generator, recipe, int(version), adapter) + for index, generator, recipe, version, path, adapter in dispatch_rows} + if len(dispatch_rows) != 4 or set(dispatch) != {0, 1, 2, 3}: + failures.append(f"{name}: expected four unique dispatch rows, got {dispatch_rows}") + continue + if not layout_path.is_file(): + failures.append(f"{name}: resolved layout missing at {layout_path}") + continue + try: + layout = json.loads(layout_path.read_text(encoding="utf-8")) + strata = {int(value["strate_index"]): value for value in layout.get("strates", [])} + if set(strata) != {0, 1, 2, 3}: + raise ValueError(f"expected four strata, got {sorted(strata)}") + voxel_size_um = int(strata[0]["voxel_size_um"]) + if voxel_size_um <= 0: + raise ValueError(f"invalid voxel_size_um={voxel_size_um}") + except (OSError, json.JSONDecodeError, KeyError, TypeError, ValueError) as error: + failures.append(f"{name}: cannot classify tiles from resolved layout: {error}") + continue + + tile_rows = TILE_HASH_RE.findall(output) + camera_counts = {index: 0 for index in strata} + for lod_text, _x, _y, z_text, *_hashes in tile_rows: + lod, z = int(lod_text), int(z_text) + if lod < 0 or lod > 30: + failures.append(f"{name}: invalid tile lod={lod}") + continue + extent_voxels = 32 << lod + center_um = (z * extent_voxels + extent_voxels // 2) * voxel_size_um + matches = [index for index, stratum in strata.items() + if int(stratum["interior_bottom_um"]) <= center_um + < int(stratum["interior_top_um"])] + if len(matches) == 1: + index = matches[0] + camera_counts[index] += 1 + aggregate.setdefault(index, {}).setdefault(dispatch[index][0], 0) + aggregate[index][dispatch[index][0]] += 1 + if not tile_rows: + failures.append(f"{name}: --density-dispatch-report emitted no tile hashes") + observed = sum(camera_counts.values()) + print(f"[DensityDispatchView] focus_strate={focus_strate} tiles={len(tile_rows)} " + f"classified_by_center={observed} status={'PASS' if observed else 'FAIL'}", + flush=True) + runner.require(f"{name}:dispatch-and-tile-counts", + observed > 0 and row["status"] == "PASS", + f"dispatch={dispatch} center_attributed_tiles={camera_counts} " + f"hashed_tiles={len(tile_rows)}") + for index, value in dispatch.items(): + previous = bound_paths.get(index) + if previous is not None and previous != value: + failures.append(f"strate {index}: bound path changed between views: " + f"{previous} vs {value}") + bound_paths[index] = value + + global_path_counts: dict[str, int] = {} + for index in range(4): + path, generator, recipe, version, adapter = bound_paths.get( + index, ("MISSING", "?", "?", 0, "?")) + path_counts = aggregate.get(index, {}) + total = sum(path_counts.values()) + for path_name, count in path_counts.items(): + global_path_counts[path_name] = global_path_counts.get(path_name, 0) + count + details = ",".join( + f"{key}:{value}/{total} ({100.0 * value / max(1, total):.1f}%)" + for key, value in sorted(path_counts.items())) or "no tile centers observed" + print(f"[DensityDispatchStrate] strate={index} generator={generator} recipe={recipe} " + f"version={version} bound_path={path} adapter={adapter} " + f"tile_centers={total} path_shares={details}", flush=True) + if path == "MISSING" or total == 0: + failures.append(f"strate {index}: dispatch path or tile-center share missing") + else: + runner.require(f"owner-density-dispatch-strate-{index}", True, + f"bound_path={path} tile_center_path_shares={details}") + all_tiles = sum(global_path_counts.values()) + for path_name, count in sorted(global_path_counts.items()): + share = 100.0 * count / max(1, all_tiles) + print(f"[DensityDispatchPathShare] path={path_name} tiles={count}/{all_tiles} " + f"share={share:.1f}%", flush=True) + if not all_tiles: + failures.append("no tile centers were attributed to any bound path") + runner.require("owner-density-dispatch-report", not failures, + "; ".join(failures) if failures else + "four owner poses reported per-strate bound paths and tile-center path shares") + + def run_landmark_reachability(runner: VerificationRunner, binaries: tuple[Path, ...]) -> None: for binary in binaries: @@ -3144,6 +3258,7 @@ def main() -> int: except VerificationFailure as error: new_plan_failure = str(error) print(f"[{runner.tier}] new-owner-plan-stage FAIL {error}", flush=True) + run_density_dispatch_report(runner, CLANG) run_landmark_reachability(runner, (CLANG,)) run_regression_controls(runner) run_hash_pairs(runner, owners)