← All findingssource · memory/feedback_cache_ttl_bug.md
STATUS: SHIPPED. Three parts to the original report; here's what actually existed and what was bugged:
| Original claim | Reality |
|---|---|
| "?refresh=true returned same fetched_at" | The handler only accepted ?force=true, not ?refresh=true. Now accepts both. |
| "?force=true also returned same fetched_at" | Real bug: assembleViaSF's singleflight leader re-checked the cache unconditionally, defeating the caller's force intent. Fixed: forceRefresh now plumbed into the leader closure. |
| "DELETE /cache/:gid → 405 Method Not Allowed" | DELETE endpoint always existed at /api/v1/snapshot/:game_id (handlers.go:179 InvalidateSnapshot). I tried wrong URL paths. Discoverability gap, not a capability gap. |
Fix shipped on data-hydrator commit dc380de (2026-05-21):
assembleViaSFtakesforceRefresh booland skips the leader-closure cache re-check when true- GetSnapshot + multi-sport GetSnapshot accept both
?force=trueand?refresh=true - Date-listing + background warmer paths intentionally pass
false(they're meant to populate cache, not bust it) - DELETE
/api/v1/snapshot/:game_idwas already working — keep using it as the explicit-bust escape hatch when needed
Keeping memory open as the process-lesson record. Same anti-pattern as
feedback_verify_before_extend: I asserted "no cache-bust path works"
after trying THREE wrong URLs, never actually grepped RegisterRoutes to
see the real DELETE endpoint. N=3 wrong tests still doesn't justify a
sweeping conclusion.
On 2026-05-21, /api/v1/snapshot/0042500312 returned a snapshot with
fetched_at: 2026-05-20T23:40:49Z — over 24 hours old. Every cache-bust
attempt failed:
?refresh=true→ same fetched_at?force=true&bust=1→ same fetched_atDELETE /api/v1/snapshot/:gid/cache→ 404 (endpoint doesn't exist)DELETE /api/v1/cache/snapshot/:gid→ 405 Method Not Allowed
This is supposed to be a 10-min TTL (per snapshot module comments). For the
playoff Game 2 ID 0042500312 specifically, refresh never propagates — possibly
because the game's status is still "scheduled" on the upstream games-service
even though the actual game is happening / already started, and the assembler
is keying cache decisions off that.
Why: stale injury report = the Fox-Out miss in feedback_active_tonight_gate. On game day, injury status changes hour-by-hour (Questionable → Out on inactive release, ~90 min before tip). A 24h-old snapshot's injury section is categorically untrustworthy for picks at tip time.
How to apply:
- Always check
snapshot.fetched_atbefore trusting any injury/lineup data — if older than 60-90 min on game day, the snapshot is stale, full stop. - Cross-check current injury status via RotoWire
nba-lineups.phpor the official inactive list before delivering any pick. Don't pretend the snapshot'sinjuriessection is current. - [internal detail removed]
See also: feedback_active_tonight_gate, project_methodology_lockdown.