Apparatus + handoff for the indoor flap. Confirmed (primary evidence): the flap is the portal-flood clip being µm-sensitive at the threshold, driven by a ~1-8µm jitter in the player RenderPosition (physics resting position not bit-stable; Lerp surfaces it). REFUTES the 2026-06-07 see-through/EnvCell/outdoor-node diagnosis (ModelId GfxObj 0x01000A2B IS the solid exterior) AND an enqueue-once attempt (retail propagates late slices via AddToCell; the existing PropagatesNewSlicesToExit test caught it; reverted). Adds: Build determinism test, A8CellAudit gfxobj dump, [pv-input] 6dp probe + [render-sig] outRoot/bshell fields. No functional fix shipped. Next: higher-precision physics rest trace -> port retail kill_velocity/contact rest-stability. Canonical: docs/research/2026-06-08-flap-rootcause-physics-rest-handoff.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
119 lines
8.1 KiB
Markdown
119 lines
8.1 KiB
Markdown
# Handoff — the indoor FLAP traced to a physics rest µm-jitter; prior diagnoses REFUTED — 2026-06-08
|
||
|
||
> **CANONICAL PICKUP for the indoor render flap.** This session refuted the 2026-06-07 cutover-flip
|
||
> diagnosis AND an enqueue-once attempt, confirmed the real mechanism with primary evidence, and traced
|
||
> the root all the way down to a **physics resting-position µm jitter**. The fix is in physics
|
||
> (rest-stability), is teed up, and needs one more **higher-precision** trace to pin the exact cause
|
||
> before porting. Spec: `docs/superpowers/specs/2026-06-08-portal-flood-membership-stability-design.md`
|
||
> (its §4 enqueue-once design is REFUTED — see §3 here; its §6 physics contingency is now the active
|
||
> direction).
|
||
|
||
---
|
||
|
||
## 1. What the flap IS (confirmed, primary evidence)
|
||
|
||
At the Holtburg cottage **doorway threshold**, the portal-visibility flood set oscillates frame-to-frame
|
||
(`ids=[0170,0171,0172,0173,0174,0175]` ↔ `[0170,0171]`, i.e. 6↔2 cells) **from a stable viewer cell**
|
||
(`root=0xA9B40170`, `outRoot=n`). The deep `0172-0175` cluster pops in/out → textures "battle."
|
||
|
||
- It is **NOT** see-through walls from outside (standing outside with the door closed is **stable** —
|
||
user visual gate), **NOT** the outdoor node, **NOT** a root toggle, **NOT** nondeterminism.
|
||
- `PortalVisibilityBuilder.Build` is a **pure deterministic** function (proved by
|
||
`PortalVisibilityBuilderTests.Build_IsDeterministic_*`, passes). So the flip requires a **varying
|
||
input**.
|
||
- The high-precision `[pv-input]` probe (6 dp) shows the camera eye AND the **player `RenderPosition`**
|
||
carry perpetual **~1–8 µm** float jitter at rest (e.g. player Z `94.000000 ↔ 94.000008`). At the
|
||
threshold a grazing portal's clip is so knife-edge that this µm jitter flips its empty/non-empty
|
||
result → the flood membership flips → the flap.
|
||
|
||
**Mechanism chain:**
|
||
`physics resting position blips ~µm → ComputeRenderPosition Lerp surfaces it as µm eye jitter → the
|
||
portal-flood clip (clip-non-empty membership) is µm-sensitive at the grazing threshold portal → flips →
|
||
flap.` Retail is flap-free because its authoritative local position is bit-stable at rest (so its same
|
||
clip-non-empty membership never crosses the boundary).
|
||
|
||
## 2. REFUTED — the 2026-06-07 cutover-flip diagnosis (do NOT act on its F1/F2)
|
||
|
||
`docs/research/2026-06-07-cutover-flip-render-residuals-diagnosis-handoff.md` is wrong on its
|
||
load-bearing claims (primary evidence in this session):
|
||
|
||
- "See-through from outside" — **not reproduced** (outside, door closed, is stable).
|
||
- "Walls ARE the EnvCell shells; ModelId is a partial frame" — **refuted**: the cottage ModelId GfxObj
|
||
`0x01000A2B` is a full closed exterior (76 render polys, bbox 20×18×10.4 m, 46 outward-facing walls +
|
||
roof — `tools/A8CellAudit gfxobj 0x01000A2B`). EnvCell shells are interior-facing. **F2 (EnvCell
|
||
back-faces) targets the wrong geometry.**
|
||
- "Oscillation = outdoor-node flood (1↔13)" — **corrected**: it is the *indoor* flood, stable root,
|
||
2↔6. F1 targeted the wrong root.
|
||
- "branch=RetailPViewInside every frame proves the flap is gone" — **tautological** (post-flip
|
||
`clipRoot = viewerRoot ?? _outdoorNode` is ~never null, so `branch` can't report `OutdoorRoot`).
|
||
|
||
## 3. REFUTED — enqueue-once traversal (TDD caught it)
|
||
|
||
Hypothesis: the flap is acdream's `MaxReprocessPerCell` re-enqueue drift; restore retail's enqueue-once
|
||
(first-discovery only, no re-enqueue). **Refuted:** retail does NOT stop at first discovery — its
|
||
`AddViewToPortals` growth branch calls **`AddToCell`** (decomp :433494), so a cell's later-grown view
|
||
IS propagated (late slices reach exit portals). The existing test
|
||
`PortalVisibilityBuilderTests.Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit` encodes exactly
|
||
this retail behavior; enqueue-once broke it. The change + its test were reverted (tree clean, 27 portal
|
||
tests green). **The divergence is the re-clip DRIFT, not the propagation — and underneath, the flap is
|
||
the µm input jitter (which removing the drift would only *reduce*, not eliminate; `Build` is
|
||
deterministic so only a bit-stable INPUT guarantees no flap).**
|
||
|
||
## 4. The root — physics resting position not bit-stable
|
||
|
||
`PlayerMovementController.ComputeRenderPosition` (line 810): `Vector3.Lerp(_prevPhysicsPos,
|
||
_currPhysicsPos, alpha)`. `Lerp(a, a, t) == a` exactly, so the µm `RenderPosition` jitter means
|
||
**`_prev != _curr`** — the physics body's resting position blips ~µm between ticks. Retail's
|
||
`kill_velocity` (`OBJECTINFO::kill_velocity` = `set_velocity(0)`, decomp :274467) is called by
|
||
`validate_transition` (:272567) on **every** grounded collision/slide with a valid contact plane,
|
||
keeping rest bit-stable.
|
||
|
||
acdream rest path:
|
||
- `calc_acceleration` (PhysicsBody.cs:191) zeroes gravity only when **`Contact && OnWalkable &&
|
||
!Sledding`**.
|
||
- `UpdatePhysicsInternal` (PhysicsBody.cs:352) skips position integration when `velocity <= 0`.
|
||
- Player flags set per tick in `PlayerMovementController` (1271-1301): `Contact|OnWalkable` only when
|
||
`resolveResult.IsOnGround && Velocity.Z <= 0`; else cleared → gravity.
|
||
- acdream's `kill_velocity` (PhysicsEngine.cs:837) is **narrower than retail's** — fires only on
|
||
`ObjectInfo.VelocityKilled` (the airborne steep-roof/wall reset), NOT on every grounded contact.
|
||
|
||
So at a *clean* rest the position is bit-stable; the blip is an **intermittent** failure (a stray
|
||
gravity tick / µm velocity residual / contact-plane not re-established). The `[resolve]` probe (3 dp)
|
||
shows the body stable to **mm** at spawn rest (`94.000` repeated) — confirming the blip is **sub-mm**,
|
||
below that probe's precision — and shows `groundedIn=True` but `walkable=False cp=none` (no contact
|
||
plane established at rest), a lead toward the Contact/contact-plane path.
|
||
|
||
## 5. NEXT STEPS (the physics rest-stability fix)
|
||
|
||
1. **Higher-precision physics rest trace (REQUIRED before fixing).** The 3-dp `[resolve]` probe is too
|
||
coarse. Add a 6-dp per-tick probe of the resting body: `_body.Position`, `Velocity`, `Acceleration`,
|
||
`TransientState` (Contact/OnWalkable), `resolveResult.IsOnGround`, contact-plane valid. Launch, let
|
||
the character sit at spawn (no input needed — autonomous), capture ~10 s, and find the tick where the
|
||
position blips µm and which condition failed (gravity applied? velocity residual? resolve re-snap?
|
||
Contact cleared?).
|
||
2. **Port the retail-faithful rest-stability fix** for the pinned cause — most likely one of:
|
||
(a) broaden `kill_velocity` to match retail's `validate_transition` (zero velocity on every grounded
|
||
contact with a valid contact plane, :272567); (b) ensure the `Contact` flag / contact plane is
|
||
re-established on the zero-distance rest sweep so `calc_acceleration` keeps gravity off; (c) a
|
||
retail-faithful "supported body at rest is frozen" (skip integration/resolve when grounded + zero
|
||
velocity + no movement input). TDD: a test asserting the resting body position is **bit-stable across
|
||
N ticks** with no input.
|
||
3. **Visual gate** at the cottage doorway threshold: hold still — the 2↔6 oscillation is gone (re-run
|
||
`[pv-input]`/`[render-sig]`, flood `ids=` constant at rest).
|
||
|
||
**DO NOT RETRY:** the overlap-predicate render band-aid (rejected by user — not retail); enqueue-once
|
||
(refuted, §3); any render-side debounce/grace (forbidden).
|
||
|
||
## 6. Apparatus (committed this session) + state
|
||
|
||
- **Keep (real regression value):** `PortalVisibilityBuilderTests.Build_IsDeterministic_*` (proves Build
|
||
deterministic); `tools/A8CellAudit` `gfxobj` mode (dumps render geometry — used to refute the ModelId
|
||
claim).
|
||
- **Diagnostic probes (env-gated, inert off; KEEP for the physics trace + flap visual gate, strip after
|
||
the fix ships):** `[pv-input]` (`ACDREAM_PROBE_PVINPUT`, 6-dp Build inputs + flood count,
|
||
RenderingDiagnostics + GameWindow); the `outRoot=`/`bshell=` fields on `[render-sig]`;
|
||
`launch-pvinput.ps1`, `launch-bshell-probe.ps1`, `launch-resolve.ps1`.
|
||
- Tree: PortalVisibilityBuilder.cs reverted to the re-enqueue (no functional change shipped). Build
|
||
green; App.Tests green (portal-visibility 27/27).
|
||
- Memory to update: `project_indoor_flap_rootcause` (root is the physics rest µm-jitter, not the render
|
||
diagnosis or enqueue-once).
|