fix: stabilize the live recommendation path
Some checks are pending
ci / backend (push) Waiting to run
ci / frontend (push) Waiting to run

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Justin Visser 2026-08-12 10:47:30 +02:00
parent bfea9efb5e
commit dbc5c2d93f
12 changed files with 113 additions and 31 deletions

View file

@ -22,6 +22,7 @@ class ResolvedSession:
session_id: str
session: SpotifySession
can_logout: bool
@router.get("/api/auth/login")
@ -81,7 +82,12 @@ def current_session(request: Request) -> JSONResponse:
resolved = resolve_session(request)
if resolved is None:
return JSONResponse({"detail": "Not authenticated"}, status_code=401)
return JSONResponse({"display_name": resolved.session.display_name})
return JSONResponse(
{
"display_name": resolved.session.display_name,
"can_logout": resolved.can_logout,
}
)
@router.get("/api/suggestions")
@ -119,16 +125,16 @@ def resolve_session(request: Request) -> ResolvedSession | None:
application_settings = cast(Settings, request.app.state.settings)
if application_settings.app_mode is AppMode.DEMO:
demo_session = cast(SpotifySession, request.app.state.demo_session)
return ResolvedSession("demo", demo_session)
return ResolvedSession("demo", demo_session, can_logout=False)
session_store = cast(SessionStore, request.app.state.session_store)
cookie_session_id = request.cookies.get(SESSION_COOKIE_NAME)
if cookie_session_id is not None:
cookie_session = session_store.get(cookie_session_id)
if cookie_session is not None:
return ResolvedSession(cookie_session_id, cookie_session)
return ResolvedSession(cookie_session_id, cookie_session, can_logout=True)
seed_session_id = cast(str | None, getattr(request.app.state, "seed_session_id", None))
seed_session = session_store.get(seed_session_id) if seed_session_id is not None else None
if seed_session is None or seed_session_id is None:
return None
return ResolvedSession(seed_session_id, seed_session)
return ResolvedSession(seed_session_id, seed_session, can_logout=False)

View file

@ -64,8 +64,12 @@ Retain understandable bridges through genre, scene, production style, instrument
energy, era, or songwriting. The profile is not exhaustive, so never claim that a
candidate is definitely unknown. When familiarity is
familiar, candidates may include supplied top or saved tracks, but remain responsive to the
current request. When familiarity is mix, combine recognizable anchors with real,
confidently identifiable adjacent discoveries rather than splitting into unrelated halves.
current request. When familiarity is mix, the first 15 candidates must be suitable anchors
copied from the supplied top or saved tracks: copy the title exactly and use one listed
artist name exactly, preferring the first. Deduplicate those anchors, then fill the remaining
candidates with confidently identifiable adjacent discoveries. If fewer than 15 supplied
tracks fit, use every suitable one before adding discoveries. When familiarity is familiar,
use supplied tracks for the majority of the list.
Use musical knowledge conservatively. Base selection on durable, commonly knowable
attributes of recordings. Do not invent listening statistics, personal memories, release

View file

@ -52,7 +52,10 @@ def test_login_callback_cookie_and_current_user_flow() -> None:
assert "discovery_session=" in callback_response.headers["set-cookie"]
assert "HttpOnly" in callback_response.headers["set-cookie"]
assert "SameSite=lax" in callback_response.headers["set-cookie"]
assert client.get("/api/auth/me").json() == {"display_name": "Ada Listener"}
assert client.get("/api/auth/me").json() == {
"display_name": "Ada Listener",
"can_logout": True,
}
def test_unknown_callback_state_redirects_to_login_error() -> None:
@ -108,9 +111,16 @@ def test_seed_session_authenticates_requests_without_a_cookie() -> None:
)
with TestClient(app) as client:
response = client.get("/api/auth/me")
logout_response = client.post("/api/auth/logout")
after_logout_response = client.get("/api/auth/me")
assert response.status_code == 200
assert response.json() == {"display_name": "Seed Listener"}
assert response.json() == {"display_name": "Seed Listener", "can_logout": False}
assert logout_response.status_code == 204
assert after_logout_response.json() == {
"display_name": "Seed Listener",
"can_logout": False,
}
def test_seed_session_failure_keeps_application_serving() -> None:

View file

@ -195,7 +195,7 @@ def test_demo_auth_playlist_and_suggestions_are_explicitly_simulated() -> None:
for scenario in load_scenarios()
if not scenario.is_refinement
]
assert current_user.json() == {"display_name": "Demo Listener"}
assert current_user.json() == {"display_name": "Demo Listener", "can_logout": False}
assert login.headers["location"] == "/?login=demo"
assert playlist.json()["url"].startswith("https://open.spotify.com/playlist/demo-")
assert suggestions.json() == expected_suggestions