acdream/docs/research/2026-06-11-t6-br7-shipped-t5-gate-post-t5-handoff.md
Erik acaaeae434 docs: session handoff - T6/BR-7 shipped + T5 verdict + post-T5 state, with the next-session prompt
docs/research/2026-06-11-t6-br7-shipped-t5-gate-post-t5-handoff.md:
the 9-commit ledger with decomp anchors, the per-cell shadow
architecture summary, the T5 gate verdict (collision half 100% passed;
#117-#120 filed), the #117 fix detail (depth-gated punch - re-gate
pending), the #118 narrowing + exit-walk harness design, the #119
up-null lead, the #120 armed tripwire, watchouts/DO-NOT-RETRY
additions, new apparatus inventory, the next-session work order, and
the paste-ready prompt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 16:17:41 +02:00

259 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
```