acdream/docs/research/2026-06-08-flap-physics-diagnosis-REFUTED-its-render-membership.md
Erik 6c3a96b26e diag(render): flap re-diagnosed as portal-flood re-clip DRIFT; physics + camera REFUTED
The 2026-06-08 AM "physics rest micro-jitter" diagnosis is refuted with primary
evidence (door-recheck 216K standstill records: 0 position re-snaps; player
byte-stable during the flap). Two adversarial verification sub-agents confirmed:

- Retail roots the render at the camera viewer_cell (swept from the player via
  SmartBox::update_viewer 0x453ce0; DrawInside(viewer_cell) 0x453aa0) and toggles
  DrawInside / LScape::draw -- so acdream's eye-cell rooting + inside/outside
  toggle are RETAIL-FAITHFUL. The locked-design "root at player cell" is wrong.
- The flap is render membership instability, eye-motion-driven: the visible-cell
  set oscillates (8<->3) as the eye sweeps monotonically. Root = the
  re-enqueue-on-growth DRIFT (PortalVisibilityBuilder.cs:322, MaxReprocessPerCell
  =16) re-clipping each grown cell every round -> sub-cm eye jitter flips membership.

Fix (spec, not yet implemented): verbatim port of retail's enqueue-once flood
(ConstructView + AddViewToPortals): enqueue once on first discovery, clip each
cell's portals once, union late growth in place (AddToCell) + draw-reorder
(FixCellList), never re-enqueue. Kills the drift; rooting/camera/seal untouched.

This commit lands VERIFIED GROUNDWORK + design only:
- spec: docs/superpowers/specs/2026-06-08-portal-flood-enqueue-once-port-design.md
- findings: docs/research/2026-06-08-flap-physics-diagnosis-REFUTED-its-render-membership.md
- [pv-input] probe gains rawPlayer + yaw (disambiguates the varying input)
- 4 GREEN physics rest-stability tests (prove rest is bit-stable -> flap not physics)
- apparatus: launch-flap-capture.ps1, analyze_flap_live.py, find_burst.py
- captured fixtures: tests/.../Fixtures/flap-doorway/0xA9B4017{0..5}.json

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

127 lines
8.9 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/Findings — the "physics rest µm-jitter" flap diagnosis is REFUTED; the flap is a RENDER membership instability at the grazing doorway portal — 2026-06-08 (PM)
> **Supersedes** `docs/research/2026-06-08-flap-rootcause-physics-rest-handoff.md` (the AM handoff). That
> handoff asked to be treated as a suspect statement and verified — this session verified it with primary
> evidence and it does **not** hold. The flap is **not physics** and **not camera drift**. It is the
> portal-flood **membership flickering non-monotonically at the grazing doorway portal as the camera eye
> sweeps** (i.e. while you turn the camera at the doorway). This is the SPEC's §2.2 diagnosis
> (`2026-06-08-portal-flood-membership-stability-design.md`), NOT its refuted §4 enqueue-once fix.
---
## 1. What was claimed (AM handoff) vs what the evidence shows
**AM claim:** the flap's varying input is a **physics resting-position µm jitter**`_body.Position`
blips ~1 ULP between ticks at rest → `RenderPosition` (Lerp of physics) jitters → eye jitters → flood
flips. Fix = physics rest-stability (broaden `kill_velocity`, hold contact plane).
**Refuted by three independent pieces of primary evidence:**
### A. The physics body is bit-stable at standstill — it does NOT blip
`door-recheck-capture.jsonl` (515 MB, 238,342 `ResolveWithTransition` records, captured standing at the
doorway, cells `0xA9B40170/0171/0174/0175/0031`):
- **216,300 true-standstill records** (zero velocity, `currentPos==targetPos`).
- **0** resolve re-snaps (`result.position != input` never happens at standstill).
- **0** cross-tick `currentPos` drift (the body position is carried forward byte-identically).
- The `grounded-but-cp=none` contradictory state DOES occur (3.5% of frames) but produces **no** position
blip.
Confirmed independently with **4 new deterministic tests** (all GREEN — they PROVE rest is bit-stable):
- `PlayerMovementControllerTests.Update_AtRestNoInput_RenderPositionBitStableAcrossManyFrames` (flat terrain)
- `PlayerMovementControllerTests.Update_WalkThenStop_SettlesToBitStableRest` (flat terrain, post-motion)
- `CellarUpTrajectoryReplayTests.IndoorCellarFloor_AtRestZeroOffset_BodyPositionBitStable` (indoor cell, resolver loop)
- `CellarUpTrajectoryReplayTests.IndoorCell_FullController_AtRestNoInput_RenderPositionBitStable` (indoor cell, full controller loop)
### B. On the actual flap frames, the PLAYER position is byte-identical
`pvinput.log` window 77487758 (a clean `flood 6↔2` flap), player `RenderPosition` for **11 consecutive
frames**: `(155.632858, 13.527222, 94.000000)`**byte-identical**. The physics output the camera reads
does not move at all during the flap. (The 1-ULP *player* blip the AM handoff cited is at the **outdoor**
`flood=1` records — a red herring, not the indoor flap.)
### C. The EYE moves while the player is still — and the camera DOES settle when idle
Same window: the eye orbits smoothly ~1 mm/frame (X ↓, Y ↑, Z constant) — a slow **camera rotation**
around the stationary player. And:
- Of **888** flood flips in the capture, only **1** had a byte-identical eye (and that one is the
outdoor→indoor root switch). Every other flip had a moved eye → **the flood is deterministic in the
eye; it changes only when the eye moves** (matches `Build_IsDeterministic_*`).
- Longest **indoor byte-identical-eye runs: 203 / 181 / 178 frames (~3.4 s)** — within each, the flood is
a **single constant value** (no flicker). **61%** of indoor frames have a byte-stable eye.
- ⇒ The camera **settles** at rest (no boom drift, no spring oscillation). When the eye is still, the
flood is stable. The flap fires **only while the eye is moving**.
## 2. The actual mechanism
When the camera eye **sweeps** through the grazing doorway portal (you turn the camera at the threshold),
the deep cell cluster `{0172,0173,0174,0175}` flickers in/out — flood `6,6,6,2,2,6,6,6,2,6,2` — i.e.
**non-monotonic membership across a monotonic eye sweep**. A correct visibility flood would transition
the deep cluster in/out **once** as the grazing portal closes; instead its clip flips empty↔non-empty as
the eye crosses and re-crosses the knife-edge. This is the SPEC's §2.2 diagnosis (the grazing portal's
clip / re-clip drift makes `clippedRegion.Count` flip `0↔N`, dropping the deep cluster on empty-clip
frames).
It is **NOT** physics (A, B). It is **NOT** camera drift/oscillation (C: eye byte-stable ~3.4 s when
idle). It is a **render-side portal-flood membership instability at grazing angles**, surfaced by camera
rotation.
## 3. Status of prior fixes / diagnoses
- **AM physics-rest fix** — would not have fixed the flap (physics rest is already bit-stable). Do not pursue.
- **SPEC §2.2 diagnosis (grazing-portal membership instability)** — CONFIRMED by this evidence.
- **SPEC §4 enqueue-once fix** — already refuted in the AM handoff (retail propagates late slices via
`AddToCell`, decomp :433494; broke `Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit`). So the
correct fix is a render-side membership stabilization that is monotonic under a sweeping eye **without**
breaking late-slice propagation — design TBD (brainstorm).
## 4. Apparatus (this session)
- 4 GREEN rest-stability tests (above) — keep as regression guards + evidence that physics rest is bit-stable.
- Analysis scripts (ad-hoc, `/tmp`): door-recheck standstill survey; pvinput flood-flip vs eye/player
delta buckets; indoor byte-stable-eye-run scan. Re-derivable from `pvinput.log` + `door-recheck-capture.jsonl`.
- Existing probes `[pv-input]` (`ACDREAM_PROBE_PVINPUT`) and `[render-sig]` remain the live gate.
## 5. Next step (proposed)
Brainstorm + design a render-side fix that makes the deep-cluster membership **monotonic/stable as the eye
sweeps the grazing portal** (candidates: more robust grazing-portal clip, a retail-faithful single-process
traversal that doesn't re-clip-drift, or matching retail's exact `GetClip`/`polyClipFinish` epsilon). Then
TDD a builder test that sweeps the eye across the grazing angle and asserts monotonic membership, and
visual-gate by turning the camera at the cottage doorway.
## 6. LIVE-CONFIRMED (2026-06-08 PM, targeted doorway capture, user-driven)
A fresh instrumented capture (`launch-flap-capture.ps1`; `[pv-input]` enhanced with `rawPlayer`=raw
physics body pos + `yaw`; cells `0170-0175` dumped to `tests/.../Fixtures/flap-doorway/`; 84K frames)
confirms the diagnosis across every state and decomposes the flap into THREE render sub-issues. **In
every case the player render-pos AND raw physics-pos are byte-identical (0 µm) — physics is conclusively
exonerated; the flap is 100% camera-eye-driven.**
| State (user-driven) | player moves | eye moves | flood |
|---|---|---|---|
| Idle, hands fully off | no (0 µm) | **no (0 µm)** | **stable** (no flap) |
| Turn / walk | no (0 µm) | yes (mm, yaw) | oscillates |
| Camera smoothing-glide after a turn (yaw byte-constant, eye glides monotonically, decelerating) | no | yes (mm) | **oscillates 8↔3** ← this is the "flickers while idle" the user perceived |
Key burst (row 11167): **yaw byte-constant**, eye X glides monotonically 155.109→155.435 (18→5 mm/frame,
decelerating), flood `8→3→8…3→8`. Monotonic eye ⇒ non-monotonic membership ⇒ **render** instability (not
camera hunting).
**Three sub-issues (all eye-driven, physics out):**
- **A — Membership oscillation:** flood non-monotonic as the eye sweeps within a *stable* root.
outside-looking-in `8↔3`; outdoor-root `17↔33` (21 flips/2500 frames); indoor-root `2↔6`.
- **B — Root toggle (the big one):** at the threshold, `outRoot` flips outdoor↔indoor as the eye crosses
the door plane → wholesale visible-set swap **≈18-33 cells ↔ 2 cells** (4 toggles in 2500 frames). This
is the "two-branch" outdoor-node-vs-indoor-cell root switch the unification was meant to remove — still
present.
- **C — Indoor-root under-inclusion:** eye just inside ⇒ `outRoot=n` flood **= 2, stable** for 2438
frames → outdoors + other rooms missing (the indoor flood does not reach back out the exit portal / to
adjacent cells). C is B's partner: the swap *to* indoor loses the scene → "textures missing."
**Fix scope:** core render pipeline (root resolution + flood + grazing-clip), NOT physics, NOT camera.
Spec §2.2 (membership instability) is right for A; B+C are the threshold root-resolution/flood issues.
Spec §4 enqueue-once stays refuted. Design needs brainstorming (saga has reverted speculative render
fixes — see `feedback_render_one_gate`, `feedback_verify_render_seal_before_layering`).
Apparatus added: `launch-flap-capture.ps1`, `analyze_flap_live.py`, `find_burst.py`, fixtures
`tests/AcDream.Core.Tests/Fixtures/flap-doorway/0xA9B4017{0..5}.json`, `flap-doorway-resolve.jsonl`.
**Memory to correct:** `project_indoor_flap_rootcause` (root is render: A membership instability + B
root-toggle + C indoor under-inclusion, all under a moving camera eye — NOT physics rest, NOT camera
drift; the "two-branch split" B is still live).