false disappears in templates. Wrap booleans in String() for ARIA attributes.
Check ctx.isCurrent() after every await in a view, before drawing.
No await between a click and browser playback, or the browser may refuse to play.
#view scrolls, not the window. Scroll and IntersectionObserver code must use document.getElementById('view').
Check contrast in all three themes. Bootstrap's built-in colours assume light or dark mode. Synthwave is a custom mode that starts from Bootstrap's light defaults, which is how its off-switch knob ended up black on black. tests/e2e/test_theme_contrast.py measures contrast in each theme.
Hidden isn't secure.isAdmin() only hides buttons. The guard on the route is what stops a request.
performance.now() starts at page load, not at zero time. Don't use 0 to mean "never" for a timestamp compared with it; use -Infinity. The lyrics panel's "scrolled by hand" check thought every page under 4 seconds old had just been scrolled.
A loaded relationship doesn't follow its foreign key. After changing playlist.owner_id, session.expire(playlist, ["owner"]), or the response names the old owner. Locking a row that's loaded with a joined relationship needs with_for_update(of=Playlist): Postgres can't lock the optional side of an outer join. It still locks just that one row.
Bootstrap modals ignore hide() while they're still opening. Use hideModal() from base.js.
Testing downloads with Playwright needs free space in /tmp. Test browser profiles, and their cached music, live there, and a full disk shows up as a "network error" while storing a song, not as "out of space". It cost an afternoon chasing a fix that wasn't needed.
No stale JavaScript after a deploy, but versioned file names would change that. App files are no-cache, and the service worker skips the HTTP cache for them. If they ever get hashed names cached forever, revisit both, or every load re-checks files that never change.
The app's web view draws nothing while the phone's screen is off. Screenshots and page.screenshot() over CDP need it awake. The test phone has no passcode: adb shell input keyevent KEYCODE_WAKEUP; adb shell wm dismiss-keyguard.
The iOS app has no service worker at all, even over HTTPS (see The iOS app). It opens offline from its own kept copy instead (see Opening the iOS app offline).
The service worker only runs on HTTPS or localhost. Testing offline features over http://192.168… shows "unavailable", and that's expected.
The classic UI uses the same API. Changing a response shape can break /v2/. Search frontend/v2/js/script_v2.js for the field.
Per-person or household. With profiles on, Most played, mixes and the Songs play sorts are the asker's own. Artist Popular and /v2/ use household totals.