Ascending the altitude dial and landing on the higher-altitude clip jumped ~3
seconds. A baked morph `transitions/{h}__{l}.mp4` spans src@0 (frac 0) ->
dst@~LOOP_TAIL_S (frac 1) and is scrubbed bidirectionally, but the steady loop
element always seeked LOOP_TAIL_S (3.0s) on landing. Descending lands on dst at
its tail (matches the seek — the s0026 loop-from-tail fix); ascending lands on
src at the morph's HEAD (src@0), so seeking the tail was a 3s jump.
Make the loop landing frame direction-aware: pure `HEFScrub.loopLandFrame(dir,
loopTailS)` returns 0 when ascending, loopTailS otherwise. `loadLoop` takes a
`landFrame`, stores it as `dataset.loopStart`, and the timeupdate/ended wrap
handlers honor it (the base clips are already seamless crossfade-loops, so a
reverse landing wraps [0,dur] cleanly). The settle path lands explicitly on the
direction's frame so a fast ascent never jumps even if the heading clip wasn't
preloaded in time. App-side only — no 154-morph LFS re-bake.
Verify: node unit (loopLandFrame) + a new reverse-landing e2e asserting the loop
anchors at the head (loopStart "0"), and the existing forward "loops from tail"
e2e still green (12/12 altitude-lock).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Simulator E2E (Playwright)
Browser end-to-end tests for the Human Experience Filter simulator. The §9 pipeline requires an E2E browser tier for any UI surface; this is that tier.
What it covers
tests/altitude-lock.spec.ts drives the real simulator and asserts the
altitude-morph behavior:
- Zooming to a new altitude plays the morph that matches the clip it lands on
(forward zoom-in, and
.revzoom-out). - After landing, the clip is locked — it does not change while parked (no secondary swap, no re-roll).
The played morph paths are read from window.__hefMorphs (a diagnostic array
populated by advance() in static/app.js), which is reliable even when a morph
is served from the in-memory blob cache. The locked clip is read from the
Dev-Mode #scale-name readout.
Running
Prerequisites: the project Python venv on PATH (the Playwright webServer boots
uvicorn simulator.app:app from the repo root), Node, and the Chromium browser.
# from this directory (simulator/e2e)
npm install
npx playwright install chromium
npm test
playwright.config.ts starts uvicorn on port 8099 automatically (reuseExistingServer
locally) and points the tests at it.