Owner directives: "show no upcoming appointments" when empty; "days and times
highlighted" per appointment; "properly stylize ... to fit with each theme
correctly"; "relatively small widget basically about the size of the weather
widget".
r3 judge blockers fixed:
- Cache semantics extracted to status/js/calendar-cache.js (bundled ahead of
the widget; module.exports for tests, window.CalendarCache for browser):
entries keyed by uid+start (recurring instances kept distinct); a FETCHED
WINDOW IS AUTHORITATIVE — on refetch, entries starting inside that window
are replaced/purged (deletions + reschedules propagate), entries outside
survive. 5 direct contract tests cover merge, duplicate suppression,
recurring instances, stale removal on refetch, cross-window survival.
- Chip filters by CURRENT INSTANT against event END (ongoing appointments
qualify; started-long-ago with no end excluded). Covered by contract test.
- Contract suite (4 cache tests) + ns suite (9) + widget-contract (4) = 18
calendar tests; smoke harness DC168-SMOKE-OK; eslint clean.
r1/r2 items carried: universal light-bg contrast (objective WCAG >= 4.5:1 for
all 10 builtin themes, worst 4.60 ocean), month-following fetch with refetch
on navigation, weather-widget-sized chip, month grid with highlighted days.