acdream/find_burst.py
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

30 lines
1.2 KiB
Python

import sys, re
path = sys.argv[1]
pat = re.compile(r'flood=(\d+) eye=\(([^)]+)\) player=\(([^)]+)\) rawPlayer=\(([^)]+)\) yaw=([-\d.]+)')
rows = []
with open(path, encoding='utf-8', errors='ignore') as fh:
for l in fh:
m = pat.search(l)
if m:
rows.append((int(m.group(1)),
tuple(float(x) for x in m.group(2).split(',')),
float(m.group(5))))
print("total pv-input rows:", len(rows))
# find first window of 25 frames containing >=4 flood changes (an oscillation burst)
def changes(seg): return sum(1 for i in range(1, len(seg)) if seg[i][0] != seg[i-1][0])
W = 25
start = None
for i in range(len(rows)-W):
if changes(rows[i:i+W]) >= 4:
start = i; break
if start is None:
print("no oscillation burst found"); sys.exit()
print(f"burst at row {start}; dumping {W+8} frames (flood, eyeX,eyeY,eyeZ, dEyeX_mm,dEyeY_mm, yaw):")
prev = None
for r in rows[start:start+W+8]:
fl, e, yaw = r
dx = (e[0]-prev[0])*1000 if prev else 0.0
dy = (e[1]-prev[1])*1000 if prev else 0.0
mark = " <" if (prev and fl != prevfl) else ""
print(f" flood={fl} eye=({e[0]:.6f},{e[1]:.6f},{e[2]:.6f}) dX={dx:+7.3f}mm dY={dy:+7.3f}mm yaw={yaw:.6f}{mark}")
prev = e; prevfl = fl