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

204 lines
13 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.

# 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-building``camera-jitter (R-A4)``camera swept/published eye``flood 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** (`0171``0031`, 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.