diff --git a/docs/research/2026-09-01-overhaul/483-far-terrain-registration-contract.md b/docs/research/2026-09-01-overhaul/483-far-terrain-registration-contract.md new file mode 100644 index 000000000..bcda71275 --- /dev/null +++ b/docs/research/2026-09-01-overhaul/483-far-terrain-registration-contract.md @@ -0,0 +1,78 @@ +# #483 — far-terrain registration repair + +2026-09-06; base c775eea1662e99ba90031986b24d4a697ad16bb6. +Owner order: **implement, owner tests, then review**. This overrides the normal +pre-owner review order for this bounded fix; no claim of review closure follows +from implementation or successful compilation. + +## Confirmed cause + +Owner-g4-20260906-r1 logged NearRadius4/FarRadius25, unchanged maximum preference. +`LandblockBuildFactory.BuildLocked` returns LoadFar without EnvCells. +`LandblockRenderPublisher.AdvanceCompleteOne` only calls +`WalkLandscapeAssembler.PublishLandblock` inside the non-null EnvCells branch. +`RetailFrameWalk.DrawLandscape` skips null landscape slots; the actual assembler +grid is51x51, not WalkLandscape's standalone11x11 test default. Far terrain can +therefore have a GPU mesh but no terrain draw turn. Demotion also removes the +whole landscape entry through RemoveBuildingRegistry while retaining terrain. +The old test BuildFar_NeverPopulatesWalkDataBecauseEnvCellsIsNull explicitly +preserves this known gap instead of asserting a usable far draw product. + +## Boundaries and implementation + +1. Carry terrain's authored vertical bounds with terrain-capable build data for + both near and far loads, independently of interior/building presence. Reuse + the existing height-byte/height-table computation unchanged; do not substitute + the rendering AABB, flat bounds or a new margin. Preserve near geometry and + the existing C1a witness's exact products, not a new golden. +2. Publish terrain presence/bounds into the existing WalkLandscapeAssembler for + both tiers. Keep exactly one landscape owner and existing walk ordering/LOD. + Near publication supplies buildings separately; far entries have no buildings. +3. Forward the terrain metadata through both PublishAsFar conversions and all + normal load/promotion routes. No extra DAT or collision/static read for Far. + Respect the retained publication receipt and existing retry/budget semantics. +4. Near-to-far retirement clears building references but retains the terrain + entry/bounds. Full terrain removal removes its landscape entry. Repeated + clearing/removal remains harmless; a later building-clear stage must not + resurrect a fully removed terrain entry. Promotion/revisit restores buildings. +5. Do not change view radius, fog, clipping, draw order, queues, streaming budgets, + package recipe, GPU resource lifetime, collision, FPS or the known retail leak. + Do not introduce a fallback far-terrain pass or a second visibility answer. + +The retail mechanism is unchanged: LScape::draw00506330 visits its complete +mid_width-squared draw list and submits each non-null in-view block. +CLandBlock::get_land_limits0052F1D0 owns the already-ported bounds. This fixes +the modern two-tier adapter's missing terrain population, not those algorithms. +AP-149's absent distant static/building closure remains a separate deviation; +do not claim it retired because distant terrain now draws. + +## Minimal regression checks (write now; run only when clients are absent) + +- Far build keeps EnvCells null and performs only its existing heightmap read, + but carries exact non-flat authored vertical bounds. +- Production publisher + real landscape/walk product produces a nonempty + terrain turn for an in-view far-only block beyond near radius4, with no + building/object population. The old missing-registration implementation must + fail this assertion; a renderer-slot count alone is insufficient. +- Both accepted-near-as-far conversions preserve bounds/terrain registration. +- Near -> far -> near, full unload/revisit and duplicate retirement: terrain + survives demotion, buildings do not; full removal has no remaining terrain + turn and late building cleanup does not resurrect it. Include the concrete + retirement/publisher path, not only an assembler helper. +- Retain existing near geometry/membership and walk-order expectations. + +## Ownership, safety and handoff + +One OpenAI implementer owns only LandblockBuild.cs, LandblockBuildFactory.cs, +LandblockRenderPublisher.cs, LandblockPresentationPipeline.cs, +Rendering/Walk/WalkLandscapeAssembler.cs and their directly affected tests. +Small related declaration/comment edits may be proposed, not broadened silently. +Lead owns issue/ledger/architecture/register and this contract. No shader/PAK edit. + +**Retail PID25988 is owner-held: DO NOT TOUCH.** No close, input, debugger attach, +build, tests or launch while either client is running. Code edits may proceed. +Do not invoke Claude or start review agents. Return the uncommitted scoped diff +and exact unrun test commands; implementation is NOT yet built or tested. +After the owner closes retail, lead does the green Release build and narrow +regression run, launches one manual client, and leaves review until after the +owner's test. No main merge and no G4 acceptance are implied.