acdream/docs/research/2026-06-08-indoor-flap-edgeon-vs-camera-position-handoff.md
Erik 8f879bd7d9 docs(render): calibrated indoor-flap handoff — MEASURED vs HYPOTHESIS vs OPEN
Separates what is measured (eye smooth + 1um at rest -> not jitter) from the leading-but-unproven hypothesis (clip edge-on) and the NOT-ruled-out alternative (camera position: retail eye collided/head-on 93%, ours floats edge-on). The one 'clean' pass had ratio 4.2x back-and-forth, so the flood claim is indicated not proven. Lists the verify-first steps before any R-A2b fix. Counters this session's pattern of overclaiming then refuting.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 08:16:51 +02:00

13 KiB
Raw Blame History

HANDOFF — Indoor crossing flap: MEASURED vs HYPOTHESIS vs OPEN (read before ANY code)

Date: 2026-06-08 (late). Branch: claude/thirsty-goldberg-51bb9b. HEAD: the docs(render): plan status commit (after c7069cf). Working tree clean.


⚠️ 0. CALIBRATION NOTE — read this first

This session's diagnosis of the indoor flap shifted four times as measurements came in: per-buildingcamera-jitter (R-A4)camera swept/published eyeflood edge-on. Each "confident" claim was refuted by the NEXT measurement, in the same session. The author (me) also stated a final scoreboard ("conclusively pinned / camera ruled out") with more confidence than the data earns.

So this handoff is structured to prevent the next session from inheriting an overclaim:

  • §2 = MEASURED (high confidence, with the data + file refs).
  • §3 = LEADING HYPOTHESIS (medium — explicitly NOT isolated).
  • §4 = ALTERNATIVE NOT RULED OUT (a real competing explanation I waved away).
  • §5 = WHAT TO VERIFY FIRST — do this BEFORE building anything. Do not trust §3 until §5 confirms it.
  • §6 = DO-NOT (genuinely refuted, high confidence — safe to not re-try).

The honest one-liner: the flap is a motion-time grey-flash at openings; eye-jitter-at-rest is ruled out (measured); but whether the cause is the clip math at edge-on (§3) or the camera not pulling the eye in like retail (§4) is NOT yet disambiguated — the one "clean" pass wasn't clean enough (it had back-and-forth). Disambiguate (§5) before choosing the fix.


1. What SHIPPED this session (all green, visual-confirmed by the user)

Commit What Verified
7fe9809 R-A1 — canonicalize outdoor root on LoadedCell.IsOutdoorNode (behavior-preserving) tests
c62663d R-A2 — per-building floods (ConstructViewBuilding; OutdoorCellNode portal-less; per-building merge in RetailPViewRenderer.DrawInside) user: outside-looking-in flap GONE
2ec189c R-A2 seam fix — flood null-BuildingId cells (don't drop) user: missing textures GONE
3c178b2 tools/cdb/flap-cam-measure.cdb — retail eye + CameraManager capture apparatus
c7069cf [flap-sweep] probe: F6 in=(desiredEye) / out=(eye) apparatus
docs… plan-doc STATUS note (R-A4 ruled out) docs

Test baseline: App ~Rendering 207, Core PlayerMovementControllerTests 14. Build green. Plan: docs/superpowers/plans/2026-06-08-full-retail-render-port-option-a.md.

The user's remaining symptom (verbatim): "grey background color flapping entrances between rooms and cellar entrance and when going outside." I.e., the clear/background color flashes through openings during MOTION; at rest it does not flap.


2. MEASURED FACTS (high confidence — data + file refs)

2.1 acdream eye at REST ≈ 1 µm stable (NOT a jitter problem)

flap-cam-measure.log (acdream run, user held completely still, 768 samples). Last 150 frames: in (desiredEye) Y range 0.000 mm (1 distinct value); out (eye) Y range 0.001 mm (2 distinct); pulledIn=0. → The convergence snap (RetailChaseCamera.ApplyConvergenceSnap, SnapEpsilon 0.0004) holds; there is no at-rest jitter. (acdream at rest is actually steadier than retail's settled eye.)

2.2 The earlier "~1.3 mm jitter at rest" was MOTION misread as rest

launch-flap-pin2.log [pv-input] showed ~1.3 mm; that was during the crossing. The F2-precision print rounded sub-cm walking to "d=0", which I mis-analyzed as a static eye. The flap is motion-time only.

2.3 On a (mostly) one-way pass: eye SMOOTH, but visible-cell count OSCILLATES

launch-camprobe.log, the clean-pass tail (~25.7k sweep frames):

  • Eye: 3D path = 34.1 m, net = 8.2 m (ratio 4.2×), direction reversals X=3, Y=18 (i.e. few large direction changes — the motion is smooth, not jittery).
  • Visible cells ([flap] vis=): oscillated 414× between 1 and 6; 648 clip=0 events. Distribution 1:4084 2:6300 3:3860 4:10634 5:439 6:427. Roots: 0171 (room) 21396, 0031 (outdoor) 4084, others tiny.
  • ⚠️ CAVEAT (why this is NOT isolated): ratio 4.2× means the pass was not purely one-way — it had back-and-forth + indoor↔outdoor. The 414 oscillations therefore mix (a) indoor-flood oscillation (root 0171, vis 2↔6) AND (b) indoor↔outdoor root swaps (01710031, the "going outside" grey; the 4084 vis=1 frames are root 0031 = legitimately just the outdoor node). These were not separated. So "smooth eye + oscillating vis ⇒ flood bug" is indicated but not proven — some of the oscillation is the back-and-forth and the root-swap.

2.4 Retail eye at the cottage doorway: COLLIDED 93%, settled jitter ~tens of µm

flap-cam-measure.log (retail, acclient.exe, PDB MATCH refs/acclient.pdb), 768 samples:

  • pub != sought 716/768 = 93% COLLIDED — retail's boom hits the cottage; the eye is pulled toward the player (a more head-on view of the doorway, not floating 3 m back).
  • Settled jitter (quarters Q2Q4) ≈ 93 µm; Q1's 1.4 mm was the user walking up (settling motion), verified by the quarter split.
  • Offsets: CameraManager.t_stiffness@+0x08, r_stiffness@+0x0c, viewer_offset@+0x48; SmartBox.camera_manager@+0xa0. acdream's damping already matches retail's formula (alpha = clamp(stiffness·dt·10,0,1), stiffness 0.45) — RetailChaseCamera.ComputeDampingAlpha.

3. LEADING HYPOTHESIS (medium confidence — NOT isolated; see §2.3 caveat)

The flood/clip's "is the room behind this opening visible?" decision is non-monotonic near a doorway's EDGE-ON angle. The portal projects to an on-screen area that hovers at ~zero (coin-on-edge); small smooth eye steps flip the clip 0↔nonzero → the cell behind drops → grey flash. This matches the "grey at room↔room / cellar entrances." Mechanism candidates inside this hypothesis: ProjectToClip / ClipToRegion collapsing at edge-on (PortalProjection.cs), and/or the band-aids MaxReprocessPerCell (D4) + EyeInsidePortalOpening (D5) re-processing differently frame-to-frame during motion.


4. ALTERNATIVE NOT RULED OUT (a real competing explanation)

The edge-on viewing might be a CONSEQUENCE of the camera not pulling the eye in like retail. §2.4: retail's eye is collided 93% (head-on); acdream's [flap-sweep] showed it uncollided much more often (floats free, 3 m back → edge-on view of the doorway). A doorway viewed edge-on is exactly when the clip collapses. So the root cause could be camera collision / eye position (acdream's spring-arm not pulling in where retail's does), NOT the clip math.

  • What IS ruled out: eye-jitter / wobble / at-rest instability (§2.1, §2.3 — smooth + 1 µm).
  • What is NOT ruled out: eye position (collision pull-in). These are different. I conflated them in the scoreboard. Disambiguate in §5.2.
  • Separately, the "going outside" grey is the root 0171↔0031 wholesale swap (§2.3) — likely a distinct sub-issue (indoor↔outdoor root transition), not the same as the room↔room edge-on flap.

5. VERIFY FIRST — before building ANY fix (this is the anti-overclaim gate)

  1. Isolate pure indoor-flood oscillation. Do a genuinely one-way, slow walk through ONE interior doorway (room→room), no going outside, no back-and-forth, camera fixed. Capture ACDREAM_PROBE_FLAP=1. Confirm whether vis (root 0171 only) oscillates 2↔6 for a monotonic eye (check [flap-sweep] out= is monotonic — ratio ≈ 1, ~0 reversals). If it does NOT oscillate when the eye is truly monotonic → the flap was the back-and-forth / root-swap, not a flood bug (then §3 is wrong). The clean pass in §2.3 had ratio 4.2× — not clean enough to conclude.
  2. Disambiguate clip-math (§3) vs camera-position (§4). At the SAME interior doorway, measure acdream's eye-to-doorway angle vs retail's (collided) — is acdream viewing it edge-on where retail views it head-on (because retail's eye is pulled in)? Add the doorway-plane signed distance + the collide state to [flap-sweep]. If acdream floats edge-on where retail pulls in → the fix is the camera collision, not the flood.
  3. Read retail's edge-on clip handling (the fix ORACLE — do NOT guess the fix without this): PView::GetClip (0x5a4320, decomp ~:432344), PView::ClipPortals (0x5a5520, :433572), ACRender::polyClipFinish (the w=0 clip, :702749). Questions: does retail's clip collapse to zero at edge-on like ours, or stay robust? Does retail keep a flooded cell once added? Cross-check PortalProjection.cs (our port — handoff says it's faithful; verify at edge-on specifically).

6. DO-NOT RETRY (genuinely refuted this session — high confidence)

  • Camera eye-JITTER / R-A4 stiffness change. Eye is smooth (3/18 reversals) + 1 µm at rest (§2.1, §2.3). acdream's damping already matches retail's stiffness 0.45. Do not "reduce jitter."
  • RenderPosition rest-snap (cd974b2, reverted). The eye isn't the jitter source; at rest it's 1 µm.
  • "1.3 mm jitter at rest" — it was MOTION (§2.2).
  • A robust-MEMBERSHIP scheme retail lacks. Retail's flood is clip-driven too — PView::add_views (0x5a5210) only pre-pushes view slices onto the stab_list cells via curr_view_push; it does NOT ground cell_draw_list membership. So the robustness must live in the clip's edge-on behavior (§3) or the camera position (§4), NOT a stab_list membership-grounding.
  • Two-pipe inside/outside split; bounded-propagation/churn; byte-stable eye — all refuted earlier (see the 2026-06-08 OPTION-A handoff §7).

7. The fix (R-A2b) — GATED on §5

Do NOT design until §5 disambiguates. Two branches:

  • If §5.1 + §5.3 show the clip collapses at edge-on (clip-math): port retail's edge-on clip robustness into PortalProjection / PortalVisibilityBuilder; conformance-test "smooth monotonic eye through a doorway ⇒ monotonic vis (no oscillation)"; visual-gate.
  • If §5.2 shows acdream's eye floats edge-on where retail pulls in (camera-position): the fix is the camera collision (PhysicsCameraCollisionProbe.SweepEye / RetailChaseCamera) — make the eye pull in like retail (93% collided at the doorway). This is the camera position, distinct from the ruled-out eye-jitter.
  • The "going outside" grey (§4, root 0171↔0031 swap) is likely a separate, smaller task.

8. Apparatus (reuse — don't rebuild)

  • acdream probes (gated in AcDream.Core.Rendering.RenderingDiagnostics):
    • ACDREAM_PROBE_FLAP=1[flap] (per-portal D/side-test TRV/CULL/clip= vertex count, vis=), [flap-sweep] (camera sweep: pulledIn, and in=(desiredEye) / out=(eye) at F6 — added c7069cf), [flap-cam]. HEAVY (per-frame per-portal) — short captures only.
    • ACDREAM_PROBE_PVINPUT=1[pv-input] (eye/player/rawPlayer F6 + flood count). Lighter.
  • cdb on retail: tools/cdb/flap-cam-measure.cdb (eye origin per frame pub+sought raw float hex + CameraManager/SmartBox type dumps). Attach: Get-Process acclient → pid; cdb.exe -p <pid> -cf tools\cdb\flap-cam-measure.cdb. Exit code 5 is expected (clean detach). PDB refs/acclient.pdb — verify with py tools/pdb-extract/check_exe_pdb.py "C:/Turbine/Asheron's Call/acclient.exe" (MATCH).
  • Logs (UNTRACKED, large — gitignore/delete): launch-camprobe.log, launch-flap-pin2.log, flap-cam-measure.log, launch-ra2.log.
  • Launch: CLAUDE.md "Running the client" env block + $env:ACDREAM_PROBE_FLAP="1".
  • Build-while-client-running gotcha: the running client locks the DLLs → build fails MSB3027. Close the client (graceful CloseMainWindow) before building.

9. Exact pickup (next session)

  1. Read this doc top-to-bottom, then memory project_indoor_flap_rootcause (2026-06-08 late CORRECTION).
  2. git log --oneline -8 (HEAD = the plan-status docs commit). dotnet build green; App ~Rendering 207 / Core PlayerMovementControllerTests 14 green.
  3. Do §5 FIRST (isolate indoor-flood vs camera-position vs root-swap). Do NOT write a fix until §5 tells you which of §3 / §4 is real. This is the whole point of the calibration note.
  4. Then §5.3 (retail clip oracle) → design the gated fix (§7) → conformance-test PRE-gate → visual-gate.
  5. Strip the throwaway probes after the visual gate; update memory + the plan + milestones.

10. One-paragraph version (for when you're tired)

Outside-flap and seams are FIXED and confirmed. The indoor flap is a motion-time grey-flash at openings. We MEASURED that it's not eye-jitter (eye is smooth + 1 µm at rest). We did NOT prove it's the clip math: the one "clean" pass had back-and-forth (ratio 4.2×) so the 414 oscillations mix flood-edge-on, back-and-forth, and indoor↔outdoor root-swaps. And we did NOT rule out the camera position (retail's eye is pulled-in/head-on 93% at the doorway; ours floats edge-on). So: isolate a truly one-way single interior-doorway pass, compare acdream's vs retail's eye angle at that doorway, and read retail's edge-on clip code — THEN choose between the clip-math fix and the camera-position fix. Don't pick one until those three are done.