Stop a removed state pinning the freshness warning on, and make the whole Explorer day cell clickable (#204) #205

Merged
claude-bot merged 1 commit from fix/freshness-banner-and-explorer-day-target into main 2026-10-13 17:18:54 +00:00
Collaborator

Fixes #204. Both items were reported by @rbrooks while checking rc5 on dev.

Freshness warning that says the data is 0 minutes old

Two defects combined:

  1. Freshness rows outlive their state. NWS alert freshness is tracked per state and a row is only refreshed by polling that state. When the last location in a state is deleted or disabled, the row is never polled again and was never removed. Dev carried nws_alerts:TX at 148 days old, which held the dashboard banner and the stale-product admin alert on permanently.
  2. The banner printed the wrong age. Health came from any unhealthy row in the category; the printed age came from the freshest row.

Changes:

  • prune_nws_alert_freshness(active_states) in app/services/observability.py, called from poll_alerts_job with the states it is about to poll. It only touches the nws_alerts category. With no monitored states it clears all of that category, which is also what keeps the heartbeat watchdog quiet when nothing is being polled.
  • The call is wrapped fail-soft: an exception is logged and the alert poll continues.
  • dashboard_freshness_summary reports the age of the degraded feed (the oldest, if several).

On dev this needs no manual cleanup: the first poll after deploy removes the orphan row.

Freshness samples for a pruned state are left to age out on the existing 14-day retention.

Explorer calendar click target

Only the day number was a link. The link now stretches over the whole cell (a::after with inset: 0 inside a position: relative cell), and the cell gets hover and keyboard-focus styling. Markup is unchanged, so there is still exactly one link per day.

Verification

  • ruff: clean
  • Bulk tier: 1011 passed (5 new), run locally on Python 3.14
  • Postgres tier: left to CI

New tests: banner shows the stale feed's age beside a fresh sibling; prune removes the orphan and keeps the monitored state and other categories; prune with no monitored states; poll_alerts_job prunes; poll_alerts_job still fetches when the prune raises.

Not verified: the click target has not been exercised in a browser. No browser is available in this session, so it wants a look on dev after the next release candidate.

🤖 Generated with Claude Code

Fixes #204. Both items were reported by @rbrooks while checking rc5 on dev. ## Freshness warning that says the data is 0 minutes old Two defects combined: 1. **Freshness rows outlive their state.** NWS alert freshness is tracked per state and a row is only refreshed by polling that state. When the last location in a state is deleted or disabled, the row is never polled again and was never removed. Dev carried `nws_alerts:TX` at 148 days old, which held the dashboard banner and the stale-product admin alert on permanently. 2. **The banner printed the wrong age.** Health came from any unhealthy row in the category; the printed age came from the freshest row. Changes: - `prune_nws_alert_freshness(active_states)` in `app/services/observability.py`, called from `poll_alerts_job` with the states it is about to poll. It only touches the `nws_alerts` category. With no monitored states it clears all of that category, which is also what keeps the heartbeat watchdog quiet when nothing is being polled. - The call is wrapped fail-soft: an exception is logged and the alert poll continues. - `dashboard_freshness_summary` reports the age of the degraded feed (the oldest, if several). On dev this needs no manual cleanup: the first poll after deploy removes the orphan row. Freshness samples for a pruned state are left to age out on the existing 14-day retention. ## Explorer calendar click target Only the day number was a link. The link now stretches over the whole cell (`a::after` with `inset: 0` inside a `position: relative` cell), and the cell gets hover and keyboard-focus styling. Markup is unchanged, so there is still exactly one link per day. ## Verification - ruff: clean - Bulk tier: **1011 passed** (5 new), run locally on Python 3.14 - Postgres tier: left to CI New tests: banner shows the stale feed's age beside a fresh sibling; prune removes the orphan and keeps the monitored state and other categories; prune with no monitored states; `poll_alerts_job` prunes; `poll_alerts_job` still fetches when the prune raises. **Not verified:** the click target has not been exercised in a browser. No browser is available in this session, so it wants a look on dev after the next release candidate. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The dashboard showed "Data freshness warning: NWS Alerts data last updated
0 min ago". Two defects combined to produce a warning that contradicts
itself.

NWS alert freshness is tracked per state, and a row is only refreshed by
polling that state. Once the last location in a state is deleted or
disabled, its row is never polled again and was never removed, so it read
as stale forever: dev carried nws_alerts:TX at 148 days old. That held both
the banner and the stale-product admin alert on permanently. poll_alerts_job
now prunes rows for states no location monitors. The prune is fail-soft, so
housekeeping can never cost an alert poll.

Separately, the banner decided health from any unhealthy row in a category
but printed the age of the freshest one. It now reports the age of the feed
that is actually degraded.

Explorer calendar: only the day number was a link, a target a few pixels
wide in an 84px cell. The link now stretches over the cell, with hover and
keyboard-focus styling on the cell. The markup is unchanged, so it is still
one link per day for screen readers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/WeatherBot!205
No description provided.