Campaign OVERHAUL S3 chunk 1 fix round 1 (docs/research/2026-09-01-overhaul/
s3-walk-ownership-map.md §11.5), applied on top of 079483bb6 after the
three-lens review. G1-G10, each verified against source and (where the item
adds a pin) mutation-checked.
G1 — the flag-off integration test was vacuous: Collect_TranscriptFlagOff_
ProducesNoConsoleOutput drove ONE frame, and WalkOracleTrace.Parse
structurally discards the last (open) frame, so it passed whether or not
the flag gate worked. Now drives TWO frames and asserts BOTH that Parse
sees zero frames AND that no captured line starts with any of the ten
transcript-kind prefixes (F /P /LS/LC /SC /BLD /DI /DC /EC /OC ) — robust
to unrelated console noise from parallel test classes.
G2 — LC/SC computed LodCellId before testing the flag (~1,300 land-cell
turns/frame of division/modulo/checked-conversion work paid even when the
flag is off). Both OnLandCellTurn and the new OnSortCellTurn now guard the
LodCellId call itself, not just WalkTranscriptDump's internal check.
G3 — the flag-on round trip never exercised LC/SC/BLD (the T2 fixture's
stub 1x1 landscape has zero blocks). New
OutdoorRoot_TranscriptFlagOn_PrintsLandscapeThenLandCellThenSortCellThen
Building_InOrder drives RetailFrameWalk.DrawLandscape directly (the same
deterministic "CY-only" admission technique the existing outdoor LandCell
test uses) over a landscape with one block + one building, and asserts
LS < LC < SC < BLD by position, with the exact cell/building ids.
G4 — the parts/alpha-depth smoke tests asserted FL/PC (Flushes/
DrawCellsSamples) but never AM/PM (MeshAdds/PortalPolyDraws) for the
alpha-depth log, even though PD/DM were already asserted for the parts
log. Added the missing Assert.Contains for MeshAdds and PortalPolyDraws.
G5 — launch-options.md's side-effects column now states the flag off/on
costs explicitly ("one bool read per print site, nothing else" / "one
console line per walk turn, ≈600-800 lines per outdoor frame").
G6 — removed the "DI f4180108... no, a9b4013f" self-correction in
Oh_doorway_still_first_frame_diff's comment; added a one-line note on why
it and Oh_foundry_deep_... stay bespoke Facts rather than folding into the
Theory (no functional reason, just history).
G7 — SC printed from OnLandscapeCellTurn, which RetailFrameWalk.
DrawLandscape calls AFTER DrawBuilding — retail's own order is
LC -> SC -> BLD (terrace-edge.walk.log:2331-2333), not LC -> BLD -> SC.
Fixed by adding IWalkEventSink.OnSortCellTurn, fired at the DrawSortCell
entry point (same alwaysDrawObjects||cellInView gate, before DrawBuilding);
the driver moves its SC print there and OnLandscapeCellTurn no longer
prints one.
G8 — the trailing "OC <viewer land cell>" retail prints once per complete
outdoor frame is GameSky::Draw's weather branch calling
DrawObjCellForDummies(after_sky_cell) @0x005070da, not a walk turn.
RetailPViewPassExecutor.DrawLandscapeSliceLate now prints it at the point
it actually calls RenderWeather; PrintObjectCellTurn's doc, which denied
this line existed, is corrected. New
Oh_terrace_edge_outdoor_frame_ends_with_the_weather_ObjectCellTurn pins
this directly against the OH capture: every complete outdoor frame's last
event is an OC whose cell id equals that same frame's own P-line cell id.
G9 — "EC and OC counts are always exactly equal per pose" was false
(terrace-edge: 12 EC vs 16 OC, the extra 4 being G8's weather OC, which
has no EC counterpart). The visit-scoped/stamp-dedupe conclusion was
right; only the count claim needed fixing. Both doc sites corrected.
G10 — the conformance signature dropped LC/SC/EC/OC entirely, which is
how G7 shipped green: no test ever compared their placement. Recorder now
tracks LC/SC via the new OnLandCellTurn/OnSortCellTurn hooks and derives
EC/OC per DC from PView::DrawCells's own two reverse loops (shell
far-to-near, then object-list far-to-near) — a look-in flood's DC derives
them immediately, the interior root's OWN flood defers to
OnInteriorFloodDrawTurn (verified against cathedral-leak.walk.log:4,1328,
where the root DC's own EC/OC print last). WalkTraceReplayContext.
Signature8 applies the identical derivation to oracle frames and never
produces the trailing weather OC (G8's own dedicated pin covers that
separately, matching the spec's "exclude from the comparison, assert
separately" instruction). Used ONLY for the four OH-capture-rooted
conformance rows (the older FW0 fixtures predate the LC/SC/EC/OC
breakpoints and carry none of those lines).
Applying the eight-kind check surfaced a real, separate bug: WalkLandscape
DatBuilder never set WalkLandBlock.LandblockId, so every LC/SC token any
DAT-built world ever produced read landblockId=0 — latent because the old
four-kind signature filtered LC/SC out. Fixed (one field, test-fixture-
only, no production behavior change). With that fixed, three of the four
OH rows (terrace-edge, cathedral-arrival, foundry-deep) reproduce EXACTLY
at the eight-kind level. Oh_doorway_still_first_frame_diff still diverges
by one extra LC/SC pair (token index 165: replay draws LC:a9c90001|
SC:a9c90001 that retail never does) — a real WalkLandscape.CheckBlocks
visibility question at a ring-2/ring-3 LOD boundary, out of this round's
print-only scope, needing the retail oracle to resolve. Marked
[Trait("Status","KnownFailure")] with the exact position recorded in its
own doc comment, matching TowerAscent's existing pattern — the InstalledDat
lane now shows FOUR known failures (the pre-existing two #383 layout tests
+ TowerAscent, plus this new finding), not three; never weakened the pin
itself.
Mutation checks (all four restored after observing the failure):
- G1: forcing WalkTranscriptDump.Enabled true made
Collect_TranscriptFlagOff_ProducesNoConsoleOutput fail with
"Assert.DoesNotContain() Failure: Filter matched in collection ... Collection:
[\"DI 00000100\", \"DC pv=00000000 ov=1 n=2: 00000100 00000101\", \"LS\", ...]".
- G3: deleting PrintLandCell's Console.WriteLine made the new outdoor test
fail with "expected an LC line".
- G4: making MeshAddPattern unmatchable made all five
Alpha_depth_log_parses_with_nonempty_samples rows fail with
"Assert.Contains() Failure: Filter not matched in collection".
- G7/G10: moving OnSortCellTurn back after DrawBuilding made the
terrace-edge conformance row fail with "walk diverged from retail
(terrace-edge.walk)", first divergence exactly at its first building
(f518002e): expected ...SC:f518002e|BLD:f518002e..., actual
...BLD:f518002e|SC:f518002e....
Gates: hermetic lane 6,816/0 (baseline 6,814 + G3's new test + G8's new
test); InstalledDat lane 244/4 known failures (two pre-existing #383
layout tests + TowerAscent + the new Oh_doorway_still_first_frame_diff
finding, documented above).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Campaign OVERHAUL S3 chunk 1 (docs/research/2026-09-01-overhaul/s3-walk-ownership-map.md
§11): the transcript-kinds/fixtures/print-only-emitter half of the walk work,
built AFTER chunk 3 landed LC/SC (the per-land-cell interleave).
B1 — the emitter (print-only, never gates admission/depth/order):
- ACDREAM_DUMP_WALK_TRANSCRIPT=1 is read once into RuntimeOptions.DumpWalkTranscript
(rule 4) and handed to RenderingDiagnostics.DumpWalkTranscriptEnabled (rule 5,
a settable static, not a second env read) once at GameWindow construction — the
deep walk call sites have no reachable RuntimeOptions reference.
- WalkTranscriptDump (new) prints the OH line kinds — F/P/LS/LC/SC/BLD/DI/DC/EC/OC —
to Console at the exact points retail's cdb breakpoints sit
(tools/walk-oracle/oh/oh-capture-walk.cdb.template), gated internally so every
method bails out before any string work when the flag is off.
- Every call site lives in WalkFrameDriver.cs, at the point the driver already
processes that turn: Collect (F/P, after BeginFrame), Emit's DI/LS/DC/BLD cases,
OnLandCellTurn/OnLandscapeCellTurn (LC/SC, at LOD resolution via the new
WalkTranscriptDump.LodCellId helper, before the 8x8-bucket expansion), and the
EmitFloodTurns/EmitCellContentsTurn loops (EC/OC — both UNCONDITIONAL per flood
visit, matching the OH captures' always-equal EC/OC counts; retail's own
DrawEnvCell stamp dedupe sits past the breakpoint, inside the function).
- DC's "pv=" field encodes interior(0)/outdoor(1) as an 8-hex-digit 0/1 so it
satisfies the same pv=[0-9a-f]{8} regex real captures use; derived from
_currentDcStage at the DC event (CellStatic = interior pview, else outdoor).
- The frame-root pose (origin, quaternion) is a reasonable orthonormal basis built
from the walk's own CyPlane.Normal forward vector and WalkLandscape's own
ViewerWorldOriginX/Y block origin — self-consistent for the round-trip parser,
not a byte-exact reproduction of retail's Frame (B4's diff never compares P).
B2 — WalkOracleTrace learns EC/OC event kinds (LC/SC already existed from chunk 3).
New WalkOraclePartsTrace.cs holds two small read-only parsers for the parts log
(PD/DM) and the alpha-depth log (AM/FL/PM/PC) — records only, no validator, no
canonical JSONL, no new tool.
B3 — fixtures: the five OH walk captures join WalkOracleTraceTests.AllFixtures
(now (root, name) pairs — FW0's own root plus the OH capture directory) for
parse + complete-frame pins. The four kit poses join WalkTraceConformanceTests'
still-fixture coverage as NEW rows (the OH cathedral-arrival root is f4180108,
not FW0's f4180106): terrace-edge/cathedral-arrival extend the existing theory
(now (root, fixture) parameterized); holtburg-doorway-still and foundry-deep get
dedicated tests mirroring their FW0 siblings' own structure. Finding: the OH
foundry-deep capture's own retail transcript draws 12 real town buildings through
its exit chain (unlike the FW0 capture, which apparently reached none at that
pose) — the FW0 test's stub 1x1 landscape undershoots it (first divergence:
nothing after "LS" vs retail's real BLD content); fixed by building the full
landscape/building world via WalkLandscapeDatBuilder.Build, matching the shared
theory's own approach, not by skipping or weakening the row.
WalkTraceReplayContext.Signature(WalkOracleFrame) now filters to the DI/DC/BLD/LS
kinds (LC/SC/EC/OC never had a WalkEvent analogue in RetailFrameWalk's own
four-kind vocabulary) instead of mapping them to a "?" placeholder, so the still-
fixture comparison stays apples-to-apples on both sides.
B4 — WalkTranscriptSignatureDiff (test-side only, no runner/tool): diffs two
transcripts (raw lines or parsed frames) at the full DI/DC/BLD/LS/LC/SC/EC/OC
level, reporting the first divergent event and position per frame. Proven over a
synthetic self-vs-self-minus-one-event pair (SignatureDiff_ReportsTheExactRemovedEvent).
Tests: T1 (flag off) is split into a unit-level zero-allocation/zero-output check
on WalkTranscriptDump itself (the walk's pre-existing allocation, e.g.
RetailFrameWalk.EmitDrawCells's per-call array, is untouched by this chunk and not
independently zero-alloc) and an integration-level Collect() check; both assert via
WalkOracleTrace.Parse returning zero frames rather than raw string equality, which
is robust to unrelated Console.WriteLine noise from other test classes running in
parallel (a real, observed hazard — WalkFrameDriverTests joins
CameraDiagnosticsCollection for the same reason CornerFloodReplayTests/
Issue181WallPressEquilibriumTests already do, issue #251). T2 proves the
emitter/parser round trip on a synthetic interior frame. T3's InstalledDat rows all
pass. T4: LaunchOptionsDocumentationTests green with the ACDREAM_DUMP_WALK_TRANSCRIPT
row (both directions).
Gates: hermetic lane 6,814/0 (was 6,795 baseline + new tests), three consecutive
clean runs; InstalledDat lane 245/3 known-failures (the two pre-existing #383
layout tests + TowerAscent) unchanged from baseline.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The re-expressed F4(c) pin installed an empty landscape view, so nothing
classified visible, the ordered stream never grew, and no StreamMark
fired: the one behavior round 2 changes was unpinned (review finding).
The test now installs a real full-viewport view and records the trace,
asserting two FLUSH lines ahead of the merged three-cell terrain batch.
The LandCell doc no longer claims an identical submission order; the
resolved image is identical (opaque-vs-opaque order is depth-resolved,
every observable order still flushes first).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Measured (§9.7): the connected R6 soak at e10765aa showed the round-1
batching fix (§9.6 F2) was inert in practice — CPU p50 flat at 31.2 ms
and FPS flat at 32 across every outdoor destination (baseline 2.0-15.5 ms
/ 51-452 FPS), GPU p50 2-4x baseline. Root cause: nearly every admitted
land cell carries scenery statics, so a StreamMark (recorded whenever the
walk's ordered stream grows) follows almost every LandCell event and was
itself treated as a flush point, splitting the pending terrain batch back
down to ~500 submissions/frame — each paying TerrainModernRenderer's full
bind sequence plus a ring allocation and an MDI.
F10: a StreamMark no longer flushes the pending terrain batch. Citation —
the walk's ordered stream holds ONLY opaque batches
(WalkStaticStreamPopulator.cs:179, `if (batch.IsOpaque)` routes translucent
batches to the alpha list instead of the stream) drawn through the
Opaque/OpaqueAlphaToCoverage pipelines, both created with depth test AND
write ON and GpuBlendMode.None (WbDrawDispatcher.Rhi.cs:240-248,343:
`Depth = new GpuDepthState(Test: true, Write: depthWrite, depthCompare)`
with depthWrite: true); DrawOrderedRange resolves every walk-stream
command's bucket to PipelineBucket.Opaque because every command it holds
is opaque (WbDrawDispatcher.OrderedStream.cs:569-571). Opaque terrain and
opaque statics are therefore depth-resolved identically in either
submission order, so deferring the terrain batch across a StreamMark is
pixel-identical (except exact z-ties, which retail itself leaves
order-independent).
Flush points that REMAIN, because their GPU order against terrain IS
observable: PunchFan (DEPTHTEST_ALWAYS + write — the far-Z stamp this
chunk's interleave exists to order correctly), AlphaBarrier and
LandscapeFlush (translucent drains), ClearInteriorDepth, ExitSeals, Sky,
CellShell, a particle turn whose cell has a renderable emitter, and the
end of Replay. AlphaSubmitMark stays a non-flush point (unchanged — it
only enqueues into the CPU alpha list). No ordered-stream stage found
that blends or disables depth write, so no amendment to the contract was
needed; the Replay doc comment records the citation so a future stage
that does must become a flush point again.
F11: no per-batch bind latch added (tens of batches/frame is acceptable).
F12: the F4(c) driver pin is re-expressed to prove a StreamMark from a
cell with real content (not just an empty particle turn) no longer
splits, while PunchFan/AlphaBarrier/a has-emitter particle turn still do;
a new punch-order pin (LandCell(far), building turn with a PunchFan,
LandCell(near)) proves the far terrain flushes before the punch and the
near terrain starts a fresh batch after it. Hermetic App lane: 6,796
passed (6,795 base + 1 new fact), 0 failed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- the LandCell event doc states the deferred cross-block batch rule (the
same-landblock lookahead text was stale);
- the AlphaSubmitMark arm's comment gives the true reason it is not a
flush point (it only enqueues into the CPU alpha list; the drain leaves
flush first);
- the pending terrain batch is cleared with the other transient frame
lists in AbortFrame/BeginFrame;
- CompleteWalkTerrainFrame runs in a finally so a throwing Replay cannot
leak its accumulated ticks into the next frame's sample;
- CopyRenderableEmittersInCell keeps its own doc comment;
- the outdoor-root LandCell pin now observes one real cell turn after its
terrain;
- the oh1 landscape contract's stale "whole pre-stage" prose is resolved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Fixes the three-lens review blockers against 671eb3ad4 (S3 section 9.6 F1-F6).
F1 - slot key (blocking, retail). TerrainModernRenderer.DrawLandCells
normalizes every incoming landblockId to (id & 0xFFFF0000u) | 0xFFFFu
before the _idToSlot lookup: the walk hands 0xXXYY0000
(WalkLandBlock.LandblockId) but AddLandblock stores under the DAT id
0xXXYYFFFF (LandblockRenderPublisher.LandblockId) - every walk lookup
was missing and the walk path drew NO terrain. Unit-tested end-to-end
through a real RecordingGpuDevice-backed TerrainModernRenderer
(TerrainWalkSlotKeyNormalizationTests): AddLandblock(0xA9B4FFFF, ...)
is found by a 0xA9B40000 lookup, an unknown landblock is a silent
per-entry no-op, and a batch mixing a known and unknown entry submits
only the known one.
F2 - deferred cross-block batching (blocking, driver). Retail's
DrawSortCell always follows DrawLandCell (LC/SC strictly alternate,
never two LC in a row - S3 section 9 R1), so chunk 3's "merge
consecutive same-landblock LandCell events" rule never actually
merged anything; the driver review flagged batching as inert.
WalkFrameDriver.Replay now keeps ONE pending terrain batch across
landblocks ((landblockId, side, cellIndex) entries, cleared at
Replay's own start); a LandCell event only appends; every OTHER event
kind that will itself submit GPU work (StreamMark, Sky, CellShell,
PunchFan, AlphaBarrier, LandscapeFlush, ClearInteriorDepth,
ExitSeals) flushes the pending batch first; a StaticParticles/
CellParticles turn asks the new ParticleSystem.
HasRenderableEmittersInCell (an allocation-free sibling of
CopyRenderableEmittersInCell) and, when the cell has no renderable
emitter, submits nothing and does NOT flush either - the whole point
of the deferred rule. The end of Replay flushes the remainder. This
is order-preserving by construction: a flush always lands at the
exact point the unbatched draw would have, so GPU submission order -
and therefore pixels - is identical to the unbatched baseline; only
the number of small terrain draw calls shrinks.
TerrainModernRenderer.DrawLandCellRuns becomes DrawLandCells(
viewProjection, IReadOnlyList<(uint LandblockId, int SideCellCount,
int CellIndex)>) - one MultiDrawIndexedIndirect over every entry's
runs, unknown slots skipped per-entry. IWalkFrameLeafRenderer.
DrawLandCellBatch drops its separate landblockId parameter to match
(a batch can span several landblocks now) and gains
HasRenderableEmittersInCell.
Batch-count demonstration: driven through a real WalkFrameDriver
Replay (OnLandCellTurn_MergesAcrossLandblocksOverAnEmptyParticleTurn_
RealSubmissionsSplit), 4 LandCell turns across 3 distinct landblocks,
separated only by an empty particle turn, a real StreamMark, and a
building's alpha barrier, submit as exactly 3 DrawLandCellBatch calls
(2+1+1) instead of 4 - the empty particle turn's non-flush merges two
otherwise-separate cross-landblock entries. At production scale the
same mechanism is expected to cut the terrace-edge frame's ~578
individual DrawLandCell events (S3 section 9's captured transcript
count) to "tens" of submitted batches, per the contract's own
expectation: most terrain cells have no particle owner nearby, so the
strict LC/[empty-SC]/LC/[empty-SC]/... run collapses into one batch
per region bounded by real content (a building, a StreamMark-worthy
cell, or a genuine emitter) rather than per cell.
F3 - per-frame terrain diagnostic (blocking, build/test). The walk
leaf no longer brackets each batch with TerrainDrawDiagnosticsController
.Begin()/Complete() (a per-batch Stopwatch Restart/Stop pair that was
pushing one timing SAMPLE per batch, not per frame).
RetailPViewPassExecutor.DrawWalkLandCellBatch instead times its own
call with a raw Stopwatch.GetTimestamp() delta (no allocation) and
hands the ticks to the controller's new AccumulateWalkBatch;
RetailPViewRenderer.DrawWalkDrivenStatics calls the new
CompleteWalkTerrainFrame() exactly once, immediately after
driver.Replay finishes - "the end of the walk replay", where the
deleted whole-stage terrain leaf's own Begin()/Complete() bracket
used to close - which pushes ONE elapsed-time sample (even a
zero-batch frame pushes a zero sample: one sample per frame, not per
landscape turn) and publishes on the existing 5-second cadence.
TerrainRenderDiagnosticFacts gains a Draws field alongside
VisibleSlots (both were the same field before); TerrainModernRenderer
tracks its own per-frame WalkVisibleSlotCount/WalkDrawCount (a
HashSet<int>/int cleared in BeginFrame, populated by DrawLandCells),
and the diagnostics source reports those whenever the walk drew at
least one batch this frame, falling back to the non-walk Draw()
path's VisibleSlots otherwise (the two paths never both run in the
same frame). The [TERRAIN-DIAG] line's meaning (cpu_us per frame) is
unchanged, so the S3 section 9.5 before/after compare stays valid.
F4 - driver pins for the LandCell position (major). Three RunFrame-
level pins replace the deleted TERRAIN:0 pins: an outdoor-root
sequence (SKY, then one LANDCELL, driving RetailFrameWalk.
DrawLandscape directly with a one-view/zero-vertex WalkPortalView so
WalkLandscape.CheckBlocks' admission stays the same deterministic
"CY-only" test RetailFrameWalkTests already relies on, while still
satisfying WalkFrameDriver's real >=1-active-view fail-loud guard);
an interior-root test with one real exit view and one populated
block (SKY, LANDCELL, LFLUSH, SEALS, SHELL...) built on the existing
RunFrame_InteriorFloodWithExitView_... fixture; and the T4 batching
pin re-expressed for the F2 rule (OnLandCellTurn_
MergesAcrossLandblocksOverAnEmptyParticleTurn_RealSubmissionsSplit,
described above). The fake leaf's DrawLandCellBatch now logs
LANDCELL:<lb>:<side>:<idx>[,...] per batch and gains
HasRenderableEmittersInCell backed by an opt-out CellsWithoutEmitters
set (default true - has-emitters - so every pre-existing pin in the
file keeps its old unconditional-submission behavior unchanged).
F5 - no code change: the walk's in-view gate is unchanged; no
whole-block terrain re-added.
F6 - minor/notes: DrawLandCells' own comment now states the walk's
CheckBlocks/landcell_check admission is the sole terrain culling
authority (retail has no separate terrain frustum test); the
HandleLandscapeTurn comment's inverted claim is corrected (a FARTHER
building's punch survived because NEARER terrain was drawn BEFORE
it, not after - the interleave now draws it after, matching retail);
the "flat/directional-shadow paths" claim is corrected to the one
actual caller, WorldScenePassExecutor.DrawFlatTerrain (a directional-
shadow receiver selects its pipeline inside the SAME DrawRhi call,
not through a second caller); the cathedral order-trace token gains
the LOD side/index (":LC<lb>/<side>:<idx>"); T2's vacuous "no
TERRAIN event" assertion in RetailFrameWalkTests is replaced by a
comment pointing at the F4 driver-level pins; and the stale
"Confirmed OH5 defect" row in oh1-construction-landscape-contract.md
is retired with "FIXED by S3 chunk 3 (commit 671eb3ad4 + fix round
1)".
App hermetic lane: 6,795/6,795 (up from 671eb3ad4's 6,786 baseline -
net +9 tests: 3 F1 slot-key tests, 2 F4a/b driver RunFrame pins, 3
TerrainDrawDiagnosticsController walk-frame tests, plus the T4->F4c
rewrite and the RetailPViewPassExecutorTests split are net neutral).
InstalledDat lane: 241 passed, the same 3 accepted failures (2
pre-existing #383 layout fixture-drift tests, 1 TowerAscent
Status=KnownFailure) - unchanged from baseline. Core Vfx tests:
109/109 (108 baseline + 1 new HasRenderableEmittersInCell lifecycle
pin mirroring CopyRenderableEmittersInCell's own add/move/remove
test).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ports RenderDeviceD3D::DrawBlock @0x005a17c0's real per-cell order:
loop 1 (@0x005a1876) prepares shadow lists; loop 2 (@0x005a197d)
DrawLandCell(cell) @0x005a19c0 fires ONLY when the cell is in view,
STRICTLY BEFORE DrawSortCell(cell) @0x005a19e6, which fires whenever
alwaysDrawObjects (retail default 1 @0x00820ed4) or the cell is in
view. RetailFrameWalk.DrawLandscape now emits sink.OnLandCellTurn(
landblockId, side, cellIndex) at that exact point, per admitted cell,
before the existing DrawBuilding + OnLandscapeCellTurn (the
DrawSortCell half). WalkFrameDriver records one LandCell frame event
per turn and deletes the whole-stage TerrainSlice(0) emission and the
DrawTerrainSlice leaf outright — drawing all terrain before every
building let a nearer building's far-Z punch survive under farther
terrain drawn afterward, the doorway-behind-a-hill fragment bug from
the owner's G2 Holtburg screenshot; this chunk removes it by ORDER
alone, with no depth-compare change (S4's punch z-func question is
untouched, per the contract).
Index-run arithmetic (S3 §9.1 R3): the terrain mesh is cell-major
(LandblockMesh.Build: cy outer, cx inner, 6 indices/cell, 384/land-
block). A retail LOD cell (side n, LOD coords X,Y) covers cx in
[X*8/n,(X+1)*8/n), cy in [Y*8/n,(Y+1)*8/n) — one contiguous run per
covered cy row: side 8 -> one run of 6, side 4 -> two runs of 12,
side 2 -> four runs of 24; side 1's single coarse cell covers every
row contiguously so its 8 per-row runs collapse into ONE run of all
384 indices. TerrainModernRenderer.AppendCellIndexRuns (pure, no GPU,
no baked table) and DrawLandCellRuns (resolves the landblock's slot,
builds one DrawElementsIndirectCommand per run, reuses the existing
DrawRhi bind-and-submit path) implement this; TerrainModernRenderer
.Draw(...) is untouched and keeps serving non-walk callers (directional-
shadow receivers, the flat terrain path).
Order-preserving batching (S3 §9.2 B2): WalkFrameDriver.Replay merges
consecutive same-landblock LandCell events with no intervening event
into ONE DrawLandCellBatch leaf call; any other event splits the
batch. In production this rarely fires because retail's own
DrawSortCell (AlwaysDrawObjects=true) always interposes an object-
list turn between one cell's LandCell event and the next's — see
perfNote in the task report for the resulting command-count increase.
Deleted as dead: the _walkTerrainInViewLandcells field and
SetWalkTerrainInViewLandcells setter on RetailPViewPassExecutor (fed
only DrawWalkTerrainSlice's inViewLandcells filter, which no longer
exists — the walk's own per-cell CellInView admission is now the
sole terrain-visibility authority) and its two call sites in
RetailPViewRenderer.DrawWalkDrivenStatics.
Weather placement (S3 §9.1 R5): confirmed unchanged. GameSky's
weather pass (RenderWeather, gated on is_player_outside) already
runs after every LandCell event for both root kinds — for an
outdoor root, DrawLandscapeDynamicsPhase is called directly after
driver.Replay() completes (which processes the whole per-cell
_events list first); for an interior root with ov>0, it fires via
the LandscapeFlush leaf (RetailPViewRenderer.FlushWalkLandscape ->
_walkPreClearDynamics), and OnInteriorFloodDrawTurn only emits
LandscapeFlush AFTER DrawLandscape's per-cell loop has fully run
and recorded every LandCell event ahead of it in the same _events
list Replay walks in order. No code change needed; verified by
reading the call sites.
Tests: WalkEvents/RetailFrameWalk/WalkFrameDriver's existing pins
updated (every "TERRAIN:0" expectation deleted, matching the deleted
event); new coverage for T1 (WalkLandCellOrderTests — LandWalkOrder +
WalkLandscape.CalcDrawOrder + WalkLandscapeAssembler
.SideCellCountForRing reproduce both cathedral captures' frame-2
LC/SC sequences byte-for-byte, 533/698 arrival and 531/757 leak, cross-
verified against the retail ring-to-LOD table), T2 (a synthetic two-
block landscape with a real WalkVisibilityMath-driven out-of-view
column, proving LC-before-building/statics, far-to-near, no-LC-but-
keeps-SC for the excluded cells, and no TerrainSlice event of any
kind), T3 (TerrainLandCellIndexRunsTests — the index-run arithmetic
for every side/cell, disjoint and exhaustive over the 384-index
landblock), T4 (batching merge/split), and the RetailPViewPassExecutor
CompiledCallGraph pin retargeted at DrawWalkLandCellBatch /
DrawLandCellRuns. WalkOracleTrace gains LC/SC event kinds and a
Load(root, name) overload for the OH capture directory — the one
parser change this chunk needs, no validator, no other infrastructure.
App hermetic lane: 6,786/6,786 (6,765 baseline + 21 new). InstalledDat
lane: 241 passed, 3 accepted failures (2 pre-existing #383 layout
fixture-drift tests, 1 TowerAscent Status=KnownFailure) — unchanged
from baseline. Core terrain tests: 116/116 (Core untouched by this
chunk).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
RetailFrameWalk.DrawInside now passes _interiorPView.OutsideView.ViewCount
into IWalkEventSink.OnInteriorFloodDrawTurn. WalkFrameDriver's own
implementation reproduces PView::DrawCells @0x005a4840's exact gating
(0x005a4852-0x005a49eb, all inside `if (outside_view.view_count > 0)`):
the landscape flush (retail FlushAlphaList(0f) @0x005a4872 plus the
pre-clear dynamics hook), the device-stamp advance @0x005a4886, a GATED
depth clear (pc:432731-432732), and the exit-portal seals
(pc:432785-432786) — all four skipped entirely when outsideViewCount == 0.
The depth clear is gated on a new driver field, PortalsDrawnCount, which
models retail's D3DPolyRender::portalsDrawnCount (uint16 @0x008719b4):
read-then-zeroed at the interior root's own flood turn
(@0x005a489c-0x005a489e), and re-armed at Replay by the count
IWalkFrameLeafRenderer.DrawExitSeals now returns (the SAME portal
enumeration RetailPViewPassExecutor.DrawPortalDepthWrite already performs
— OtherCellId==0xFFFF, >=3 vertices). The field persists across frames
(never cleared by BeginFrame/AbortFrame/EndFrame/Replay), reproducing
retail's documented quirk: a fresh driver's first ov>0 frame never clears;
every later ov>0 frame clears because the previous frame's own seals armed
the counter.
_skyDrawnThisFrame — the proxy for outside_view.view_count != 0 that used
to gate the stamp re-arm — is deleted; its "second Landscape turn in one
frame" fail-loud guard moves to a frame-scoped counter
(_landscapeTurnsThisFrame). RetailPViewRenderer.ClearWalkInteriorDepth
splits into FlushWalkLandscape (pre-clear dynamics + FlushLandscapeAlpha)
and ClearWalkInteriorDepth (the Z clear only), both wired through the new
IWalkFrameLeafRenderer.FlushLandscape leaf and the WalkLeaf production
adapter.
Tests: flipped the ov==0 pin to expect no landscape-flush/clear/seals at
all (T1); added the two-frame first-frame-no-clear / armed-clear pin plus
a no-exit-portal-never-clears pin (T2); added a look-in-neither-arms-
nor-consumes-the-counter pin (T3); added RetailFrameWalk's two-PView
draw_landscape wiring pin and a WalkPView.ConstructView reset pin (T4);
updated every direct OnInteriorFloodDrawTurn caller to pass the
outsideViewCount it models (T5). The four per-category leaf-contract pins
(whole-once shell, Boolean sphere admission, portal-polygon-only clip,
local-player repeated submission) already existed and needed no additions
(B4).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Retail-lens review of chunk 6 (no blocking finding). Fixed:
- a cell that gets two object-list turns in one frame (a chamber reached
through two portals) submitted its emitters twice; retail's particle parts
sit in the same shadow_part_list as every other part and CPhysicsPart::Draw's
frame stamp suppresses the second draw, so the walk now dedupes the particle
turn with the same frame-scoped set that dedupes the cell shell;
- every visited land cell paid the full per-cell draw setup even with no
emitter; DrawForCell now returns after the cell lookup, retail's own cost
(DrawPartCell 0x005a07a0 `num_shadow_parts > 0`);
- CopyRenderableEmittersInCell maintains LastRenderScopeEmitterVisitCount;
- the OutdoorSceneParticleEntityIds / outdoorOwnerIds stub chain (permanently
empty, never read) is deleted through IWorldSceneRenderer,
WorldScenePViewRenderer, IWorldScenePasses and the composition root;
- AD-117 item 4 names the two behavioral residuals (owner-cell substitution;
no per-emission AddPartToShadowCells);
- ParticleHookSinkTests pins that an emitter's draw cell is its owner's pose
cell and survives the projection-visibility switch across the per-frame
view pass.
Gates: Core 4,988/4,988 (Vfx 108/108), App hermetic 6,760/6,760, Runtime
1,884/1,884.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Owner G2 finding: the purple cloud around an arriving character no longer
drew. The server keeps the player Hidden until acdream sends LoginComplete at
reveal completion (retail-correct); the Hidden-state script's emitters spawn
in the arrival cell and are view-eligible when the world appears, but the
walk drew an owner's emitters only through the owner's registry rows, and a
hidden owner's shadow is suspended. Retail's
CPhysicsObj::add_particle_shadow_to_cell (0x00514a70) gives an emitter one
shadow in its OWN current cell, drawn at that cell's turn regardless of the
parent's hidden state (add_shadows_to_cells 0x00514aed skips the flood for
state & 0x1000).
Port: ParticleSystem keeps a per-pass cell -> renderable-handles index
(maintained at every renderable/OwnerCellId change) and
CopyRenderableEmittersInCell; ParticleRenderer.DrawForCell; the walk draws
particles BY CELL at the existing turns (interior CellParticles, landscape
LandscapeCellParticles), the events fire for every visited cell, and every
owner-union particle path is deleted (UnionOwners/UnionNewOwners for
particles, the outdoor drawn-owner dedupe, the executor's owner
classification sets, the context ParticleOwnerIds members). The post-replay
per-cell pass double-submitted the root flood's emitters and is deleted: an
emitter draws once, at its cell's replay turn. AD-117 item 4 becomes a port
note (the index lives in the particle system; an emitter is not a physics
object in acdream). The temporary [pes-spawn]/[pes-vis] traces are removed
and the ACDREAM_DUMP_PLAYSCRIPT row restored.
Verified: timed arrival route logs/selfgate-20260903-062522-haze-chunk6,
frame h02-arrive-400ms shows the cloud at the character in Facility Hub.
Gates (Release): Core 4,987/4,987; Content 214/214; Runtime 1,884/1,884; App
hermetic lane 6,760/6,760; App InstalledDat 217 pass / 2 pre-existing #383.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Chunk 5 (consumer cutover): WalkProductionWorldData's per-cell views are
borrowed from ShadowObjectRegistry.GetRetailPartEntriesInCell and resolved
through RenderSceneQuery.TryGetByLocalEntityId; every render-side sweep,
bucket, parent-cell and root-position fallback is deleted (AD-116 for the
one-frame registry→scene window, counted in UnregisteredRenderMembershipCount).
A live entity with visual parts but no collision geometry registers
render-only (LiveEntityCollisionBuilder computes the part array before the
empty-shapes gate).
Closeout fixes found while landing it:
- RefloodOwnerForLandblock forwards the retained part array — a reflood is
retail's recalc_cross_cells over the SAME CPartArray; without it every owner
touched by a landblock replacement commit lost its render membership.
- Non-colliding DAT statics register render-only from BOTH publishers
(LandblockPhysicsPublisher.PublishStaticEntity,
LandblockPhysicsContentBuilder.RegisterRenderOnlyStatic). The G2 self-gate
pixel diff caught them vanishing (Facility Hub wall panels): retail floods
every object regardless of collision (CEnvCell::init_static_objects
0x0052c350, add_shadows_to_cells 0x00514ae0).
- S2 dual review fix batch (arch + retail lens, lead-verified):
Suspend clears the retail product (remove_shadows_from_cells 0x00511230 is
one transaction); AttachChild/DetachChild advance the mutation revision so
a prepared SetPosition cannot clobber a child's rows; an attached child
never floods on its own re-registration; RemoveLandblock and the non-rooted
RetireOwnerFromLandblock prune retail rows (render-only statics end with
their landblock); a render-only owner's no-cell-array commit republishes at
its destination cell (AD-117); an empty non-null part array is treated as
null; per-move closures/LINQ replaced by index loops; EnvCell shells stay
out of the scene's LocalEntityId index (payload-less records); the index
predicate compares the id; the dead per-cell scene indices are deleted.
Register: AD-116 (chunk 5), AD-117 (four residual Contract A/B readings).
Evidence: s2-membership-ownership-map.md §8 (chunk 5) and §9 (closeout).
Gates (Release): Core 4,984/4,984; Content 214/214; Runtime 1,884/1,884;
App hermetic lane 6,760/6,760; App InstalledDat lane 217 pass / 1 skip /
2 pre-existing #383 layout-fixture failures; App Windows lane 1/1.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Movement: CommitSetPosition's RefreshPositionRows/ReplacePositionRows and
the staged apply publish the retail render product from the exact cell
list collision just used, the transition's cell_array retail feeds
add_shadows_to_cells in CPhysicsObj::SetPositionInternal @0x00515330
(pseudo-C 283526-283539); the separate move-path bbox recompute is deleted.
calc_cross_cells @0x00515230 stays the distinct full-recompute path
(PhysicsShadowCommitAction.Recalculate).
Children (Contract B recursion): ShadowObjectRegistry.AttachChild/DetachChild
give an attached object the root's current cells as part entries only,
republished whenever the root's array changes, detached at withdrawal and
cascaded from the root's Deregister; nested attachment resolves to the root
with a bounded, cycle-safe chain. EquippedChildRenderController attaches at
realization (FromSetupRenderParts over the child's Setup) and detaches at
its single removal funnel. WalkProductionWorldData's dynamic sweep reads
TryGetRetailCellArray directly; the 64-hop parent-chain walk and its
FindParentLocalId plumbing are deleted. CollisionWorldState.Clear now
also clears the retail products.
Gates (implementer's isolated worktree at identical content): Release
build 0/0; Core 4,970/4,970; App hermetic 6,761/6,761; targeted
walk/child/live-entity/placement/comparator 166/166; Runtime 1,884/1,884.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
WalkProductionWorldData no longer floods; its indoor and outdoor static
sweeps read ShadowObjectRegistry.TryGetRetailCellArray (retail's
calc_cross_cells_static @0x00515160 -> AddPartsShadow @0x00517e40
CELLARRAY, computed once at registration). The App-owned render flood
(ResolveStaticRenderCells, its fingerprint cache, the primitive-Setup
special case) and Core's ComputeStaticRenderCells are deleted. The one
remaining fallback, an entity the physics publisher has not registered yet
while the projection journal already published it, buckets to the authored
parent cell and is counted per frame (UnregisteredStaticRenderFallbackCount)
for chunk 5 to judge on the connected route.
The Facility stair pin now registers at the projection's own entity id
(the old pure-function test never carried identity) and reads the retail
array; the installed-DAT comparator compares retail against collision.
Gates (run in the implementer's isolated worktree at identical content):
Release build 0/0; Core Physics 2,202/2,202; App hermetic 6,760/6,760 (the
two added WalkProductionWorldData tests); installed-DAT walk/flood/stair
family 24/24; Runtime 1,884/1,884.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Uploads a cell-shell mesh whose storage order is the reverse of its surface
order and asserts each uploaded batch's FirstIndex reads back its own
indices from the arena. Guards the G1 regression fixed at 8c6563ca.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The b3b7d922 investigation checkpoint committed two probe read sites
(ACDREAM_PROBE_CATHEDRAL_SKIP_PUNCH, ACDREAM_PROBE_FACILITY_STAIRS) without
their launch-options rows, and removed the #117/#129 bias from
portal_depth.vert without re-pinning its SPIR-V oracle hash. Both left the
hermetic App lane red at the OVERHAUL v2 base. Rows added (both default-off,
scheduled for deletion in S5); the shader pin now records the binary on the
branch, with S4 named as the owner of its acceptance.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The [dyn-route] trace pinned the through-wall remote player (visible
from 0xF4180101 AND outdoors while parented at 0xF4180112 - a cell
with no retail sightline chain from either): the legacy visibility
builder invents cross-building cell views at the cathedral (REAL
3-5-plane cones for 0x112, not the zero-plane trapdoor), the viewcone
admits his sphere, and he rides the last dynamics pass - post-clear on
interior roots (walls'' depth wiped) and post-world outdoors.
The stage-set split (synthesis plan step 4): an interior-parented
dynamic may ride DynamicLast ONLY when its parent cell is in THE
WALK''S OWN ROOT FLOOD (oracle-trace-conformant; retail draws look-in
occupants inside the landscape stage through the composed portal
chain, and unreachable cells'' occupants not at all). The frame
product''s build input gains RootFloodCells (the driver''s
InteriorFloodCells as a per-frame set); non-walk/diagnostic frames and
the comparison wrapper keep the legacy drawableCells meaning. The
[dyn-route] probe logs the new rootflood-excluded state.
Hermetic 6,762/0; InstalledDat walk conformance 40/1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports the user-verified #132 invariant (e102fb36 on the old pipeline)
walk-natively, on the owner''s direction: retail''s falls containment
is POSITIONAL - an outdoor emitter''s polys join the ONE alpha list
during its owner cell''s DrawObjCell in the far-to-near landscape walk
(DrawSortCell @0x005a17c0), so every nearer building''s pre-punch
barrier (DrawBuilding @0x0059f2a0''s FlushAlphaList @0x0059f30b)
drains the already-queued FARTHER content against still-true depth
BEFORE its punch stamps far-Z into the aperture. The walk path had
kept the per-building AlphaBarrier events but batched ALL landscape
static-owner submission into one lump at the pre-clear closure - after
every punch had run: the barriers fired over an empty queue and the
falls drained against punched-far aperture pixels (phase=pre in every
probe line, which is why six rounds of phase-staging repairs could
never see it - the phase was right, the position within the phase was
wrong).
New WalkFrameEventKind.StaticParticles: each landscape cell''s and
each building shell''s emitter owners now submit AT THAT TURN during
Replay (marked so the owner''s meshes flush first - retail''s
per-object order); the batched SubmitWalkLandscapeStaticParticles and
its closure/post-replay call sites are deleted for both root kinds.
Two driver sequence pins adjudicated to the new turn order.
Hermetic 6,762/0; InstalledDat walk conformance 40/1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The FW3 visual gate's stairwell/grass transition flash (grass briefly
covering floor openings at doorway crossings - the #119 family) was the
FW3 dual path leaking: the walk decided WHETHER terrain draws while the
old PortalVisibilityBuilder assembly decided WHERE (slice planes, count,
scissor), and punch fans indexed the old slice array with walk view
indices. The new ACDREAM_PROBE_WALK_ROOT apparatus pinned the boundary
frames: fat/degenerate old-apparatus exit views splash terrain over
interior pixels, the interior depth-clear preserves color, and cells
absent from the walk's flood never repaint. Retail has ONE visibility
structure and cannot produce this.
ClipFrameAssembler.ReassembleOutsideViewFromWalk now materializes the
walk's own outside_view (pixel screen points -> standard NDC -> the
existing ClipPlaneSet machinery) into the assembly's outside-view block
after Collect, ahead of the single PrepareClipFrame publication (moved
below the walk block). The Landscape event carries the walk's active
view count on the record's existing OutsideViewCount field (trace
mapping compares kind only - zero oracle-fixture churn) and the driver
fans exactly that many terrain slices; activeTerrainSliceCount is
deleted end to end. Outdoor roots keep the assembler's single
full-screen slice, asserted ==1.
Hermetic 6,762/0 (4 new materializer tests pin the y-flip and
plane-sign conventions), Walk lane 209/1, InstalledDat walk conformance
40/1. Seals/cell slices/look-in seeding stay on the old per-cell views
for the rest of FW4 (identical dat polygons; only the visible set can
differ).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The total-fallback assertion pinned ViewerCellId == 0 - the pre-fix
adaptation. Retail set_viewer copies the whole player Position,
objcell_id included, so the fallback inherits the player cell; the
re-extend-from-the-player property the test exists for is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The dense-Arwic re-measure crashed with VK_ERROR_DEVICE_LOST: between
ordered ranges the walk leaf draws (terrain, shells, sky, punch fans)
and RetailAlphaQueue flushes rebind the SAME set-0 storage slots to
their own sections, so the bind-once latch made the next range draw
against foreign buffers - out-of-bounds instance reads and a GPU
fault. Sections now re-bind on every DrawOrderedRange call, exactly
like the proven DrawPreparedAlphaBatchRhi; the once-per-frame ring
WRITES in PrepareOrderedStream (the actual measured cost) are
unchanged. The bind-once referee test flips to assert per-range
rebinds with unchanged draw coverage.
Suites: full Release build 0 warnings; hermetic 6,758/0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The FW3.4 dense-Arwic pair triggered the +/-20% stop rule (+33.5% CPU
p50, 14x frame allocation). This slice removes the three measured
costs without changing GPU command order (the referee suites assert
identical recorded call sequences):
- WalkFrameDriver: Collect (ONE walk per frame - no GPU work; leaf
calls and flush points become a recorded event list; the driver
absorbed the renderer collection pass and exposes the visited sets)
+ Replay (prepare the whole stream once, then replay events,
interleaving DrawOrderedRange with leaf calls in the exact recorded
order). RunFrame = Collect+Replay for existing callers.
- WbDrawDispatcher: SubmitOrderedStream split into PrepareOrderedStream
(all sections + commands + merge runs uploaded once per frame) and
DrawOrderedRange (bind-once latch; per-run pipeline + DrawIdOffset +
DrawIndirectRangeRhi). Load-bearing correctness catch from the
implementation round: merge runs take FORCED BREAKS at the recorded
event marks - whole-stream merging must not fuse two segments that
retail separates with a leaf GPU call (shell, punch); the straddle
assert stays as a dead-code safety net.
- WalkProductionWorldData: WalkFrameStaticRecords carries an
ArraySegment into a per-frame grow-only arena; the per-cell
fresh-array copies (the 1.9 MB/frame alloc p50) are gone - zero
steady-state allocation after warmup.
Suites (lead-verified): full Release build 0 warnings; hermetic
6,758/0; Walk lane 209/1; InstalledDat Walk conformance 40/1
untouched. Next: the dense-Arwic re-measure against the same-session
baseline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Retail draws cell geometry at the dat EnvCell origin verbatim; the
0.02 m lift was our z-fight stand-in (register row AP-32, deleted in
this commit). With the walk owning retail draw ORDER under
WorldDepthContract Less (first-drawn-wins: DrawBlock terrain-then-
objects per cell, DrawCells shells-then-contents), the coplanar
tie-breaks the lift compensated for are now resolved the way retail
resolves them.
Deleted at every site: the PortalVisibilityBuilder const + the
drawLiftZ Build parameter and its lifted exit-portal projection branch
(gate and drawn geometry now share ONE space); the seal/punch fan
lifts (DrawPortalDepthWrite + the walk's DrawWalkPunchFan); the
LandblockBuildFactory drawn-cell-transform lift (render and physics
share the one verbatim transform).
The #130 proof flipped exactly as its own doc predicted:
UnliftedGate_LeavesTheStripAtTheDrawnTopEdge is deleted (its premise -
gate space != drawn space - no longer exists), and the renamed
ExitDoorTopEdge_GateCoversTheDrawnApertureWithinPixelTolerance sweep
(147 eye/gaze combos at the Holtburg corner door) passes with both in
the same unlifted space (worst plane gap <= 1.2 px, scissor <= 0.15 px
- unchanged tolerances). Ten more replay-test call sites swept to the
new Build signature.
Suites: full Release build 0 warnings; hermetic 6,750/0; the 21
affected InstalledDat replay tests green; Walk conformance 40/1
untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The retail frame walk now drives every production static draw. In
RetailPViewRenderer.DrawInside, when the concrete executor + the
packed product + the FW3.1 walk registries are all wired (all
production compositions - anything less throws):
- A pre-walk events-only collection pass (the shadow sink generalized
to WalkVisitedSetCollector) gathers the frame's visited cells,
buildings, and landscape-cell turns; the visited cells union into
prepareCells so EnvCellRenderer prepares every shell the driver
draws.
- DrawWalkDrivenStatics runs the WalkFrameDriver over the production
world data (WalkProductionWorldData over RenderSceneQuery + the
building registry): sky, terrain slices, outdoor statics at their
landscape-cell turns, buildings (alpha barrier -> punch/look-in
passes -> shell) in retail order, interior clear+seals as leaf
closures (the old tail block's drain reasoning moves with them),
flood cells shell-then-contents. Landscape/cell-stage particle
owners re-source from the walk's visited sets - retail gates
particles per cell turn (ShouldDrawParticles @0x0050FE60), which
this is; the old sphere filter was the approximation.
- DrawLandscapeDynamicsPhase + DrawBuildingLookInDynamics carry the
dynamics-only remainder (LookInObject now dynamic-classified,
late outside-dynamics + weather, particle unions); DrawDynamicsLast
and the outdoor flush are unchanged.
- The product builder stops emitting LandscapeOutdoorStatic /
LandscapeBuildingShell / CellStatic (methods deleted, dead index
tracking removed); LookInObject loads cells with
includeStatics: false.
The old static path survives ONLY behind !walkActive for the
standalone/diagnostic executor-fake path that keeps 15 retail-ordering
regression tests exercising the barrier/punch/seal machinery; no
production composition can reach it. Its deletion is FW4 scope (the
plan's "deleting the patch apparatus") - recorded in the plan.
Transitional risks recorded in code/report: the two-pass walk cost
(FW3.4 measures), the interior slice-count reconciliation between the
old clip assembly and the walk's own exit-view survival, and the
outdoor merged-flood punch coverage now riding the walk's own
building-BSP punches (retail-faithful per FW1; the owner visual gate
verifies).
Suites (lead-verified): full Release build 0 warnings; hermetic
6,750/0 (baseline minus the three deleted route tests); Walk lane
201/1; InstalledDat Walk conformance 40/1 untouched. The two
IL-branch tests the implementation round reported failing pass in
every lead run - the recurring parallel-load flake pair.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Additive, nothing invokes it yet (the cutover flip is step 2):
- WalkProductionLeafRenderer + RetailPViewPassExecutor.WalkLeaf: the
walk driver's leaf turns over the SAME executor renderers the packed
path uses today - DrawWalkSky (the per-slice sky block looped under
one driver turn), DrawWalkTerrainSlice (the terrain block of
DrawLandscapeSlice), per-cell shells via EnvCellRenderer,
DrawWalkPunchFan (PortalDepthMaskRenderer far-Z, +ShellDrawLiftZ
matching today's DrawPortalDepthWrite until FW3.3 retires it), the
alpha barrier via FlushLandscapeAlphaFartherThan, and caller-supplied
clear/seal actions (the renderer owns the pass scope). The cutover
changes ORDER, never leaf mechanics.
- Punch fans now carry the ACTIVE VIEW INDEX end to end (retail pins
building_view = Render::portal_view_num @0x0059f3bf for the whole
two-pass walk; the fan clips by that view's slice planes):
PortalPassSink.ActiveViewIndex -> IWalkEventSink.OnPunchGeometry ->
IWalkFrameLeafRenderer.DrawPunchFan.
- The FW3.2b-2 rooting design is recorded in the plan (dual-compute
split, LookInObject route filtered to dynamics, consumer
re-pointing, gate list).
Suites: full Release build 0 warnings; Walk lane green; hermetic green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The oracle trace order DI|DC|LS is breakpoint-ENTRY order; retail's
actual DRAW order inside PView::DrawCells @0x005a4840 for an interior
root is LScape::draw FIRST (pc:432719, only when exit views survive),
then the depth clear (pc:432731-432732), the exit-portal seals
(pc:432785-432786), THEN the flood's own cells far-to-near. The
driver drew flood cells before the landscape - inverted.
RetailFrameWalk.DrawInside gains the additive
OnInteriorFloodDrawTurn(cells) hook firing after the conditional
landscape turn; the DC EVENT stays at its original point (conformance
untouched - 40/1 InstalledDat green). WalkFrameDriver records the
interior flood at the DC turn and draws it at the new turn:
flush -> ClearInteriorDepth -> flush -> DrawExitSeals -> per-cell
shell-then-contents. Building look-in floods still draw immediately at
their building turn (retail's reentrant DrawCells with no clear/seal).
Two new leaf members map to IWorldPassScope.ClearInteriorDepth and the
seal-fan machinery at FW3.2b-2. Reconciliation note recorded: the
driver clears unconditionally for interior roots while production
stages the clear on OutsideViewSlices>0 - observably equivalent at
ov=0, awaiting a firmer decomp read of the clear's gate.
Suites: full Release build 0 warnings; Walk lane 201/1 skip;
hermetic 6,753/0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WalkFrameDriver executes one full static-content frame from the walk's
turns so GPU command-buffer order equals retail's walk order. One rule
does the interleaving: the accumulated OrderedDrawStream flushes
through SubmitOrderedStream immediately before EVERY non-stream draw
(sky, terrain slice, cell shell, punch fan, alpha barrier).
Turn script, all decomp-cited and two of them corrected in review:
- Interior flood: per cell IN FLOOD ORDER, shell first then contents
(PView::DrawCells @0x005a4840: DrawEnvCell @0x005a4abe precedes
DrawObjCellForDummies @0x005a4b0d).
- Landscape: sky once, terrain per active slice, then blocks
far-to-near; per cell the building turn precedes the cell's outdoor
statics (DrawSortCell @0x0059f140).
- Building (DrawBuilding @0x0059f2a0): the BLD probe event stays at
entry, but the ENTIRE body - alpha barrier, portal passes, shell -
sits inside retail's gfxobj[deg_level]!=0 gate @0x0059f2d3, and the
order is FlushAlphaList @0x0059f30b -> the two-pass punch/look-in
walk -> THEN the shell draw @0x0059f345. The driver review caught
both the missing gate and a shell-before-punch inversion; fixed
with the addresses cited.
Walk seam: three additive default-implemented IWalkEventSink hooks
(OnLandscapeCellTurn / OnBuildingTurn / OnBuildingShellTurn /
OnPunchGeometry) - every existing sink and all FW1 conformance
fixtures unchanged. WalkLandBlock gains LandblockId for the cell-id
encoding. Leaf draws go through IWalkFrameLeafRenderer so FW3.2b-2
wires the real renderers and the referee suite runs on fakes +
RecordingGpuDevice.
Flagged for FW3.2b-2/FW4 adjudication (documented in code):
FlushFartherThan(building distance) vs retail flush-all
FlushAlphaList(0f); terrain-before-statics within the landscape turn.
Suites: full Release build 0 warnings; Walk lane 200/1 skip;
InstalledDat Walk conformance 40/1 untouched; hermetic 6,752/0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The piece that turns walk-visited static content into draws, with no
production frame wiring (FW3.2b roots the frame):
- TryClassifyBatch: ONE shared per-batch classify core (the #426
untextured gate, #188 opacity promotion, texture resolve, foliage
classification, in the exact original order) extracted from
ClassifyBatches; the classic and packed classifiers now call it -
behavior-identical, proven by the full hermetic + InstalledDat +
Core Wb suites.
- ClassifyEntityForWalk / WalkClassifiedBatch: the per-entity seam
yielding per-batch keys + instance data WITHOUT InstanceGroup
bucketing, plus the per-part selection data (picking stays alive on
the walk path - the survey's unlisted-consumer fix).
- WalkStaticStreamPopulator: per-entity walk-ordered opaque appends
(under depth Less, opaque order is pixel-relevant only for coplanar
surfaces, which retail resolves first-drawn-wins in ITS order -
never material-grouped), translucent instances to the SAME
RetailAlphaQueue via SubmitWalkAlphaInstance (identical viewer
distances; walk-order submission improves retail's tie fidelity),
selection parts published per entity.
- SubmitOrderedStream now owns _orderedDrawCullModes, retiring the
FW2-recorded alpha-scope interleaving constraint;
DrawIndirectRangeRhi takes an optional cull array (all existing
call sites unchanged). The referee test was verified to FAIL
against the old shared-scratch behavior.
- WalkDrawStage.OutdoorStatic added for the landscape turn.
Suites: full Release build 0 warnings; Walk lane 195/1 skip;
hermetic 6,747/0 (the two failures the implementation round reported
were transient - both pass in isolation and in the full run).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The retail frame walk's world model now materializes from production
landblock-build owners through the legal IDatReaderWriter seam, with
zero frame wiring (FW3.2 roots the frame):
- WalkCellFactory: WalkCell built in the SAME pass as LoadedCell
(EnvCellLandblockBuild.BuildVisibilityCell) from the raw portal
Flags/polygons/planes/stab lists already parsed there; stored as
LoadedCell.Walk, committed atomically with the cell. The
fixture-pinned decodes (inverse-0x2 portal side, 0xFFFF->0xFFFFFFFF
exit widening) live here.
- WalkBuildingFactory + WalkBuildingRegistry: the production
WalkBuilding build (drawing BSP with PORT nodes, degrade ladder,
portal sides/stab lists, sort center, model frame) from the SAME
LandBlockInfo the streaming build already fetches, under the
factory's existing DAT lock - closing the gap where BuildingLoader
drops every walk field at load.
- WalkLandscapeAssembler: the retail 51x51 viewer-centred grid
(mid_radius 25) fed incrementally from landblock publish/retire;
per-block z-slab (heightTable[max]+200 / [min]-1) computed
worker-side in LandblockBuildFactory from the heights already in
hand. O(1) SetViewer on same-block frames.
- WalkProductionFrameContext: the walk's frame contexts over
CellVisibility + WalkBuildingRegistry with a generic
inverse-view-projection ray caster (rays feed cross products only -
scale-free) and the znear=0.1 CY plane.
- Publication: LandblockRenderPublisher owns both walk registries,
publishing in the same AdvanceCompleteOne step as BuildingRegistry
and retiring in RemoveBuildingRegistry - same commit, same
retirement, no new ticket stage.
Conformance: ALL TEN oracle fixtures replay identically through the
PRODUCTION builders (WalkProductionWorldConformanceTests) - same
signatures as the test adapter, first run. Known gap documented for
FW3.2: far-tier landblocks carry no EnvCell transaction, so their
z-slab never reaches the assembler.
Suites: full Release build 0 warnings; Walk lane 186/1 skip;
hermetic 6,738/0 (+24); RuntimeDatAccessArchitectureTests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The walk-order submission layer over the existing RHI (plan section FW2):
- OrderedDrawStream: append-only walk-ordered draw commands
(GroupKey + transform + per-instance data + WalkDrawStage + cell
provenance), struct-of-arrays with one lockstep Reset (#193 shape).
The PortalPunch stage exists but has no FW2 submission path - the
submitter throws on it; punch emission lands with FW3 wiring.
- WbDrawDispatcher.OrderedStream partial: per-instance-first emission
(the deferred-alpha shape - command i owns instance i, walk order
survives into the indirect array), each SSBO section written once,
then one DrawIndirectRangeRhi call per maximal merge run. Runs are
built by pure-CPU BuildOrderedMergeRuns and may never span a stage,
pipeline-bucket, or cull boundary; ValidateMergeRun re-checks every
emitted run and throws (the campaign fail-loud rule). Nothing is
sorted, reordered, or dropped: N commands in, N indirect commands
out, covered exactly once.
- WorldDepthContract: retail world depth verified verbatim from the
decomp - Render::zfuncVal @0x00820e1c = 0x2, SetDepthBufferMode
@0x005a2d10 writes the enum directly as D3DRS_ZFUNC so the value IS
D3DCMP_LESS, applied by the surface-state applier @0x0059c80a with
Z-write toggled by blend; the LESSEQUAL sites are GameSky::Draw-local.
Seven world pipeline sites now cite the named constant (no value
changes).
- Plan updated: FW1 status block + gate amendment (the ten pose-stamped
retail traces supersede re-expressing the old-builder replay
fixtures; those retire with the old builder at FW4 and their
scenario classes re-verify at the FW3/FW4 connected gates).
Known FW2 scope notes recorded in the code: the building-detail
overlay replay is production wiring (FW3); the _drawCullModes scratch
may not interleave with a mid-flight RetailAlphaQueue scope (FW3
sequencing constraint). The pixel A/B equivalence proof rides FW3's
cutover toggle where a walk-driven scene first exists.
Suites: full Release build 0 warnings; Walk lane 154/1 skip;
hermetic 6,714/0 (+27 new).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Render::deg_mul is auto-tuned by frame load and SWUNG between oracle
captures: doorway-still ran right after the heavy terrace-edge capture
with the multiplier depressed (mul <= 0 puts thresholds at/below ideal,
so 001e/0026/002f select the portless level 1 - retail's zero look-in
floods, reproduced exactly at mul = 0), while every other fixture pins
~ +0.99 (thresholds at max). The recon session's "-0.99" live dump was
real - taken under the same cdb load. RetailFrameWalk now exposes the
multiplier; the doorway test pins 0, the rest use the default.
foundry-entry: frames 1-66 reproduce exactly; the F67-F79 standing
segment diverges ONLY in building 0036's intra-building DC order
(retail 116,118,11d vs replay 11d,116,118). The BSP-traversal microscope
pins the flip to 0036's ROOT plane (N=(0,0,-1) D=2.8): replay eye z
2.33 (d=+0.47, NEG-first) vs retail behaving as d<0 (POS-first) - a
structural ~0.5 m frame question (positionPush(2)/part-scale), only
adjudicable live. Turnkey probe:
tools/walk-oracle/fw1-f67-viewpoint-probe.cdb.
Fixture status: nine of ten fully conformant; foundry-entry exact
through F66 with the 13-frame order segment parked on the probe.
Suites: Walk 127/1 skip; hermetic 6,687/0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The moving-fixture divergence was the building degrade ladder:
GfxObjDegradeInfo::get_degrade @0x0051e4b0 (Ghidra-verified - BN's
FPU-flag pseudo-C misread BOTH arm selection and one formula) slides
each level's threshold from ideal toward MAX as the multiplier
approaches 1, and the live client runs the positive arm at ~0.99, so
level 0's portal-bearing BSP survives to ~max_dist (48 for the Holtburg
cottages), not ideal (24). The recon note's "deg_mul = -0.99" was a
sign misread; the negative arm's threshold slides toward MIN and
contradicts the fixtures from both directions. With the two-arm port,
holtburg-walkout, holtburg-transitions, and holtburg-walkabout pass
every pairable frame - the walkout-F2 microscope's prediction
(103,100 | 100x3 | 124 through the cottage exit views) landed exactly.
Also this round, falsified and reverted: a BN-driven swap of the
portal walker's negative/in-plane arms (Ghidra shows side 1 = negative
EMITS, side 2 = in-plane does not - the original port was correct; the
swap broke four fixtures). The walker docs and unit tests now pin the
Ghidra-verified truth table.
Parked with findings: foundry-entry F67 (right flood set, one
plane-side classification at the +/-eps boundary orders 11d before
116/118) and doorway-still (retail shows zero floods at a pose one
meter from walkout-F2's flooding pose; multi-portal clip boundary).
Suites: Walk 124/3 skips; hermetic 6,687/0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>