From ee54571c7f13273559aeab5005971111d3f5aeb5 Mon Sep 17 00:00:00 2001 From: Christian Pillsbury Date: Tue, 14 Jul 2026 07:59:30 -0700 Subject: [PATCH] docs(plan): pre-PR checklist + deferred e2e-coverage effort (spike) Record the canonical pre-PR checklist (items 1-10), mark the ~0-PTS threshold done, and capture the playback-lifecycle e2e tests as a separate deferred effort (the current e2e suite is UI/skin-expectation only). Co-Authored-By: Claude Opus 4.8 (1M context) --- ...spf-non-zero-pts-timestamp-offset-spike.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/.claude/plans/spf-non-zero-pts-timestamp-offset-spike.md b/.claude/plans/spf-non-zero-pts-timestamp-offset-spike.md index e5cd663b..f348b822 100644 --- a/.claude/plans/spf-non-zero-pts-timestamp-offset-spike.md +++ b/.claude/plans/spf-non-zero-pts-timestamp-offset-spike.md @@ -33,6 +33,28 @@ The design of record is `internal/design/spf/presentation-timeline-model.md` **KEEP** = lasting, review it; **TODO** = designed, not built; **REVIEW** = needs your detailed pass; **DEFERRED** = later/open. +### Pre-PR checklist (canonical — before converting the spike to a PR) +Working order agreed 2026-07-14: 1→3 first. +1. [x] **~0-PTS threshold** — DONE (uncommitted). `NEAR_ZERO_ORIGIN_THRESHOLD = 1`s (absolute) + in the derive: shared/per-type origin `< 1s` (incl. negatives) → `0` → native, no relocation. + Loader stamp already no-ops on derived `0`. 13 derive tests + full suite green. +2. [ ] **Rebase `--onto main` below the live PR** — branch stacked on `feat/spf-hls-live`; + isolate the relocation commits; manual fixups where anchor/relocation meet. Back up first. +3. [ ] **Live-vs-VOD mutual-exclusion gate** — relocation composed unconditionally; must not + both fire with `anchorPresentationTimeline` on a live source. (Post-rebase / live-integration.) +4. [ ] **Track-id-matched parser** for muxed `clcp` (presumptive first-box → `findMediaTrack`). +5. [ ] **Barrier-liveness bound** — shared-`min` stamp `awaitDefined` unbounded → hang edge case. +6. [ ] **Load-behavior coordinate-assumption audit** — loader planning/flush keyed off `currentTime`. +7. [ ] **Negative-DTS on Chromium** — headline risk; looks validated (shared-`min` keeps DTS ≥ 0 + + cross-browser smoke played to end, no append errors) — confirm + note in PR. +8. [ ] **Composition opt-in / tree-shaking + Tier-0 bundle measure** (currently baked-in w/ markers). +9. [ ] **Commit cleanup** — reword `(spike)`/`wip` commits; resolve `StepDeps` typing (tighten vs leave). +10. [ ] **Docs** — flip `presentation-timeline-model.md` to `implemented`; update stale + `features/non-zero-pts-support.md`; write PR description. +- Deferred (separate effort): playback-lifecycle **e2e** (see "E2E coverage" below). + +Detail for each item is in the sections below (TODO / REVIEW / DEFERRED). + ### Landed (committed) - mp4 box parser + decode-time origin — `cf8aaca45` - presentation timeline coordinate model doc (+ reactor rearchitecture) — `555f9fdac`, `ae1b0beee` @@ -146,6 +168,35 @@ Apple stalled at the end + wouldn't loop. Two compounding causes, two narrow fix - Env: `pnpm size` leaves `packages/spf/tsdown.config.ts` swapped to the single-entry size config (breaks `build` + sandbox until `git restore`d). Interrupted-restore bug. +### E2E coverage — SEPARATE EFFORT (do NOT bake into the current suite yet) +The existing `apps/e2e` suite is entirely **UI/skin-expectation** tests (controls, keyboard, +gestures, captions, visual snapshots); nothing asserts **playback lifecycle** (reaches +`ended`, loops, 0-based timeline). Playback-lifecycle e2e is a NEW category — track it with +the media-renderer-reliability effort ([[project_e2e_renderer_reliability]]), not here. +Cross-browser validation of this work currently lives in the git-ignored sandbox smoke script +`apps/sandbox/src/spf-non-zero-pts/eos-smoke.mjs` (chromium/firefox/webkit × apple/muxclip, all +green 2026-07-14; needs a sandbox server on :5173) — +reusable driving pattern: mute, seek-to-near-end for speed, bounded polls, never `await +media.play()`. +Candidates when this lands: +- **General `SimpleHlsMedia` lifecycle** (NOT spike-coupled; landable independently): plays + through to `ended` + loops, across the 3 browsers, on the existing `simple-hls-video` + + `hlsFmp4` page. Assert the **outcome** (reaches `ended` within N s via seek-to-near-end), + never "the nudge fired" — the outcome holds whether or not Chrome's freeze occurs, so it's + CI-stable. +- **Non-zero-PTS relocation correctness** (spike): 0-based `currentTime`/`seekable`/`buffered` + + active caption cue brackets the 0-based playhead. The owned Mux clip + (`asset_start_time=60`) works and we control it. +- **`recover-end-stall` on skewed A/V** (spike): reaches `ended` + loops on a source that + actually reproduces the tiny-final-segment / audio-clock freeze. **Prereq: an OWNED skewed + asset.** Apple `bipbop_adv_example_hevc` reproduces it but is a third-party CDN + HEVC (forces + the `avcOnly` pin); the Mux clip's ~0.6ms skew does NOT reproduce the freeze. Without an owned + skewed / tiny-final-segment asset this test is either flaky (Apple CDN) or doesn't exercise + the fix. +- e2e media-source convention: external streams centralized in `apps/e2e/fixtures/resources.ts`; + pages generated by `scripts/generate-pages.ts` from `fixtures/media.ts`; parametrized across + chromium/webkit/firefox. + ## Problems the other branch surfaced — keep in view (likely changed, not gone) 1. **Initial-load stall** (anchor-shift / origin-seek / flush mis-coordination) —