diff --git a/lib/microwaveprop_web/live/contact_live/show.ex b/lib/microwaveprop_web/live/contact_live/show.ex index 3361b004..5b36cbe4 100644 --- a/lib/microwaveprop_web/live/contact_live/show.ex +++ b/lib/microwaveprop_web/live/contact_live/show.ex @@ -748,14 +748,27 @@ defmodule MicrowavepropWeb.ContactLive.Show do lat = contact.pos1 && contact.pos1["lat"] lon = contact.pos1 && contact.pos1["lon"] - if lat && lon && contact.hrrr_status != :queued do + # Always re-enqueue when there's no profile on this page render. + # The `hrrr_status == :queued` guard used to short-circuit here, but + # contacts can end up stuck in :queued without a live task row + # (HrrrPointEnqueuer swallowing an exception, Rust worker marking a + # task :done with a points array that never contained this contact's + # point, or a later retention prune removing the row). Re-entering + # the upsert is idempotent — the ON CONFLICT clause in + # HrrrPointEnqueuer unions points, resets :done/:failed rows back + # to :queued, and no-ops if the point + valid_time already match. + if lat && lon do valid_time = HrrrClient.nearest_hrrr_hour(contact.qso_timestamp) {rlat, rlon} = Weather.round_to_hrrr_grid(lat, lon) # Phase 3 Stream C: route retries through hrrr_fetch_tasks so the # Rust hrrr-point-worker picks them up alongside backfill work. {:ok, _} = HrrrPointEnqueuer.enqueue(%{valid_time => [{rlat, rlon}]}) - Radio.set_enrichment_status!([contact.id], :hrrr_status, :queued) + + if contact.hrrr_status != :queued do + Radio.set_enrichment_status!([contact.id], :hrrr_status, :queued) + end + {nil, %{contact | hrrr_status: :queued}} else {nil, contact}