Skip to main content

mapv10 Next Work

Last audited: 2026-05-17, during Wave Final-A (M11l) — scenarios + gates + grep guard + audit closures.

This is the prioritized work queue. Pick from the top. Completed or stale items are kept in the "Recently resolved" section so future waves do not re-open old work by accident.

Column meaning:

  • Type - Implement (new code) / Replace (delete + rewrite) / Improve (tune existing) / Fix (bug) / Investigate (read first, decide later) / Decide (needs direction before code)
  • Goal - what "done" looks like, observable from outside
  • Verify - concrete test, scenario, or gate that proves it landed
  • Benefit - what the user / next wave gets after this lands
  • Risk / Depends-on - what could go wrong; prerequisites
  • Wave? - Yes means use wave-protocol.md; Direct means build directly per extending.md

Recently resolved / no longer open

These were in the old queue but the code has moved on.

#AreaStatusEvidenceKeep / watch
R-1Regional / realm skipped-public-LOD generator panicDonetile_pyramid.rs now supports hidden repeated 2:1 cascades when public LOD bands skip from sample step 1 -> 4 or 1 -> 8. Regional and realm generation completed cleanly in the latest check.Do not reopen T0-1. Old text used stale CLI names (--preset, realm-draft); actual CLI is --scale-preset regional-slice / --scale-preset realm-slice.
R-2Iterative 2:1 mip-pyramidDoneTerrain and border-SDF pyramids now cascade internally through repeated 2:1 downsample steps.Optional future work: add more schema coverage assertions, but it is not an open architecture issue.
R-3Channel-appropriate downsamplingDoneContinuous channels use mean-style filters; discrete ID/class channels use mode-style filters. Border-SDF R is continuous, GBA nearest-id channels are discrete.Keep tests around filter dispatch.
R-4Borders checkbox shader hookCode done, scenario missingBorders now route through uBorderOpacity / setBorderSdfEnabled; the UI layer state gates SDF borders.T2-1 below still adds explicit visual scenarios.
R-5Browser verifier HTTP + default manifestDoneverify-browser.mjs and heap-stability-check.mjs both use the plain-HTTP dev server and the canonical /mapv10/runs/continent-lod6/manifest.json fixture.Keep browser-driving tools on the same canonical fixture path; --run-root is ad-hoc only.
R-6Political LUT 1xN texture overflowDone before this auditCurrent code uses square-packed 2D LUT/selection textures and validates against MAX_TEXTURE_SIZE.Keep SamplerProbe gates.
R-7Visual exaggeration config plumbing (was T0-2)Config layer done; visual goal moved to lighting clusterviewer/src/config/TerrainVisualConfig.ts exports typed config + validator + factory; current defaults include exaggeration, sun azimuth/elevation, ambient/diffuse relief strength, relief contrast, and altitude tint thresholds. Setter plumbing keeps decoder-baked displacement and world-to-scene projection in sync.Do not reopen R-7 for visual quality. R-14 now owns the first lighting fix; remaining terrain readability work is T0-2B/T0-2C/T0-2E.
R-8Source-cell Voronoi tessellation (was T0-3)Donepolitical.rs now generates deterministic blue-noise seeds and half-plane-clipped Voronoi-style province/location cells; raster ownership and neighbor edges are derived from those polygons; generator tests prove determinism, coverage, and non-axis-aligned province boundaries.Keep visual validation against graph-paper regressions. Future density policy can be explicit, but row/column subdivision is gone.
R-9Synchronous pick readback removal (was T1-2)DonePointer hover now raycasts active terrain meshes and samples generated semantic locationId rasters on CPU; readRenderTargetPixels, pick-id FBO allocation, and the pick-id shader mode were deleted. The browser verifier also instruments WebGL/canvas readback APIs and records totalCalls: 0 in m10-lod6-continuity-proof.json.Add an interaction hover-sweep scenario when scenario CLI work lands, but do not reintroduce GPU readback for picking.
R-10Canonical fixture and border-SDF contract driftDonearchitecture.md, roadmap.md, schema/README.md, mesh-product.schema.json, and the generator stage description now point at the canonical viewer/public/continent-lod6 proof and the current border-SDF terrain-shader path.Keep T3-4 fixture distribution open; fresh clones still need an intentional way to obtain or build continent-lod6.
R-11Scale ruler + camera HUD (was T0-2D)DoneScaleRulerConfig, the shell HUD, renderer terrain/sea-plane raycast probe, and scale-ruler scenarios now provide snapped metre/km labels plus camera/terrain readouts. Browser proof covers the canonical fixture, and scenario evidence exercises continent/realm/location ruler bands without fake altitude retunes.Keep the HUD probe in future scenario/baseline automation; scale chrome is no longer the next open work item.
R-12Strategic route graph generation (was T1-4)Doneroutes.rs now builds route candidates from generated Location adjacency, province hubs, height/slope cost, lake/river penalties, and river intersections instead of input-order chains. The regenerated continent-lod6 fixture has 11,000 route nodes, 14,300 edges/centerlines, 56 realm-visible strategic roads, 119 crossings, and 0 orphan nodes; 07-routes validation passes connected/no-orphan/one-centerline checks.Route visual density still belongs to T1-3 LOD visual configuration; route foundation is no longer fake.
R-13Robust water polygon triangulation (was T2-5)Donemeshes.rs now builds lake surfaces through earcutr from an outer ring plus optional hole rings. LakePolygon serializes holes, generated lakes emit holes: [], validation checks lake hole rings, and unit fixtures cover concave and holed lake meshes by area/centroid/index validity. The regenerated continent-lod6 fixture writes mapv10-lake-polygons-v2, has 2 lake meshes with 28 outline vertices and 26 triangles each, and browser proof fetches all 7 water assets.Future lake generation can become concave or holed without re-opening the old triangle-fan foundation.
R-14Directional terrain relief lighting (was T0-2A)DoneThe unified terrain shader now computes relief from decoder-computed terrain normals using a world-space vTerrainWorldNormal varying and TerrainVisualConfig sun direction. __mapv10TerrainShadingProbe exposes { hasDirectionalLight, lightDirectionWorld, normalsAreComputed }; browser proof asserts the probe and records normalized light [-0.591, 0.469, 0.656], active terrain normals, relief uniforms, and world-normal shader use.Continue the visual cluster with T0-2B/T0-2C. Lighting is no longer the blocker, but altitude color and atmospheric depth are still needed for Google-Earth-style readability.
R-15LOD visual configuration for routes, borders, labels (was T1-3)DoneRoute ribbon generation now uses explicit per-LOD width bands, so z4/z5 connectivity no longer inherits overview-corridor widths. The viewer has LodVisualConfig for route opacity/color, border SDF opacity, and label budgets by camera band/mode; debug state and npm run scenarios report the active band. Close-up proof: verification/scenarios/lod-visual-config/ shows geography z5 at routeOpacity=0.04, borderOpacity=0.18, labels capped at 42; routes z5 at routeOpacity=0.46, borderOpacity=0.14, labels capped at 28.Keep future visual tuning inside typed config or generated mesh policy. Do not reintroduce scattered route/border/label constants.
R-16Elevation tint + aerial perspective (was T0-2B/T0-2C)DoneTerrainVisualConfig now owns snow/rock altitude thresholds plus configurable aerial perspective color/start/density/max-opacity. The unified terrain shader blends geography/routes highlands toward rock/snow from vTerrainSceneY and blends terrain toward haze from camera-fragment distance using vViewPosition. Terrain uses this explicit shader haze instead of generic Three terrain fog; __mapv10TerrainShadingProbe and npm run scenarios report the active haze settings. Proof: verification/scenarios/aerial-perspective/ passes continent oblique, low-horizon, close overlay, and clean close scenarios with terrainMaterialUsesAerialPerspective=true, no page/probe errors, and close z5 p95 at 11.9-19.2ms.The next terrain-quality blocker is close-detail micro-relief and material variety, not missing atmosphere. Tune haze only through TerrainVisualConfig plus scenario evidence.
R-17Close-detail normal-map micro-relief (was T0-2E)DoneThe unified terrain shader now samples a shared tileable RGBA normal texture by world-space UV, perturbs the mesh-normal lighting term at close range, fades the effect out by camera distance, and gates companion material grain through TerrainVisualConfig. SamplerProbe now records the 10-sampler terrain architecture; __mapv10TerrainShadingProbe and npm run scenarios report active micro-relief strength/scale/fade. Proof: verification/scenarios/micro-relief/ covers continent oblique, low-horizon oblique, z5 overlay, z5 clean highland, and a new z5 clean lowland target around 997 m; all passed with terrainMaterialUsesMicroReliefNormalMap=true and close z5 p95 at 8.2-18.7ms. Unit proof adds a per-pixel lateral-normal variance assertion on the generated normal texture.The lighting/readability cluster is no longer the next blocker. Remaining highland palette/snowline tuning must stay config-driven and scenario-backed; do not replace micro-relief with geometry displacement hacks.
R-18Zoom-trace measurement foundation (first T2-7 slice)DoneMapv10ThreeRenderer now records Mapv10ZoomTraceReport samples with frame work, camera distance/altitude/zoom band, terrain/runtime residency, scheduler pending/in-flight work, cache hit/miss/eviction deltas, route/label counts, frame-budget pressure, and long-task markers. window.__mapv10StartZoomTrace / __mapv10StopZoomTrace expose manual tracing, and npm run scenarios writes *-zoom-trace.json for scenarios with zoomTrace. Proof: verification/scenarios/zoom-trace/ covers continent->realm->province->location clean lowland and realm->province->location overlay zooms; the overlay trace caught route-family pending at 577/601 total pending with max frame 110.1ms.Performance work can now be evidence-driven. Next T2-7 work is choosing explicit budgets and fixing measured breaches, especially route/label rebuild and streaming spikes during close overlay zooms.
R-19First zoom performance budget + route-streaming fixDonemapv10_zoom_continent_to_location_clean_lowland and mapv10_zoom_realm_to_location_overlays_z5 now carry enforced performanceBudget.zoomTrace thresholds. npm run scenarios records performanceBudgetIssues and exits non-zero on budget breach. Route auxiliary streaming is capped to 64 missing route assets per frame while retaining loaded/pending routes, and route batches are sharded by route tile instead of one giant route material batch. Proof: verification/scenarios/performance-budget/ passed both zoom scenarios; overlay route pending dropped from the R-18 spike of 601 to 144, p95 was 14.7ms, max was 64.9ms, cache evictions stayed 0, and final z5 routes/labels were present.The route fan-out cliff is no longer the first performance target. Remaining isolated long frames are now label/vector lifecycle and general commit spikes, not route request flooding.
R-20Lifecycle/hitch metrics + warn/fail budget schema (was T2-7 PR 1)DoneMapv10ZoomTraceSample now records render calls/triangles, route batch/draw/upload/build proxies, mesh/material/texture create-dispose counters, label create/remove/churn/resident/visible/fading counters, and fallback/omitted-slot counters. performanceBudget.zoomTrace accepts legacy flat hard-fail budgets or nested { fail, warn }; npm run scenarios writes performanceBudgetWarnings without failing CI. Proof: viewer/verification/scenarios/lifecycle-hitch-metrics/ passed both canonical zoom scenarios with 0 hard failures and 17 warnings. The warnings named the next owners: clean labels-off still peaked at 816 label/material creates and 916 texture creates; overlay zoom peaked at 672 label churn, 638 label removals, 8.06 MB route upload proxy, and 38.3 ms route build.Next work is T2-7b label/vector frame-budgeted lifecycle; route adaptive batching remains measured but should follow label churn unless scenario evidence changes.
R-21Label lifecycle candidate gating + frame-budgeted residency (was T2-7b PR 2)DoneLabels now run through candidate selection before resource allocation: layer state, zoom band, priority, projection, and collision estimates are evaluated before any sprite/material/texture is created. Selected labels are queued through the label frame-budget lane, and faded labels retire through the same capped lane. Markers no longer allocate while the markers layer is off. Proof: viewer/verification/scenarios/label-lifecycle-budget/ passed the two canonical zoom scenarios. Clean labels-off improved from R-20 max 93.6ms / 816 label creates / 916 texture creates to max 12.1ms / 0 label creates / 0 slow frames. Overlay zoom improved from R-20 max 119.9ms / 672 label churn to max 23.3ms / 16 label churn / 0 slow frames.Keep label caps as UX controls, not perf crutches. Remaining warnings are route upload/build proxies, so route adaptive batching is now measurable but not blocking parent fallback unless it regresses.
R-22Strict parent fallback contract (was T1-2 PR 3)DoneRenderResolver now emits a per-primary fallback ledger with direct, resident-ancestor, previous-rendered, and omitted sources; Mapv10ThreeRenderer records resolver-owned fallback metrics in zoomTrace instead of inferring them after render. Resolver tests cover pending/failed/canceled child absence, partial child residency, previous-rendered fallback, omitted-slot accounting, fallback-duration increment/reset, and transient empty commits preserving previous metadata. Planner tests cover predicted preload requests not counting as current residency gaps. Proof: viewer/verification/scenarios/parent-fallback-contract/ passed clean zoom, slow-network zoom, and overlay zoom. Slow-network recorded fallbackSlotCountMax=20, omittedSlotCountMax=0, fallbackDurationFramesMax=3, no page errors. Overlay stayed under frame gates with fallbackDurationFramesMax=13; only route upload/build warnings remain.Parent fallback is now strict enough for T1-3 geometric-error SSE. Keep omittedSlotCountMax=0 as a hard gate on canonical zoom scenarios.
R-23Terrain geometric-error SSE upgrade (was T1-3 PR 4)DoneVisibilitySet now chooses terrain LOD from measured geometricError using (geometricError * viewportHeight) / (2 * tan(fov/2) * distance) and keeps ssePixelsByKey as true terrain-error telemetry. Tile manifests/types/schemas now carry tileId, parentId, childIds, 3D bounds, measured generator geometricError, and cost hints; the loader normalizes older fixtures into the stricter runtime contract. __mapv10TerrainSelectionProbe and scenario results expose the selector state. Proof: viewer npm test passed 212 tests, npm run build passed, generator cargo check passed, cargo test -q stages::tile_pyramid passed, and npm run scenarios passed all 23 canonical scenarios with 0 hard budget failures.SSE made close/low-angle scenarios select finer terrain intentionally. Watch the 12 warn-only budget owners from the proof: terrain mesh/texture create-dispose spikes, fallback-slot peak 28 > 24, and route upload/build proxies in the overlay zoom. These are follow-up pacing/baseline-ratchet work, not correctness blockers.
R-24SSE terrain lifecycle pacing + baseline ratchet (was T2-9)DoneTerrain render commits are now staged across frames, old active terrain remains visible while the new commit fills in, terrain texture creation is capped per frame, and retired terrain node disposal is capped per frame. The clean zoom fallback warn baseline was ratcheted from 24 to 32 because R-23's strict resolver intentionally records a stable fallbackSlotCountMax=28 with omittedSlotCountMax=0. Proof: viewer npm test passed 212 tests, npm run build passed, and npm run scenarios passed all 23 canonical scenarios with 0 hard failures and only 2 route warnings. Clean zoom now records frameMaxMs=10.4, meshCreate=48, meshDispose=38, textureCreate=96, textureDispose=95, fallbackSlot=28, omitted=0. Slow-network records frameMaxMs=9.3, same lifecycle caps, fallbackSlot=28, omitted=0. Overlay records frameMaxMs=21.8; only routeUploadBytesProxyMax=8060160 and routeBuildMsMax=12.2 warn.Terrain lifecycle is no longer the zoom owner. Next renderer work should target route upload/build pacing; Valenar export remains a deferred product lane until the user explicitly asks for runtime import packaging.
R-25Route adaptive batching + upload pacing (was T2-8)DoneRoute mesh decode no longer emits normals for unlit MeshBasicMaterial ribbons, one-member auxiliary batches draw their source geometry directly instead of copying through a merge buffer, oversized intermediate route aggregates (route.aggregate.z2/z3) normalize to the coarse z1 fallback when available, and never-drawn stale route batch work is canceled instead of filling the frame-budget queue. Planner tests cover aggregate fallback and missing-fallback behavior. Proof: viewer npm test passed 214 tests, npm run build passed, and npm run scenarios passed all 23 canonical scenarios with 0 hard failures and 0 warnings. Overlay zoom now records frameMaxMs=20.5, maxFrameBudgetPending=169, routeUploadBytesProxyMax=67584, routeBuildMsMax=4.6, routeBatchCountMax=24, and final z5 routes/labels present.The canonical renderer hardening chain is no longer warning on terrain lifecycle, label churn, fallback, or route upload/build. Next work can move to the product lane and process/tooling gates unless new scenario evidence opens a fresh owner.
R-26Generated artifact server boundaryDoneThe viewer now exposes generated runs under /mapv10/runs/<run-id>/... through viewer/server/mapv10ArtifactMiddleware.ts. The default manifest, browser verifier, heap check, scenario runner, and slow-network URL filters use /mapv10/runs/continent-lod6/manifest.json. Missing runs/artifacts return typed application/problem+json; binary artifacts support byte ranges; legacy /continent-lod6/... paths are intercepted so they cannot fall through to the Vite app shell.Keep Vite responsible for the app shell and the artifact middleware responsible for generated run files. Do not add gameplay REST endpoints for immutable map products.
R-27Browser screenshot baselines (was T3-2)Doneverify-browser.mjs now supports --update-baseline and --check-baseline for the canonical full browser proof. The baseline covers overview, slow zoom, constant-distance camera move, all current MapModeId values, close/detail, boundary, zoom-back, and required-failure UX screenshots; check mode reports changed-pixel ratio, RGB RMS, max channel delta, and diff PNGs. viewer exposes npm run verify:browser:baseline:update and npm run verify:browser:baseline; local baseline PNGs are ignored until LFS/artifact storage is chosen.T3-3 can now wire the visual baseline gate into CI or a clean-fixture harness. Do not update baselines to hide a regression; update only after visual acceptance.
R-28Scenario CLI wrapper (was T2-6)Doneviewer/package.json exposes npm run scenarios, backed by viewer/scripts/run-scenarios.mjs. It drives all scenario IDs from viewer/src/scenarios/mapv10_scenarios.json, writes screenshots and scenario-results.json, records load/frame/cache/LOD probes, and exits non-zero on hard budget issues.Keep scenario poses in mapv10_scenarios.json; do not duplicate them in separate verifier scripts.
R-29Source-of-truth docs reconciliation (was T3-1)DoneThe portable packet now states source-of-truth boundaries, validation commands, current scenario warning status, implemented M0-M10 status, deferrals, and next queue; governance mirrors and bundle guidance point at the same packet and canonical manifest URL.Future feature waves should reconcile docs first when source/doc drift appears; hidden source behavior is not architecture.
R-30LOD/Data Coherence Foundation chain (Waves 1-4.5 + MZ foundation)DoneWave 1 installed scenario gates that hard-FAIL on master before fixes landed — gate vocabulary now lists forbidZ0UnderlayAtPrimaryZ, ancestorZDistanceMax, sidecarReadyForActiveMeshes, noPlaceholderTextureBound, coverageHoleMax, labelTextUniqueness, labelTextMinDistinctCount, labelMaxDuplicatesForText (viewer/src/scenarios/scenarioTypes.ts, evaluated by viewer/scripts/scenario-gates.mjs). Wave 2 redesigned the resolver+sidecar contract after 3+ patches failed: MAX_ANCESTOR_Z_DISTANCE = 1 plus structural-parent walk plus isFullyResident AND-gate of CPU+GPU residency replaced bounds-containment-alone ancestor promotion and silent fallback (viewer/src/renderer/lod/RenderResolver.ts). Wave 3a split SSE telemetry by role × metric: primary/underlay × geometric/raster four-way budget (docs/performance-and-streaming-hardening-spec.md §7.3). MZ implements the accepted z6/z7 physical LOD decision: z0 is now a true coarse rung, z1 is repaired as macro-region, z6/z7 are generated physical residency levels, closeDetailNormal supplies generated close-detail truth, and semanticDisplayPolicy separates strategic semantic zoom from physical lodBand. Wave 4 replaced the 120-name location_name pool with a per-biome procedural namer (see docs/generator.md § "Naming"). Wave 4.5 completed the *Stats → *Channels rename (engine + benchmarks + tests). Latest committed scenario evidence is incomplete: viewer/verification/scenarios/latest/scenario-results.json declares 27 scenarios but stores 25 entries, missing mapv10_location_height_z5 and mapv10_location_label_uniqueness_z5; 24 stored entries have no hard issues, and the slow-network stored entry carries the old blocking omitted-slot issue with actual value 24 under remote RTT throttle.Do not reopen these as new tier-0/tier-1 items. Follow-up work is full scenario evidence regeneration and direct polish; do not refresh visual baselines until foundation behavior is accepted.
R-31Slow-network strict-fallback contract under throttleDecision recordeddocs/local-only-product-premise.md records the accepted policy: mapv10 is a local-only generated product, so remote/network-throttled evidence is future-resilience / cold-cache stress evidence, not a blocking product gate by itself. mapv10_continent_to_location_slow_network is retained, but the throttled omittedSlotCountMax check moves out of performanceBudget.zoomTrace.fail and into non-blocking warning evidence. Clean/local scenarios that model shipped local filesystem/decode/upload latency still own blocking omitted-slot regressions. Do not widen RenderResolver.MAX_ANCESTOR_Z_DISTANCE, raise the raster SSE target, or treat this as permission to hide real local-cache holes.Regenerate full scenario evidence later; do not fabricate missing results or update visual baselines in this process wave.

Tier 0 - Blocks normal user-visible quality

These are the issues that still make mapv10 look or feel wrong as a strategic map.

#AreaTypeWhatWhyGoalVerifyBenefitRisk / Depends-onWave?
M11f-politicalWave T M11f political.rs truth-product ingestImplementResume the Wave T M11f rewrite of examples/map/mapv10/generator/src/stages/political.rs so realm/province/Location classification ingests basin_id / valley_id / orogen_segment_id membership from the Stage-2 terrain-genesis truth products. The wave was paused mid-implementation on master; political-naming and political-rasterization currently still derive from heightfield + Voronoi cells alone.The M11 invariants require Stage-6 political polygons to incorporate genesis truth (basins, valleys, orogen segments) per ADR §M11f. Without this, downstream Stage-4b hydrology-location associations and Stage-7 routes consume incomplete classification. Blocks every M11 wave that depends on political truth (M11h map-features, the world-lab compare-runs metrics, the recipe-satisfaction scenarios).examples/map/mapv10/generator/src/stages/political.rs consumes Stage-2 truth products as inputs; emitted realm/province/location records carry basin_id / valley_id / orogen_segment_id fields populated from the genesis raster ownership oracle.Run cargo test --release in the generator; run a full continent generation against preset-south-america-like and confirm the manifest carries Stage-6 records with non-null genesis-truth membership fields.Unblocks Wave T-M11h, Wave D, and Wave Final-B.Significant Rust rewrite; depends on stable Stage-2 truth products (landed).Yes

Tier 1 - Architecture waves

Cross-cutting changes that should use the full wave protocol.

#AreaTypeWhatWhyGoalVerifyBenefitRisk / Depends-onWave?
T1-1State-field producer transportImplementBuild the runtime state-field producer that writes per-tick scalar fields into the existing world-space RT. The shader contract is already corrected: uStateField samples continent/world UV derived from per-fragment world km and uWorldBoundsKm, not tile-local vUv.The RT, uniform binding, world-bounds uniform, and shader sampling contract exist; the missing piece is the cross-process producer/transport that fills the RT with live strategic fields.Server publishes ticks; viewer updates uStateField at >= 1 Hz; overlays line up across tile boundaries and are governed by alpha.Add a mocked gradient/checker state overlay scenario that crosses tile boundaries; fail if it repeats per tile. Add __mapv10Ready.stateField.connected = true once transport is live.Turns mapv10 from a terrain viewer into a strategic-simulation surface for weather, corruption, economy, war intensity, etc.Cross-process protocol design, backpressure, update cadence, and producer lifecycle.Yes
M11hWave T M11h map_features.rs directive-driven anchorsReplaceReplace the six hardcoded map-feature anchors in examples/map/mapv10/generator/src/stages/map_features.rs (Westwatch Harbor, Crown Pass, Crown Ford, Mirrorfen, Greenfold Woods, Westmere Shore) with anchors derived from World Intent Directives. Also delete the closed_ellipse(...) footprints and the map_features.rs:57 panic on zero crossings. TODO(M11h).The M11 invariants (ADR §M11 invariant 2 + invariant 5) require named map features to land via World Intent Directives, not hardcoded literals. Until this lands, viewer/scripts/forbidden-pattern-check.mjs reports 13 forbidden tokens against stages/map_features.rs — expected-FAIL state by design until this row resolves. The scenario mapv10_no_hardcoded_anchors_in_continent_run fails for the same reason.examples/map/mapv10/generator/src/stages/map_features.rs carries zero literal feature-name strings and zero closed_ellipse( calls; named features are constructed from world-intent-directives.schema.json customAnchor entries.npm run check:forbidden-patterns from viewer/ exits 0; mapv10_no_hardcoded_anchors_in_continent_run scenario passes against a regenerated continent fixture; generator cargo test --release clean.Closes the last M11-redesign hard regression; unblocks Wave Final-B baseline refresh.Depends on Wave T-M11f political.rs ingest landing so directive resolution against political polygons is unambiguous.Yes
M11iWave D M11i viewer-side typed loaders for new raster + hydrology meshesImplementAdd typed loaders + manifestLoader.ts integration for terrainGenesis.json, hydrology.json, and the M11c/d new raster channels (uplift.f32.bin, crustAge.f32.bin, bedrockHardness.f32.bin, flowAccumulationD8.f32.bin, streamOrder.u8.bin, etc.). Fail-hard on missing required products per the no-fallback doctrine.The Wave G1+G2 truth products are written by the generator but the viewer has no typed loader for them; viewer/src/data/types.ts and viewer/src/data/manifestLoader.ts still operate on the pre-M11 channel set. Without this, the new gate variants in the Wave Final-A scenarios can fetch the products via page.evaluate(fetch) but cannot bind them into the renderer's typed product registry.viewer/src/data/manifestLoader.ts loads the new products into typed records; missing required products throw a typed error visible in __mapv10Ready.errors[].npm run typecheck clean; new Vitest case in viewer/src/data/__tests__/ proves the loader reads a sample terrainGenesis.json end-to-end.Unblocks the M11k-inspect overlay inspector and Wave Final-B scenario population.Schema bumps in the manifest contract; deferred until Wave T M11h + M11f are stable.Yes
M11k-inspectWave P-M11k-inspect 16-channel overlay inspectorImplementWire the deferred 16-channel overlay inspector planned in Wave P's brief: per-channel typed loaders, renderer overlay API, "Inspect" tab populated by every loaded raster channel with histogram + colour-ramp + lookup-by-cell readout. TODO(M11k-inspect).The Wave P (M11k-edit) shell explicitly shipped the "Inspect" tab as a TODO(M11k-inspect) placeholder per docs/ad-mapv10-m11-world-genesis-source-of-truth.md § "M11k-inspect (deferred follow-up)".The world-lab "Inspect" tab exposes every loaded raster channel with the documented readout.Manual UX verification in the dev server; targeted Vitest unit test for the histogram + lookup path.Completes the Wave P UX surface; unblocks recipe-author workflow on close-detail debugging.Depends on M11i typed loaders landing first.Yes
M11mWave M11m hydrology-locations-v2 schema bumpImplementAdd the additional join-relevant fields (mountainPassIds, orogenIds, catchmentId, waterBodyId) to hydrology-locations.schema.json per docs/generator.md § "Stage 4b — hydrology-locations (M11h)". Bump to hydrology-locations-v2.Adding these fields under v1 would silently expand the additionalProperties: false contract; forbidden per the schema closure rule. The deferral is documented in docs/generator.md § "Stage 4b — Deferrals".New schema file hydrology-locations-v2.schema.json committed alongside v1; Stage 4b emits the new fields; validator covers them.npm run validate:recipes from viewer/ passes against the new schema; generator cargo test --release clean.Surfaces additional Location-level join truth for downstream Stage 7 routes and Stage 8 map-features.Schema bump requires coordinated viewer + generator change.Yes
M11l-Final-BWave Final-B baseline + capture refreshImplementRefresh viewer/baselines/browser/continent-lod6/ and tools/llm-bundles/captures/ against a real M11 run that exercises the new recipe surface end to end, AND regenerate viewer/public/continent-lod6/ and viewer/public/sample-run/ against the M11x five-family mesh manifest + seven conditioning rasters. TODO(Final-B).Wave Final-A ships the new scenarios in pre-implement FAIL state per the wave-protocol fail-first pattern. A green baseline requires the generator to produce real recipeRef.contentHash / recipeSatisfaction.directives[] / terrainGenesis.json / hydrology.json artifacts plus the M11x mesh-family + conditioning surface, which depends on Wave T-M11f + M11h + M11x-W3..W7 landing first.A regenerated continent-lod6 fixture exists on disk; npm run scenarios runs against it with all new scenarios green; baseline screenshots refreshed under explicit user acceptance.Run npm run fixture:continent --prefix examples/map/mapv10/viewer to regenerate the fixture; npm run scenarios clean; sbundle capture mapv10 <scenario> refreshed for each new scenario.Closes Milestone 11; consolidates the prior M11x-W7 (fixture regen) and M11x-W9 (baseline refresh) rows.Depends on the Tier-0 political.rs + M11h + M11i + M11x-W3..W7 rows above.Yes
M11x-W3Wave M11x-W3 terrain land/seafloor/shoreline-transition emitter rewriteReplaceRewrite examples/map/mapv10/generator/src/stages/meshes.rs::terrain_mesh_for_tile to clip the tile-local continent polygon (lake interiors removed via hydrology.json composition) into a terrain-land asset, the tile-local sea polygon into a terrain-seafloor asset, and the touching coast/lake BoundaryId segments into a shoreline-transition seam ribbon. Delete terrain_mesh_emits_full_seabed_for_mixed_coastline_tiles and replace with tests asserting the clipped behavior. Choose the clipping algorithm (Sutherland-Hodgman / Greiner-Hormann / Vatti) and the triangulation algorithm (earcutr or Shewchuk CDT) per the AD-MAPV10-M11X §Wave 3 implementation hooks.The Wave 2 contract makes the full-rectangular-grid coastal mesh impossible to express in schema/mesh-product.schema.json and meshes.rs::contract(). The emitter is now out of sync with the schema and the contract until this row lands.terrain_mesh_for_tile is deleted or rewritten; new seafloor_mesh_for_tile and shoreline_transition_mesh_for_tile writers exist; mesh manifests carry terrain-land, terrain-seafloor, shoreline-transition families; each emitted mesh asset declares boundaryIds[].cargo test --release clean; new tests assert clipped-not-full-grid behavior; the deleted bbox-grid test no longer exists; generated continent-lod6 has three terrain-family manifests; manifest validates against mesh-product.schema.json v2.The bbox-grid coast tile is eliminated; mixed coast tiles render with true coastline-conforming geometry.Depends on Wave 2 contract (landed). Algorithm choice is Wave 3's decision; the supported AAA references are listed in the ADR.Yes
M11x-W4Wave M11x-W4 coastline-geometry validatorsImplementShip the validators named in docs/ad-mapv10-m11x-coast-geometry-contract.md §Wave 4 — coast_mesh_terrain_land_clipped_to_continent_polygon, coast_mesh_terrain_seafloor_clipped_to_sea_polygon, coast_mesh_no_triangle_straddles_boundary_id, coast_mesh_shoreline_transition_owns_every_coast_segment, coast_mesh_shoreline_transition_owns_every_lake_segment, coast_mesh_boundary_ids_referenced_by_exactly_two_families, coast_mesh_terrain_land_excludes_lake_interiors, coastlines_json_boundary_edges_match_segment_count, coast_mesh_no_full_rectangular_grid_for_mixed_coast_tile. Add viewer/scripts/scenario-gates.mjs variants matching each validator.Validators turn the contract into runtime evidence; without them, the contract drifts at the next emitter change.Generator validation.rs emits the new rules; viewer scenarios gate against them; npm run scenarios exits non-zero on violation.cargo test --release; npm run scenarios from viewer/.Hardens the M11x contract.Depends on M11x-W3 emitter rewrite.Yes
M11x-W5Wave M11x-W5 hydrology conditioning contractImplementCut the architectural seam between Stage 4 hydrology truth and downstream terrain-conditioning consumers so river incision, riverbed elevation, channel depth, water-surface elevation, and bank masks become first-class generated truth products instead of ribbon-on-raw-height visual decoration. New sub-stage examples/map/mapv10/generator/src/stages/terrain_conditioning.rs with generate(&GeneratorConfig, &ContinentPolygons, &HeightfieldProducts, &WaterProducts) -> ConditioningProducts (all borrows immutable; &HeightfieldProducts is NOT mutated). New schema examples/map/mapv10/schema/terrain-conditioning.schema.json declaring conditionedHeight, conditionedSlope, conditionedNormal, riverbedElevation, channelDepth, waterSurfaceElevation, bankMask as required products. ADR examples/map/mapv10/docs/ad-mapv10-m11x-hydrology-conditioning-contract.md. pipeline.rs registers Stage 4c with id: 4, key: "c-terrain-conditioning" between Stage 4 hydrology and Stage 5 biomes. validation.rs ships validate_terrain_conditioning stub asserting product length + presence. Wave 5 body is a stub returning zero-allocated products — Wave 6 fills them in; the products are required (schema fails otherwise), not optional.The Wave 5 explorer A4 audit found that mapv10 has no contract for conditioning the heightfield AFTER hydrology truth lands. Stage 12 emits river ribbons at 0.000_22 km above height[row * width + col], which is the raw post-erosion height — not an incised channel. The viewer paints rivers as 5cm-thick textured strips floating above terrain. The fix is contractual: a schema that still permits ribbon-only rivers as the canonical product satisfies the letter of the tenet but not the spirit. Without this row the no-fallback doctrine has no surface to bind river incision to.Stage 4c module exists; ConditioningProducts struct has all 7 fields; new schema parses and lists 7 products as required; pipeline registers Stage 4c between Stage 4 and Stage 5; cargo check clean; cargo fmt --check clean; ADR ships with required sections; roadmap + next-work updated with collision resolution; .claude/rules/mapv10.md carries the no-ribbon-only-rivers invariant.cargo check --manifest-path examples/map/mapv10/generator/Cargo.toml; cargo fmt --check --manifest-path examples/map/mapv10/generator/Cargo.toml; cargo test --manifest-path examples/map/mapv10/generator/Cargo.toml --no-fail-fast; schema validates against Draft 2020-12.Establishes the architectural seam for Wave 6 channel incision. Makes ribbon-only rivers structurally insufficient: any "river" without matching channel-depth / riverbed-elevation / water-surface products is not the canonical river truth.Depends on Wave 2 contract (landed). Wave 6 owns the actual incision algorithm.Yes
M11x-W6Wave M11x-W6 hydrology incision implementationImplementFill in terrain_conditioning::generate() with the real algorithm: priority-flood-conditioned heightfield (re-using WaterProducts.conditioned_dem) carved by Howard 1994 stream-power incision (dz/dt = -K * A^m * S^n) at every cell with streamOrder > 0. Compute channelDepth from Leopold-Maddock 1953 hydraulic geometry depth = c * Q^f calibrated against drainage_area_km2 and average_width_km. riverbedElevation = conditioned_height - channelDepth. waterSurfaceElevation = riverbedElevation + waterColumnDepth where the water column reuses HeightfieldProducts.flow_accumulation Mei 2007 truth. Recompute conditionedSlope (Horn 1981) and conditionedNormal (centred-difference) from the incised height. bankMask is a 2-cell-radius band immediately adjacent to active channel cells (distinct from floodplain_mask's wider band). Thread ConditioningProducts into Stage 5 biomes-materials, Stage 11 tile-pyramid, and Stage 12 meshes consumers. Extend validate_terrain_conditioning to assert channelDepth[i] > 0 where streamOrder[i] > 0; riverbedElevation[i] <= waterSurfaceElevation[i] everywhere; conditionedHeight[i] >= riverbedElevation[i] everywhere; no NaN/infinite values.Wave 5 ships the contract with stub products. Without this row, the contract is toothless; Stage 5 biomes and Stage 12 meshes still consume raw HeightfieldProducts.height and the river ribbons keep floating above raw terrain.Conditioning products carry real values; ribbon emit in meshes.rs consumes waterSurfaceElevation instead of raw height; biome classification consumes conditionedSlope rather than HeightfieldProducts.slope; tile-pyramid emits the new conditioning rasters per LOD.cargo test --release clean; channel-depth-positive / riverbed-below-surface validators pass on the south-america-like fixture; npm run scenarios shows rivers with visible carved channels in the close-zoom-detail scenarios.River channels appear as carved-in features instead of stripes painted over flat terrain. Closes the M11x foundation hole the Wave 5 explorer flagged.Depends on M11x-W5 (this wave) + M11x-W3 mesh family rewrite (so Stage 12 has the right consumer surface to thread into).Yes
M11x-W7Wave M11x-W7 viewer loader / schema alignmentImplementRetire the terrain mesh family across the viewer. viewer/src/data/types.ts: split LoadedRun.meshes.terrainMeshes into three per-family slots (terrainLandMeshes, terrainSeafloorMeshes, shorelineTransitionMeshes); MeshFamilyManifest.schemaVersion bumps to mapv10-mesh-family-v2; MeshFamilyManifest.family becomes the five-member union; MeshAssetRef.renderPass adds shoreline-transition; MeshTileSlot carries three per-family key vecs; FailedTileRecord adds the optional meshFamily discriminator; new ConditioningChannel union. viewer/src/data/manifestLoader.ts: replace the retired terrainMeshManifest requiredProduct call with three calls; require all seven conditioning rasters; update the validators. viewer/src/data/tileManifestStore.ts::createRuntimeMeshTileManifest emits per-family keys. Renderer: add RenderPasses.shorelineTransition + RendererMaterials.shorelineTransition; the polymorphic TerrainTileCache is fed assets from all three terrain families and namespace stripping at TileCache.ts::refreshBands handles all three prefixes; RuntimeTileCache.LoadedRuntimeTile.raster carries the seven conditioning channels; TextureResidency.SidecarKey and RenderResolver.REQUIRED_SIDECARS cover the seven channels; inspector exposes channelDepth / riverbedElevation / waterSurfaceElevation / bankMask + boundary proximity. viewer/scripts/forbidden-pattern-check.mjs adds two patterns (viewer-side coastline extraction; viewer-side hydrology derivation). Generator-side ripple: MeshTileSlot replaces terrain_mesh_key: String with three per-family Vec<String> keys (generator/src/model.rs); tile_pyramid.rs::generate populates the three vecs and exposes per-family key generators (terrain_land_mesh_key, terrain_seafloor_mesh_key, shoreline_transition_mesh_key); valenar_worlddata.rs::mesh_artifacts() emits terrain_land_mesh_manifest, terrain_seafloor_mesh_manifest, shoreline_transition_mesh_manifest instead of the retired terrain_mesh_manifest; validation.rs mesh-slot-key assertion spans the three vecs. Folds the prior M11x-W7b waterMask cleanup as a single ripple.The Wave 2 contract forbids viewer-side coastline extraction. The Wave 5 contract forbids viewer-side hydrology conditioning. The viewer still referenced the retired terrain family and lacked typed loaders for the new mesh families and conditioning rasters; the generator's MeshTileSlot.terrain_mesh_key and the Valenar artifact map were both stale. Without this row the viewer fails loudly on a regenerated fixture.The viewer accepts the three new mesh families and the seven new conditioning products; missing required families/products fail loudly per the no-fallback doctrine; the generator's MeshTileSlot and mesh_artifacts() table match the new contract; forbidden-pattern-check.mjs carries the two new patterns.npm run typecheck clean from viewer/; npm test from viewer/ (data + renderer unit tests) green; cargo check + cargo test from generator/; cargo fmt --check from generator/.Wires the contract end-to-end in the renderer; closes the stale generator references the Wave 7 explorer flagged.Depends on M11x-W3..W6. The Final wave owns fixture regen + scenario / capture baseline refresh.Yes
Final (M11l-Final-B)Final wave fixture regen + scenarios + capture baselinesImplementRegenerate viewer/public/continent-lod6/ and viewer/public/sample-run/ against the new five-family mesh manifest + seven conditioning rasters; refresh viewer/baselines/browser/continent-lod6/ and tools/llm-bundles/captures/. Consolidates the prior M11x-W7 (fixture regen) and M11x-W9 (baseline refresh) rows into the existing M11l-Final-B baseline wave.Wave 2 / Wave 5 / Wave 6 ship contracts and the algorithm in pre-fixture state; the committed fixtures predate every M11x contract.Fresh fixtures on disk validating against mesh-product.schema.json v2, coastlines.schema.json v2, terrain-conditioning.schema.json v1; npm run scenarios clean; capture baselines refreshed under explicit user acceptance.npm run fixture:continent --prefix examples/map/mapv10/viewer; npm run scenarios; npm run verify:browser:baseline:update; sbundle capture mapv10 <scenario> refreshed for each new scenario.Closes M11x.Depends on M11x-W3..W7 + Final-B political.rs + M11h.Yes
T1-5Valenar WorldData exportImplementFirst slice implemented: generator stage 15-valenar-worlddata writes run-local valenar_world gameplay JSON and valenar_world_mesh manifest products from mapv10 truth, with stable ids, derived Region/Area/Province/Location hierarchy, normalized facts, symmetric neighbors, anchors, content hash, and mesh artifact references. Next slice should package/import the generated files into Valenar content without replacing the dummy fixture by accident.Valenar needs a narrow gameplay import contract, not the whole raw mapv10 artifact tree. The current dummy fixture is useful for fast tests, but production must be generated from the same continent pipeline as the viewer proof.valenar/world-<seed>.json and .mesh.json are generated from a mapv10 run and registered in the run manifest. The remaining product bridge is an explicit Valenar content import/copy lane that runs the C# validators against the generated mapv10 export and preserves traceability back to the source run. Dummy data remains allowed as a fixture, never as hidden fallback.Run cargo test in the generator, run the mapv10 export command, inspect stage 15-valenar-worlddata validation, then dotnet test tests/Valenar.Host.Tests/Valenar.Host.Tests.csproj. Add the next import/package test before treating generated mapv10 WorldData as the runtime default. Real-browser pass on the source map before declaring production export done.Gives Valenar a stable production-world input while keeping mapv10's richer artifacts and viewer pipeline intact. It is now the product lane after the R-20 through R-25 renderer hardening chain went green.Area hierarchy is currently derived deterministically from realm/province clusters during export; decide later whether areas become native mapv10 generator truth. Coordinate-system and hash semantics are locked enough for run-local products, but runtime import packaging still needs an explicit policy.Yes

Tier 2 - Quality / polish

Direct builds with discipline unless the row explicitly grows larger during exploration.

#AreaTypeWhatWhyGoalVerifyBenefitRisk / Depends-onWave?
T2-1Borders checkbox scenariosImplementAdd explicit mapv10_continent_political_borders_off and _on scenarios, ideally same camera/mode with only layers.borders changed.The shader toggle is fixed in code, but the scenario file has no direct on/off visual pair.On screenshot shows SDF borders; off screenshot shows none.Visual diff between the pair; fail if OFF has border darkening or ON has no borders.Locks in the recent border-layer fix.None.Direct
T2-2Label scenarios and threshold tuningImproveReframe the old label item: collision, priority sorting, and min/max zoom bands already exist. Add label-on scenarios per LOD and tune thresholds only through explicit config/data, not ad hoc visibility hacks.Label code is ahead of next-work.md, but it lacks scenario coverage and acceptance criteria.Continent shows realm labels; realm/province zoom shows province labels; location zoom shows locations/features without overlap.Add mapv10_*_labels_on scenarios. Check visibleLabelsByBand in debug state and screenshot overlap.Professional strategic-map readability.Depends on label-anchor quality in the regenerated R-8 organic fixture.Direct
T2-3Geometry validator agent promptImplementAdd a committed mapv10-validate-geometry agent prompt in the active agent surface and list it in wave-protocol.md Part 6's file map. It should use __mapv10TerrainGeometryProbe, oblique scenarios, console checks, and screenshot judgement.The protocol mentions geometry validation through scenario-driven gates, but there is no dedicated geometry validator agent to dispatch.Future terrain/render waves have a structured geometry validator.Grep the active agent surface for mapv10-validate-geometry; run a mock validation prompt from it.Prevents future "mountains exist in code but not on screen" false passes.None.Direct
T2-4Document browser-driving tool ownershipFix / InvestigateThe verifier and heap check now share HTTP and /mapv10/runs/continent-lod6/manifest.json; the remaining work is to document which tool owns scenario visuals, network/failure proof, and heap stability.There are multiple browser-driving tools, and contributors still need an explicit ownership table.extending.md documents which tool to use for scenario visuals, network/failure proof, and heap stability.Run npm run verify:browser:continent; run heap check once; docs include a tool-purpose table.Keeps performance and visual validation responsibilities clear.Fixture bootstrap remains open under T3-4 because continent-lod6 is untracked.Direct

Tier 3 - Process / tooling

Infrastructure that improves future waves and review quality.

#AreaTypeWhatWhyGoalVerifyBenefitRisk / Depends-onWave?
T3-3CI integration of scenariosImplementAdd CI/local headless Playwright scenario runs after the baseline strategy is decided.Scenarios currently run only manually.CI fails on scenario errors, WebGL console errors, missing fixtures, or visual diff failures.Push/branch with intentional regression; CI fails with the right scenario id.Locks visual behavior into the build gate.Headless WebGL can be environment-sensitive; prove locally first.Direct (medium)
T3-7CI integration of check:forbidden-patternsImplementWire npm run check:forbidden-patterns from examples/map/mapv10/viewer/ into the CI pipeline (or a local pre-commit hook) so any reintroduction of the M11 forbidden patterns (closed_ellipse(, bent_centerline, ridge_graph.nodes[N], the six hardcoded feature names, renderer-side new THREE.PlaneGeometry, recipe-token names on renderer config, or world-lab renderer imports) fails the build.Wave Final-A ships the grep guard, but it is only run manually today. The guard's expected-FAIL state on master (until Wave T-M11h ships) means the row should land after M11h so the CI gate is clean.The guard runs on every CI invocation; a forbidden pattern fails the build with a clear scenario-id reference.Push a branch that reintroduces a forbidden token; CI fails with the right pattern id.Permanent regression guard for M11 invariants.Depends on Wave T-M11h closing out the expected-FAIL state.Direct
T3-4Fixture distribution / bootstrapImplementedGenerator bootstrap is the selected fresh-clone path for now. npm run fixture:continent from examples/map/mapv10/viewer regenerates ignored public/continent-lod6 with the release Rust generator, then validates stage 15-valenar-worlddata and prints Valenar export counts, hashes, and byte sizes.The generated fixture is untracked. Fresh clones need one explicit setup path before scenarios or verifier can run.One documented command/script creates the required fixture and validates the run-local Valenar export. Valenar still defaults to world-42; generated exports stay side products until an explicit import/package step selects them.Run npm run fixture:continent, then npm run fixture:continent:validate, npm run verify:browser:continent, or scenario commands.Makes mapv10 reproducible outside one machine without committing the huge fixture.Generator bootstrap can be slow; Git LFS/artifact download remains a later option if regeneration time becomes the bottleneck.Direct
T3-5External checker as formal Step 3.5Decide -> ImplementDecide whether the external Codex-style review is a required wave step. If yes, document it in wave-protocol.md between validation and tenets check.Recent work showed validators can still false-pass. A separate cross-check catches assumptions.Protocol states when Step 3.5 runs, what it checks, and what evidence it must provide.Run one wave dry-run and confirm the new step has clear inputs/outputs.Reduces "agent said PASS but user sees broken map" risk.More process overhead; keep scope tight.Direct
T3-6Sub-agent prefix enforcementDecide -> ImplementOptional: add lint/checking for mapv10 agent prompt names if transcript access is stable.Naming convention exists in protocol, but enforcement is process-only.Misnamed mapv10 dispatches are easy to detect.Run hook/check on known-good and known-bad transcript samples.Easier wave traceability.Depends on transcript format stability.Direct if accepted

Tier 4 - Hygiene

#AreaTypeWhatWhyGoalVerifyBenefitRisk / Depends-onWave?
T4-3Remove unused SSL dependencyFixRemove @vitejs/plugin-basic-ssl from viewer/package.json and lockfile if no longer used.Vite config is plain HTTP. The dependency now implies a dev-server mode that does not exist.Package files contain no unused SSL plugin dependency.npm install/lockfile update, npm run typecheck, npm test, npm run build.Less dependency drift and less confusion.None.Direct
T4-5Memory rules consolidationImproveOptional: consolidate stale project-memory entries outside the repo if they are slowing future sessions.Old mapv5/mapv6/mapv8 memories can distract from current mapv10 architecture.Active rules are lean; historical notes are archived rather than deleted.Manual review of memory index before/after.Lower context noise.Outside repo; only do if user wants process cleanup.Direct
T4-6Stale sample-run fixture cleanupDecideThe on-disk fixture viewer/public/sample-run/labelAnchors.json and viewer/public/sample-run/locationPolygons.json still contain pre-Wave-4 "Cairnfield"-era labels from the deleted legacy 120-pool location_name function (superseded by the per-biome procedural namer in docs/generator.md § "Naming"). Commit a644e88 carried the message "drop sample-run" but left the directory on disk, and the fixture is still referenced by viewer/src/ui/__tests__/manifestUrl.test.ts (the manifest-URL test fixture path), viewer/server/mapv10ArtifactMiddleware.ts (legacy-path interception), and viewer/scripts/verify-browser.mjs (alternate fixture lookup). See docs/local-only-product-premise.md — the fixture's historical justification was partly "simulate a slow load," which is weakened once the local-only product premise is acknowledged; this affects how strong option (b) "remove entirely" looks vs option (c) "keep as documented historical."The fixture is inconsistent with the Wave 4 namer and the commit message implies it should already be gone, so its on-disk state diverges from doc + commit truth.Pick one of: (a) regenerate sample-run with the Wave 4 procedural namer so the legacy "Cairnfield" labels are gone and the fixture stays internally consistent with the current naming contract, (b) confirm sample-run is genuinely unused outside test/middleware/verifier paths and remove the entire directory plus the three referencing usages in manifestUrl.test.ts, mapv10ArtifactMiddleware.ts, and verify-browser.mjs, or (c) explicitly document why the legacy fixture is retained as historical (tenet-disfavored; record the rationale on this row). Do not silently delete or auto-regenerate this fixtureviewer/src/ui/__tests__/manifestUrl.test.ts, viewer/server/mapv10ArtifactMiddleware.ts, and viewer/scripts/verify-browser.mjs may depend on its current shape.After resolution: npm test and npm run verify:browser:continent from viewer/ pass; the three referencing files are either updated to the new fixture or have their sample-run references removed; no other code path silently falls back to sample-run content.Removes a pre-Wave-4 source-of-truth drift item that future namer/label work would otherwise re-trip over.Touches viewer test fixture + dev server middleware + browser verifier in lockstep; misordered changes break npm test.Direct (after user decision a/b/c)

MZ is now the most recent foundation work on top of the LOD/Data Coherence chain. Latest committed scenario evidence (viewer/verification/scenarios/latest/scenario-results.json, 2026-05-11) is incomplete and predates MZ: 27 scenario definitions exist, the artifact declares 27, but it stores 25 result entries and omits mapv10_location_height_z5 plus mapv10_location_label_uniqueness_z5. Of the stored entries, 24 have no hard issues and the slow-network entry carries the old blocking omitted-slot issue under remote RTT throttle. The R-25 historical baseline showed 23 canonical scenarios green with 0 warnings; the gate vocabulary has since grown (Wave 1 + MZ) and the scenario count and budget targets are now stricter. Visual baselines under viewer/baselines/browser/continent-lod6/ were captured 2026-05-07 with the pre-Wave-2 fixture content-hash; recapture/update them only after foundation behaviour is accepted, never as part of process reconciliation.

  1. Regenerate and validate full MZ scenario evidence. The next proof must record all 27 definitions under the accepted local-only slow-network classification and the new semantic/close-detail gates.
  2. Inspect and accept/reject the MZ visual result before baseline work. Do not refresh visual baselines until the z6/z7 fixture, semantic policy, underlay, and close-detail product behavior are accepted.
  3. T3-3 CI scenario integration. Baselines now exist as local verifier artifacts; next wire the scenario/verifier suite from a clean fixture bootstrap, decide where CI obtains golden PNGs, and only commit baselines/browser/<run-id>/manifest.json (SHA-256 contract) while keeping PNG screenshots gitignored.
  4. Remaining direct polish: T2-1 borders on/off scenarios, T2-2 label scenarios, T2-4 browser-driving tool ownership, T4-3 unused SSL dependency cleanup.
  5. Deferred product lane: Valenar keeps the dummy fixture by default. Generated mapv10 WorldData stays produced as data only until an explicit import/package step is requested.

Dependency sketch:

R-14/R-16/R-17 terrain readability cluster
-> R-18 zoom-trace measurement
-> R-19 first zoom performance budget + route-streaming fix
-> R-20 lifecycle/hitch metrics + warn/fail schema
-> R-21 label lifecycle candidate gating + frame-budgeted residency
-> R-22 strict parent fallback contract
-> R-23 terrain geometric-error SSE upgrade
-> R-24 SSE terrain lifecycle pacing
-> R-25 route adaptive batching + upload pacing
-> R-26 generated artifact server boundary
-> R-27 browser screenshot baselines
-> R-28 scenario CLI wrapper
-> R-29 source-of-truth docs reconciliation
-> T3-3 CI scenario integration
T3-4 fixture distribution
-> needed before CI/baselines are reliable on fresh clones

T1-5 Valenar WorldData export (deferred product lane; keep generating data, do not switch Valenar off dummy fixture yet)

Open decisions

  1. Fixture distribution: generator bootstrap, Git LFS, or artifact download script?
  2. External checker: formal Step 3.5 in wave-protocol.md, or keep ad hoc?
  3. Performance thresholds: choose scenario budgets for p95/p99/long tasks/memory before optimizing.
  4. Highland palette policy: keep global snow/rock thresholds, or later make them biome/latitude conditional? Any retune must stay inside TerrainVisualConfig plus scenario evidence.
  5. Valenar WorldData hierarchy mapping: the first export derives Areas deterministically from realm/province clusters; decide later whether Areas become native mapv10 generator truth.
  6. Warn/fail budget ratchet policy: nested zoomTrace.{fail,warn} is now locked. Decide when current zero-warning baselines should graduate selected warn gates to hard-fail.
  7. SSE target pixel threshold: keep the default 1.5 px until new scenario evidence says the target, not lifecycle pacing, is the problem.
  8. Fallback-duration budget ratchet: omittedSlotCountMax=0 is hard-fail; clean fallback-slot warn baseline is 32 after R-24; fallback duration is measured and should become a tighter fail gate after repeated stable baselines.
  9. Vectors share label state machine vs separate: recommend shared with collapsed visible/fading_out for vectors; lock if vector churn reappears as an owner metric.
  10. Stale sample-run fixture (T4-6): viewer/public/sample-run/labelAnchors.json and viewer/public/sample-run/locationPolygons.json still carry pre-Wave-4 "Cairnfield" labels and survive on disk despite commit a644e88's "drop sample-run" message; referenced by viewer/src/ui/__tests__/manifestUrl.test.ts, viewer/server/mapv10ArtifactMiddleware.ts, and viewer/scripts/verify-browser.mjs. Decide between (a) regenerate with the Wave 4 namer, (b) remove the directory and the three referencing usages, or (c) keep as documented historical. Do not silently delete or auto-regenerate — tests/middleware/verifier depend on its current shape.