An artist's hero was re-discovered every 24 hours through a tier that fails
per-name, and the stale manifest was deleted on the way in -- before the
replacement lookup had run. So one bad discovery day did not merely fail to
refresh a hero, it destroyed the good one first and left the artist blank.
On this machine that took the persisted store from 26 artists down to 3;
Crazy P, MSPAINT, Sword II, Hermeth, Billy Cobb, Pond, Weezer, Daft Punk and
King Krule all had correct banners on disk that nothing would ever read again.
Three changes:
- The manifest TTL goes from 24 hours to 30 days, matching the cover
resolution horizon. A press photo is still the artist's press photo a month
later; it does not go stale the way metadata does.
- Reading a manifest no longer destroys it. Stale and older-recipe manifests
are retained as a floor and only replaced once a lookup returns something
better, which also migrates them forward to the current recipe. Only files
that are unusable -- oversized, unparseable, or belonging to another artist
-- are removed.
- A failed hero discovery is detected by an EMPTY BANNER LIST, not a None:
catalog_artist_art returns Some as soon as the Deezer profile resolves, so
the previous None check could never fire, and an empty result would also
overwrite good banners whenever logos were present.
Also widens discovery so fewer artists need the floor at all. The query ladder
gains the bare artist name: decorated queries can collapse a rare artist onto a
popular entity sharing a word -- every "Hana Vu ... photo" phrasing returns the
Japanese group HANA (525 of 525 cards, quoting included), while the bare name
returns her real press photos with 16 of 17 passing the identity gate as
Strong. The gate, not the query, is what filters.
And a near-empty Bing page now counts as degraded. The retry test was
is_empty(), which let the commonest degraded shape through: a one-card page of
something unrelated, cached as that artist's verdict for a quarter hour.
Measured 15 of 30 waves for MSPAINT and Billy Cobb. The richer of the two
reads is kept so a thin retry cannot lose a good first answer.
Verified live: Crazy P, MSPAINT, Sword II and Hermeth all render heroes again.
Preview modal audio, five rounds:
- True track-change crossfade: scheduleSettle no longer silences the
outgoing clip at t=0; preview_load's equal-power fade owns both ramps,
gated on the incoming voice's first Playing event (audible_marks), with
failure/overstay fallbacks. CROSSFADE_MS = 340 — locked to the reel's
card-slide so audio and visuals land together.
- Two-neighbour lease prefetch + a 15-min on-disk clip byte-cache
(recent_clips): steps in BOTH directions crossfade in ~15 ms; revisits
never re-download.
- Adaptive settle (isolated steps fire immediately, bursts coalesce) and
anticipatory auto-advance (step at clip end minus 400 ms) so auto-play
crossfades over the tail instead of gapping.
- Session resume memory: revisited tracks continue where they left off
(decoder-level start offset via play_local_from/resume_at); finished
tracks restart from the top.
- Open/close crossfades with the MAIN player: fade-pause with the
desired-paused latch landing at ramp end, fade-resume from the paused
position; MAIN_FADE_GEN guards; focus handshake relaxation documented
in focusBridge.
Volume normalization (Settings > Playback, default on):
- Dependency-free BS.1770/EBU R128 meter in the player crate (K-weighting,
400 ms gated blocks, incremental) + analyzer that reads the same growing
file playback streams from — first plays are measured at download speed.
- Gain layered under user volume; audible start GATED on a preliminary
measurement (10 s of audio or 800 ms budget) so the level is right from
the first sample and never changes mid-track; prelim boosts capped 3 dB,
peak-aware clipping guard, persistent loudness-cache.json.
- Gapless boundaries get their own sample-accurate gain via
SetNextNormGainDb, fed by preload analysis.
Debug overlay (Ctrl+Shift+D): process-tree CPU / PDH GPU / working-set +
private-commit RAM / network rates / FPS / current-track download progress
read from the backend's own StreamProgress / live normalization state /
preview session phase. Polls only while visible.
131 JS + player + app tests green; every path verified live over CDP.
The previous .git directory was found gutted (only info/ remained), so
history is unrecoverable locally. This commit restores version control
with the tree exactly as it stands: the working app, the preview-tracks
launcher on the artist hero, the scrollable-track-previews design bundle
(docs + approved mockups), and the Phase 0 validation artifacts.