Part of #5097. P2 — systemic root cause + prevention. Verified 2026-07-09.
Every forecast feed failure degrades to silent no-fire: readInputKeys.parse() maps any missing/malformed value to null (seed-forecasts.mjs:757), and every detector coalesces null → []. No feed absence ever raises — which is exactly why the FRED (#5097 sub) and ACLED (#5097 sub) dead-signals went unnoticed for so long.
Fixes
- Per-feed presence logging. The coverage counters at
:10420 already compute input lengths — emit a warn for any forecast input that resolves empty/absent on a run (feed key + zero-length), so a dead signal is visible in logs immediately.
- Health coverage for forecast inputs. Add the forecast input feeds (esp.
conflict:acled:v1:all:0:0, the FRED family, temporal:anomalies:v1) to api/health.js freshness monitoring so a producer outage surfaces as a health problem, not silence.
temporal:anomalies:v1 keep-warm. Its only writer is a request-driven API handler with a 15-min TTL and no cron/warm-ping (list-temporal-anomalies.ts:184), so the hourly forecast run reads null whenever the endpoint hasn't been hit recently → detectSupplyChainScenarios / detectPoliticalScenarios / detectMilitaryScenarios intermittently drop their temporal branch. Add a warm-ping or cron for it.
Related: #5097, #4930.
Part of #5097. P2 — systemic root cause + prevention. Verified 2026-07-09.
Every forecast feed failure degrades to silent no-fire:
readInputKeys.parse()maps any missing/malformed value tonull(seed-forecasts.mjs:757), and every detector coalescesnull → []. No feed absence ever raises — which is exactly why the FRED (#5097 sub) and ACLED (#5097 sub) dead-signals went unnoticed for so long.Fixes
:10420already compute input lengths — emit awarnfor any forecast input that resolves empty/absent on a run (feed key + zero-length), so a dead signal is visible in logs immediately.conflict:acled:v1:all:0:0, the FRED family,temporal:anomalies:v1) toapi/health.jsfreshness monitoring so a producer outage surfaces as a health problem, not silence.temporal:anomalies:v1keep-warm. Its only writer is a request-driven API handler with a 15-min TTL and no cron/warm-ping (list-temporal-anomalies.ts:184), so the hourly forecast run readsnullwhenever the endpoint hasn't been hit recently →detectSupplyChainScenarios/detectPoliticalScenarios/detectMilitaryScenariosintermittently drop their temporal branch. Add a warm-ping or cron for it.Related: #5097, #4930.