feat(climatology): self-healing nightly cron via AdminTaskWorker

The hrrr_climatology table previously rebuilt only when an operator
remembered to run `mix hrrr_climatology` from a workstation, which
the prod environment can't do. AdminTaskWorker already exposes the
same logic as a "climatology" task, so a one-line cron entry
(02:30 UTC daily) lets prod self-heal: empty/stale rows refold from
the hrrr_profiles archive within 24h, no manual intervention.

Recalibration audit remediation now points at the cron path instead
of the dev-only mix task.
This commit is contained in:
Graham McIntire 2026-05-04 14:19:01 -05:00
parent a1f561dfb8
commit 2f0424a47b
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
2 changed files with 12 additions and 2 deletions

View file

@ -304,7 +304,13 @@ if config_env() == :prod do
{"*/10 * * * *", Microwaveprop.Workers.IonosphereFetchWorker},
# NOAA SWPC: Kp / GOES X-ray at 1-min cadence, F10.7 hourly.
# 5-min poll balances freshness vs request volume.
{"*/5 * * * *", Microwaveprop.Workers.SpaceWeatherFetchWorker}
{"*/5 * * * *", Microwaveprop.Workers.SpaceWeatherFetchWorker},
# Recompute hrrr_climatology nightly from the hrrr_profiles
# archive. Idempotent — UPSERT on (lat, lon, month, hour) — and
# cheap enough to run daily so the table self-heals if a
# deploy clears it or new profiles need to fold into the means.
# 02:30 UTC sits inside the quiet pre-NARR/HRRR ingestion gap.
{"30 2 * * *", Microwaveprop.Workers.AdminTaskWorker, args: %{"task" => "climatology"}}
]}
base_plugins = [

View file

@ -343,7 +343,11 @@ DATA_GAP_RULES: list[dict] = [
"key": "hrrr_climatology_rows",
"broken_when": lambda v: v == 0,
"warn_when": lambda _v: False,
"remediation": "Run `mix hrrr_climatology` (see lib/mix/tasks/hrrr_climatology.ex).",
"remediation": (
"Should self-heal within 24h via the daily 02:30Z cron that "
"runs AdminTaskWorker(task=climatology). If still 0 after a "
"day, check Oban dashboard for failed admin jobs."
),
},
{
"key": "rtma_rows",