Two related gaps in the journal weather panel:
1. Saving locations via PUT /journal/config didn't trigger a weather
fetch, so newly-entered sites had no cache row (or a stale one) until
the user manually clicked the panel's refresh button. The panel
rendered "two sites with empty values" against pre-existing cache
rows that no longer matched what the user had configured.
2. get_cached_weather_rows returned every WeatherCache row for the user
regardless of whether the location was still in journal_config.
Briefing-era rows survived migration 0040 (which only deleted the
briefing_config setting, not the cache table) and showed up as
ghost tabs in the UI.
Changes:
- get_cached_weather_rows accepts an optional valid_keys filter; rows
whose location_key is not in the set are excluded.
- routes/journal.py:
- put_config kicks off a background refresh_location_cache for any
saved location with valid lat/lon.
- GET /weather and POST /weather/refresh both pass valid_keys derived
from the current config so orphaned rows don't surface.
- services/journal_prep.py filters the weather section to currently-
configured locations as well; uses a lazy import of get_journal_config
to avoid a cycle (journal_scheduler imports journal_prep).
153 tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The /api/journal/weather route was filtering out cache rows older than 24
hours via parse_weather_card_data, while journal_prep.py read the same
rows raw without freshness checking. Result: the daily prep referenced
"home" and "work" temperatures while the right-rail UI showed nothing —
two surfaces, same backing data, inconsistent visibility.
Two changes:
1. parse_weather_card_data no longer returns None for stale data.
WeatherCard already exposes fetched_at and gracefully hides
today_high / forecast fields when they're absent, so old data renders
with whatever fields the cached forecast still covers.
2. The /weather route opportunistically schedules a background refresh
for any cache row older than 4 hours. If the user's journal_config
has lat/lon for that location_key, the refresh runs and the next
page load gets fresh data; if no usable config, the refresh is a
silent no-op and the stale cache is still served.
This makes prep and UI consistent. It also self-heals over time — once
locations are configured, stale caches get refreshed on the next page
load instead of waiting indefinitely.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fetch hourly precipitation probabilities from Open-Meteo alongside daily
forecasts. Generate human-readable precip summaries ("Rain likely 2–5 PM",
"Rain likely all day") for today and each forecast day. Display today's
summary as a styled callout and show peak precipitation hour in forecast rows.
Also fix briefing pipeline to parse all weather location rows (not just first).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- KnowledgeView mini-chat: replace harsh border-top with upward box-shadow
and rounded top corners (16px); remove padding-bottom from content area
so widget truly overlays cards without pushing layout; add collapse
toggle (chevron) that hides messages without closing the conversation
- WeatherCard: show precip_mm as fallback when precipitation_probability_max
is null but actual rainfall is expected (Open-Meteo omits probability for
some forecast days even when rain is shown)
- Pass precip_mm through weather service → frontend type definitions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fetch precipitation_probability_max from Open-Meteo (replaces precip_sum
in the card display — probability is more useful at a glance than mm)
- Show WMO condition description text on each forecast day
- Convert wind speed to mph when temp unit is F; pass wind_unit in response
- Display 💧 X% chance of rain; 💨 X mph/km/h wind per day
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WeatherCard: show precipitation (mm) and max wind speed per forecast day
- DashboardChatInput: add PTT mic button (transcribe-to-input, voice-gated)
- Remove global VoiceOverlay floating button and Space PTT shortcut from
App.vue — inline mic buttons in chat/briefing/dashboard are the right UX;
global overlay had focus/latency/context issues
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>