Listening for events…

FEMA Disaster Eventdex — Scope + Frozen Settings

Status: rule FROZEN 2026-06-15 (Mike) · Owner: Mike + Claude (engine room) Parent: docs/event-spine-framework.md (the Eventdex framework; this is kind #10 after tc, gst, eq, tor, vol, entry, gw, uhecr, and neutrino)

This document scopes the FEMA disaster kind of the event storehouse: every federally-declared disaster in the United States. Each event is one declaration the President issued under the Stafford Act, the moment a hazard overwhelmed state and local capacity and federal aid was turned on. The catalog runs from DR-1 (1953, a Georgia tornado outbreak) to the present.

This is the first administrative-record kind. Unlike the geophysical kinds (a quake is a seismometer reading, a bolide a light curve), the primary record here is a federal act about a real event. The slot is still one real disaster per slot; the measured reality is the recorded fact that a hurricane, flood, fire, or storm happened severely enough to be declared, plus the when / what / where of that declaration. See the measured-reality rule below for the bright line.

The theme

One timeline of the United States' worst days, as the federal government itself recorded them. Not a sensor feed: the authoritative administrative ledger of which hazards crossed the threshold from "local emergency" to "federal disaster." It spans the full hazard spectrum (hurricanes, floods, tornadoes, fires, snow and ice storms, earthquakes, volcanic eruptions, mudslides, tsunamis, droughts) and, by Mike's scope call, the rarer non-geophysical declarations too (biological/pandemic, chemical, toxic substances, terrorist, human-cause, fishing-fleet losses). Every slot carries incident_type, so any consumer can narrow to just storms or just floods later.

The spine (one source)

Source Population Floor (FROZEN) Slots History Status
FEMA OpenFEMA, DisasterDeclarationsSummaries v2 every federal disaster declaration none (full catalog) ~6.6k 1953+ NEW spine (fema_disasters)

69,935 declaration-area rows collapse to one slot per declared disaster. OpenFEMA stores one row per county/area designation: a single hurricane that touched 80 counties is 80 rows sharing one declarationType+disasterNumber. The disaster is the event, the counties are its footprint, so the collapse is one slot per (declarationType, disasterNumber) with the designated areas listed inside. Exact distinct-declaration count is verified in Brick B.

The PG-staged FEMA rows are junk and are NOT the spine. The two existing AutoSense sources (v2-disasterdeclarationssummaries, v1-femawebdisasterdeclarations) hold ~155 empty catalog_item stubs (blank title/description) the autosense crawler left behind, no real records. The spine is a fresh pull of the actual OpenFEMA dataset into a clean fema_disasters source; the junk AutoSense sources are superseded and flagged for cleanup.

Slot ID — declarationType + disasterNumber (FROZEN)

The slot ID is {declarationType}-{disasterNumber}, e.g. DR-4480 (major disaster), EM-3079 (emergency), FM-5637 (fire management assistance). This is FEMA's own femaDeclarationString with the trailing state code dropped (the string is per-area, e.g. FM-5637-NE; the disaster is FM-5637). It is globally unique per declaration and human-readable. DR / EM / FM each carry their own number series, so the declarationType prefix is load-bearing, not decorative.

Collapse — one disaster, many areas (FROZEN)

For each (declarationType, disasterNumber), collapse the area rows to one canonical slot: incident_begin = earliest incidentBeginDate across the rows, incident_end = latest incidentEndDate, plus the full list of designated states and counties (with FIPS). The declaration title, incident type, declaration date, region, and assistance-program flags are constant across an event's rows and taken from any. This is the same multiplicity collapse as UHECR's SD/hybrid or GW's catalog versions, here over geographic designations instead of reconstructions.

The floor / population — FROZEN

Full catalog, no floor, all incident types, all declaration types (Mike, 2026-06-15, two scope calls). Every DR, EM, and FM declaration since 1953 is a slot. Biological, Chemical, Toxic Substances, Terrorist, Human Cause, Fishing Losses, and Other are all IN — once pandemic/chemical declarations are kept, the line is "real disasters that were declared," not "geophysical only," and incident_type makes downstream filtering trivial. No magnitude/severity floor: a declaration is already the severity selection (the event crossed the federal-aid threshold).

The sweep — catalog + dossier v1 (FROZEN), terrestrial cross-match deferred

  • Geometry: a disaster is an area, not a point (often many counties, sometimes many states or the whole nation). v1 stores the designated-area list and county/state FIPS in the slot and leaves the observation's lat/lon NULL (a single point would misrepresent a multi-county footprint). A representative centroid is a later refinement, not v1.
  • v1: catalog + dossier, no sensor sweep. No terrestrial radius sweep is fired. The slot accretes the full declaration record (dates, incident type, areas, programs declared). Firing an all-county temporal sweep against our raw layers would flood exactly the way GW's Brick C did.
  • v2 (deferred): the terrestrial Eventdex cross-match. The interesting question is whether a FEMA declaration lines up with the other Eventdex kinds — does FEMA's "HURRICANE KATRINA" slot match our tc Katrina slot in time and place; does a FEMA earthquake declaration match a usgs_earthquake event; does a FEMA severe-storm declaration coincide with tor or storm-report activity. That is a read-across over slots (the terrestrial analogue of the cosmic-messenger directional cross-match), built once for the natural-hazard kinds. Out of scope for this freeze.

Honest expectation (FROZEN): every FEMA slot is catalog+dossier. The product is the complete, authoritative ledger of US federal disaster declarations, one slot per declaration, ready to be read-across against the geophysical kinds when the terrestrial cross-match is built.

Measured-reality rule (FROZEN)

A disaster declaration is an administrative record of a real event: the federal government recording that a specific hazard occurred, on specific dates, over specific counties, severely enough to declare. Per the bright line ([[feedback_measured_reality_only]]):

  • IN: the declaration as a record of a real disaster, with its recorded facts, declaration date, incident type, incident begin/end dates, designated areas (states + county FIPS), declaration type, and which assistance programs were declared. This is the same class as the other administrative records we already ship (NWS alerts; tornado/hurricane catalogs are likewise records of real events).
  • OUT: FEMA's risk / loss / cost projections and benefit-cost models (e.g. National Risk Index expected-loss scores) are model output, not a record of what happened. None are in the DisasterDeclarationsSummaries dataset; the spine is the declaration ledger only.

The nuance, stated plainly: the declaration is an administrative act, but it indexes a real disaster that physically happened. We treat the declaration as a record of that real event, the way the catalog of observed meteorite falls records real falls. We do not ingest anyone's estimate of a disaster's future cost or a hazard's modeled probability.

Dossier shape

Common header (event_id = DR-4480, when = incident begin, kind_subtype = incident type). Source-native extras: declaration_type, disaster_number, declaration_title, declaration_date, incident_begin / incident_end, n_states / n_counties and the states / designated_areas lists, region, and the IH/IA/PA/HM assistance-program flags. No ground point in v1 (area, not point); the empty sensor section follows the catalog-only dossier pattern. Kind directory: data/event_storehouse/fema/. Kind code fema.

Data-source notes / gotchas (recorded for the build bricks)

  • OpenFEMA is clean and no-auth. https://www.fema.gov/api/open/v2/DisasterDeclarationsSummaries, paginated with $top (max 1000) + $skip, $inlinecount=allpages returns the total. ~70 page requests for the full ~70k rows; cache pages to disk like the other spines.
  • disasterNumber is NOT globally unique, the (type, number) pair is. DR / EM / FM each number independently. Always key the slot on declarationType+disasterNumber, never the bare number.
  • incidentBeginDate can be null (older records); fall back to declarationDate for the slot timestamp so every slot has a when.
  • Live edge is light but real. New declarations land continuously. A FemaDisastersFetcher re-polls the most recent declarations and newcomer-skips on (type, number). Per Mike's call, historical-without-a-fetcher would be acceptable; we build the light fetcher anyway for the live edge, mirroring the neutrino kind.
  • Reuse the catalog-only machinery. uhecr_sweep / neutrino_sweep are the templates: same get-events-collapse + build-dossier shape, sweep/cross-match deferred. The difference is FEMA carries geography (area lists) in the slot rather than a sky direction.

Build bricks

  • Brick A — freeze. THIS DOC (2026-06-15, Mike): theme, single OpenFEMA spine, full-catalog floor (all incident + declaration types, no severity floor), (type, number) slot ID, area-collapse, catalog+dossier v1 with the terrestrial Eventdex cross-match deferred, area-not-point geometry, and the measured-reality rule placing the declaration record IN as an administrative record of a real disaster while holding FEMA's risk/loss models OUT.
  • Brick B — spine reload. DONE 2026-06-15: scripts/reload_fema_spine.py pulled the full OpenFEMA v2 catalog (69,935 area-rows, cached) and collapsed it to 5,191 distinct declarations, 1953-05-02 -> 2026-06-09. By declaration type: DR 2,909 / FM 1,636 / EM 646. Top incident types: Fire 1,741, Severe Storm 1,129, Flood 919, Hurricane 454, Tornado 182, Snowstorm 171, Biological 167 (the include-all scope landing). Raw archived to DuckDB; audit roster -> data/fema_spine_roster.parquet; loaded into the clean fema_disasters source (self-registered).
  • Brick C — kind registration, catalog+dossier. DONE 2026-06-15: src/terrapulse/monitor/ fema_sweep.py registers the fema kind (FEMA_CONFIG: radius_km=None, sensor_slugs=(); slot-id rebuilt from declaration_type+disaster_number), FemaDisastersFetcher live edge (OpenFEMA recent-window pull, collapses area rows, newcomer-skips on slot id), normalizer (normalize_fema_disasters) + orchestrator + seed + scheduler job (fema_sweep every 3600 s). Catalog+dossier from the start — no sensor sweep is built or fired; build_dossier records the full declaration + area footprint and marks cross_match="deferred-v2-terrestrial". 4 unit tests.
  • Brick D — dossier backfill. DONE 2026-06-15: fema_sweep.backfill_and_store() built 5,191 dossiers in 15 s (0 sensor hits by the catalog+dossier design). 1-slot-per-event verified (5,191 files == 5,191 unique slot ids). Marquee = DR-1604 HURRICANE KATRINA (Mississippi, 82 counties, 2005-08-29; Louisiana's Katrina is the separate DR-1603, faithful to FEMA's per-state filing). Full suite 227 green. The FEMA kind is COMPLETE (A+B+C+D).
  • First report: deferred. Engine room, not paper mode.
Live Feed