title: Bootstrap health reports missing compact projections as EMPTY
date: 2026-07-14T00:00:00.000Z
category: logic-errors
module: api/health.js
problem_type: logic_error
component: service_object
severity: high
symptoms:
- >-
Fresh seed metadata with a missing compact projection payload reported OK in
/api/health.
- Dashboard panels could render blank without an actionable health signal.
root_cause: logic_error
resolution_type: code_fix
tags:
- bootstrap-hydration
- seed-meta
- freshness-tracking
- redis
- health
Bootstrap health reports missing compact projections as EMPTY
Problem
/api/health previously allowed every EMPTY_DATA_OK source to report OK when its seed-meta was fresh, even if its Redis payload was absent. That contract was too broad for compact bootstrap projections: a writer or transform failure could blank a user-facing panel while health remained green. The affected issue is #5321.
api/health.js:710-725 puts both quiet metadata-only sources and bootstrap projections in EMPTY_DATA_OK_KEYS. Treating both categories alike erased the distinction between an expected quiet result and a missing required projection.
Symptoms
- Health reported
OKfor a fresh bootstrap seed even though its data key was gone. - Panels backed by compact projections could render empty without an actionable health signal.
- Operators could not distinguish a quiet successful source cycle from a missing projection payload.
What Didn't Work
Making every EMPTY_DATA_OK key fail when the payload was missing would have fixed the projection blind spot, but it would also have broken normal quiet-source behavior. ddosAttacks, trafficAnomalies, weatherAlerts, and newsThreatSummary legitimately write fresh metadata without a payload after a successful quiet cycle; they must remain healthy in that state. The test contract at tests/health-empty-data-ok.test.mjs:16-92 covers this distinction.
Earlier bootstrap work had correctly added compact dashboard-shaped side keys, but it also showed that a side key needs its own availability signal; healthy metadata alone cannot prove that a required projection is present (session history).
Solution
Keep the broad EMPTY_DATA_OK_KEYS list, then add an explicit strict subset for projections that must have a payload:
const MISSING_DATA_IS_FAILURE_KEYS = new Set([
'thermalEscalationBootstrap',
'ucdpEventsBootstrap',
'wildfiresBootstrap',
'forecastsBootstrap',
'positiveGeoEvents',
]);
The set is defined at api/health.js:727-737. In the health evaluation, a strict key with fresh seed metadata and no data key now reports EMPTY before the general EMPTY_DATA_OK path runs. All other EMPTY_DATA_OK keys retain the prior OK-when-fresh and stale-status behavior (api/health.js:926-933).
Why This Works
The check is narrow and expresses the real operational invariant: the five named projections represent expected bootstrap data, whereas the quiet sources represent an optional observation that may legitimately have no payload. Giving strict missing-payload detection precedence catches projection writer and transform failures without turning expected no-event cycles into false alarms. The exact source categorization is visible in api/health.js:710-737, and the two resulting contracts are exercised in tests/health-empty-data-ok.test.mjs:16-92: strict keys are expected to be EMPTY, while quiet keys are expected to be OK.
Prevention
When adding a source to EMPTY_DATA_OK_KEYS, decide explicitly which contract it needs:
- Quiet or metadata-only sources may stay in the general list, where fresh metadata plus no payload is healthy.
- Bootstrap projections or other data-required outputs must also be added to
MISSING_DATA_IS_FAILURE_KEYSso a missing fresh payload is visible asEMPTY.
Extend tests/health-empty-data-ok.test.mjs with both the intended strict and quiet expectation for any new category. This protects the distinction at the Vercel Edge health endpoint instead of relying on an implicit interpretation of EMPTY_DATA_OK.
Related Issues
- #5321: health: EMPTY_DATA_OK bootstrap projections report OK while their key is GONE
- Health must not grade an unconfigured optional source — related classifier precedent for preserving an actionable source-state distinction.
- Merged is not ran long cron seeders — distinguishes this fresh-metadata/missing-payload condition from a producer that has not run.