fix(forecast): surface silent forecast input feed gaps#5109
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
843dd6f to
9642fdc
Compare
Greptile SummaryThis PR surfaces silent forecast input feed failures by adding per-feed record-count logging, wiring temporal anomalies into the strict health-check path, and keeping the feed warm via relay and
Confidence Score: 4/5Safe to merge; the observability and health wiring is well-structured, tested, and consistent with existing patterns in the codebase. The core logic is sound — scripts/seed-forecasts.mjs — specifically the interaction between Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant SI as seed-infra (warm-ping)
participant Relay as relay warm-ping
participant LTA as list-temporal-anomalies
participant Redis as Redis
participant SF as seed-forecasts (readInputKeys)
participant HC as /api/health
SI->>LTA: POST /api/infrastructure/v1/list-temporal-anomalies
Relay->>LTA: POST /api/infrastructure/v1/list-temporal-anomalies
LTA->>Redis: GET temporal:anomalies:v1
alt "cache hit (age < 900s)"
Redis-->>LTA: cached snapshot
LTA-->>SI: return cached (no seed-meta update)
else cache miss or stale
LTA->>LTA: compute fresh snapshot
LTA->>Redis: "SET temporal:anomalies:v1 (TTL=900s)"
Redis-->>LTA: "published=true"
LTA->>Redis: "SET seed-meta:temporal:anomalies {fetchedAt, recordCount} (TTL=604800s)"
LTA-->>SI: return snapshot
end
SF->>Redis: pipeline GET all forecast input keys
Redis-->>SF: parsedByKey
SF->>SF: buildForecastInputPresenceRows(parsedByKey)
SF->>SF: warnOnMissingForecastInputs(rows)
Note over SF: logs [ForecastInputs] warn per zero-record feed
HC->>Redis: GET temporal:anomalies:v1 strlen
HC->>Redis: GET seed-meta:temporal:anomalies
alt "data present AND seed-meta.fetchedAt < 45min ago"
HC-->>HC: "status=OK"
else missing or stale
HC-->>HC: "status=EMPTY or STALE_SEED (CRIT)"
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant SI as seed-infra (warm-ping)
participant Relay as relay warm-ping
participant LTA as list-temporal-anomalies
participant Redis as Redis
participant SF as seed-forecasts (readInputKeys)
participant HC as /api/health
SI->>LTA: POST /api/infrastructure/v1/list-temporal-anomalies
Relay->>LTA: POST /api/infrastructure/v1/list-temporal-anomalies
LTA->>Redis: GET temporal:anomalies:v1
alt "cache hit (age < 900s)"
Redis-->>LTA: cached snapshot
LTA-->>SI: return cached (no seed-meta update)
else cache miss or stale
LTA->>LTA: compute fresh snapshot
LTA->>Redis: "SET temporal:anomalies:v1 (TTL=900s)"
Redis-->>LTA: "published=true"
LTA->>Redis: "SET seed-meta:temporal:anomalies {fetchedAt, recordCount} (TTL=604800s)"
LTA-->>SI: return snapshot
end
SF->>Redis: pipeline GET all forecast input keys
Redis-->>SF: parsedByKey
SF->>SF: buildForecastInputPresenceRows(parsedByKey)
SF->>SF: warnOnMissingForecastInputs(rows)
Note over SF: logs [ForecastInputs] warn per zero-record feed
HC->>Redis: GET temporal:anomalies:v1 strlen
HC->>Redis: GET seed-meta:temporal:anomalies
alt "data present AND seed-meta.fetchedAt < 45min ago"
HC-->>HC: "status=OK"
else missing or stale
HC-->>HC: "status=EMPTY or STALE_SEED (CRIT)"
end
Reviews (1): Last reviewed commit: "fix(forecast): surface missing forecast ..." | Re-trigger Greptile |
Summary
Forecast runs now call out silent input-feed failures instead of letting missing or empty Redis inputs collapse into empty detector arrays with no operator signal. The run logs emit one
[ForecastInputs]warning per zero-record feed, including the Redis key andrecords=0, so ACLED, temporal anomalies, FRED series, and market-side inputs are visible when they no-fire.Forecast input coverage is also wired into health: temporal anomalies now require a strict data/meta signal, the forecast-specific FRED keys are registered with seed-meta expectations, and a successfully computed zero-anomaly temporal snapshot stays healthy rather than alarming as empty data.
Temporal anomalies are now kept warm through the relay warm-ping path and
seed-infra, and the producer writesseed-meta:temporal:anomalieswhen it refreshes the forecast input snapshot.Validation
./node_modules/.bin/tsx --test tests/forecast-input-observability.test.mjs tests/forecast-health-registration.test.mjs tests/relay-warm-ping-auth.test.mts tests/seed-warm-ping-origin.test.mjs tests/forecast-trace-export.test.mjs(339 pass)npm run typecheck:apigit diff --check origin/main...HEADnpm run typecheck,npm run typecheck:api, Convex typecheck, CJS syntax, Unicode safety, boundary/safe-html/Sentry/rate-limit/premium-fetch guards, edge bundle/tests, changed tests, version syncFixes #5101