diff --git a/docs/research/2026-06-11-t6-br7-shipped-t5-gate-post-t5-handoff.md b/docs/research/2026-06-11-t6-br7-shipped-t5-gate-post-t5-handoff.md new file mode 100644 index 00000000..14be8c8d --- /dev/null +++ b/docs/research/2026-06-11-t6-br7-shipped-t5-gate-post-t5-handoff.md @@ -0,0 +1,259 @@ +# T6 (BR-7) SHIPPED + T5 gate verdict + post-T5 fixes — session handoff (2026-06-11) + +**Branch:** `claude/thirsty-goldberg-51bb9b`, HEAD `0e6e24f`. **Nothing on main.** +**Suites (all green):** Core **1416 / 0 / 2 skips** (skip 1 = pre-existing +`PvsConformanceTests`; skip 2 = `BSPStepUpTests.D4` with the #116 reference), +App **227**, UI **420**, Net **294**. + +This session: (1) shipped **T6 / BR-7** — the A6.P4 per-cell shadow collision +architecture, the last code phase of the holistic building-render port; +(2) ran **T5**, the single comprehensive user visual gate (PARTIAL PASS — +the entire collision half passed, four render artifacts filed); (3) fixed +**#117** (aperture punch-through), armed **#120** (flood-growth tripwire), +narrowed **#118** (exit-vanish). The holistic port (BR-1…BR-7) is now +**code-complete and partially visually validated**. + +Read first, always: `claude-memory/project_render_pipeline_digest.md` + +`claude-memory/project_physics_collision_digest.md` (both updated this +session — current-truth banners on top). + +--- + +## 1. The session's commit ledger (oldest first) + +| SHA | What | +|---|---| +| `6ec4cde` | T6 C1 — signed `OtherPortalId` + the `>= 0` building-transit gate (`check_building_transit` 0x0052c5d0; BN renders the comparison unsigned — Ghidra-proven sign extension; wire 0xFFFF = −1 = no reciprocal portal). Multi-sphere `CheckBuildingTransit` overload + `hitsInteriorCell` out. | +| `abf36e2` | T6 C2 — `CellTransit.BuildShadowCellSet`: the registration-side sphere-overlap portal flood (verbatim `find_cell_list` 0x0052b4e0 via `calc_cross_cells(_static)`). Indoor seed → that cell + growing-array walk; outdoor seed → block-crossing `AddAllOutsideCells`; outdoor cells in the walk run the building bridge; statics get the `do_not_load` prune ({seed} ∪ stab list — also strips outdoor cells, retail-faithful). The spec's VisibleCellIds rule was REFUTED (WF1) — no visibility list anywhere in shadow placement. 11 unit tests. | +| `dbfbf85` | T6 C3 (FUSED — the BR-2 half-port lesson: registration+query co-dependent) — registry rewritten to flood-seeded per-cell lists (CylSphere flood rule: base pt + cyl radius, cap 10, 0x0052b9f0; keep-when-empty pc:283540; `RefloodLandblock` streaming hook = retail `init_objects → recalc_cross_cells`); **building shells left the registry** (per-LandCell channel: `Transition.FindBuildingCollisions` = `CSortCell::find_collisions` 0x005340a0 → `find_building_collisions` 0x006b5300; ONE building per ORIGIN landcell, `init_buildings` 0x0052fd80 verified verbatim + ACE cross-ref; only caller is 0x005340aa); **query strictly per-cell** (`FindObjCollisionsInCell` = `find_obj_collisions` 0x0052b750; insert order env→building→objects on the primary, then `CheckOtherCells` runs env+building+objects per OTHER cell with the carried-cell advance AFTER all object passes — `transitional_insert` 0x0050b6f0 OK_TS case); placement weakening `center_solid=0` when `BldgCheck && HitsInteriorCell` (0x0053a82e / 0x005399d8; both fields added to SpherePath, rebuilt at every cell-array build). **DELETED: the radial 9-LB sweep, the +5 m query pad, the b3ce505 indoor gate, the isViewer exemption.** 3 of the 4 #99-era reds flipped green (door apparatus → `…_Blocks`, tick-13558 asserts the door BLOCKS, tick-22760 pins the blocking invariant). | +| `ca4b482` | T6 C4 — A6.P5 `hasExitPortal` topology widening DELETED (outside cells enter the collision array ONLY on the retail straddle — same flag as the membership pick; the WF1 correction "re-gate, don't delete" implemented); **#90 stickiness REMOVED** (dead code — `ResolveCellId`'s only caller is the cache-null test fallback; the ordered-pick hysteresis owns doorway behavior). Two old A6.P5 pins inverted to retail truth. | +| `60c1070` | docs — T6 ship closeout: #99/#90 closed in ISSUES, #97 likely-close note, **#116 filed** (slide-response family), plan stamped. | +| `af5d424` | docs — **T5 gate verdict** (§3 below): #108/#109/#97 closed (user-confirmed), #117–#120 filed. | +| `2d15084` | **#120 armed** — tripwire self-attribution (`DumpPropagationChain`: root, eye, per-cell frequency, 24-entry chain tail) + `ConvergenceTripwireCount` observable + 2 dat-backed convergence sweeps as regression pins (3024 builds, 0 firings — production-only ingredients suspected). Retail finding: retail RECURSES natively too (`AddViewToPortals → FixCellList → AdjustCellView`, 0x005a52d0/0x005a5250/0x005a5770, no depth guard) — depth-128 = slow-convergence laps, not necessarily a true loop. | +| `478c549` | **#117 FIXED** — depth-gated punch (§4 below). | +| `0e6e24f` | docs — **#118 narrowed** (§5 below): two suspects exonerated by read; candidates + exit-walk harness design recorded in ISSUES. | + +--- + +## 2. T6 / BR-7 — what the architecture is now (one paragraph) + +Objects register into the EXACT cells their collision footprint overlaps, +computed once at registration by the retail sphere-overlap portal flood +(`BuildShadowCellSet`); a door straddling a threshold lands in BOTH the +outdoor landcell and the vestibule list. The collision query is strictly +per-cell (`GetObjectsInCell`) at retail's two sites — the primary insert +(env → building → objects) and `check_other_cells` (the same per OTHER +overlapped cell), with the carried-cell advance after all object passes. +Building shells are NOT shadow objects: an outdoor LandCell carries at most +one building reference (its origin cell) and runs the shell part-0 BSP +through `FindBuildingCollisions` with the `bldg_check`/`hits_interior_cell` +placement weakening. There is no spatial radius anywhere in the query path +— cell membership IS the broad phase. The b3ce505 stopgap, the A6.P5 +widening, and the #90 stickiness are all gone. + +**#116 (filed):** tick-22760's lateral-slide loss + BSPStepUp D4's +first-frame slide are a PRE-EXISTING slide-response family (probes prove +the cell-set layer innocent; BR-7 left both byte-identical). Fix shape: +ONE oracle-driven pass over `SlideSphere` + the first-contact frame +(`get_object_info` pc:279992 only seeds the NEXT frame). Do NOT patch the +degenerate-offset guard ad hoc. + +--- + +## 3. T5 verdict (the user's reports are AXIOMS) + +**✅ PASSED:** doors block both ways incl. off-center (#99 visual); cellar +descent/ascent clean + #108 grass-sweep GONE; interiors stable through +doorways incl. edge-on; inn 2nd floor clean (#97 CLOSED); #109 far-door +oscillation GONE; formerly-popping stairs now STABLE at all ranges +(the distance-pop class is dead). Rain-indoors not verifiable (clear). + +**❌ Filed:** +- **#117** — aperture-shaped see-through: doors/interiors visible through + terrain hills + through nearer buildings. → FIXED this session (§4). +- **#118** — character clipped + vanishes for a moment when exiting houses + (viewer-indoor/player-outdoor window). → narrowed (§5), fix queued. +- **#119** — old tower: stair parts invisible (pre-existing, "same issue as + before"; the tower stairs ARE visible in retail — user axiom) + an + extraneous water barrel. **Lead from the T5 log:** exactly two + `[up-null] upload returned null for 0x00010002B4 / 0x00010008A8 — caching + EMPTY render data (permanently invisible)` lines at startup + (`t5-gate-launch.log:33-34`, untracked in the worktree root). Untouched. +- **#120** — `[pv-ERROR] in-place propagation tripwire at depth 128` on + cottage cells 0x…0175/0174/0162 during normal play (T2 invariant + tripwire). → armed for self-attribution (§1, `2d15084`); wait for the + next natural firing (any launch's log will carry the chain dump). + +--- + +## 4. #117 — the fix that needs the re-gate + +Decomp-settled root cause: retail's `DrawPortalPolyInternal` (0x0059bc90) +draws the punch with **DEPTHTEST_ALWAYS** + per-vertex far-Z (0.99999899, +`maxZ1` bit0) — it stomps ANY occluder depth unconditionally. Retail is +safe only because its outdoor pass is **painter's-ordered far→near**: +anything nearer redraws after the punch. Our z-buffered MDI frame has no +such order → the far house's aperture punch erased the near house's wall / +the hill's depth, and interiors + door entities (dynamics drawn last) +painted through — both #117 shapes. + +Fix (`478c549`, `PortalDepthMaskRenderer`): the punch is now two passes — +**A)** stencil-mark where the aperture fan passes LEQUAL at its true depth +biased 0.0005 NDC toward the viewer (≈6 cm at 5 m; keeps #108's +terrain-hugging-the-door case punched), no depth write; **B)** far-Z write +with depth ALWAYS, stencil-gated EQUAL 1, zeroing stencil as it goes +(self-cleaning). The frame order guarantees correctness: terrain + ALL +building shells draw in the landscape stage BEFORE `DrawExitPortalMasks`, +so pass A tests against the real occluders. The SEAL (interior roots) +stays retail-verbatim single-pass (it runs right after the gated full +depth clear — nothing nearer to stomp). `WindowOptions` now requests +8 stencil bits explicitly. The stale "RESERVED — unwired" banner on the +class was corrected (T1 wired it via `DrawRetailPViewPortalDepthWrite`). + +**Acceptance = the focused re-gate:** downhill door check, behind-house +openings, AND #108 cellar stays clean (the bias is the only regression +surface). + +--- + +## 5. #118 — narrowed; the exit-walk harness is the next step + +Exonerated by read: the draw partition (the local player carries +ServerGuid → Dynamics, never dropped) and entity-cell staleness +(`pe.ParentCellId = result.CellId` syncs per tick, GameWindow ~6855). + +Live candidates (the doorway-crossing decision stack), in order: +1. **Eye/cell incoherence under camera damping** — the render root is the + sweep's `RetailChaseCamera.ViewerCellId` while the projection eye + (`camPos`) is the DAMPED position; during a crossing they can disagree + by the damping lag. This is the already-VERIFIED #115/BR-8a divergence + (retail damps FROM the published collided viewer; we damp from our own + damped eye) — fixing BR-8a may fix #118 outright. +2. **Exit-portal side test at the threshold** — eye ε-outside the door + plane while the root is still the interior cell → + `CameraOnInteriorSide` culls the exit portal → OutsideView EMPTY → + `SphereVisibleOutside` culls ALL outdoor dynamics (the player) for + those frames. Retail's `AdjustPosition` demotes the viewer cell to + outdoor the same moment the point exits (`seen_outside → + adjust_to_outside`), making the inconsistent state structurally brief. +3. The doorway-aperture cone tightness for an outdoor player + indoor + viewer. + +**Next step (apparatus, not guessing — 3 hypotheses = build the +harness):** a deterministic exit-walk harness over the corner-building +cells (`CornerFloodReplayTests` infrastructure): per step of an eye+player +path crossing the doorway, drive the production decision stack headlessly +— viewer-cell resolution → `PortalVisibilityBuilder.Build(root)` → +`ViewconeCuller.Build` → the exact `DrawDynamicsLast` visibility predicate +— and assert the player sphere stays visible on every step. All CPU; the +failing step pins which candidate fires. Full design in the ISSUES #118 +entry. + +--- + +## 6. Watchouts / DO-NOT-RETRY for the next session + +- **No radial/spatial sweeps back into `ShadowObjectRegistry`**, no + topology-based outside-add, no gates — cell membership IS the broad + phase; the straddle flag is live-binary verified. (Physics digest rules.) +- **Do not patch the `SlideSphere` degenerate-offset guard ad hoc** — + #116 wants the oracle read first. +- **The punch must stay depth-gated.** Reverting to bare ALWAYS without + painter's ordering re-opens #117 by construction. +- **#120: don't tune the tripwire constant** (128). The chain dump is the + lead; retail's own recursion has no guard — the fix will be about WHY + convergence is slow (dedup/merge admitting near-duplicates per lap), not + about the limit. +- The two convergence sweeps in `CornerFloodReplayTests` + (`PortalPlaneCrossings_…` / `InCellDirectionSweep_…`) are regression + pins — keep green. +- **Building channel is origin-cell-only** (retail-verbatim, + `init_buildings` + ACE cross-checked). If the re-gate shows soft wall + collision on a LARGE building, first write the dat conformance fact + (shell extent vs origin landcell for Holtburg models) before touching + the dispatch. +- The 4 GameWindow registration sites pass `seedCellId:`; live entities + seed from the wire cell id; `IsBuildingShell` entities skip the registry + entirely. Don't re-add them. +- xunit swallows `Console.WriteLine` — use ITestOutputHelper or %TEMP% + for harness diagnostics. + +## 7. New apparatus (this session) + +| Tool | Where | Purpose | +|---|---|---| +| `PortalVisibilityBuilder.ConvergenceTripwireCount` | static, test-visible | #120 observable; both Build + look-in sites | +| `DumpPropagationChain` | fires with the tripwire | root + eye + per-cell frequency + 24-entry chain tail | +| `PortalPlaneCrossings_InPlacePropagationConverges` | CornerFloodReplayTests | ±6 cm sweep across every portal plane, both seed sides | +| `InCellDirectionSweep_InPlacePropagationConverges` | CornerFloodReplayTests | 3×3×2 in-cell eye grid × 8 yaw × 3 pitch (3024 builds) | +| `Diagnostic_Tick22760_DumpEngineInternals` | DoorBugTrajectoryReplayTests | #116 repro dump (door found + BSP-only dispatched correctly) | +| `[bldg-channel]` probe | `Transition.FindBuildingCollisions` | per-channel-hit line under `ACDREAM_PROBE_BUILDING` | +| `t5-gate-launch.log` | worktree root (untracked) | the T5 session log — `[up-null]` ×2 + `[pv-ERROR]` ×3 evidence | + +## 8. Next-session work order (work-order autonomy: drive, don't ask) + +1. **#118** — build the exit-walk harness; fix what it pins (BR-8a is the + likely fix if candidate 1 confirms — the retail damping shape is + already verified in the plan). +2. **#119** — chase the `[up-null]` pair (identify the two models; why + `ObjectMeshManager`'s upload returned null; whether the + permanently-invisible cache should retry); then the barrel + (static-inclusion question). +3. **Focused re-gate with the user** (one launch, short list): downhill + door check + behind-house openings (#117), house-exit character + (#118), tower stairs + barrel (#119), #108 cellar stays clean, and + grep the log for `[pv-ERROR]` chain dumps (#120's self-attribution). +4. Then per the plan: BR-8a camera (may already be consumed by #118), + #116 oracle pass, roadmap/milestones closeout of the holistic port. + +--- + +## 9. Paste-ready next-session prompt + +``` +Pick up acdream as a SENIOR 3D ENGINE DEVELOPER on the POST-T5 residual +fixes of the holistic building-render port. Worktree branch +claude/thirsty-goldberg-51bb9b, HEAD 0e6e24f. Nothing goes to main. + +STATE: the holistic port is CODE-COMPLETE and T5-gated (partial pass). +T6/BR-7 (per-cell shadow collision) SHIPPED — #99/#90/#97/#108/#109 all +CLOSED (user-confirmed at T5). #117 (aperture punch-through occluders) +FIXED 478c549 — pending visual re-gate. Suites green: Core 1416/0/2skip, +App 227, UI 420, Net 294. + +READ FIRST (in order): +1. Memory digests: project_render_pipeline_digest + + project_physics_collision_digest (current-truth banners on top; the + DO-NOT-RETRY tables APPLY — note the new no-radial-sweep / + no-topology-add / punch-stays-depth-gated / #116-oracle-first rules). +2. docs/research/2026-06-11-t6-br7-shipped-t5-gate-post-t5-handoff.md + (THE handoff: T6 architecture summary, T5 verdict, #117 fix detail, + #118 narrowing + harness design, #119 up-null lead, #120 armed + tripwire, watchouts, work order). + +DO NEXT — #118 (character clipped+vanishes on house exit): +build the deterministic exit-walk harness designed in ISSUES #118 / +handoff §5 (CornerFloodReplayTests infrastructure; per step of an +eye+player doorway-crossing path, drive viewer-cell resolution → +PortalVisibilityBuilder.Build → ViewconeCuller → the DrawDynamicsLast +visibility predicate; assert the player sphere stays visible). The +failing step pins one of three candidates (handoff §5); candidate 1 +(eye/cell incoherence under damping) is the verified #115/BR-8a +divergence — if it confirms, the fix is BR-8a's retail damping shape +(damp FROM the published collided viewer). 3 hypotheses already exist — +apparatus first, no speculative fixes. + +THEN — #119 (tower stairs + barrel): chase the [up-null] +0x00010002B4/0x00010008A8 permanently-invisible upload failures +(t5-gate-launch.log:33-34) before any draw-path theorizing; the barrel +is a separate static-inclusion question. + +THEN — the FOCUSED RE-GATE with the user (one launch, short checklist): +downhill door check + behind-house openings (#117), house-exit character +(#118), tower stairs + barrel (#119), #108 cellar stays clean; grep the +log for [pv-ERROR] chain dumps (#120 self-attributes on its next firing). +User retail reports are AXIOMS; no per-artifact live-probing. + +Build + test green per commit. Baselines: App 227 / Core 1416 + 2 skips +/ UI 420 / Net 294. +```