fix: recompute yesterday in daily cache to prevent stale menubar data

The daily cache never re-processed yesterday once cached, so a mid-day
run would freeze partial cost/call data permanently. The "All" provider
path in menubar-json relied on this cache, causing the menubar to show
wildly incorrect numbers while per-provider views (which parse fresh)
were correct. Now yesterday is evicted and recomputed on every run, and
addNewDays upserts instead of skipping duplicates as defense-in-depth.
This commit is contained in:
iamtoruk 2026-04-19 03:07:54 -07:00 committed by AgentSeal
parent 64aae10175
commit 888030fce3
3 changed files with 15 additions and 8 deletions

View file

@ -339,8 +339,18 @@ program
// The daily cache is provider-agnostic: always backfill it from .all so subsequent
// provider-filtered reads can derive per-provider cost+calls from DailyEntry.providers.
// Yesterday is always recomputed: it may have been cached mid-day with partial data.
const cache = await withDailyCacheLock(async () => {
let c = await loadDailyCache()
// Evict yesterday (and any stale future entries) so the gap fill recomputes them.
const hadYesterday = c.days.some(d => d.date >= yesterdayStr)
if (hadYesterday) {
const freshDays = c.days.filter(d => d.date < yesterdayStr)
const latestFresh = freshDays.length > 0 ? freshDays[freshDays.length - 1].date : null
c = { ...c, days: freshDays, lastComputedDate: latestFresh }
}
const gapStart = c.lastComputedDate
? new Date(new Date(`${c.lastComputedDate}T00:00:00.000Z`).getTime() + MS_PER_DAY)
: new Date(todayStart.getTime() - BACKFILL_DAYS * MS_PER_DAY)

View file

@ -91,14 +91,11 @@ export async function saveDailyCache(cache: DailyCache): Promise<void> {
}
export function addNewDays(cache: DailyCache, incoming: DailyEntry[], newestDate: string): DailyCache {
const seen = new Set(cache.days.map(d => d.date))
const merged = [...cache.days]
const byDate = new Map(cache.days.map(d => [d.date, d]))
for (const day of incoming) {
if (seen.has(day.date)) continue
seen.add(day.date)
merged.push(day)
byDate.set(day.date, day)
}
merged.sort((a, b) => a.date.localeCompare(b.date))
const merged = Array.from(byDate.values()).sort((a, b) => a.date.localeCompare(b.date))
const nextLast = cache.lastComputedDate && cache.lastComputedDate > newestDate
? cache.lastComputedDate
: newestDate

View file

@ -117,7 +117,7 @@ describe('addNewDays', () => {
expect(updated.lastComputedDate).toBe('2026-04-10')
})
it('skips days already present in the cache (first write wins)', () => {
it('replaces existing days with incoming data (last write wins)', () => {
const base: DailyCache = {
version: DAILY_CACHE_VERSION,
lastComputedDate: '2026-04-08',
@ -125,7 +125,7 @@ describe('addNewDays', () => {
}
const updated = addNewDays(base, [emptyDay('2026-04-08', 99)], '2026-04-08')
const aprilEight = updated.days.find(d => d.date === '2026-04-08')!
expect(aprilEight.cost).toBe(5)
expect(aprilEight.cost).toBe(99)
})
it('does not regress lastComputedDate if incoming newestDate is older', () => {