Symptom
Live console on https://worldmonitor.app/desktop (anonymous session, observed 2026-07-05 while verifying #4902/#4907):
[News Summarization] Failed: ApiError: Request failed with status 401
GET .../api/news/v1/summarize-article 401 [repeating]
Repeats continuously as news panels refresh (8+ in the first minute of page life).
Root cause — third instance of the server-gate-without-client-sweep pattern
#4675 → PR #4687 gated /api/news/v1/summarize-article LLM spend server-side (premium entitlement). The dashboard caller was never swept: src/services/summarization.ts routes every summarize attempt through premiumNewsClient = premiumFetch(..., { forcePremium: true }) — and premiumFetch is an auth-injection wrapper, not an entitlement gate (#4865): anon users fall through to a wms_ session token, dispatch the RPC anyway, and eat the 401.
Worse, API_PROVIDERS fans out ollama → groq → openrouter through the same RPC, so a single summarization attempt can burn up to 3 gated calls before falling back to browser T5. The tryApiProvider catch returns null (no 401/403-specific suppression), so every news refresh retries the whole chain.
Same class as: classify-event #4779 → #4865 (fixed by #4869 enqueue-gate + timed 403 suppress-drain) and health #4715 → #4902 (fixed by PR #4905).
Fix direction (per the #4869 pattern)
- Enqueue gate: check
panel-gating.hasPremiumAccess() before dispatching any API-provider summarize attempt; anon/free goes straight to the browser T5 fallback (the designed anon path) with zero network calls.
- Server 401/403 → timed suppression window (suppress the whole API-provider chain, not per-provider) so signal drift can't recreate the flood; a mid-session upgrade self-heals when the window expires.
- Regression tests for the anon path (no RPC dispatched) and the suppression window.
Evidence
Symptom
Live console on https://worldmonitor.app/desktop (anonymous session, observed 2026-07-05 while verifying #4902/#4907):
Repeats continuously as news panels refresh (8+ in the first minute of page life).
Root cause — third instance of the server-gate-without-client-sweep pattern
#4675 → PR #4687 gated
/api/news/v1/summarize-articleLLM spend server-side (premium entitlement). The dashboard caller was never swept:src/services/summarization.tsroutes every summarize attempt throughpremiumNewsClient=premiumFetch(..., { forcePremium: true })— andpremiumFetchis an auth-injection wrapper, not an entitlement gate (#4865): anon users fall through to awms_session token, dispatch the RPC anyway, and eat the 401.Worse,
API_PROVIDERSfans out ollama → groq → openrouter through the same RPC, so a single summarization attempt can burn up to 3 gated calls before falling back to browser T5. ThetryApiProvidercatch returns null (no 401/403-specific suppression), so every news refresh retries the whole chain.Same class as: classify-event #4779 → #4865 (fixed by #4869 enqueue-gate + timed 403 suppress-drain) and health #4715 → #4902 (fixed by PR #4905).
Fix direction (per the #4869 pattern)
panel-gating.hasPremiumAccess()before dispatching any API-provider summarize attempt; anon/free goes straight to the browser T5 fallback (the designed anon path) with zero network calls.Evidence
src/services/summarization.ts:50-52(premiumNewsClient/forcePremium),:73-77(3-provider fan-out),:117-119(catch → null, no auth suppression).