You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(programs): persist last-known-good scope per program (program_scope) +
failed/rate-limited fetches never overwrite
Architectural fix for the user's "Ryan-BBP was visible before, now shows null"
problem. The warmer used to rebuild the entire payload from scratch every refresh
and overwrite the cache file — so a refresh that rate-limited on a program wiped
that program's previously-known scope from the dashboard.
Now: each program's last-known-good scope is persisted to a new program_scope
table (sqlite + postgres), keyed by (platform, handle):
- UpsertProgramScope is called ONLY on successful fetches (H1 enrich on ok=true,
BC/IT enrich on non-empty summary, force-fetch endpoint on fetched=true).
- A failed/rate-limited fetch NEVER touches the DB row → prior data is preserved.
- buildProgramsPayload now ends with overlayPersistedProgramScope which fills any
program row that came back empty this refresh from the persistent DB. Fresh data
always wins where present.
Net effect: once Ryan-BBP is successfully fetched even once (via search trigger or
a non-rate-limited warmer pass), the dashboard keeps showing those values until a
successful fetch updates them — never "—".
Bugs from the post-implementation review (3 underlying issues, 10 finding rephrasings):
- HIGH: BC force-fetch (apiProgramScopeSummaries) was setting fetched=true
unconditionally — guard with summary.ScopeTargets>0 || summary.LatestTarget!=""
to match the enrich-path's guard, so 8 BC failure modes don't overwrite good data.
- HIGH: overlayPersistedProgramScope's 3-condition gate included
LatestTargetUpdatedAt — which fetchITPrograms set from program-level
lastUpdatedAt, suppressing overlay for IT even when scope was empty. Dropped to
2-condition gate (ScopeTargets / LatestTarget) so the overlay actually runs.
- MEDIUM: IT was persisting the program-level lastUpdatedAt instead of asset-level,
causing the watch to fire on non-scope program edits (bounty bumps, etc.).
fetchITScopeSummary now extracts per-asset timestamp from common field names
(updatedAt/lastUpdatedAt/modifiedAt/addedAt/createdAt) and enrichITScopeCounts
unconditionally adopts it (replacing the program-level value).
Verified: go build/vet, db tests, focused post-fix review — no regressions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
0 commit comments