Add episodeCacheMode/count/days preferences (default: date, 60 days). Apply the bound when reading instead of writing, so a preference change takes effect without a refetch; the full parse cache stays intact so fetch-more can page beyond the bound. Thread the window through load/saveFeedsToFile and update tests and task docs.
1.8 KiB
1.8 KiB
Bounded Feed Lifecycle
Objective: Bound feed episode storage to a rolling 30-day persisted window (older episodes volatile-only unless downloaded), keep feed loading nonblocking, and surface all load/download activity in a global top-right indicator.
Status legend: [ ] todo, [~] in-progress, [x] done
Tasks
- 01 — persisted-retention-window →
01-persisted-retention-window.md - 02 — volatile-episode-merge →
02-volatile-episode-merge.md - 03 — nonblocking-feed-refresh →
03-nonblocking-feed-refresh.md - 04 — global-activity-indicator →
04-global-activity-indicator.md
Dependencies
- 02 depends on 01 (the volatile merge preserves exactly what 01 drops from disk)
- 03 depends on 01 (debounced persistence layers onto the pruning save path)
- 03 depends on 02 (incremental per-feed apply consumes the merge helper from 02)
- 04 depends on 03 (the indicator subscribes to the activity wiring added across refresh/load-more paths in 03)
Exit criteria
- After any refresh + save,
config.jsonfeeds[*].episodescontains only episodes withpubDatewithin the last 30 days or episodes markedcompletedindownloads.json; loading a legacy config prunes stale episodes on first launch. - The volatile episode list and pagination cache are bounded by the same 30-day window as persistence: only in-window episodes are cached/loadable, and everything in-window is (no episode-count ceiling).
- A refresh batch never exceeds a fixed fetch concurrency, applies each feed's result as it lands (no
Promise.allbarrier), and persistence writes are debounced; keyboard input stays responsive throughout. - The top-right indicator is visible iff at least one feed refresh, fetch-more, subscribe fetch, search, or episode download is in flight, hidden otherwise.
bun testandbun run lintpass.