mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-07-22 21:01:40 +00:00
|
Some checks are pending
ship-ci / ci (push) Waiting to run
* fix(nav): the library sometimes showed the legacy screen — map 'home' inside showScreen
Testers: "randomly, when moving to the library from another menu option, the library shows the
old interface — never when a song ends."
━━━ WHAT WAS ACTUALLY HAPPENING ━━━
#home is the PRE-V3 library screen. The v3 shell replaced it with #v3-songs, and the mapping DID
exist — but only inside WRAPPERS on window.showScreen, and only for callers that go through
`window`. THREE independent parties monkey-patch it, each capturing whatever happens to be there
at the time:
app.js publishes the raw function
-> shell.js wraps it, adding the home -> v3-songs mapping
-> the stems plugin wraps it AGAIN (src/main.js:1029), capturing the current value
Plugins load ASYNCHRONOUSLY. The chain links up in whatever order the race settles, and any
capture taken before shell.js installs — or any re-assignment after it — silently drops the
mapping. Hence "randomly".
AND THE INTERNAL CALLERS NEVER TOUCHED window.showScreen AT ALL. closeCurrentSong and the
Esc-from-settings shortcut call the IMPORTED showScreen, which no wrapper ever sees. Reproduced
in a browser: the unwrapped function with 'home' lands on the dead legacy screen EVERY time.
"Never when a song ends" is the tell, and it is what identified the mechanism: closeCurrentSong
resolves its target through _resolvePlayerOrigin(), which ALREADY applies this mapping. That one
path was fine — which is exactly why the bug looked random rather than total.
PRE-EXISTING, not a regression from the module carve: the onclick="showScreen('home')" links and
the wrapper-only mapping both date to 2026-06-22.
━━━ THE FIX ━━━
The guard lives inside showScreen now: ONE place, in the function every caller routes through,
instead of a chain of monkey-patches that must each remember. Wrapper order stops mattering, and
the module-internal callers are covered for the first time.
Verified in a browser: the raw, unwrapped showScreen('home') — which reproduced as #home — now
lands on #v3-songs, and cannot be undone by any wrapper order.
━━━ AND A [P1] I INTRODUCED, WHICH CODEX CAUGHT ━━━
My first cut mapped BOTH 'home' and 'v3-home', copied straight from _resolvePlayerOrigin.
That is correct THERE and wrong HERE. _resolvePlayerOrigin computes where to RETURN TO after a
song, and landing on the Songs list from the dashboard is the right behaviour. But #v3-home is
the v3 DASHBOARD — a real screen that the shell's Home nav, the onboarding tour and the dashboard
re-render listener all target. Redirecting it would have made Home unreachable.
A LEGACY ALIAS IS NOT THE SAME THING AS A RETURN TARGET. Only 'home' is mapped now, and a test
pins that: re-adding 'v3-home' to the guard fails it.
4 tests, bite-tested both ways.
node 1049, pytest 2425, ESLint 0, Codex 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(nav): nobody may monkey-patch window.showScreen — add screen:changing, make the shell listen (#924)
window.showScreen was wrapped by THREE independent parties, each capturing whatever happened to be
there at the time:
app.js publishes the raw function
-> static/v3/shell.js wrapped it (to call syncActive, and to map home -> v3-songs)
-> the stems plugin wrapped it AGAIN (to tear down on leaving the player)
Plugins load ASYNCHRONOUSLY, so the chain linked up in whatever order the race settled. A capture
taken before shell.js installed silently dropped the mapping it carried — and the library opened on
the dead legacy #home screen. Testers saw that as "randomly, the library shows the old interface"
(#923).
#923 fixed the symptom by moving the mapping inside showScreen. This removes the CAUSE: neither
wrapper ever needed to be one.
━━━ TWO EVENTS, AND THE DISTINCTION IS THE WHOLE POINT ━━━
screen:changing emitted BEFORE anything happens. "I am leaving `from`." Teardown/cancel here.
screen:changed emitted after the DOM and data settle. "I am on `id`." Now carries `from`.
screen:changing is new, and it exists because Codex caught me collapsing the two. The stems plugin
tore down its audio graph BEFORE showScreen did anything; screen:changed fires at the very END,
after core awaits library and provider loads — so moving the plugin onto it would have delayed
teardown behind a slow fetch, or skipped it entirely if that fetch threw, and stems would keep
playing on a non-player screen. A test pins the ordering: screen:changing must precede the first
await.
shell.js is a plain screen:changed listener now, like app.js, audio-mixer.js and tour-engine.js
already were. window.showScreen is an unwrapped function again, and tests/js/
no_showscreen_monkeypatch.test.js fails CI if anything in static/ ever assigns to it again — so the
hazard is structurally impossible rather than merely avoided.
━━━ AND A FALLBACK THAT COULD NEVER FIRE ━━━
My retry-if-the-bus-is-late path listened for `slopsmith:capabilities:ready`. Core dispatches
`feedBack:capabilities:ready` (capabilities.js:1536) — the slopsmith: name is the PRE-DMCA event
and nothing has emitted it since the rename. Codex caught it. A guard that cannot fire is worse
than no guard: it reads as protection and is decoration.
(The same dead-event bug turned out to be sitting in THREE of the stems plugin's fallbacks, where
it has silently disabled its lifecycle wiring whenever the bus was late. Fixed in
feedback-plugin-stems#38.)
VERIFIED. A/B against origin/main: the nav highlight and topbar title follow IDENTICALLY with
shell.js as a listener; screen:changing -> screen:changed fire in order with the right {id, from};
window.showScreen is unwrapped; and showScreen('home') still lands on v3-songs.
node 1053, pytest 2425, ESLint 0, Codex 0.
Closes #924
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| browser | ||
| fixtures | ||
| js | ||
| plugins | ||
| __init__.py | ||
| conftest.py | ||
| test_acoustid_match.py | ||
| test_albums_view.py | ||
| test_appstate.py | ||
| test_art_candidates.py | ||
| test_art_layer.py | ||
| test_artist_alias.py | ||
| test_artist_page.py | ||
| test_artist_sort_title_order.py | ||
| test_audio_bundled.py | ||
| test_audio_effect_mappings.py | ||
| test_audio_local_path.py | ||
| test_audio.py | ||
| test_batch_user_meta.py | ||
| test_builtin_diagnostic_seed.py | ||
| test_builtin_starter_seed.py | ||
| test_collections_api.py | ||
| test_context_menu_api.py | ||
| test_correlation_id.py | ||
| test_curated_album.py | ||
| test_demo_mode.py | ||
| test_diagnostics_bundle.py | ||
| test_diagnostics_hardware.py | ||
| test_diagnostics_redact.py | ||
| test_dlc_junction.py | ||
| test_drums_lib.py | ||
| test_enrichment_plumbing.py | ||
| test_env_compat.py | ||
| test_feedpak_extension.py | ||
| test_field_overrides.py | ||
| test_gap_fill.py | ||
| test_gp2midi.py | ||
| test_gp2notation.py | ||
| test_gp2rs_drums.py | ||
| test_gp2rs_gpx.py | ||
| test_gp2rs.py | ||
| test_gp_audio_sync.py | ||
| test_gp_autosync_warp.py | ||
| test_group_filter_law.py | ||
| test_highway_3d_routes.py | ||
| test_highway_ws_authors.py | ||
| test_highway_ws_instrument_routing.py | ||
| test_highway_ws_notation.py | ||
| test_library_filters.py | ||
| test_library_keyset.py | ||
| test_library_providers.py | ||
| test_lift_keys_notation.py | ||
| test_logging_setup.py | ||
| test_loops_concurrency.py | ||
| test_loose_traversal.py | ||
| test_loosefolder.py | ||
| test_lyrics_transcribe.py | ||
| test_mb_enrichment.py | ||
| test_mb_match.py | ||
| test_midi_import_drums.py | ||
| test_midi_import.py | ||
| test_midi_tempo_map.py | ||
| test_minigames_routes.py | ||
| test_notation_lib.py | ||
| test_notation_lift.py | ||
| test_notation_to_notes.py | ||
| test_packaging.py | ||
| test_parse_arrangement.py | ||
| test_playlist_cover_errors.py | ||
| test_playlists_api.py | ||
| test_plugin_context_contract.py | ||
| test_plugin_manifest_contract.py | ||
| test_plugin_runtime_idempotence.py | ||
| test_plugin_src_route.py | ||
| test_plugins.py | ||
| test_practice_suggestions.py | ||
| test_profile_api.py | ||
| test_profile_domains.py | ||
| test_progression_api.py | ||
| test_progression.py | ||
| test_safepath.py | ||
| test_scraper_options.py | ||
| test_settings_api.py | ||
| test_settings_export_library_db.py | ||
| test_settings_export.py | ||
| test_settings_instrument.py | ||
| test_sloppak_cent_offset.py | ||
| test_sloppak_cover_art.py | ||
| test_sloppak_drumtab_load.py | ||
| test_sloppak_feedpak_version.py | ||
| test_sloppak_file_traversal.py | ||
| test_sloppak_jsonc_load.py | ||
| test_sloppak_keys_load.py | ||
| test_sloppak_notation_load.py | ||
| test_sloppak_original_audio_load.py | ||
| test_sloppak_song_timeline_load.py | ||
| test_song_score.py | ||
| test_song_stats_api.py | ||
| test_song_user_meta.py | ||
| test_song.py | ||
| test_songmeta.py | ||
| test_starter_suggestions.py | ||
| test_startup_status.py | ||
| test_tailwind_rebuild.py | ||
| test_tailwind_runtime_path.py | ||
| test_tones.py | ||
| test_tuning_provider_isolation.py | ||
| test_tunings.py | ||
| test_version_endpoint.py | ||
| test_vocal_pitch.py | ||
| test_wanted_api.py | ||
| test_work_charts_api.py | ||
| test_work_grouping.py | ||
| test_ws_highway_disconnect.py | ||
| test_xp.py | ||