From 5c64679b11ed216e8a83fc0a2638ebbf13d0e59a Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 3 Sep 2026 07:14:39 +0200 Subject: [PATCH] docs(render): S3 chunk 2 contract -- exact PView state Decomp-verified during the capture session: PView::DrawCells 0x005a4840 keeps the landscape flush, stamp advance, gated Z clear and exit seals inside outside_view.view_count > 0; the clear is gated by the persistent D3DPolyRender::portalsDrawnCount (0x008719b4), which only exit seals feed in the building path (pass 1 punches pass TRUE; forceClear is never written). acdream's unconditional clear/seals and the _skyDrawnThisFrame proxy are the divergence chunk 2 retires. The captures' second PView is outdoor_pview's look-in flood (RenderDeviceD3D::Init 0x0059efb0). Also: the cathedral floating-stairs scene row now expects retail's exterior-slab leak (retail axiom), the retail screenshot is saved at logs/oh-capture/cathedral-leak.retail.png, and the S3 ledger row moves to chunk 2 in progress. Co-Authored-By: Claude Fable 5.1 --- ...-09-01-campaign-overhaul-world-solidity.md | 4 +- .../s3-walk-ownership-map.md | 160 +++++++++++++++++- 2 files changed, 159 insertions(+), 5 deletions(-) diff --git a/docs/plans/2026-09-01-campaign-overhaul-world-solidity.md b/docs/plans/2026-09-01-campaign-overhaul-world-solidity.md index f4a5dbe5..a2235fb0 100644 --- a/docs/plans/2026-09-01-campaign-overhaul-world-solidity.md +++ b/docs/plans/2026-09-01-campaign-overhaul-world-solidity.md @@ -583,7 +583,7 @@ code. |---|---|---|---| | Cathedral south transition | `0xF4180106 [37.181568 46.790077 169.804993]` ↔ `0xF4180104 [37.310383 48.895710 169.804993]` | Player and walls whole; no wand/body depth inversion | S3–S4 | | Cathedral wall actor | Observer `0x104`, `0x101`, outdoors; remote in `0xF4180112 [36.299465 18.594580 169.804993]` | Actor visible only through a retail-valid sightline | S2–S4 | -| Cathedral floating stairs | `0xF4180107 [38.311169 24.270454 177.423584]` ↔ `0xF4180112 [38.333950 23.704699 177.868729]` | Every slab stable; no moving wall-textured triangles | S1–S4 | +| Cathedral floating stairs | `0xF4180107 [38.311169 24.270454 177.423584]` ↔ `0xF4180112 [38.333950 23.704699 177.868729]` | Every slab stable; no moving wall-textured triangles; the exterior-wall slab through the stair chamber MATCHES retail's (retail axiom 2026-09-03, `logs/oh-capture/cathedral-leak.retail.png`) — same slab, same extent, not absent | S1–S4 | | Cathedral exterior ramp | Stand on the formerly invisible ramp | Authored shell/member always renders | S1–S3 | | Facility Hub stairs | `0x8A02015E [60.971485 -42.752495 -4.121752]`, `0x8A02015F [58.815380 -49.425373 -0.857726]` | Stairs/player stable at bottom, side, ascent, top, zoom | S1–S4 | | Nanto waterfalls | `0xE43D001E [93.826614 126.522484 120.005005]` | Falls do not vanish while rotating; houses occlude | S4–S5 | @@ -660,7 +660,7 @@ Update immediately when a slice changes state. Chat is not the ledger. | S1 Geometry | **CLOSED — G1 PASSED 2026-09-02** | `oh2-cellstruct-surface-contract.md` (§3.5 arbitrated on the binary) | chunk A `acf17246`; chunk B `b681717c`; hygiene `0840d5fb`; review fixes `e2543d0e`; G1 regression fix `8c6563ca` (segment/batch pairing) + its pin test (next commit) | lead verified both chunks against the named pseudo-C and the paired binary; two Opus lens reviews, 8 findings all verified by the lead and fixed; the review round's own upload-order change desynchronized cell-shell index segments from batches (first G1 launch: magenta/stretched/missing walls in every dungeon) and was root-caused and fixed by the lead before the re-gate | build 0/0; Content 213/213 (incl. PreparedPackage on the recipe-8 pak); Core 4,948/4,948 (one load-sensitive allocation test outside S1 passes alone); Bake 21/21; Launcher.Core 365/365; Runtime 1,884/1,884; App hermetic 6,757/6,757 | **G1 PASS**: Facility Hub stairs/doorways, Holtburg house interior, ordinary dungeon, town portal network all whole with correct materials on `8c6563ca` + recipe 8; cathedral shows the ORIGINAL symptom family (neighbor-cell walls drawn through portals over the room), explicitly outside G1 and carried to S3/S4 | dev pak recipe 8 (597,369,112 B; `acdream.recipe7.pak` kept); TOC delta explained; corpus scan old-only 3,197 (all untextured), new-only 0, unexplained 0 | | Capture | READY | §7 + `tools/walk-oracle/oh/README.md` | scripts committed `ebaa41df` + `14d8fe64`; lead resolved DBObj::m_DID=+0x28 and the cdecl alpha-function conventions; recon attach re-confirms | — | recon dt/x/uf dumps at session start | owner session ~1 h | before S3 | | S2 World graph | CLOSED — G2 PASSED (owner, 2026-09-03): Facility Hub PASS, Holtburg house PASS, cathedral leak unchanged/not worse (S3/S4 target), portal haze FIXED by chunk 6 and owner-confirmed; the terrain doorway-punch finding is carried to S4; the chunk-6 retail-lens review (Opus) returned no blocking finding; its one MAJOR (a cell reached through two portals got two particle turns per frame) and the minor items are fixed in the follow-up commit: frame-scoped particle-turn dedupe beside the shell dedupe, zero-emitter cells cost one count check, the scope-cost counter maintained on the cell path, the dead `OutdoorSceneParticleEntityIds` stub chain deleted through the composition root, AD-117 item 4 names the owner-cell substitution and the absent per-emission `AddPartToShadowCells`, and a sink-level pin that an emitter's cell membership is the owner's pose cell and survives the projection-visibility switch (the review's F7 one-frame eligibility lag is the per-frame view pass, documented in the pin). RETAIL AXIOM (owner, 2026-09-03, at the capture session, 2013 v11.4186 client on local ACE): standing on the cathedral stairs, RETAIL ITSELF shows part of the same artifact — the outside structure leaking into the interior — CONFIRMED in BOTH the 2013 v11.4186 client and the 2015 EoR client (owner switched clients to check), so the cathedral leak is retail behavior S3/S4 must REPRODUCE, not remove; the retail transcript at that pose is captured as `logs/oh-capture/cathedral-leak.{walk,parts,alphadepth}.log` (walk 5 frames with LC/SC landscape order, parts 3 frames, alpha-depth 5 frames, recon-verified offsets); owner's retail screenshot at the pose: an exterior wall slab cutting diagonally through the stair chamber over the interior ramps (`logs/oh-capture/cathedral-leak.retail.png`, owner-saved) | `s2-membership-ownership-map.md` (`e7ad25a7`) + Contracts A/B; closeout evidence §8/§9 | chunk 1a `059b8883`; chunk 1b `5a2792d6`; chunk 2 `707d2803` (render statics borrow the registry's retail array; render flood deleted); chunk 3 `afbd2410` (one flood per registration feeds collision rows and render entries; staged SetPosition carries both products); chunk 4 `75ea269d` (movement publishes from the transition's cells; children inherit at registration); chunk 5 + closeout `c94a1a40` (borrowed per-cell views, render-only registration, sweeps/fallbacks deleted; reflood forwards the part array; NON-COLLIDING DAT STATICS register render-only from both publishers; the dual-review fix batch: Suspend clears the retail product, attach/detach advance the mutation revision, an attached child never floods on its own, RemoveLandblock/RetireOwnerFromLandblock prune retail rows, render-only owners move with their destination cell (retail's zero-sphere `find_cell_list` 0x0052b4e0, verified statically 2026-09-03 — AD-117 item 1 dropped), empty part array == null, per-move closures removed, EnvCell shells out of the entity-id index, index predicate compares the id, dead per-cell scene indices deleted) | lead corrected 1a's route decision before landing; chunk 2 verified by the lead; chunk 5's agent report missed the non-colliding-static population (found by the self-gate PIXEL DIFF, not by eye: Facility wall panels grey); S2 dual review (arch + retail lens, Opus) produced 12 + 7 findings, every one lead-verified against the source before an edit; the four BLOCKING/MAJOR lifetime gaps were real | at `c94a1a40`: Core 4,984/4,984; Content 214/214; Runtime 1,884/1,884; App hermetic lane 6,760/6,760; App InstalledDat 217 pass / 1 skip / 2 pre-existing #383; App Windows 1/1 | G2 PROVISIONAL PASS. Self-gate runs (kit `ea546f38`, pwsh): `logs/selfgate-20260902-231426-baseline-g1-binary` (reference), `…231642-s2-chunk3`, `…233845-s2-chunk4` (identical), `logs/selfgate-20260903-002632-s2-chunk5-reflood` (FAIL: Facility wall panels grey, left-wall mean RGB 29,26,23 vs baseline 65,17,21 — missing non-colliding statics), `logs/selfgate-20260903-010022-s2-review-fixes-3` (candidate: panels back at 79,20,25 = chunk-3/4; cathedral 13.6% / Facility 15.0% / house 13.4% px differ vs baseline, the chunk-3/4 band; graceful logout, zero exceptions). Membership-probe run `logs/selfgate-20260903-003013-s2-chunk5-membership-probe`: `[walk-membership]` fired only on two transient Holtburg login frames, never in Facility Hub or the cathedral. Connected R6 soak on the chunk-4 build `6717a3e5` (`logs/connected-r6-soak-20260902-234016.report.json`): 9 destinations in 511 s, zero invariant failures, graceful exit — route PASS (its `-CollisionShadowEvery` referee is the obsolete I5 graph-vs-flat comparator; retire in S5). Connected R6 soak on the FINAL S2 build (`logs/connected-r6-soak-20260903-010403.report.json`, binary matches source, exit 0, graceful=True): 9 canonical checkpoints, 0 failures, 10 warnings (server-side population drift + DAT VFX table messages), 522 s — route PASS. OWNER G2 CHECK 2026-09-03 (client at `4b0b29e4`): Facility Hub PASS, Holtburg house PASS; cathedral: the original leak unchanged, NOT worse (S3/S4 target, G2 bar met); TWO NEW OWNER FINDINGS block G2 closure — (a) parts of buildings leak through terrain outdoors in Holtburg — owner screenshot shows doorway-sized fragments of the houses on the terrace BELOW showing through the hill; mechanism identified: `PortalDepthMaskRenderer` draws every visited cell's `OtherCellId == 0xFFFF` portal fan (`RetailPViewPassExecutor` `DrawDepthFan`) with depth compare `Always` + depth write, so a building doorway BEHIND terrain still punches far depth through the hill and the interior draws through the hole; retail's far punch cannot be `Always` against terrain — the exact z-func is S4's first question (the #117 punch stencil is already on S4's delete list; the retail-world contract §5.4 wording must be re-read at the capture). Pre-existing (not an S2 regression), assigned to S4; (b) the purple portal-exit haze on the character no longer appears on arrival — ROOT CAUSE FOUND (instrumented runs `logs/selfgate-20260903-053731-haze-candidate`, `…-054153-haze-candidate-2`, chunk-4 control `…/bisect-chunk4/logs/selfgate-20260903-054328-haze-chunk4`): the server keeps the player Hidden until acdream sends LoginComplete at reveal `complete` (retail-correct); the Hidden-state script's emitters spawn in the arrival cell at `materialized`, are view-eligible at `world-visible`, and are never DRAWN (frame `h02-arrive-400ms`: room, no character, no cloud) because the walk draws an owner's emitters only through the owner's registry rows and a hidden owner's shadow is suspended; the deleted chunk-5 fallback used to carry them. Retail gives every emitter its OWN cell membership (`add_particle_shadow_to_cell` 0x00514a70, one shadow in its own cell, no clip planes, drawn at that cell's turn regardless of the parent's hidden state) — the branch AD-117 item 4 had deferred as 'no production path'. Fix = S2 chunk 6 `f6b4584b` (packet §10): `ParticleSystem` per-pass cell→handles index + `DrawForCell`; the walk draws particles BY CELL at the existing turns; every owner-union particle path deleted; the post-replay per-cell pass deleted (it double-submitted the root flood — the implementer flagged it, the lead verified `EmitCellContentsTurn` fires `CellParticles` for root-flood and look-in cells alike); AD-117 item 4 → port note; temporary probes removed. VERIFIED: `logs/selfgate-20260903-062522-haze-chunk6` frame `h02-arrive-400ms` shows the purple cloud at the character in Facility Hub; three-pose self-gate `logs/selfgate-20260903-062615-s2-chunk6` in the accepted band (cathedral 12.7% / Facility 15.1% / house 13.9% px vs baseline, wall-panel mean 79,20,25), zero exceptions. Gates at `f6b4584b`: Core 4,987/4,987; Content 214/214; Runtime 1,884/1,884; App hermetic 6,760/6,760; App InstalledDat 217 / 2 pre-existing #383. Owner re-check of the haze: CONFIRMED FIXED 2026-09-03 ("Ok yeah it is fixed now"). Morning handoff: `docs/research/2026-09-01-overhaul/2026-09-03-g2-morning-handoff.md` | owner double-checks G2 (handoff checklist); then the owner capture session (the S3 packet §7 names the two traces); then S3 | -| S3 Walk | PACKET DRAFTED 2026-09-03 — implementation BLOCKED on the §7 capture session | `s3-walk-ownership-map.md` (retail table, current owners, four chunks, review lenses, gate, the three exact captures to ask for) + built-mesh/world contracts | — | — | — | folded into G3 | owner capture session first (§7 of the packet names the three traces); chunk 2 may start on the decomp oracle alone | +| S3 Walk | CHUNK 2 IN PROGRESS 2026-09-03 (contract `s3-walk-ownership-map.md` §8, decomp-verified during the capture session; captures done: cathedral-leak, cathedral-arrival; owed: holtburg-doorway-still, terrace-edge, foundry-deep). Chunk 2 finding: retail gates the landscape flush, stamp advance, Z clear and exit seals on `outside_view.view_count > 0` (`PView::DrawCells` 0x005a4840) and the Z clear additionally on the persistent seal counter `D3DPolyRender::portalsDrawnCount` (fed by exit seals alone in the building path) — acdream's unconditional clear/seals and the `_skyDrawnThisFrame` proxy are the divergence chunk 2 retires | `s3-walk-ownership-map.md` (retail table, current owners, four chunks, review lenses, gate, the three exact captures to ask for) + built-mesh/world contracts | — | — | — | folded into G3 | chunk 2 implementing on the decomp oracle; the remaining three captures land when the owner is at each pose; chunk 1 (transcript emitter/parser) follows the captures | | S4 Depth + alpha | PLANNED | depth/alpha contracts + captures | — | — | — | G3 | retires AP-34 | | S5 Consumers + closeout | PLANNED | landscape contract; AP-232 | — | — | — | G4 | retires AP-117/AP-232; deletes probes | diff --git a/docs/research/2026-09-01-overhaul/s3-walk-ownership-map.md b/docs/research/2026-09-01-overhaul/s3-walk-ownership-map.md index 75315c10..7140391d 100644 --- a/docs/research/2026-09-01-overhaul/s3-walk-ownership-map.md +++ b/docs/research/2026-09-01-overhaul/s3-walk-ownership-map.md @@ -145,8 +145,8 @@ Lead verifies every finding against the source before an edit (S2 precedent: `ov=4`, EC/OC stamps, 531 `LC`/`SC` land-cell draws per frame in far-to-near order over landblocks `f3`–`fe` × `00`–`18`), `cathedral-leak.parts.log` (3 frames, 5,933 PD / 965 DM), `cathedral-leak.alphadepth.log` (5 frames, - 1,407 AM / 2,856 FL / 28 PM / 16 PC). Owner's retail screenshot (2026-09-03, 2013 client, to be saved as - `logs/oh-capture/cathedral-leak.retail.png`): standing on the floating + 1,407 AM / 2,856 FL / 28 PM / 16 PC). Owner's retail screenshot (2026-09-03, 2013 client, SAVED at + `logs/oh-capture/cathedral-leak.retail.png`, lead-verified): standing on the floating stairs in `f4180108`, a large slab of the EXTERIOR wall (arched exterior stone texture) cuts diagonally through the stair chamber from the upper left, drawn over the interior landings and ramps that remain visible around @@ -157,7 +157,8 @@ Lead verifies every finding against the source before an edit (S2 precedent: `logs/oh-capture/cathedral-arrival.walk.log` (5 frames, 5,604 lines: main PView `009d4f48` DI `f4180108`, DC `ov=3` n=2 `f4180108 f4180107`; a SECOND PView `009d4530` with `ov=0` draws cells `f4180113/f4180112/f4180114` each - frame with no DI of its own — its identity is a chunk-2 audit item; 454 + frame with no DI of its own — RESOLVED (§8 R1): it is `RenderDeviceD3D::outdoor_pview`'s + look-in `DrawCells`, driven by `PView::DrawPortal` from the building pass; 454 `LC` + 619 `SC` per frame, 40 EC / 40 OC total), `cathedral-arrival.parts.log` (3 frames, 7,822 PD / 1,240 DM), `cathedral-arrival.alphadepth.log` (5 frames, 1,735 AM / 3,570 FL / 35 PM / 19 PC). NOTE: the kit README expected @@ -174,3 +175,156 @@ Lead verifies every finding against the source before an edit (S2 precedent: `DrawSortCell` 0x0059f140, one line per call with the cell's `m_DID`). 2. Per-frame `outside_view.view_count` and the device-stamp advance count at each pose — chunk 2's oracle for the re-arm gate. + +## 8. Chunk 2 contract — exact PView state (lead, 2026-09-03, written during the capture session) + +Every claim below was re-read this session in the named pseudo-C and, where +BN's FPU rendering was ambiguous, in Ghidra (port 8081, `patchmem.gpr`, the +paired 2013 build). Addresses are the Sept 2013 build. + +### 8.1 Retail facts (verified) + +- **R1 — two PViews.** `RenderDeviceD3D::Init` @0x0059efb0 constructs + `indoor_pview = PView(…, 1)` and `outdoor_pview = PView(…, 0)`; the ctor + argument is `draw_landscape` (@0x005a52c1). `RenderDeviceD3D::DrawInside` + @0x0059f0d0 tail-calls `PView::DrawInside(indoor_pview, cell)`; + `RenderDeviceD3D::DrawPortal` @0x0059f0e0 pins `building_view = -1` around + `PView::DrawPortal(outdoor_pview, …)`. The captures' second `pv` with + `ov=0` (`009d50e0` at cathedral-leak, `009d4530` at cathedral-arrival, + cells `f4180112/13/14`, four `DrawCells` per frame, no `DI`) is + outdoor_pview's look-in flood through the cathedral's exterior portals. + acdream's `RetailFrameWalk._outdoorPView { DrawLandscape = false }` and + `WalkBuildingPortals.DrawPortal` already model this. PIN ONLY. +- **R2 — flood reset.** `PView::ConstructView(CEnvCell)` @0x005a57b0 sets + `outside_view.view_count = 0`, `master_timestamp++`, `cell_todo_num = 0`, + `cell_draw_num = 0` before `InitCell`. `WalkPView.ConstructView` matches. + PIN ONLY. +- **R3 — the `outside_view.view_count > 0` branch encloses the whole + landscape turn.** `PView::DrawCells` @0x005a4840: from 0x005a4852 to + 0x005a49eb, ALL of `useSunlightSet(1)`, `PortalList = this`, + `LScape::draw`, `FlushAlphaList(0f)` @0x005a4872, `m_nFrameStamp += 1` + @0x005a4886, the gated Z clear @0x005a4893–0x005a48a9, and the exit-seal + loop @0x005a48c0–0x005a49eb (per draw-list cell far-to-near, per live view + `CEnvCell::setup_view` @0x005a4983, then `DrawPortalPolyInternal(portal_poly, + 0)` @0x005a49b7 for every portal whose `other_cell_id == 0xffffffff`) sit + INSIDE `if (outside_view.view_count > 0)`. Only `useSunlightSet(0)`, + `restore_all_lighting`, the reverse shell loop @0x005a4a00 and the reverse + object-list loop @0x005a4ade are unconditional. + **DIVERGENCE TODAY:** `WalkFrameDriver.OnInteriorFloodDrawTurn` emits + `ClearInteriorDepth` and `ExitSeals` unconditionally and gates the stamp + advance on the `_skyDrawnThisFrame` proxy; its comment and the driver's + type doc claim "both unconditional for an interior root's own flood" — that + claim is wrong against the decomp, and the `WalkFrameDriverTests` pin that + reads "ov==0 … CLEAR/SEALS still fire unconditionally" pins the wrong + behavior. (The plan's S5 cleanup inventory already lists "unconditional + clear code"; chunk 2 retires it now.) +- **R4 — the Z clear is gated by a persistent seal counter.** The clear is + `if (forceClear != 0 || portalsDrawnCount != 0) Clear(Z)`, with + `D3DPolyRender::portalsDrawnCount` (uint16 @0x008719b4) read-then-zeroed + at that point (@0x005a489c–0x005a489e). `forceClear` (@0x008ed824) is a + `.data` int with no write anywhere in the pseudo-C (its only references + are the two reads and its definition) → the clear fires iff + `portalsDrawnCount != 0`. The counter increments ONLY in + `DrawPortalPolyInternal` @0x0059bc90 at 0x0059bd74, and only when its + second argument is FALSE, after a degenerate-polygon guard (Ghidra: skip + when every vertex has `x == +12`, or every `x == −12`, or every `y == + +12`, or every `y == −12` — an equality sentinel, NOT a ±12 clip box as + BN's FPU rendering suggests) and BEFORE `polyClipFinish` — it counts + calls, not surviving fans. False-argument callers: the exit seals + (@0x005a49b7, literal 0); pass-3 `ConstructView(CBldPortal)` punches + (@0x005a5a7b, `arg5 == 1` → false for pass 3); `PView::DrawPortal`'s + `arg4 == 3` failure branch (@0x005a5b7c). But `DrawMeshInternal` runs + buildings through passes 1 and 2 only (@0x0059f3cc / @0x0059f3d9), and + pass 1's punch passes TRUE (`maxZ1`, no increment) — so in the building + path the counter is fed by EXIT SEALS ALONE. Consequence, a retail quirk + to port as-is: the first `ov>0` interior frame after a period without + seals draws NO Z clear; every later `ov>0` frame clears because the + previous frame's seals armed the counter. Outdoor frames and look-in + `DrawCells` (`ov=0`) neither arm nor consume it. +- **R5 — punch/seal depth state.** `SetDepthBufferMode(DEPTHTEST_ALWAYS, + (maxZ >> 2) & 1)` with `maxZ1 = 7` (argument true: building pass 1) and + `maxZ2 = 6` (argument false: seals, pass 3); both write depth. S4's + terrain-punch question is untouched by chunk 2. +- **R6 — part identity.** Retail stamps the `CPhysicsPart` (+0xdc against the + device's +0xb0). After S2, one logical part reaches the classifier from + every cell it is in with the SAME projection id, so `(RenderProjectionId, + PartIndex)` is the identity. PIN ONLY. + +### 8.2 Production behavior change + +- **B1 — the real count gates the whole turn.** `RetailFrameWalk.DrawInside` + passes `_interiorPView.OutsideView.ViewCount` to + `IWalkEventSink.OnInteriorFloodDrawTurn(cells, outsideViewCount)`. The + driver then executes retail's branch exactly: `outsideViewCount > 0` → + the landscape flush leaf (retail `FlushAlphaList(0f)` @0x005a4872 plus the + pre-clear dynamics hook that `RetailPViewRenderer.ClearWalkInteriorDepth` + bundles today), the stamp advance (`AdvanceWalkPartPassStamp` + the shell + and particle-turn re-arms), then `ClearInteriorDepth` ONLY IF + `PortalsDrawnCount != 0` (read-then-zero, R4), then `ExitSeals`; + `outsideViewCount == 0` → none of them. `_skyDrawnThisFrame` is deleted; + its "second landscape turn" fail-loud guard is kept on a frame-scoped + count field, its "landscape ran" use is replaced by the count the sink + receives. +- **B2 — the counter.** The driver owns `PortalsDrawnCount` (retail + `D3DPolyRender::portalsDrawnCount`), retained ACROSS frames (the driver is + retained by `RetailPViewRenderer`; never cleared by `BeginFrame`/ + `AbortFrame`/`EndFrame`), incremented at the exit-seal turn by the number of + seal polygons actually submitted: for each flood cell (far-to-near) × each + live view slice × each portal with `OtherCellId == 0xFFFF` and ≥3 + vertices — the same enumeration `RetailPViewPassExecutor.DrawPortalDepthWrite` + performs. Implement it as a return value from the seal leaf + (`DrawExitSeals` returns the submitted count) so the count and the draws + come from ONE enumeration; the driver adds it. Never incremented by punch + fans. Not touched by look-in `DrawCells`. +- **B3 — leaf split.** `RetailPViewRenderer.ClearWalkInteriorDepth` splits into + `FlushWalkLandscape` (pre-clear dynamics + `FlushLandscapeAlpha`) and + `ClearWalkInteriorDepth` (the Z clear only); `IWalkFrameLeafRenderer` gains + the flush leaf and `DrawExitSeals` returns `int`; `WalkFrameEventKind` + gains `LandscapeFlush`. The fake leaf in `WalkFrameDriverTests` logs it as + `"LFLUSH"`. +- **B4 — no new owner class.** The packet's "`WalkPView` (new)" is the + EXISTING `WalkPView`; chunk 2 adds no class. The per-category leaf + contract (whole-once built shell; Boolean sphere for ordinary parts; + polygon clip for portal polygons only; repeated submission for the local + player) is pinned by tests — the implementer first LISTS which of these + four pins already exist (`WalkFrameDriverTests`, `WbDrawDispatcher` + WalkClassify tests, `WalkVisibilityMath` pins) and adds ONLY the missing + ones, naming the retail address in each test's comment. + +### 8.3 Tests (flip, add, keep) + +- T1 (flip): the `ov==0` pin expects NO `LFLUSH`/`CLEAR`/`SEALS` and no stamp + advance before the flood — the opposite of today's expectation. +- T2 (add): `ov>0` first frame → `LFLUSH`, NO `CLEAR`, `SEALS`, flood; the next + frame of the same driver → `LFLUSH`, `CLEAR`, `SEALS`, flood (armed by frame + 1's seals); a driver whose flood cells have no exit portal never clears. +- T3 (add): a look-in `DrawCells` (ov=0, `LookInStatic` stage) neither arms + nor consumes the counter. +- T4 (add if absent): R1 (two pviews, the outdoor one never raises an + outside view) and R2 (the reset) pins. +- T5 (update): the existing direct `OnInteriorFloodDrawTurn` callers + (`WalkFrameDriverTests` ≈ lines 349, 602–610, 642, 693–703) pass the count + they model; the two "look-in shell repaints after the clear" tests model + `ov>0`: one asserts the first-frame no-clear quirk explicitly, the other + pre-arms the counter (a prior seal turn) and asserts the clear — both + behaviors stay pinned. +- Keep: every other driver/walk pin unchanged; `LaunchOptionsDocumentationTests` + is unaffected (no flag added or removed). + +### 8.4 Out of scope for chunk 2 + +Terrain interleave (chunk 3); clip-slot deletions and the +`ProbeCathedral*` discriminators (chunk 4 / the S5 inventory); S4's punch +z-func; the pre-clear dynamics hook's retail home (`LScape::draw`'s per-cell +object turn — chunk 3 places it). + +### 8.5 Lead verification and gate + +Every R-fact re-read at its address before the commit; the App hermetic +lane plus the InstalledDat lane green; the three-pose self-gate plus the two +captured cathedral poses pixel-diffed against +`logs/selfgate-20260903-062615-s2-chunk6` — the expected delta is zero at +steady state (the only retail-different frame is the first `ov>0` frame after +a no-seal period, which no still captures); the walk transcript at +cathedral-arrival must still show the root `DC ov=3` and four look-in +`DC ov=0` turns per frame.