docs(phase-12): revise spec — Direction B, per-track datum, full-parity controls
Daniel resolved the open questions: high-res compute for all media (B); the waveform datum is per-track, not per-release (release is just the host — dissolves the multi-track-Cut question); full-parity lava controls on all detail hosts. Splits 12.B into compute+backfill / fetch+bridge; renames the scaffold slot to Ambient. NowPlaying-card controls left as open sub-question.
This commit is contained in:
@@ -202,22 +202,7 @@ Full design, renderer architecture, the four effects, acceptance criteria, and p
|
||||
|
||||
### Phase 10 — Reframe (Lava): Waves R1–R4
|
||||
|
||||
A **major reframe of the Mix visualizer's effects, controls, and color model**, folded **under this same Phase 10** as a reframe wave-set (**Waves R1–R4** — labelled to stay unambiguous against the landed Waves 1–4 above). It builds on the landed Phase 10 renderer infrastructure (pipeline, datum texture, playhead interp, bridge, widened body, lava-lamp trigger) but **replaces what it paints**. Daniel tested the Phase 10 effects end-to-end and rejected the visual result: the lava read as "giant disconnected circles," the colors drifted to cyan (an HSL saturation-boost artifact), and the waveform and lava read as two unrelated things sharing a canvas. The diagnosis (staff-engineer research pass) is that the rejected look is **structural to the effect approach, not a tuning miss**.
|
||||
|
||||
**This supersedes the original Phase 10 (Waves 1–4) effects/controls/color design** — `product-notes/mix-visualizer-webgl-renderer.md` §4 (effects) and §7 (popover-controls) are marked superseded with a pointer to the reframe spec. The renderer *infrastructure* carries forward unchanged.
|
||||
|
||||
**The three reframes:**
|
||||
- **Lava → CPU-physics wax blobs.** Keep the single-pass WebGL2 fragment renderer; add a small CPU-side per-frame physics step modeling ~16–32 Lagrangian "wax blobs" (position/velocity/temperature/radius) uploaded as uniforms and blended with `smin` SDF metaballs. The waveform and lava share **the same plane WITH real 2D elastic collision** (blob↔waveform-boundary + blob↔blob) — the waveform pushes the fluid out of its way (read-only authority preserved; the fluid never deforms the waveform). **Refined by Daniel's Wave R2 eval:** the fluid must read **flat** (an evenly-lit unified body, NOT blobs with bright pointed centers / cone-like radial gradients) and **melt into one fluid** (low viscosity / strong coalescence — "behave more like a fluid," not stiff globs in contact; the slow wax-like *motion* damping and the freely-coalescing *surface* are independent). **Energy-coupled dynamics:** higher heat/energy → **smaller bubbles + more turbulence** (many small lively turbulent bubbles at high heat; fewer, larger, calmer wax at low heat) — the lamp should feel dynamic and fun. At heat 0 the wax rests on the floor and only collision moves it (collision always on, independent of heat); at heat max many small turbulent bubbles rise/morph per second. **Tuning anchor: Daniel's sweet spot is ~20% gravity / ~100% heat** — calibrate defaults/ranges so that combination lands lively. **Rejected: a full ping-pong FBO Navier-Stokes fluid sim** — a lava lamp is high-viscosity/low-turbulence, the opposite regime; large rewrite for unwanted realism. Deliberate later upgrade only.
|
||||
- **Color → three-color OKLab gradient with three motions.** One source of truth (`DeepDrftPalettes`), no hardcoded hexes. Always A→B linear from the center line outward. Three combined motions: (1) anchors A/B **rotate among three theme colors X/Y/Z** at the rotation-speed control's rate — **OKLab interpolation, never through the rainbow** (the cyan fix is structural, not a tuning dial); (2) per-bar sinusoidal variation **baked at segment entry and fixed as the segment scrolls** — realized by **keying the sinusoid to mix-time** so it travels with the segment by construction (decided 2026-06-16; the explicit ring-buffer alternative is rejected for maintainability); (3) per-bar gradient curve shifts with scroll height (mostly A at bottom → mostly B at top). The static noise/frost texture is **removed** (Daniel: makes the screen look dirty).
|
||||
- **Controls → seven knobs in an inline collapse/expand knob-bar.** Replaces the four: (1) waveform scroll speed [replaces resolution/zoom as a standalone control], (2) gradient rotation speed, (3) lava gravity, (4) lava heat, (5) blob density/size, (6) collision strength (soft mush→hard up-and-out throw), (7) **waveform width** [added in the Wave R2 eval — narrows the waveform band so the lava fluid has more room to move on loud/busy songs; scales the waveform's amplitude→width mapping and its collision boundary]. **NOT a popover or drawer — an in-flow controls container BETWEEN the back link (left) and the lava-lamp toggle (right)** on the detail top row (redesigned 2026-06-16, superseding the first realization). The first implementation read "inline collapse/expand" as an `position:absolute` bar floating under the lamp (`.mix-visualizer-controls-anchor`) — it clipped to a vertical sliver and read as a detached popover; **Daniel rejected it.** The redesign: `ReleaseDetailScaffold` gains a new optional **`TopRowCenter`** slot so the top row is `back | center-controls | lamp` (Track/Cut/Session supply neither → back-link-alone, reusability preserved); the seven `RadialKnob`s live in that center slot and **expand/collapse in the layout flow** (CSS width/opacity transition, container grows horizontally between back and lamp — **no `position:absolute`, no float, no overlay, no clipping, never overlaps the masthead/hero**). On narrow widths the container wraps in-flow to a second line / drops to the scaffold's `TopContent` band — never edge-clips. The lava-lamp icon button toggles a bound bool — no MudPopover/MudDrawer. Styled to **match the NowPlaying hero aesthetic** (the session-detail hero overlay — translucent dark glass, overlay-label typography, `Color.Secondary`). Also: overflow-clip the visualizer to the **dynamic footer height** (the player bar changes height minimized/expanded) so visuals stop cleanly above it; the clip line is also the lava rest line.
|
||||
|
||||
**Plus: redraw the lava-lamp glyph.** The current `DDIcons.LavaLamp` is rejected (Daniel: "form is shit, colors are shit"). Redraw to the classic 1970s silhouette — a wide truncated-cone metal **base**, a bulbous→roundedly-pointed teardrop **glass body**, a small cone **cap** ("offset cones") — with **navy fluid + moss blobs** (the theme's blue+green, faithful to the reference and on-theme) and a neutral/metallic base+cap. Authored in `DeepDrftShared.Client/Common/DDIcons.cs` as inner SVG markup (no `<svg>` wrapper; 24×24 viewBox); body silhouette `currentColor`, the two accent fills are commented literals traced to their `DeepDrftPalettes` source (SVG cannot resolve `var()`).
|
||||
|
||||
Heat→intensity and collision soft↔hard transfer functions are **staff-engineer tuning tasks** (endpoints fixed in the spec — heat 0 = wax rests on floor / heat max = many small turbulent rising bubbles; collision soft = gentle mush / hard = high-elasticity up-and-out throw, wide range, smooth/no jitter — formulas not). Full design, the wax-blob model, the collision model, the three-motion color model, the inline knob-bar, the icon redraw, observable acceptance criteria, and phasing: `product-notes/phase-10-mix-visualizer-lava-reframe.md`.
|
||||
|
||||
**Open / undecided (spec §10):** (a) **pause behavior** — whether the lava keeps convecting while audio is *paused* (lamp "always on") vs. the current freeze-on-pause (rAF gated on `isPlaying`) is an **undecided Daniel call**; default to freeze-on-pause until decided, switching is a localized gating change, not a model change. (b) **Future enhancement (deferred, NOT in R1–R4 scope):** a per-control **"auto-modulate" checkbox** that slowly oscillates each knob's value via a low-frequency oscillator so the visualizer drifts on its own — Daniel flagged it as a cool future idea; the knob-bar layout leaves room but nothing builds it now.
|
||||
|
||||
**Sequenced as four reframe waves.** `Wave R1 → Wave R2 → (Wave R3 ‖ Wave R4)`. Wave R1 (de-noise + dynamic footer clip + icon redraw) is a cheap unblock for a clean substrate. Wave R2 (wax-blob physics + 2D collision) is the load-bearing prerequisite — prove the lava before the color and the UI. Wave R3 (OKLab three-color gradient, the three motions) and Wave R4 (seven controls — including the new waveform-width knob — + NowPlaying-styled inline knob-bar + widened state to seven properties + extended bridge handle) both depend on Wave R2 but are independent of each other. **Both prior open Daniel calls are now decided:** controls-UI is an inline collapse/expand knob-bar (not popover/drawer); per-segment color is mix-time-keyed.
|
||||
**Landed:** 2026-06-17 on dev. See `COMPLETED.md` for the full completion record.
|
||||
|
||||
---
|
||||
|
||||
@@ -256,74 +241,120 @@ Sequenced as **eight waves**; the critical path is `11.A → 11.B → 11.C → 1
|
||||
|
||||
## Phase 12 — Waveform Visualizer Generalization + NowPlayingHero Rewire
|
||||
|
||||
Take the landed Mix waveform visualizer (the WebGL2 lava renderer + its seven-knob controls, Phase 10
|
||||
reframe) and **make it the one release-cardinal visualizer** — serving Mix detail, all Release Detail
|
||||
pages, *and* the home-page NowPlaying card — instead of a Mix-only backdrop forked three ways. **Two
|
||||
deliverables, one engine, DRY/SOLID the explicit ask.** Full design, the extraction analysis, the datum
|
||||
decision, wave decomposition, and open questions: `product-notes/phase-12-waveform-visualizer-generalization.md`.
|
||||
Take the landed Mix waveform visualizer (the WebGL2 lava renderer + its eight-knob controls, Phase 10
|
||||
reframe) and **make it the one track-cardinal visualizer** — serving Mix detail, all Release Detail
|
||||
pages, *and* the home-page NowPlaying card — rendering the waveform of **whatever track is currently
|
||||
playing/selected**, instead of a Mix-only treatment forked three ways. **Two deliverables, one engine in
|
||||
three hosting modes, DRY/SOLID the explicit ask.** Full design, the extraction analysis, the per-track
|
||||
model, Direction B compute, wave decomposition, and open questions:
|
||||
`product-notes/phase-12-waveform-visualizer-generalization.md`.
|
||||
|
||||
**Central finding (verified read, 2026-06-17): the engine is already release-cardinal below the surface.**
|
||||
**Keystone model correction (Daniel, 2026-06-17): the datum is PER-TRACK, not per-release.** *"Each track
|
||||
in the release must get the metadata… the release is just the host."* Every track carries its own high-res
|
||||
waveform datum; the visualizer renders the *currently playing/selected* track's datum, and the release is
|
||||
merely the host surface. This *simplifies* the design — it aligns with the bridge already keying on
|
||||
`TrackId`, and it **dissolves** the old "what is a multi-track Cut's waveform?" question (no release-level
|
||||
datum to choose). Threaded through the datum source, the endpoint shape, the bridge, and acceptance.
|
||||
|
||||
**Central finding (verified read, 2026-06-17): the engine is already track-cardinal below the surface.**
|
||||
`MixWaveformVisualizer`'s bridge keys on `ReleaseEntryKey` + `TrackId` (not Mix); the renderer is a pure
|
||||
function of a loudness datum + duration; the controls/state are renderer-agnostic. The *only* genuinely
|
||||
Mix-coupled surface is (1) the datum **fetch** (`GET api/release/{entryKey}/mix/waveform` 404s unless
|
||||
`Medium == Mix`) and (2) the high-res datum **source** (the `mix-waveforms` vault, Mix-only). Everything
|
||||
else is just *named* `Mix*`. So "generalize from Mix to all releases" is a **rename + a data-source
|
||||
generalization, not a rebuild** — the renderer, bridge, controls, read-only contract all carry forward
|
||||
from the Phase 10 reframe unchanged.
|
||||
Mix-coupled surface is (1) the datum **fetch** (per-release, `GET api/release/{entryKey}/mix/waveform` 404s
|
||||
unless `Medium == Mix`) and (2) the high-res datum **source** (the `mix-waveforms` vault, Mix-track-only).
|
||||
Everything else is just *named* `Mix*`. So "generalize from Mix to all tracks" is a **rename + a per-track
|
||||
high-res compute generalization, not a rebuild** — the renderer, bridge, controls, read-only contract all
|
||||
carry forward from the Phase 10 reframe unchanged.
|
||||
|
||||
**Crucial data fact (verified): non-Mix releases are not a data gap.** *Every* uploaded track already gets
|
||||
a **512-bucket** waveform profile (`UnifiedTrackService.UploadAsync` → `waveform-profiles` vault, the
|
||||
datum the player-bar `WaveformSeeker` consumes). Mixes *additionally* get a duration-derived **high-res**
|
||||
datum (~333 samples/sec, `mix-waveforms` vault, CMS-triggered). So the only question for non-Mix is
|
||||
*resolution*, not existence — and §8a frames that as the one real product call (recommend: serve the
|
||||
existing 512-bucket profile for ambient non-Mix backdrops in v1; high-res-for-all is a roadmap upgrade).
|
||||
**Datum decision (Daniel, 2026-06-17): Direction B — high-res for ALL media.** Today every uploaded track
|
||||
gets a **512-bucket** profile (`UnifiedTrackService.UploadAsync` → `waveform-profiles` vault, consumed by
|
||||
the player-bar `WaveformSeeker`); only **Mix tracks** *additionally* get the duration-derived **high-res**
|
||||
datum (~333 samples/sec, `mix-waveforms` vault, CMS-triggered). Direction B **generalizes the high-res
|
||||
compute to every track**: the content compute path goes medium-neutral, the upload path computes a per-track
|
||||
high-res datum for every new track, the CMS generate action generalizes off Mix-only, and a **backfill**
|
||||
populates existing tracks. The cheaper road (serve the existing 512-bucket profile to non-Mix, zero new
|
||||
compute — old "Direction A") is **declined** in favor of uniform high-res. So 12.B is no longer "a new
|
||||
endpoint" — it is a content + upload + CMS + backfill + fetch slice (split into 12.B1 / 12.B2 below).
|
||||
|
||||
**Deliverable 2 — NowPlayingHero overhaul.** `NowPlayingCard.razor` today animates **20 hardcoded
|
||||
**Three hosting modes of the one engine (Daniel corrected "backdrop").** *"backdrop?? MIXES doesn't really
|
||||
have a backdrop?"* — right: on Mix the visualizer is the full-bleed **centerpiece that IS the page**, not
|
||||
something behind content. The one engine is hosted three ways (spec §3f): **mode A — visualizer-is-the-page**
|
||||
(Mix detail, full-bleed, seven-knob bar); **mode B — ambient environment** (Cut/Session detail, living
|
||||
texture *behind* the hero+content — this is the only mode that is genuinely a "backdrop"); **mode C —
|
||||
contained live element** (NowPlaying card, a bounded live readout, `Fill`-sized to the card). Same engine,
|
||||
same datum contract — variance is entirely in hosting composition. **Controls (Daniel, full parity, §8b):**
|
||||
the seven-knob bar + lava-lamp toggle ride **every Release Detail host** — Mix, Cut, **and** Session (modes
|
||||
A and B both carry the controls, not Mix-only); only the contained NowPlaying card (mode C) suppresses them
|
||||
by default (one open sub-question — §8b-followup).
|
||||
|
||||
**Deliverable 2 — NowPlayingHero overhaul (mode C).** `NowPlayingCard.razor` today animates **20 hardcoded
|
||||
CSS-bounce bars** with no audio coupling (the "stochastic" visualizer). Replace them with the *same*
|
||||
`WaveformVisualizer`, mounted inside the existing player cascade and pointed at "whatever is playing right
|
||||
now" — so the home card shows the **real** waveform of the live track, Mix or not. The payoff of the
|
||||
generalization: the NowPlaying card is *just another host* of the one engine, not a fork. The one genuine
|
||||
engineering wrinkle is that the renderer assumes full-viewport (`position: fixed; inset: 0`,
|
||||
clip-to-footer) and the card needs it container-relative — recommend a `Fill` mode parameter (spec §6c).
|
||||
`WaveformVisualizer`, mounted inside the existing player cascade and pointed at the **current track** — so
|
||||
the home card shows the **real** high-res waveform of the live track, Mix or not. The payoff of the
|
||||
generalization: the NowPlaying card is *just another host* of the one engine. The one genuine engineering
|
||||
wrinkle is that the renderer assumes full-viewport (`position: fixed; inset: 0`, clip-to-footer) and the
|
||||
card needs it container-relative — recommend a `Fill` mode parameter (spec §6c).
|
||||
|
||||
**Design discipline.** Rename the engine to its abstraction (`MixWaveformVisualizer` → `WaveformVisualizer`,
|
||||
etc.) — a `Mix`-named component on a Cut page is a lie that cements the wrong model. Variance rides
|
||||
**composition** (a new optional `Backdrop` slot on `ReleaseDetailScaffold`; per-host control suppression),
|
||||
never a `switch (medium)` in the engine (memory *One source, multiple views*; scaffold's "variance rides a
|
||||
slot, never a flag" idiom, Phase 9 §5.3). The lava controls stay a **Mix affordance by default**;
|
||||
Cut/Session (if they get a backdrop) mount it controls-suppressed as ambient — the seam supports controls-
|
||||
everywhere later with no engine change (memory *Design for adaptability up front*).
|
||||
**composition** (a new optional `Ambient` slot on `ReleaseDetailScaffold` for mode B; Mix keeps its own
|
||||
mode-A mount; the card is a mode-C contained mount; per-host control suppression), never a `switch (medium)`
|
||||
in the engine (memory *One source, multiple views*; scaffold's "variance rides a slot, never a flag" idiom,
|
||||
Phase 9 §5.3). The slot is named `Ambient` not `Backdrop` precisely because Mix doesn't use it. The lava
|
||||
controls ride **every Release Detail host** (Mix, Cut, Session — full parity, Daniel's §8b call); only the
|
||||
contained NowPlaying card mounts controls-suppressed by default. The seam still supports flipping the card
|
||||
to controls-on later with no engine change (memory *Design for adaptability up front*).
|
||||
|
||||
Sequenced as **four waves**: `12.A → 12.B → (12.C ‖ 12.D)`.
|
||||
Sequenced as **five waves**: `12.A → 12.B2 → (12.C ‖ 12.D)`, with **12.B1 a parallel server-side track**
|
||||
(`12.B1 → 12.B2`) that can start cold day one.
|
||||
|
||||
- **12.A — Rename to the abstraction (mechanical, no behavior change).** `Mix*` → `Waveform*` across the
|
||||
five C#/Razor files + the TS module + its import path + the DI registration. **Load-bearing
|
||||
prerequisite** — every later wave references the generalized names. Acceptance: Mix detail identical;
|
||||
diff is identifiers only.
|
||||
- **12.B — Generalize the datum fetch + endpoint.** New unauthenticated `GET
|
||||
api/release/{entryKey}/waveform` resolving the **best-available** datum (high-res for Mix-with-datum;
|
||||
512-bucket per-track profile otherwise); `IReleaseDataService.GetReleaseWaveform`; bridge calls it
|
||||
instead of `GetMixWaveform`. **Depends on 12.A.** Direction A (recommended) needs no decision to start;
|
||||
the high-res-for-all alternative (Direction B) does — §8a.
|
||||
- **12.C — `Backdrop` slot on `ReleaseDetailScaffold` + mount on detail pages.** Promote the full-bleed /
|
||||
foreground-stacking / dynamic-footer-clip pattern into the scaffold as an optional `Backdrop` slot; Mix
|
||||
re-expresses its current mount through it; Cut mounts the controls-suppressed ambient backdrop; Session
|
||||
mounts directly (it doesn't compose the scaffold — spec §3e) **if** §8b opts non-Mix in. **Depends on
|
||||
12.B.**
|
||||
- **12.D — NowPlayingHero rewire.** Replace the synthetic bars with a contained, controls-suppressed
|
||||
`<WaveformVisualizer>` driven by the live cascaded player; add the `Fill`/container-sizing mode (spec
|
||||
§6c). **Depends on 12.A + 12.B; independent of 12.C** (different host). Acceptance: home card shows the
|
||||
real playing-track waveform, at-rest when nothing plays; no synthetic bars remain.
|
||||
- **12.B1 — Generalize the high-res compute to every track + backfill (Direction B, the data change).**
|
||||
Generalize the duration-derived compute off Mix-only (`WaveformProfileService` / `MixWaveformResolution`),
|
||||
store per-track keyed by `EntryKey` in a (renamed) `track-waveforms` vault, add per-track high-res compute
|
||||
to `UnifiedTrackService.UploadAsync`, generalize the CMS generate action to any track, and run the
|
||||
**Daniel-gated backfill** for existing tracks (§8a-new). **Independent of 12.A** (server/content-side).
|
||||
The new load-bearing heavy. Acceptance: every track has a high-res datum; new uploads get one; the
|
||||
generate action works for any track.
|
||||
- **12.B2 — Per-track datum fetch + bridge rewire.** New track-cardinal `GET
|
||||
api/track/{trackEntryKey}/waveform` (spec §5b); `GetTrackWaveform`; bridge resolves the *current track's*
|
||||
`EntryKey` and re-fetches on **track** change (not release change). **Depends on 12.A + 12.B1.**
|
||||
Acceptance: Mix renders the same high-res lava via the track-cardinal fetch; a non-Mix track returns
|
||||
high-res.
|
||||
- **12.C — `Ambient` slot on `ReleaseDetailScaffold` + mount on detail pages (mode B, full parity).**
|
||||
Promote the full-bleed / foreground-stacking / dynamic-footer-clip pattern into the scaffold as an optional
|
||||
`Ambient` slot; Cut mounts the ambient layer **with the full seven-knob bar + lava-lamp toggle** (full
|
||||
parity); Session mounts directly **also full-parity** (it doesn't compose the scaffold — spec §3e). Mix is
|
||||
**unchanged** (mode A keeps its own mount + controls). Also makes the state-scoping call (recommend one
|
||||
shared `WaveformVisualizerControlState`). **Depends on 12.B2.** **§8b resolved (full parity) — no longer
|
||||
gated**; Cut and Session ship with both the ambient layer and the controls.
|
||||
- **12.D — NowPlayingHero rewire (mode C).** Replace the synthetic bars with a contained,
|
||||
controls-suppressed `<WaveformVisualizer>` driven by the live cascaded player, pointed at the current
|
||||
track; add the `Fill`/container-sizing mode (spec §6c). **Depends on 12.A + 12.B2; independent of 12.C**
|
||||
(different host). Acceptance: home card shows the real playing-track high-res waveform, at-rest when
|
||||
nothing plays; no synthetic bars remain.
|
||||
|
||||
**Open product decisions (spec §8, need Daniel before the dependent wave):** (a) **non-Mix datum
|
||||
resolution** — existing 512-bucket per-track profile (Direction A, recommended, zero new compute) vs.
|
||||
high-res-for-all (Direction B, new CMS/API/content + backfill); gates 12.B's *richness*, blocks nothing if
|
||||
A. (b) **Do Cut/Session get a backdrop, and with controls?** — recommend ambient-on-all-media /
|
||||
controls-Mix-only; note even Mix-only still wants 12.A/B/D for the NowPlaying win. (c) **What is a
|
||||
multi-track Cut's waveform?** — recommend first track by `TrackNumber` for v1; only bites if (b) gives Cut
|
||||
a backdrop. (d) **NowPlaying container-sizing + home-page perf** — staff-engineer-owned (`Fill` mode;
|
||||
`isPlaying`-gated rAF means an idle home page pays nothing), flagged so a lava lamp on the landing page is
|
||||
no surprise.
|
||||
**Resolved by Daniel (2026-06-17), kept visible per file convention:** datum resolution → **Direction B**
|
||||
(high-res all media; 512-bucket-fallback "Direction A" declined); multi-track-Cut datum → **dissolved by
|
||||
the per-track model** (renders the current track's datum, no album-representative choice); Cut/Session
|
||||
hosting + controls → **full parity (option 3)**: all three hosting modes ship **and** the seven-knob bar +
|
||||
lava-lamp toggle ride every Release Detail host (Mix, Cut, Session), not Mix-only — the three-mode *layout*
|
||||
framing is retained, the change is that controls are no longer Mix-suppressed on Cut/Session (the old "mode
|
||||
1 Mix-only" and "controls Mix-only" alternatives are both closed). **Newly open (created by the full-parity
|
||||
flip + Direction B + per-track):** (a) **§8b-followup — do full-parity controls extend onto the NowPlaying
|
||||
home card (mode C)?** Full parity was answered against the *detail* pages; the small contained home card
|
||||
stays **controls-suppressed by default** (a seven-knob bar on a compact now-playing card may be awkward) —
|
||||
recommend keeping it suppressed, but flag for Daniel; one-line composition flip in 12.D either way.
|
||||
(b) **§8a-new — backfill shape + gate**: one-shot migration/script vs. a CMS
|
||||
batch action over the generalized generate action (recommend the CMS action; Daniel-gated to *run* either
|
||||
way; the fetch 404s gracefully for not-yet-backfilled tracks so it can ship before the backfill completes).
|
||||
(c) **§8b-new — per-track high-res compute cost** (flag only): upload latency (recommend inline; deferral is
|
||||
the escape hatch) + storage growth (every track now stores a high-res datum, a multi-track Cut stores N —
|
||||
modest, surfaced not blocking). (d) **§8d — NowPlaying container-sizing + home-page perf** —
|
||||
staff-engineer-owned (`Fill` mode; `isPlaying`-gated rAF means an idle home page pays nothing), flagged so
|
||||
a lava lamp on the landing page is no surprise.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user