mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-11 01:14:45 +02:00
feat(v0.7-fire-tracker-1): registry correlation + 2 new categories
Phase 1 of the FIRMS+WFIGS fusion design doc. v13.sql adds fire_pixels table for per-fire pixel history + spread_radius_mi/current_centroid_*/ last_hotspot_at on fires. FIRMS handler now attributes incoming pixels to fires via point-in-circle within configurable radius (default 5 mi), updating per-fire centroid as median of recent pixels. Unattributed pixels go through a cluster detector: 3+ pixels within 1 mi within 60 min triggers a single unattributed_hotspot_cluster broadcast (Possible new fire). Two new ALERT_CATEGORIES: wildfire_declared (priority, WFIGS first-sight) and unattributed_hotspot_cluster (priority, FIRMS cluster). All thresholds GUI-editable via adapter_config.fires.* and adapter_config.firms.*. Phases 2-4 (movement analysis, spotting, LLM summaries) deferred to subsequent commits. Schema (v13.sql): - fire_pixels table (irwin_id FK CASCADE, acq_time, lat/lon, frp, satellite, pass_id, attributed_at). Indexed on (irwin_id, acq_time) for centroid queries + on (acq_time) for Phase 2. - fires gains spread_radius_mi (nullable; NULL => use global default), current_centroid_lat/lon (median of last 24h pixels, distinct from the WFIGS-declared anchor lat/lon), last_hotspot_at (Phase 2 halt detector). - firms_pixels gains attributed_at + cluster_broadcast_at + compound index on (attributed_at, cluster_broadcast_at, acq_time) for the cluster query. adapter_config (defaults.py REGISTRY + ADAPTER_META): - fires.spread_radius_mi_default = 5.0 (float) - firms.cluster_min_pixels = 3 (int) - firms.cluster_max_radius_mi = 1.0 (float) - firms.cluster_time_window_minutes = 60 (int) - ADAPTER_META["fires"] meta block (display_name + description). ALERT_CATEGORIES (notifications/categories.py): - wildfire_declared: priority/fire. WFIGS handler tags data["category"] on cases (i)+(ii) [INSERT or row-exists-but-never-broadcast]; case (iii) Update keeps the existing wildfire_incident category. - unattributed_hotspot_cluster: priority/fire. FIRMS handler tags data["category"] + data["severity"] when emitting the cluster wire. FIRMS handler (central/firms_handler.py): - Unchanged storage path: filter, INSERT OR IGNORE into firms_pixels. - New _attribute_or_cluster() runs on every newly-stored pixel (dedup hits skip -- the original insert had its shot already). - Attribution: bbox prefilter on fires.tombstoned_at IS NULL, then exact Haversine to fires(current_centroid_lat ?? lat, current_centroid_lon ?? lon) inside spread_radius_mi (per-fire ?? global default). Multi-match resolves to nearest (design doc Q2). On match: INSERT fire_pixels, UPDATE firms_pixels.attributed_at, recompute centroid as median of last 24h pixels for this fire. - Cluster: on attribution miss, query firms_pixels WHERE attributed_at IS NULL AND cluster_broadcast_at IS NULL AND acq_time > NOW-window. If count >= cluster_min_pixels, fire the cluster wire and stamp cluster_broadcast_at on every member so a 4th arrival cannot re-fire. WFIGS handler (central/wfigs_handler.py): the existing prefix=New branches (i)+(ii) now set data["category"]="wildfire_declared". Existing _render() unchanged. Wire strings: - wildfire_declared: re-uses _render(prefix="New") -- emoji + name + type + anchor + acres + containment + coords. - unattributed_hotspot_cluster: _render_cluster_wire() emits "Possible new fire: <N> hotspots within <r> mi @ <lat>,<lon> (combined <total_frp> MW)". Tests (tests/test_fire_tracker_phase1.py, 10 cases all green): - Pixel within radius -> attribution + centroid + last_hotspot_at. - Centroid recomputes as median across multiple passes. - Pixel outside radius -> NO attribution + stays unattributed. - 3 unattributed within 1 mi within 60 min -> cluster broadcast fires exactly once, all 3 stamped cluster_broadcast_at. - 4th pixel in the same footprint -> NO second broadcast (existing 3 are stamped so SQL filter excludes them). - 5th-7th pixels 2h later -> form a NEW cluster (window prune fires). - WFIGS first-sight tags data["category"]="wildfire_declared". - WFIGS Update branch does NOT retag wildfire_declared. - New adapter_config rows seeded on init_db. - ALERT_CATEGORIES contains both new entries with correct toggle/severity. Live verification on CT108 after rebuild: - v13 migration applied (schema_meta version=13, no Traceback). - adapter_config.fires.spread_radius_mi_default = 5.0 - adapter_config.firms.cluster_min_pixels = 3 - adapter_config.firms.cluster_max_radius_mi = 1.0 - adapter_config.firms.cluster_time_window_minutes = 60 - fires gains 4 new columns; firms_pixels gains 2 new columns; fire_pixels table created. - Container healthy, FIRMS pixels continue arriving (126 pre-deploy). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3351e7b444
commit
dd8e687aca
7 changed files with 832 additions and 6 deletions
|
|
@ -277,8 +277,21 @@ REGISTRY: dict[tuple[str, str], dict[str, Any]] = {
|
|||
},
|
||||
|
||||
# =================================================================
|
||||
# FIRMS -- 4 settings (confidence floor + FRP floor + spatial bbox +
|
||||
# dedup quantization distance in METERS)
|
||||
# FIRES -- 1 setting (default attribution radius for FIRMS -> fire matching)
|
||||
# =================================================================
|
||||
# Per-fire override lives in the fires.spread_radius_mi column; this
|
||||
# is the global default used when that column is NULL. v0.7-fire-1
|
||||
# ships with 5 mi based on the design doc's open question #1
|
||||
# ("Spread radius default. Start with 5 mi per fire?"). Tune from
|
||||
# operations once we have a week of observed attribution rates.
|
||||
("fires", "spread_radius_mi_default"): {
|
||||
"default": 5.0,
|
||||
"type": "float",
|
||||
"description": "Default attribution radius for FIRMS hotspot -> fire matching, miles. Per-fire override in fires.spread_radius_mi.",
|
||||
},
|
||||
|
||||
# =================================================================
|
||||
# FIRMS -- 7 settings (storage floors + dedup + 3 v0.7 cluster knobs)
|
||||
# =================================================================
|
||||
("firms", "confidence_floor"): {
|
||||
"default": "low", # firms_handler.py FIRMS_CONFIDENCE_FLOOR
|
||||
|
|
@ -307,6 +320,29 @@ REGISTRY: dict[tuple[str, str], dict[str, Any]] = {
|
|||
"description": "Distance in meters within which two FIRMS pixel observations from the same satellite + acquisition time are considered duplicates.",
|
||||
},
|
||||
|
||||
# ---- v0.7-fire-tracker-1 unattributed-cluster knobs ----
|
||||
# On every FIRMS pixel that fails attribution to any known fire, the
|
||||
# handler asks: "are there enough other unattributed pixels nearby
|
||||
# right now to suggest a new ignition?" The three knobs below define
|
||||
# "enough", "nearby", and "right now". Defaults match design doc
|
||||
# open question #6 ("3 pixels within 1 mi") -- tune from ops once we
|
||||
# have false-positive data.
|
||||
("firms", "cluster_min_pixels"): {
|
||||
"default": 3,
|
||||
"type": "int",
|
||||
"description": "Minimum unattributed pixels within cluster_max_radius_mi over cluster_time_window_minutes to fire an unattributed_hotspot_cluster broadcast.",
|
||||
},
|
||||
("firms", "cluster_max_radius_mi"): {
|
||||
"default": 1.0,
|
||||
"type": "float",
|
||||
"description": "Spatial radius (miles) defining a candidate hotspot cluster.",
|
||||
},
|
||||
("firms", "cluster_time_window_minutes"): {
|
||||
"default": 60,
|
||||
"type": "int",
|
||||
"description": "Temporal window (minutes); unattributed pixels older than this don't count toward a new cluster.",
|
||||
},
|
||||
|
||||
# =================================================================
|
||||
# PIPELINE (Inhibitor + Grouper) -- 2 settings
|
||||
# =================================================================
|
||||
|
|
@ -426,6 +462,15 @@ ADAPTER_META: dict[str, dict[str, Any]] = {
|
|||
"reminder_enabled": True,
|
||||
"description": "NIFC-authoritative wildfire registry (named incidents, acres, containment).",
|
||||
},
|
||||
# v0.7-fire-tracker-1: "fires" is not a feed; it's the registry table
|
||||
# populated by WFIGS first-sight + FIRMS attribution. Surfacing it as
|
||||
# an adapter_meta family lets the GUI show "spread radius default"
|
||||
# alongside the per-feed knobs.
|
||||
"fires": {
|
||||
"display_name": "Fire registry",
|
||||
"include_in_llm_context": True,
|
||||
"description": "Cross-feed fire registry: WFIGS declares them; FIRMS pixels grow them. spread_radius_mi_default tunes the attribution gate.",
|
||||
},
|
||||
"firms": {
|
||||
"display_name": "FIRMS satellite hotspots",
|
||||
"include_in_llm_context": True,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue