docs: resolve Phase 18 OQ7 seek-index granularity to 0.5s buckets

This commit is contained in:
daniel-c-harvey
2026-06-23 05:36:25 -04:00
parent 274d0ace62
commit 8752fc0c98
2 changed files with 34 additions and 31 deletions
+6 -6
View File
@@ -473,8 +473,8 @@ Settings menu** (not a bare app-bar control); OQ2 default → **Opus by default,
network-awareness); OQ3 remembered → **persisted via the dark-mode seam** (cookie → prerender-read →
`PersistentComponentState` → client cookie service); OQ4 → **always-on Opus + Backfill-Opus**; OQ5 →
**Ogg Opus**; OQ6 transcode model → **background job after the file is available, with a visible
Post-Processing phase on the CMS upload meter.** One new tuning OQ (OQ7: seek-index granularity — recommend
~12 s buckets) is non-blocking.
Post-Processing phase on the CMS upload meter.** OQ7 (seek-index granularity) → **0.5 s (half-second)
buckets** (~115 KB index for a 1-hour mix).
**Architectural spine — a derived artifact set + a delivery param + one new decoder + a precomputed
accurate seek index; leaf implementations only, zero changes to existing format code (the strong OCP
@@ -496,8 +496,8 @@ path on browsers that can't decode it.
byte-offset seek and rough page interpolation are inadequate for VBR Opus — there is no linear time↔byte
relationship. The fix is an **accurate transfer function built at transcode time** (the one moment the
whole encoded stream is walked): a precomputed **seek index** mapping Ogg-page `granulepos` (48 kHz sample
counts → time) → exact byte offset (recommend ~12 s buckets snapped to page starts; ~58 KB for a 1-hour
mix). The decode **setup header** (`OpusHead`/`OpusTags`, needed to decode any mid-stream slice) is made
counts → time) → exact byte offset (**0.5 s buckets** snapped to page starts — OQ7; ~7,200 entries ×
16 bytes ≈ ~115 KB for a 1-hour mix). The decode **setup header** (`OpusHead`/`OpusTags`, needed to decode any mid-stream slice) is made
available too. Recommended concrete design: **one sidecar artifact per track = `[setup header][seek
index]`, built at transcode, stored beside the Opus bytes, fetched once on track load**, parsed into
`OpusSeekData`. Client seek flow: `calculateByteOffset(t)` binary-searches the index for the exact page
@@ -554,8 +554,8 @@ bytes to serve, decode, or seek against until those artifacts exist.
**Dependency shape:** `18.1 → 18.2 → {18.3 ∥ 18.4} → 18.5`; `18.6 ∥` (needs 18.3 for the live toggle);
18.1 is the only cold-start wave. **Phase-level: 18 precedes Phase 21** (windowed refill consumes the Phase
18 seek-index resolver). **OQ1OQ6 RESOLVED (above); OQ7 (seek-index granularity, recommend ~12 s buckets)
is a non-blocking tuning steer.** None block 18.1.
18 seek-index resolver). **OQ1OQ7 RESOLVED (above); OQ7 (seek-index granularity) = 0.5 s buckets.** None
block 18.1.
---
@@ -3,8 +3,9 @@
Product spec. Status: **design / framing — open questions RESOLVED (Daniel, 2026-06-23); implementation-ready.**
Author: product-designer. Date: 2026-06-23. **No code has been written by this doc.**
> **Resolution pass (Daniel, 2026-06-23).** OQ1OQ6 are resolved (see §6 — each marked RESOLVED, kept
> visible per file convention). Two resolutions reshaped the spec materially: (a) the listener quality
> **Resolution pass (Daniel, 2026-06-23).** OQ1OQ7 are resolved (see §6 — each marked RESOLVED, kept
> visible per file convention; OQ7 — seek-index granularity — set to **0.5 s buckets**). Two resolutions
> reshaped the spec materially: (a) the listener quality
> selection lives inside a **new public-site Settings menu surface** (not a bare app-bar control) — §4 +
> §4a; and (b) Daniel rejected the "approximate page-interpolation" seek hand-wave outright — **VBR-safe
> *accurate* seeking is now a first-class part of the architecture** (a precomputed seek-index artifact +
@@ -329,15 +330,17 @@ estimated.
fixed-width records is the natural shape (e.g. a `uint64 granulepos` + `uint64 byteOffset` per entry);
the exact encoding is staff-engineer's, but it should be a **compact binary blob**, fetched once and
parsed into a typed array client-side.
- **Granularity vs. size (the one real tuning knob).** One entry per Ogg page is the most precise but
largest; an Ogg page is typically a few KB of audio (~tens of ms to a few hundred ms), so a 1-hour mix
could be tens of thousands of pages. Recommend a **coarser bucket: one index entry per ~12 seconds of
audio** (snap each bucket boundary to the *nearest enclosing page start*, so every indexed offset is
still an exact page boundary). At ~1 s granularity a 1-hour mix is ~3,600 entries × 16 bytes ≈ **~58 KB**
a trivial one-time fetch, and 1 s seek resolution is more than fine (the decoder re-syncs to the exact
page within the bucket anyway — see the client flow). **Per-page precision is the fallback if 1 s buckets
ever prove too coarse**, at a larger index. The number is staff-engineer's call; the *shape* (precomputed
exact granule→byte, bucketed, snapped to page starts) is fixed.
- **Granularity vs. size — RESOLVED: 0.5 s (half-second) buckets (Daniel, 2026-06-23).** One entry per
Ogg page is the most precise but largest; an Ogg page is typically a few KB of audio (~tens of ms to a
few hundred ms), so a 1-hour mix could be tens of thousands of pages. The chosen bucket is **one index
entry per 0.5 seconds of audio** (snap each bucket boundary to the *nearest enclosing page start*, so
every indexed offset is still an exact page boundary). At 0.5 s granularity a 1-hour mix is
~7,200 entries × 16 bytes ≈ **~115 KB** — still a trivial one-time fetch, and 0.5 s seek resolution is
finer than required (the decoder re-syncs to the exact page within the bucket anyway — see the client
flow — so the in-bucket trim is *sub-half-second*, tighter than the earlier ~12 s recommendation).
**Per-page precision remains the fallback if 0.5 s buckets ever prove too coarse**, at a larger index.
The bucket size is now fixed; the *shape* (precomputed exact granule→byte, bucketed, snapped to page
starts) is unchanged.
- **Sidecar, not embedded (recommended).** Store the index as a **third derived artifact** alongside the
Opus bytes and the waveform datum — the same "derived artifacts get their own vault" pattern this phase
already uses (S2 / `track-opus`; the `track-waveforms` precedent). Keep it a separate vault resource
@@ -395,8 +398,8 @@ With the sidecar (`OpusSeekData` = setup header + granule→byte index) fetched
page start, the stream is immediately Ogg-sync-aligned.
4. **Fine re-sync within the bucket.** The granule of the first decoded page tells the decoder the *exact*
time it landed at (≤ the bucket granularity ahead of `t`); the scheduler trims/positions to land
playback at `t` precisely. With ~1 s buckets the trim is sub-second; with per-page granularity it is
near-zero. **Either way the listener lands at the correct time, not approximately** (AC9).
playback at `t` precisely. With 0.5 s buckets the trim is sub-half-second; with per-page granularity it
is near-zero. **Either way the listener lands at the correct time, not approximately** (AC9).
#### D. Composition with Phase 21 windowed refill
@@ -581,9 +584,9 @@ the "one source, multiple views" / design-for-adaptability discipline applied to
## 6. Open questions — RESOLVED (Daniel, 2026-06-23)
All six original open questions are resolved. Kept visible per file convention, each with the decision and
the section that now carries it. One new open question (OQ7) is raised by the seek-model design; it is a
narrow tuning/scoping call, not a blocker.
All seven open questions are resolved. Kept visible per file convention, each with the decision and
the section that now carries it. OQ7 (raised by the seek-model design) is a narrow tuning call, now set to
0.5 s buckets.
- **OQ1 — Selection UX — RESOLVED: global, via a Settings *menu* (not a bare app-bar control).** Daniel:
*"Global is perfect, but we need a menu system for settings, don't just slap the quality control directly
@@ -612,15 +615,15 @@ narrow tuning/scoping call, not a blocker.
track is lossless-only until its Opus finishes — accepted, and now made visible rather than implicit.
`[RESOLVED — §3.1a]`
**New open question raised by the seek-model design (§3.4a) — narrow, non-blocking:**
**New open question raised by the seek-model design (§3.4a) — RESOLVED:**
- **OQ7 — Seek-index granularity (tuning, leans implementation).** The seek index trades precision against
size: per-Ogg-page (most precise, largest) vs. coarser time buckets snapped to page starts. Recommend
**~12 s buckets** (~58 KB for a 1-hour mix at 1 s; the decoder fine-re-syncs within the bucket so seek
*accuracy* is unaffected — only the in-bucket trim distance changes). This is largely staff-engineer's
call at implementation; flagged because the *number* is a deliberate choice and Daniel may have a feel
for acceptable index size vs. in-bucket trim. Does **not** block — the shape (precomputed exact
granule→byte, page-snapped) is fixed regardless of the bucket size. `[Daniel steer — not a blocker]`
- **OQ7 — Seek-index granularity — RESOLVED: 0.5 s (half-second) buckets (Daniel, 2026-06-23).** The seek
index trades precision against size: per-Ogg-page (most precise, largest) vs. coarser time buckets snapped
to page starts. Daniel set the bucket at **0.5 s** (finer than the ~12 s the spec had recommended):
~7,200 entries × 16 bytes ≈ **~115 KB** for a 1-hour mix — still a trivial one-time fetch. The decoder
fine-re-syncs within the bucket so seek *accuracy* is unaffected; at 0.5 s the in-bucket trim is
sub-half-second, tighter than before. The shape (precomputed exact granule→byte, page-snapped) is
unchanged. `[RESOLVED — §3.4a A]`
---
@@ -652,7 +655,7 @@ narrow tuning/scoping call, not a blocker.
the client fetches and parses it once on track load (into `OpusSeekData`) before issuing any seek.
- **AC9 (the seek-accuracy criterion) — an Opus seek lands at the *correct* time, not approximately.**
Seeking to time `t` in an Opus stream resolves via the precomputed index and lands playback at `t`
(within the fine-resync tolerance — sub-second at the recommended bucket granularity), **measurably
(within the fine-resync tolerance — sub-half-second at the chosen 0.5 s bucket granularity), **measurably
accurate**, not a `byteRate`/interpolation estimate. Verifiable: seek to a known marker (e.g. a downbeat
at a known timestamp) and confirm playback resumes there, not seconds off. This holds **without** the
full PCM decoded in memory (composes with Phase 21).