chore(central-ripout 2d-i): relocate the fire engine + renderer out of central/

Moves the last two live files out of the retired-Central folder. central/ is
now EMPTY and deleted entirely (incl. __init__.py).

- fire-fusion engine (ingest_hotspot_pixel + the growth/cluster/spotting
  engine) → env/fire_fusion.py, next to its sole consumer env/firms.py.
- wildfire text renderer (_render + its live helpers) → env/fire_render.py.

THE COUPLING RESOLVED: firms_handler._handle_pass_boundary had a LAZY import
inside a function body — `from meshai.central.wfigs_handler import _render` —
sitting on the live FIRMS fire-growth path. It is now a normal top-of-file
import (`from meshai.env.fire_render import _render`), visible and greppable.

PURE MOVE — no behavior change:
- The parity oracle (test_fire_refactor.py) PASSES UNCHANGED (only its import
  paths updated) — proving the fire wire output is byte-for-byte identical
  before and after. Fire alerts say exactly what they said.
- handle_firms / handle_wfigs (dead entrypoints, only caller was the deleted
  consumer.py) were KEPT and moved rather than dropped — the wording-cleanup
  PR removes them deliberately. "When unsure, keep."
- fire_render.py's geo-helper imports still point at central_normalizer — that
  file's split is a SEPARATE PR; carried the imports along, did not touch it.

Consumers + test import paths rewired. Full suite: 2059 passed, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Johnson 2026-07-17 22:27:08 +00:00
commit 1ffde64c3c
19 changed files with 97 additions and 72 deletions

View file

@ -1,8 +0,0 @@
"""Central connector package (v0.4) — historically consumed Central's NATS
JetStream firehose and normalized it into meshai pipeline Events. The NATS
consumer is retired (Central is gone); this package now only holds the
split-file modules still used by the native adapter paths (firms_handler,
wfigs_handler _render). The satellite pieces (satpass_handler, tle_handler,
pass_predictor) have moved to meshai.env.satellite, and the gauge-site
lookups have moved to meshai.env.gauge_sites they're feed-adapter support
code, not consumer leftovers."""

View file

@ -1,5 +1,15 @@
"""v0.7-fire-tracker-3 FIRMS handler -- storage + attribution + cluster + growth/halt/spotting. """v0.7-fire-tracker-3 FIRMS handler -- storage + attribution + cluster + growth/halt/spotting.
Relocated from `meshai.central.firms_handler` during the Central ripout
(central/ handler retirement, chore/ripout-2d). ``ingest_hotspot_pixel`` is
the LIVE fire-fusion engine with one consumer: the native FIRMS adapter
(`meshai.env.firms` -> `_run_fusion`). ``handle_firms`` (the Central
NATS-envelope entrypoint) has no live production caller -- Central's
consumer that drove it is gone -- but it remains the parity-tested legacy
contract (see `tests/test_firms_handler.py`, `tests/test_firms_refactor.py`,
`tests/test_fire_tracker_phase1/2/3.py`) -- kept verbatim, not deleted, per
that coverage.
Pre-v0.6-1 the v0.5.13 default-deny gate at consumer._normalize() silently Pre-v0.6-1 the v0.5.13 default-deny gate at consumer._normalize() silently
dropped every `central.fire.hotspot.>` envelope because no per-adapter handler dropped every `central.fire.hotspot.>` envelope because no per-adapter handler
existed (audit doc v0.6-phase1-audit.md finding #2). The `firms_pixels` existed (audit doc v0.6-phase1-audit.md finding #2). The `firms_pixels`
@ -70,6 +80,7 @@ from datetime import datetime, timezone
from typing import Any, Optional from typing import Any, Optional
from meshai.persistence import get_db from meshai.persistence import get_db
from meshai.env.fire_render import _render
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -919,7 +930,6 @@ def _handle_pass_boundary(conn, *, irwin_id, pass_id, lat, lon,
if fire is None: if fire is None:
return None return None
from meshai.central.wfigs_handler import _render
movement = {"direction": drift_direction, "speed_mph": drift_mi_per_hour or 0.0} movement = {"direction": drift_direction, "speed_mph": drift_mi_per_hour or 0.0}
normalized = dict(fire) normalized = dict(fire)
normalized["irwin_id"] = irwin_id normalized["irwin_id"] = irwin_id
@ -948,7 +958,14 @@ def _handle_pass_boundary(conn, *, irwin_id, pass_id, lat, lon,
def _render_growth_wire(*, incident_name, direction, speed_mph, def _render_growth_wire(*, incident_name, direction, speed_mph,
lat, lon): lat, lon):
"""Per design doc section 4 + Phase 2 spec item 3.""" """Per design doc section 4 + Phase 2 spec item 3.
NOTE: unreferenced (verified via repo-wide rg during the chore/ripout-2d
relocation -- zero callers in source or tests). `_handle_pass_boundary`
renders growth wires via the WFIGS `_render` instead. Left in place
(moved verbatim, not deleted) since dropping dead code is out of scope
for this pure-move PR.
"""
near_part = "" near_part = ""
try: try:
from meshai.central_normalizer import nearest_town from meshai.central_normalizer import nearest_town

View file

@ -1,5 +1,15 @@
"""WFIGS handler: persistence-backed change-detection + wire renderer. """WFIGS handler: persistence-backed change-detection + wire renderer.
Relocated from `meshai.central.wfigs_handler` during the Central ripout
(central/ handler retirement, chore/ripout-2d). ``handle_wfigs`` has no live
production caller (Central's NATS consumer that drove it is gone), but it
remains the parity-tested legacy contract for the WFIGS wildfire wire format
(see `tests/test_fire_refactor.py`, `tests/test_wfigs_handler.py`) -- kept
verbatim, not deleted, per that oracle. ``_render`` IS live: it is imported
by `meshai.env.fire_fusion._handle_pass_boundary` on the FIRMS growth path
(`env/firms.py` -> `ingest_hotspot_pixel` -> ... -> `_handle_pass_boundary`
-> `_render`) to render the `wildfire_growth` wire.
v0.5.8b refactor: New: vs Update: decision now keys on `last_broadcast_at`, v0.5.8b refactor: New: vs Update: decision now keys on `last_broadcast_at`,
not on row existence. Cold-start scenarios where the dispatcher drops the not on row existence. Cold-start scenarios where the dispatcher drops the
broadcast (cold-start grace, stale filter, cooldown, dedup) leave the fires broadcast (cold-start grace, stale filter, cooldown, dedup) leave the fires
@ -469,7 +479,7 @@ def _render(n: dict, *, prefix: str = "",
lines: list[str] = [] lines: list[str] = []
# Line 1: header # Line 1: header
lines.append(f"🔥 {name} \u2014 {prefix}") lines.append(f"🔥 {name} {prefix}")
# Line 2: size / containment with delta (plain text -- no bold markdown). # Line 2: size / containment with delta (plain text -- no bold markdown).
acres_str = f"{int(acres):,} ac" if acres is not None else "size unknown" acres_str = f"{int(acres):,} ac" if acres is not None else "size unknown"

View file

@ -138,7 +138,7 @@ class FIRMSAdapter:
new_events = self._parse_csv(csv_data) new_events = self._parse_csv(csv_data)
# Feed every fetched pixel into the SHARED source-agnostic fusion engine # Feed every fetched pixel into the SHARED source-agnostic fusion engine
# (central.firms_handler.ingest_hotspot_pixel): attribution + growth / # (env.fire_fusion.ingest_hotspot_pixel): attribution + growth /
# spotting / halt. DB-level dedup (firms_pixels unique key) makes this # spotting / halt. DB-level dedup (firms_pixels unique key) makes this
# idempotent across ticks, so re-fetched pixels never double-count. # idempotent across ticks, so re-fetched pixels never double-count.
# NEVER broadcasts raw hotspots -- only the fusion wires it returns. # NEVER broadcasts raw hotspots -- only the fusion wires it returns.
@ -422,14 +422,14 @@ class FIRMSAdapter:
engine and collect the fire-fusion broadcasts. engine and collect the fire-fusion broadcasts.
Every parsed pixel is mapped to the canonical FIRMS schema and passed to Every parsed pixel is mapped to the canonical FIRMS schema and passed to
``central.firms_handler.ingest_hotspot_pixel`` -- the exact same engine ``env.fire_fusion.ingest_hotspot_pixel`` -- the exact same engine
the Central NATS handler drives. The engine INSERTs the pixel, runs the Central NATS handler drives. The engine INSERTs the pixel, runs
attribution, and runs the growth / spotting / halt fusion; it returns attribution, and runs the growth / spotting / halt fusion; it returns
ONLY fusion wires (never a raw-hotspot / cluster broadcast). DB-level ONLY fusion wires (never a raw-hotspot / cluster broadcast). DB-level
dedup makes re-fetched pixels no-ops, so nothing double-counts. dedup makes re-fetched pixels no-ops, so nothing double-counts.
""" """
try: try:
from meshai.central.firms_handler import ( from meshai.env.fire_fusion import (
ingest_hotspot_pixel, _parse_acq_epoch, ingest_hotspot_pixel, _parse_acq_epoch,
) )
except Exception: except Exception:

View file

@ -10,7 +10,7 @@ import time
import pytest import pytest
import meshai.notifications.clock as clock_mod import meshai.notifications.clock as clock_mod
import meshai.central.wfigs_handler as wfigs_handler import meshai.env.fire_render as wfigs_handler
_FROZEN_TS = 1_700_000_000.0 _FROZEN_TS = 1_700_000_000.0

View file

@ -1,6 +1,6 @@
"""Step 3 fire age-gate tests (Step 5 verification, age-gate part). """Step 3 fire age-gate tests (Step 5 verification, age-gate part).
Targets `meshai.central.wfigs_handler._fire_too_old_to_announce` and the Targets `meshai.env.fire_render._fire_too_old_to_announce` and the
handler's "New"-path suppression behaviour. handler's "New"-path suppression behaviour.
The gate exists because MeshAI broadcast a 6-week-old, already-closed fire The gate exists because MeshAI broadcast a 6-week-old, already-closed fire
@ -21,7 +21,7 @@ import time
import pytest import pytest
from meshai.central.wfigs_handler import _fire_too_old_to_announce from meshai.env.fire_render import _fire_too_old_to_announce
_14D = 14 * 86400 _14D = 14 * 86400
@ -131,7 +131,7 @@ def test_handler_new_path_suppresses_old_fire():
"""Case (i): a brand-new fire whose declared_at is ~45d old is INSERTed """Case (i): a brand-new fire whose declared_at is ~45d old is INSERTed
but the 'New' broadcast is suppressed (wire is None), and the New-path but the 'New' broadcast is suppressed (wire is None), and the New-path
category tag is NOT applied.""" category tag is NOT applied."""
from meshai.central.wfigs_handler import handle_wfigs from meshai.env.fire_render import handle_wfigs
from meshai.persistence import get_db from meshai.persistence import get_db
now = int(time.time()) now = int(time.time())
@ -154,7 +154,7 @@ def test_handler_new_path_suppresses_old_fire():
def test_handler_new_path_announces_recent_fire(): def test_handler_new_path_announces_recent_fire():
"""Case (i): a recent fire (~5d) DOES broadcast 'New' and tags """Case (i): a recent fire (~5d) DOES broadcast 'New' and tags
wildfire_declared.""" wildfire_declared."""
from meshai.central.wfigs_handler import handle_wfigs from meshai.env.fire_render import handle_wfigs
now = int(time.time()) now = int(time.time())
declared = now - _5D declared = now - _5D
@ -171,7 +171,7 @@ def test_handler_update_path_still_emits_for_old_fire():
"""An already-broadcast OLD fire that grows acreage still emits an """An already-broadcast OLD fire that grows acreage still emits an
'Update' (Case (iii) is NOT gated -- genuine old-but-active fires keep 'Update' (Case (iii) is NOT gated -- genuine old-but-active fires keep
getting containment/acreage updates).""" getting containment/acreage updates)."""
from meshai.central.wfigs_handler import handle_wfigs from meshai.env.fire_render import handle_wfigs
from meshai.persistence import get_db from meshai.persistence import get_db
now = int(time.time()) now = int(time.time())

View file

@ -23,7 +23,7 @@ import pytest
from meshai import central_normalizer as cn from meshai import central_normalizer as cn
from meshai.notifications.formatters._budget import budget_for from meshai.notifications.formatters._budget import budget_for
from meshai.central.wfigs_handler import ( from meshai.env.fire_render import (
_build_canonical, _build_canonical,
_render as _wfigs_render, _render as _wfigs_render,
handle_wfigs, handle_wfigs,
@ -55,7 +55,7 @@ def mem_db(monkeypatch, tmp_path):
except Exception: except Exception:
pass pass
try: try:
from meshai.central import wfigs_handler as _wh from meshai.env import fire_render as _wh
_wh._last_cleanup = 0 _wh._last_cleanup = 0
except Exception: except Exception:
pass pass

View file

@ -81,7 +81,7 @@ def _envelope(*, lat, lon, acq_date="2026-06-06", acq_time="1200",
def test_pixel_within_radius_attributes_to_fire(): def test_pixel_within_radius_attributes_to_fire():
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
# Cache Peak Fire stub @ 42.118, -113.643. # Cache Peak Fire stub @ 42.118, -113.643.
@ -120,7 +120,7 @@ def test_pixel_within_radius_attributes_to_fire():
def test_centroid_recomputes_as_median_across_passes(): def test_centroid_recomputes_as_median_across_passes():
"""A second attributed pixel updates the centroid to the median, not """A second attributed pixel updates the centroid to the median, not
just the latest pixel's coords.""" just the latest pixel's coords."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
_seed_fire(irwin_id="ID-TEST-002", _seed_fire(irwin_id="ID-TEST-002",
@ -157,7 +157,7 @@ def test_centroid_recomputes_as_median_across_passes():
def test_pixel_outside_radius_stays_unattributed(): def test_pixel_outside_radius_stays_unattributed():
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
_seed_fire(irwin_id="ID-TEST-003", _seed_fire(irwin_id="ID-TEST-003",
@ -196,7 +196,7 @@ def _hhmm(h, m=0):
def test_three_unattributed_pixels_fire_cluster_once(): def test_three_unattributed_pixels_fire_cluster_once():
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
# No fires seeded -- everything is unattributed. # No fires seeded -- everything is unattributed.
@ -235,7 +235,7 @@ def test_three_unattributed_pixels_fire_cluster_once():
def test_fourth_pixel_in_same_cluster_does_not_refire(): def test_fourth_pixel_in_same_cluster_does_not_refire():
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
base_lat, base_lon = 43.500, -114.500 base_lat, base_lon = 43.500, -114.500
@ -279,7 +279,7 @@ def test_fifth_pixel_after_time_window_can_form_new_cluster():
no nearby unstamped pixels to count, so it stays silent -- but if no nearby unstamped pixels to count, so it stays silent -- but if
we then ingest TWO more nearby pixels (also outside the original we then ingest TWO more nearby pixels (also outside the original
window), we should fire a NEW cluster.""" window), we should fire a NEW cluster."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
base_lat, base_lon = 43.500, -114.500 base_lat, base_lon = 43.500, -114.500
# First cluster at 12:00..12:20 -> fires + stamps all 3. # First cluster at 12:00..12:20 -> fires + stamps all 3.
@ -321,7 +321,7 @@ def test_fifth_pixel_after_time_window_can_form_new_cluster():
def test_wfigs_first_sight_tags_wildfire_declared(): def test_wfigs_first_sight_tags_wildfire_declared():
from meshai.central.wfigs_handler import handle_wfigs from meshai.env.fire_render import handle_wfigs
normalized = { normalized = {
"_kind": "wfigs_incident", "_kind": "wfigs_incident",
@ -351,7 +351,7 @@ def test_wfigs_first_sight_tags_wildfire_declared():
def test_wfigs_update_does_not_retag_wildfire_declared(): def test_wfigs_update_does_not_retag_wildfire_declared():
"""After a row exists AND has been broadcast, an acres-grew Update """After a row exists AND has been broadcast, an acres-grew Update
must NOT carry the wildfire_declared category.""" must NOT carry the wildfire_declared category."""
from meshai.central.wfigs_handler import handle_wfigs from meshai.env.fire_render import handle_wfigs
from meshai.persistence import get_db from meshai.persistence import get_db
# Pre-existing row that has already been broadcast. # Pre-existing row that has already been broadcast.

View file

@ -73,7 +73,7 @@ def _envelope(*, lat, lon, acq_date="2026-06-06", acq_time="1200",
def test_two_pass_drift_emits_growth_with_direction_and_speed(): def test_two_pass_drift_emits_growth_with_direction_and_speed():
"""Pass 1 (N20 bucket A), pass 2 (N20 bucket B, ~6h later, centroid """Pass 1 (N20 bucket A), pass 2 (N20 bucket B, ~6h later, centroid
1.0 mi N of pass 1). Drift should be ~1.0 mi N, broadcast must fire.""" 1.0 mi N of pass 1). Drift should be ~1.0 mi N, broadcast must fire."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
_seed_fire(irwin_id="ID-GROWTH-001", _seed_fire(irwin_id="ID-GROWTH-001",
@ -139,7 +139,7 @@ def test_two_pass_drift_emits_growth_with_direction_and_speed():
def test_drift_below_threshold_does_not_emit_growth(): def test_drift_below_threshold_does_not_emit_growth():
"""0.3 mi drift between consecutive passes -- below the 0.5 mi """0.3 mi drift between consecutive passes -- below the 0.5 mi
default -- must NOT broadcast wildfire_growth.""" default -- must NOT broadcast wildfire_growth."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
_seed_fire(irwin_id="ID-DRIFT-001", _seed_fire(irwin_id="ID-DRIFT-001",
@ -181,7 +181,7 @@ def test_drift_below_threshold_does_not_emit_growth():
def test_halt_detector_fires_once_after_12h_idle(): def test_halt_detector_fires_once_after_12h_idle():
"""Fire with last_pass_at 14h ago + no new pixels in that fire """Fire with last_pass_at 14h ago + no new pixels in that fire
triggers halt on the next FIRMS pixel arrival (for any fire).""" triggers halt on the next FIRMS pixel arrival (for any fire)."""
from meshai.central.firms_handler import handle_firms, _maybe_emit_halt from meshai.env.fire_fusion import handle_firms, _maybe_emit_halt
from meshai.persistence import get_db from meshai.persistence import get_db
now_epoch = 1780768800 # 2026-06-06 18:00 UTC now_epoch = 1780768800 # 2026-06-06 18:00 UTC
@ -214,7 +214,7 @@ def test_halt_detector_fires_once_after_12h_idle():
def test_halt_detector_no_second_broadcast_for_same_fire(): def test_halt_detector_no_second_broadcast_for_same_fire():
"""Once halt_broadcast_at is stamped, the detector skips that fire.""" """Once halt_broadcast_at is stamped, the detector skips that fire."""
from meshai.central.firms_handler import _maybe_emit_halt from meshai.env.fire_fusion import _maybe_emit_halt
from meshai.persistence import get_db from meshai.persistence import get_db
now_epoch = 1780768800 now_epoch = 1780768800
@ -238,7 +238,7 @@ def test_halt_eligibility_returns_after_new_pass_arrives():
"""A previously halted fire that receives a new pixel becomes eligible """A previously halted fire that receives a new pixel becomes eligible
for halt again if it goes idle a second time. The detector filter is for halt again if it goes idle a second time. The detector filter is
halt_broadcast_at IS NULL OR halt_broadcast_at < last_pass_at.""" halt_broadcast_at IS NULL OR halt_broadcast_at < last_pass_at."""
from meshai.central.firms_handler import _maybe_emit_halt from meshai.env.fire_fusion import _maybe_emit_halt
from meshai.persistence import get_db from meshai.persistence import get_db
now_epoch = 1780768800 now_epoch = 1780768800
@ -267,7 +267,7 @@ def test_halt_eligibility_returns_after_new_pass_arrives():
def test_bearing_and_direction_round_trip(): def test_bearing_and_direction_round_trip():
"""Bearing helper + 8-way mapping cover all cardinals/intercardinals.""" """Bearing helper + 8-way mapping cover all cardinals/intercardinals."""
from meshai.central.firms_handler import _bearing, _direction_8 from meshai.env.fire_fusion import _bearing, _direction_8
# Source point. # Source point.
s_lat, s_lon = 42.0, -114.0 s_lat, s_lon = 42.0, -114.0
# Each cardinal/intercardinal direction we test by walking ~1 mi. # Each cardinal/intercardinal direction we test by walking ~1 mi.
@ -289,7 +289,7 @@ def test_bearing_and_direction_round_trip():
def test_direction_8_boundary_cases(): def test_direction_8_boundary_cases():
from meshai.central.firms_handler import _direction_8 from meshai.env.fire_fusion import _direction_8
# Bearings on the boundary -- check the +22.5 offset rounds correctly. # Bearings on the boundary -- check the +22.5 offset rounds correctly.
assert _direction_8(0.0) == "N" assert _direction_8(0.0) == "N"
assert _direction_8(22.4) == "N" assert _direction_8(22.4) == "N"
@ -339,7 +339,7 @@ def test_pass_row_aggregates_match_member_pixels():
"""5 pixels attributed in the same pass yield ONE fire_passes row """5 pixels attributed in the same pass yield ONE fire_passes row
with pixel_count=5, total_frp = sum, pass_started_at = min(acq), with pixel_count=5, total_frp = sum, pass_started_at = min(acq),
pass_ended_at = max(acq), centroid = median.""" pass_ended_at = max(acq), centroid = median."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
_seed_fire(irwin_id="ID-AGG-001", _seed_fire(irwin_id="ID-AGG-001",

View file

@ -71,7 +71,7 @@ def test_pass_close_stamps_perimeter_geojson():
"""Pass A: 6 pixels in a hex around the seeded center. First pixel """Pass A: 6 pixels in a hex around the seeded center. First pixel
of pass B triggers boundary close -> perimeter_geojson written for of pass B triggers boundary close -> perimeter_geojson written for
pass A as a closed GeoJSON Polygon.""" pass A as a closed GeoJSON Polygon."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
center_lat, center_lon = 42.500, -114.500 center_lat, center_lon = 42.500, -114.500
@ -121,7 +121,7 @@ def _seed_pass_a_hex_then_close(*, irwin_id, center_lat, center_lon,
start_now=1780747200): start_now=1780747200):
"""Helper: seed a fire + 6 hex-vertex pass A pixels. Caller follows up """Helper: seed a fire + 6 hex-vertex pass A pixels. Caller follows up
with a pass B pixel to trigger boundary close + perimeter write.""" with a pass B pixel to trigger boundary close + perimeter write."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
_seed_fire(irwin_id=irwin_id, lat=center_lat, lon=center_lon, _seed_fire(irwin_id=irwin_id, lat=center_lat, lon=center_lon,
name=irwin_id) name=irwin_id)
for i in range(6): for i in range(6):
@ -138,7 +138,7 @@ def _seed_pass_a_hex_then_close(*, irwin_id, center_lat, center_lon,
def test_pixel_2mi_ne_of_perimeter_emits_spotting(): def test_pixel_2mi_ne_of_perimeter_emits_spotting():
"""Pass B pixel 2 mi NE of pass A's perimeter centroid fires """Pass B pixel 2 mi NE of pass A's perimeter centroid fires
wildfire_spotting with the correct distance + direction.""" wildfire_spotting with the correct distance + direction."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
center_lat, center_lon = 43.000, -115.000 center_lat, center_lon = 43.000, -115.000
@ -188,7 +188,7 @@ def test_pixel_2mi_ne_of_perimeter_emits_spotting():
def test_pixel_inside_perimeter_no_spotting(): def test_pixel_inside_perimeter_no_spotting():
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
center_lat, center_lon = 43.500, -114.500 center_lat, center_lon = 43.500, -114.500
_seed_pass_a_hex_then_close(irwin_id="ID-SPOT-003", _seed_pass_a_hex_then_close(irwin_id="ID-SPOT-003",
@ -223,7 +223,7 @@ def test_pixel_inside_perimeter_no_spotting():
def test_second_spotting_within_cooldown_suppressed(): def test_second_spotting_within_cooldown_suppressed():
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
center_lat, center_lon = 44.000, -116.000 center_lat, center_lon = 44.000, -116.000
_seed_pass_a_hex_then_close(irwin_id="ID-SPOT-004", _seed_pass_a_hex_then_close(irwin_id="ID-SPOT-004",
@ -257,7 +257,7 @@ def test_second_spotting_within_cooldown_suppressed():
def test_spotting_refires_after_cooldown(): def test_spotting_refires_after_cooldown():
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
center_lat, center_lon = 44.500, -116.500 center_lat, center_lon = 44.500, -116.500
_seed_pass_a_hex_then_close(irwin_id="ID-SPOT-005", _seed_pass_a_hex_then_close(irwin_id="ID-SPOT-005",
@ -286,7 +286,7 @@ def test_spotting_refires_after_cooldown():
def test_convex_hull_basic(): def test_convex_hull_basic():
from meshai.central.firms_handler import _convex_hull from meshai.env.fire_fusion import _convex_hull
pts = [(0, 0), (1, 0), (1, 1), (0, 1), (0.5, 0.5)] pts = [(0, 0), (1, 0), (1, 1), (0, 1), (0.5, 0.5)]
hull = _convex_hull(pts) hull = _convex_hull(pts)
assert (0.5, 0.5) not in hull assert (0.5, 0.5) not in hull
@ -294,7 +294,7 @@ def test_convex_hull_basic():
def test_point_in_polygon_basic(): def test_point_in_polygon_basic():
from meshai.central.firms_handler import _point_in_polygon from meshai.env.fire_fusion import _point_in_polygon
square = [(0, 0), (0, 10), (10, 10), (10, 0)] # (lat, lon) square = [(0, 0), (0, 10), (10, 10), (10, 0)] # (lat, lon)
assert _point_in_polygon((5, 5), square) is True assert _point_in_polygon((5, 5), square) is True
assert _point_in_polygon((15, 5), square) is False assert _point_in_polygon((15, 5), square) is False
@ -303,7 +303,7 @@ def test_point_in_polygon_basic():
def test_geojson_round_trip_via_hull(): def test_geojson_round_trip_via_hull():
"""Hull -> GeoJSON -> parse -> ring shape sane.""" """Hull -> GeoJSON -> parse -> ring shape sane."""
from meshai.central.firms_handler import _convex_hull, _hull_to_geojson from meshai.env.fire_fusion import _convex_hull, _hull_to_geojson
hull = _convex_hull([(0, 0), (1, 0), (0, 1), (1, 1)]) hull = _convex_hull([(0, 0), (1, 0), (0, 1), (1, 1)])
raw = _hull_to_geojson(hull) raw = _hull_to_geojson(hull)
parsed = json.loads(raw) parsed = json.loads(raw)

View file

@ -73,7 +73,7 @@ def _pixel(*, lat, lon, acq_date="2026-06-06", acq_time="1200",
def _feed(pixel, *, now, seed=False): def _feed(pixel, *, now, seed=False):
from meshai.central.firms_handler import ingest_hotspot_pixel from meshai.env.fire_fusion import ingest_hotspot_pixel
return ingest_hotspot_pixel(pixel, now=now, seed=seed) return ingest_hotspot_pixel(pixel, now=now, seed=seed)

View file

@ -10,8 +10,8 @@ Envelope shape sourced from firms-investigation.md sampling (250 envelopes
""" """
import pytest import pytest
from meshai.central import firms_handler from meshai.env import fire_fusion as firms_handler
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import close_thread_connection, init_db from meshai.persistence import close_thread_connection, init_db
from meshai.persistence import db as persistence_db from meshai.persistence import db as persistence_db

View file

@ -100,7 +100,7 @@ def _offset_mi(lat, lon, north_mi, east_mi):
def _feed(pixel, *, now): def _feed(pixel, *, now):
from meshai.central.firms_handler import ingest_hotspot_pixel from meshai.env.fire_fusion import ingest_hotspot_pixel
return ingest_hotspot_pixel(pixel, now=now) return ingest_hotspot_pixel(pixel, now=now)
@ -407,7 +407,7 @@ _SUBJECT = "central.fire.hotspot.N20.high.us.id"
class TestCentralPathUnchanged: class TestCentralPathUnchanged:
def test_storage_only_pixel_still_stores_and_returns_none(self): def test_storage_only_pixel_still_stores_and_returns_none(self):
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
from meshai.persistence import get_db from meshai.persistence import get_db
env = _envelope(lat=42.19664, lon=-113.70981) env = _envelope(lat=42.19664, lon=-113.70981)
out = handle_firms(env, subject=_SUBJECT, data={}, now=1780660000) out = handle_firms(env, subject=_SUBJECT, data={}, now=1780660000)
@ -427,7 +427,7 @@ class TestCentralPathUnchanged:
def test_central_growth_wire_and_stamps_identical(self): def test_central_growth_wire_and_stamps_identical(self):
"""The extracted core produces the SAME growth wire/stamps on the """The extracted core produces the SAME growth wire/stamps on the
Central envelope path that the inline handler did.""" Central envelope path that the inline handler did."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
center_lat, center_lon = 42.0, -114.0 center_lat, center_lon = 42.0, -114.0
_seed_fire(irwin_id="ID-CG", lat=center_lat, lon=center_lon, _seed_fire(irwin_id="ID-CG", lat=center_lat, lon=center_lon,
name="Pine Gulch") name="Pine Gulch")

View file

@ -119,7 +119,7 @@ _SUBJECT = "central.fire.hotspot.N20.high.us.id"
def _drive_two_pass_growth(irwin_id, center_lat, center_lon): def _drive_two_pass_growth(irwin_id, center_lat, center_lon):
"""Seed a fire + pass A (5 px) + first pass-B pixel 1 mi N. Returns the """Seed a fire + pass A (5 px) + first pass-B pixel 1 mi N. Returns the
(wire, data) from the boundary pixel that fires wildfire_growth.""" (wire, data) from the boundary pixel that fires wildfire_growth."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
_seed_fire(irwin_id=irwin_id, lat=center_lat, lon=center_lon, name="Pine Gulch") _seed_fire(irwin_id=irwin_id, lat=center_lat, lon=center_lon, name="Pine Gulch")
for i in range(5): for i in range(5):
env = _envelope(lat=center_lat + 0.0001 * i, env = _envelope(lat=center_lat + 0.0001 * i,
@ -136,7 +136,7 @@ def _drive_two_pass_growth(irwin_id, center_lat, center_lon):
def _seed_pass_a_hex_then_close(irwin_id, center_lat, center_lon, def _seed_pass_a_hex_then_close(irwin_id, center_lat, center_lon,
start_now=1780747200): start_now=1780747200):
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
_seed_fire(irwin_id=irwin_id, lat=center_lat, lon=center_lon, name=irwin_id) _seed_fire(irwin_id=irwin_id, lat=center_lat, lon=center_lon, name=irwin_id)
for i in range(6): for i in range(6):
angle = i * math.pi / 3 angle = i * math.pi / 3
@ -156,7 +156,7 @@ def _offset_mi(lat, lon, north_mi, east_mi):
def _drive_spotting(irwin_id, center_lat, center_lon, now=1780768800): def _drive_spotting(irwin_id, center_lat, center_lon, now=1780768800):
"""Seed hex pass A + closed perimeter, then a pass-B pixel 2 mi NE that """Seed hex pass A + closed perimeter, then a pass-B pixel 2 mi NE that
fires wildfire_spotting. Returns (wire, data).""" fires wildfire_spotting. Returns (wire, data)."""
from meshai.central.firms_handler import handle_firms from meshai.env.fire_fusion import handle_firms
_seed_pass_a_hex_then_close(irwin_id, center_lat, center_lon) _seed_pass_a_hex_then_close(irwin_id, center_lat, center_lon)
sp_lat, sp_lon = _offset_mi(center_lat, center_lon, sp_lat, sp_lon = _offset_mi(center_lat, center_lon,
north_mi=2.0 / math.sqrt(2), north_mi=2.0 / math.sqrt(2),
@ -354,7 +354,7 @@ class TestFormatterGolden:
_clear_cache() _clear_cache()
def test_halt_wire_golden(self, monkeypatch): def test_halt_wire_golden(self, monkeypatch):
from meshai.central.firms_handler import _maybe_emit_halt from meshai.env.fire_fusion import _maybe_emit_halt
from meshai.persistence import get_db from meshai.persistence import get_db
_cutover(monkeypatch, "wildfire_halted") _cutover(monkeypatch, "wildfire_halted")
try: try:
@ -416,7 +416,7 @@ class TestNotCutoverLegacyVerbatim:
assert "_on_broadcast_committed" not in data assert "_on_broadcast_committed" not in data
def test_halt_eager_latch_stamped(self, _no_cutover): def test_halt_eager_latch_stamped(self, _no_cutover):
from meshai.central.firms_handler import _maybe_emit_halt from meshai.env.fire_fusion import _maybe_emit_halt
from meshai.persistence import get_db from meshai.persistence import get_db
now = 1780768800 now = 1780768800
_seed_stale_fire("ID-HN", now_epoch=now, idle_hours=14) _seed_stale_fire("ID-HN", now_epoch=now, idle_hours=14)
@ -439,7 +439,7 @@ class TestNotCutoverLegacyVerbatim:
class TestClusterBelowThreshold: class TestClusterBelowThreshold:
def test_maybe_emit_cluster_below_threshold_returns_none(self): def test_maybe_emit_cluster_below_threshold_returns_none(self):
from meshai.central.firms_handler import _maybe_emit_cluster from meshai.env.fire_fusion import _maybe_emit_cluster
from meshai.persistence import get_db from meshai.persistence import get_db
data = {} data = {}
# No pixels in firms_pixels -> the cluster query finds < min_pixels # No pixels in firms_pixels -> the cluster query finds < min_pixels

View file

@ -3,6 +3,13 @@ must be importable without error after the budget shim refactor.
This guards against a broken import chain (e.g. circular imports or a bad This guards against a broken import chain (e.g. circular imports or a bad
re-export in the shim) that would silently break all handlers. re-export in the shim) that would silently break all handlers.
chore/ripout-2d: the last two central/ handlers (firms_handler.py,
wfigs_handler.py) relocated to meshai/env/fire_fusion.py + fire_render.py
(central/ has no *_handler.py left -- the fusion engine's only consumer is
the native env/firms.py adapter). The glob below now also covers those two
explicitly so this guard still exercises the relocated live import chain;
it keeps globbing central/ too in case a future handler lands back there.
""" """
import glob import glob
@ -11,17 +18,16 @@ import os
def _handler_modules(): def _handler_modules():
"""Collect meshai.central.*_handler module names by globbing the source tree.""" """Collect handler-ish module names: meshai.central.*_handler (glob) +
pattern = os.path.join( the fire-fusion modules relocated out of central/ during the ripout."""
central_pattern = os.path.join(
os.path.dirname(__file__), os.path.dirname(__file__),
"..", "meshai", "central", "*_handler.py", "..", "meshai", "central", "*_handler.py",
) )
paths = sorted(glob.glob(pattern)) central_paths = sorted(glob.glob(central_pattern))
assert paths, "No *_handler.py files found — check the glob path" modules = [f"meshai.central.{os.path.basename(p)[:-3]}" for p in central_paths]
modules = [] modules += ["meshai.env.fire_fusion", "meshai.env.fire_render"]
for p in paths: assert modules, "No handler-ish modules found — check the glob path"
name = os.path.basename(p)[:-3] # strip .py
modules.append(f"meshai.central.{name}")
return modules return modules

View file

@ -279,7 +279,7 @@ def test_fires_has_tombstoned_at_column():
def test_wfigs_tombstone_stamps_column(): def test_wfigs_tombstone_stamps_column():
"""A tombstone envelope sets fires.tombstoned_at.""" """A tombstone envelope sets fires.tombstoned_at."""
from meshai.central.wfigs_handler import handle_wfigs from meshai.env.fire_render import handle_wfigs
conn = get_db() conn = get_db()
# Seed an active fire row. # Seed an active fire row.
irwin = "TOMB-1" irwin = "TOMB-1"

View file

@ -23,7 +23,7 @@ import time
import pytest import pytest
from meshai.central.wfigs_handler import handle_wfigs from meshai.env.fire_render import handle_wfigs
from meshai.notifications.env_reporter import EnvReporter from meshai.notifications.env_reporter import EnvReporter
from meshai.persistence import get_db from meshai.persistence import get_db

View file

@ -169,7 +169,7 @@ def test_dedup_suffix_lets_updates_pass_and_repeats_dedup():
def test_wfigs_handler_stamps_dedup_suffix(): def test_wfigs_handler_stamps_dedup_suffix():
from meshai.central.wfigs_handler import _attach_commit_handles from meshai.env.fire_render import _attach_commit_handles
data = {} data = {}
_attach_commit_handles(data, irwin_id="{X}", acres=42.0, _attach_commit_handles(data, irwin_id="{X}", acres=42.0,
contained_pct=15, event_log_row_id=None) contained_pct=15, event_log_row_id=None)

View file

@ -20,7 +20,7 @@ import time
import pytest import pytest
from meshai import central_normalizer as cn from meshai import central_normalizer as cn
from meshai.central.wfigs_handler import ( from meshai.env.fire_render import (
WFIGS_BROADCAST_COOLDOWN_S, WFIGS_BROADCAST_COOLDOWN_S,
handle_wfigs, handle_wfigs,
_render as _wfigs_render, _render as _wfigs_render,
@ -47,7 +47,7 @@ def mem_db(monkeypatch, tmp_path):
pass pass
# Reset the stale-fire cleanup throttle so it runs deterministically. # Reset the stale-fire cleanup throttle so it runs deterministically.
try: try:
from meshai.central import wfigs_handler as _wh from meshai.env import fire_render as _wh
_wh._last_cleanup = 0 _wh._last_cleanup = 0
except Exception: except Exception:
pass pass