diff --git a/plans/260428-0927-implement-todo-backlog/phase-08-audio-cache-lru.md b/plans/260428-0927-implement-todo-backlog/phase-08-audio-cache-lru.md index 850351e..83c2095 100644 --- a/plans/260428-0927-implement-todo-backlog/phase-08-audio-cache-lru.md +++ b/plans/260428-0927-implement-todo-backlog/phase-08-audio-cache-lru.md @@ -1,9 +1,10 @@ --- name: Audio cache LRU rule phase: 8 -status: todo +status: completed priority: low effort: 30m +completed: 2026-04-28 --- # Phase 8 — Audio cache LRU rule diff --git a/plans/260428-0927-implement-todo-backlog/plan.md b/plans/260428-0927-implement-todo-backlog/plan.md index 0e99673..5c93e3d 100644 --- a/plans/260428-0927-implement-todo-backlog/plan.md +++ b/plans/260428-0927-implement-todo-backlog/plan.md @@ -24,7 +24,7 @@ YAGNI — parking-lot features and upstream-blocked items skipped. | 5 | Per-row "Chờ" indicator ✅ | `phase-05-cho-row-indicator.md` | | 6 | Confetti polish (threshold + variety) ✅ | `phase-06-confetti-polish.md` | | 7 | Strict CSP via hashed inline script ✅ | `phase-07-strict-csp-hashed.md` | -| 8 | Audio cache LRU rule | `phase-08-audio-cache-lru.md` | +| 8 | Audio cache LRU rule ✅ | `phase-08-audio-cache-lru.md` | | 9 | PWA install verification checklist | `phase-09-pwa-verify-install.md` | ## Cuts (YAGNI) diff --git a/vite.config.js b/vite.config.js index 416d953..2321917 100644 --- a/vite.config.js +++ b/vite.config.js @@ -61,7 +61,15 @@ export default defineConfig(({ mode }) => { expiration: { // Headroom for future voice growth: 4× current 184 clips. maxEntries: 400, - maxAgeSeconds: 60 * 60 * 24 * 30, + // 7d (was 30d). A clip stays cached as long as it gets + // played at least once a week. Voices that go unused + // for 7d fall out and re-fetch on next play — + // approximates LRU without a custom workbox plugin. + // Each clip <200KB so re-fetch cost is negligible. + maxAgeSeconds: 60 * 60 * 24 * 7, + // Belt-and-suspenders for the rare quota-pressure case + // (lots of voices precached + other origins competing). + purgeOnQuotaError: true, }, // Audio is same-origin → no need to allow opaque (0). cacheableResponse: { statuses: [200] },