From de91751bbc0f74ff8648c75c249e481cc55a5025 Mon Sep 17 00:00:00 2001 From: malice Date: Fri, 17 Jul 2026 16:31:48 -0600 Subject: [PATCH] chore(central-ripout 2d-i): relocate the fire engine + renderer out of central/ (#164) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: Matt Johnson Co-authored-by: Claude Opus 4.8 (1M context) --- work/meshai/central/__init__.py | 8 ------- .../firms_handler.py => env/fire_fusion.py} | 21 ++++++++++++++++-- .../wfigs_handler.py => env/fire_render.py} | 12 +++++++++- work/meshai/env/firms.py | 6 ++--- work/tests/test_clock_seam.py | 2 +- work/tests/test_fire_age_gate.py | 10 ++++----- work/tests/test_fire_refactor.py | 4 ++-- work/tests/test_fire_tracker_phase1.py | 16 +++++++------- work/tests/test_fire_tracker_phase2.py | 16 +++++++------- work/tests/test_fire_tracker_phase3.py | 18 +++++++-------- work/tests/test_firms_cluster_f3.py | 2 +- work/tests/test_firms_handler.py | 4 ++-- work/tests/test_firms_native_fusion.py | 6 ++--- work/tests/test_firms_refactor.py | 12 +++++----- work/tests/test_import_smoke.py | 22 ++++++++++++------- work/tests/test_tail_followups.py | 2 +- work/tests/test_tombstone_broadcast.py | 2 +- work/tests/test_v064_guard_commit.py | 2 +- work/tests/test_wfigs_handler.py | 4 ++-- 19 files changed, 97 insertions(+), 72 deletions(-) delete mode 100644 work/meshai/central/__init__.py rename work/meshai/{central/firms_handler.py => env/fire_fusion.py} (98%) rename work/meshai/{central/wfigs_handler.py => env/fire_render.py} (97%) diff --git a/work/meshai/central/__init__.py b/work/meshai/central/__init__.py deleted file mode 100644 index e87a682..0000000 --- a/work/meshai/central/__init__.py +++ /dev/null @@ -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.""" diff --git a/work/meshai/central/firms_handler.py b/work/meshai/env/fire_fusion.py similarity index 98% rename from work/meshai/central/firms_handler.py rename to work/meshai/env/fire_fusion.py index 7353df7..3e835e3 100644 --- a/work/meshai/central/firms_handler.py +++ b/work/meshai/env/fire_fusion.py @@ -1,5 +1,15 @@ """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 dropped every `central.fire.hotspot.>` envelope because no per-adapter handler 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 meshai.persistence import get_db +from meshai.env.fire_render import _render logger = logging.getLogger(__name__) @@ -919,7 +930,6 @@ def _handle_pass_boundary(conn, *, irwin_id, pass_id, lat, lon, if fire is None: return None - from meshai.central.wfigs_handler import _render movement = {"direction": drift_direction, "speed_mph": drift_mi_per_hour or 0.0} normalized = dict(fire) 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, 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 = "" try: from meshai.central_normalizer import nearest_town diff --git a/work/meshai/central/wfigs_handler.py b/work/meshai/env/fire_render.py similarity index 97% rename from work/meshai/central/wfigs_handler.py rename to work/meshai/env/fire_render.py index 66f0698..d0937ee 100644 --- a/work/meshai/central/wfigs_handler.py +++ b/work/meshai/env/fire_render.py @@ -1,5 +1,15 @@ """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`, not on row existence. Cold-start scenarios where the dispatcher drops the broadcast (cold-start grace, stale filter, cooldown, dedup) leave the fires @@ -469,7 +479,7 @@ def _render(n: dict, *, prefix: str = "", lines: list[str] = [] # 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). acres_str = f"{int(acres):,} ac" if acres is not None else "size unknown" diff --git a/work/meshai/env/firms.py b/work/meshai/env/firms.py index 5f54db8..31a886b 100644 --- a/work/meshai/env/firms.py +++ b/work/meshai/env/firms.py @@ -138,7 +138,7 @@ class FIRMSAdapter: new_events = self._parse_csv(csv_data) # 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 # idempotent across ticks, so re-fetched pixels never double-count. # NEVER broadcasts raw hotspots -- only the fusion wires it returns. @@ -422,14 +422,14 @@ class FIRMSAdapter: engine and collect the fire-fusion broadcasts. 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 attribution, and runs the growth / spotting / halt fusion; it returns ONLY fusion wires (never a raw-hotspot / cluster broadcast). DB-level dedup makes re-fetched pixels no-ops, so nothing double-counts. """ try: - from meshai.central.firms_handler import ( + from meshai.env.fire_fusion import ( ingest_hotspot_pixel, _parse_acq_epoch, ) except Exception: diff --git a/work/tests/test_clock_seam.py b/work/tests/test_clock_seam.py index 688c82a..1e44fe9 100644 --- a/work/tests/test_clock_seam.py +++ b/work/tests/test_clock_seam.py @@ -10,7 +10,7 @@ import time import pytest 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 diff --git a/work/tests/test_fire_age_gate.py b/work/tests/test_fire_age_gate.py index 8c22e23..b376f8a 100644 --- a/work/tests/test_fire_age_gate.py +++ b/work/tests/test_fire_age_gate.py @@ -1,6 +1,6 @@ """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. The gate exists because MeshAI broadcast a 6-week-old, already-closed fire @@ -21,7 +21,7 @@ import time 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 @@ -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 but the 'New' broadcast is suppressed (wire is None), and the New-path 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 now = int(time.time()) @@ -154,7 +154,7 @@ def test_handler_new_path_suppresses_old_fire(): def test_handler_new_path_announces_recent_fire(): """Case (i): a recent fire (~5d) DOES broadcast 'New' and tags wildfire_declared.""" - from meshai.central.wfigs_handler import handle_wfigs + from meshai.env.fire_render import handle_wfigs now = int(time.time()) 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 'Update' (Case (iii) is NOT gated -- genuine old-but-active fires keep 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 now = int(time.time()) diff --git a/work/tests/test_fire_refactor.py b/work/tests/test_fire_refactor.py index c7aa747..f90de02 100644 --- a/work/tests/test_fire_refactor.py +++ b/work/tests/test_fire_refactor.py @@ -23,7 +23,7 @@ import pytest from meshai import central_normalizer as cn from meshai.notifications.formatters._budget import budget_for -from meshai.central.wfigs_handler import ( +from meshai.env.fire_render import ( _build_canonical, _render as _wfigs_render, handle_wfigs, @@ -55,7 +55,7 @@ def mem_db(monkeypatch, tmp_path): except Exception: pass try: - from meshai.central import wfigs_handler as _wh + from meshai.env import fire_render as _wh _wh._last_cleanup = 0 except Exception: pass diff --git a/work/tests/test_fire_tracker_phase1.py b/work/tests/test_fire_tracker_phase1.py index 0a4f77d..5d0ab4a 100644 --- a/work/tests/test_fire_tracker_phase1.py +++ b/work/tests/test_fire_tracker_phase1.py @@ -81,7 +81,7 @@ def _envelope(*, lat, lon, acq_date="2026-06-06", acq_time="1200", 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 # 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(): """A second attributed pixel updates the centroid to the median, not 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 _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(): - from meshai.central.firms_handler import handle_firms + from meshai.env.fire_fusion import handle_firms from meshai.persistence import get_db _seed_fire(irwin_id="ID-TEST-003", @@ -196,7 +196,7 @@ def _hhmm(h, m=0): 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 # 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(): - from meshai.central.firms_handler import handle_firms + from meshai.env.fire_fusion import handle_firms from meshai.persistence import get_db 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 we then ingest TWO more nearby pixels (also outside the original 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 # 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(): - from meshai.central.wfigs_handler import handle_wfigs + from meshai.env.fire_render import handle_wfigs normalized = { "_kind": "wfigs_incident", @@ -351,7 +351,7 @@ def test_wfigs_first_sight_tags_wildfire_declared(): def test_wfigs_update_does_not_retag_wildfire_declared(): """After a row exists AND has been broadcast, an acres-grew Update 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 # Pre-existing row that has already been broadcast. diff --git a/work/tests/test_fire_tracker_phase2.py b/work/tests/test_fire_tracker_phase2.py index 53a2e06..9fbe862 100644 --- a/work/tests/test_fire_tracker_phase2.py +++ b/work/tests/test_fire_tracker_phase2.py @@ -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(): """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.""" - from meshai.central.firms_handler import handle_firms + from meshai.env.fire_fusion import handle_firms from meshai.persistence import get_db _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(): """0.3 mi drift between consecutive passes -- below the 0.5 mi 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 _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(): """Fire with last_pass_at 14h ago + no new pixels in that 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 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(): """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 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 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.""" - from meshai.central.firms_handler import _maybe_emit_halt + from meshai.env.fire_fusion import _maybe_emit_halt from meshai.persistence import get_db now_epoch = 1780768800 @@ -267,7 +267,7 @@ def test_halt_eligibility_returns_after_new_pass_arrives(): def test_bearing_and_direction_round_trip(): """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. s_lat, s_lon = 42.0, -114.0 # 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(): - 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. assert _direction_8(0.0) == "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 with pixel_count=5, total_frp = sum, pass_started_at = min(acq), 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 _seed_fire(irwin_id="ID-AGG-001", diff --git a/work/tests/test_fire_tracker_phase3.py b/work/tests/test_fire_tracker_phase3.py index 75f883b..2b4d0a2 100644 --- a/work/tests/test_fire_tracker_phase3.py +++ b/work/tests/test_fire_tracker_phase3.py @@ -71,7 +71,7 @@ def test_pass_close_stamps_perimeter_geojson(): """Pass A: 6 pixels in a hex around the seeded center. First pixel of pass B triggers boundary close -> perimeter_geojson written for 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 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): """Helper: seed a fire + 6 hex-vertex pass A pixels. Caller follows up 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, name=irwin_id) 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(): """Pass B pixel 2 mi NE of pass A's perimeter centroid fires 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 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(): - from meshai.central.firms_handler import handle_firms + from meshai.env.fire_fusion import handle_firms center_lat, center_lon = 43.500, -114.500 _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(): - from meshai.central.firms_handler import handle_firms + from meshai.env.fire_fusion import handle_firms center_lat, center_lon = 44.000, -116.000 _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(): - from meshai.central.firms_handler import handle_firms + from meshai.env.fire_fusion import handle_firms center_lat, center_lon = 44.500, -116.500 _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(): - 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)] hull = _convex_hull(pts) assert (0.5, 0.5) not in hull @@ -294,7 +294,7 @@ def test_convex_hull_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) assert _point_in_polygon((5, 5), square) is True 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(): """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)]) raw = _hull_to_geojson(hull) parsed = json.loads(raw) diff --git a/work/tests/test_firms_cluster_f3.py b/work/tests/test_firms_cluster_f3.py index da99f98..d1732d9 100644 --- a/work/tests/test_firms_cluster_f3.py +++ b/work/tests/test_firms_cluster_f3.py @@ -73,7 +73,7 @@ def _pixel(*, lat, lon, acq_date="2026-06-06", acq_time="1200", 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) diff --git a/work/tests/test_firms_handler.py b/work/tests/test_firms_handler.py index a86c83e..7f685ba 100644 --- a/work/tests/test_firms_handler.py +++ b/work/tests/test_firms_handler.py @@ -10,8 +10,8 @@ Envelope shape sourced from firms-investigation.md sampling (250 envelopes """ import pytest -from meshai.central import firms_handler -from meshai.central.firms_handler import handle_firms +from meshai.env import fire_fusion as firms_handler +from meshai.env.fire_fusion import handle_firms from meshai.persistence import close_thread_connection, init_db from meshai.persistence import db as persistence_db diff --git a/work/tests/test_firms_native_fusion.py b/work/tests/test_firms_native_fusion.py index b86cf91..d57709c 100644 --- a/work/tests/test_firms_native_fusion.py +++ b/work/tests/test_firms_native_fusion.py @@ -100,7 +100,7 @@ def _offset_mi(lat, lon, north_mi, east_mi): 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) @@ -407,7 +407,7 @@ _SUBJECT = "central.fire.hotspot.N20.high.us.id" class TestCentralPathUnchanged: 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 env = _envelope(lat=42.19664, lon=-113.70981) out = handle_firms(env, subject=_SUBJECT, data={}, now=1780660000) @@ -427,7 +427,7 @@ class TestCentralPathUnchanged: def test_central_growth_wire_and_stamps_identical(self): """The extracted core produces the SAME growth wire/stamps on the 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 _seed_fire(irwin_id="ID-CG", lat=center_lat, lon=center_lon, name="Pine Gulch") diff --git a/work/tests/test_firms_refactor.py b/work/tests/test_firms_refactor.py index abc3d96..e866f31 100644 --- a/work/tests/test_firms_refactor.py +++ b/work/tests/test_firms_refactor.py @@ -119,7 +119,7 @@ _SUBJECT = "central.fire.hotspot.N20.high.us.id" 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 (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") for i in range(5): 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, 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) for i in range(6): 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): """Seed hex pass A + closed perimeter, then a pass-B pixel 2 mi NE that 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) sp_lat, sp_lon = _offset_mi(center_lat, center_lon, north_mi=2.0 / math.sqrt(2), @@ -354,7 +354,7 @@ class TestFormatterGolden: _clear_cache() 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 _cutover(monkeypatch, "wildfire_halted") try: @@ -416,7 +416,7 @@ class TestNotCutoverLegacyVerbatim: assert "_on_broadcast_committed" not in data 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 now = 1780768800 _seed_stale_fire("ID-HN", now_epoch=now, idle_hours=14) @@ -439,7 +439,7 @@ class TestNotCutoverLegacyVerbatim: class TestClusterBelowThreshold: 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 data = {} # No pixels in firms_pixels -> the cluster query finds < min_pixels diff --git a/work/tests/test_import_smoke.py b/work/tests/test_import_smoke.py index 5fa0b1b..1e2d22e 100644 --- a/work/tests/test_import_smoke.py +++ b/work/tests/test_import_smoke.py @@ -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 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 @@ -11,17 +18,16 @@ import os def _handler_modules(): - """Collect meshai.central.*_handler module names by globbing the source tree.""" - pattern = os.path.join( + """Collect handler-ish module names: meshai.central.*_handler (glob) + + the fire-fusion modules relocated out of central/ during the ripout.""" + central_pattern = os.path.join( os.path.dirname(__file__), "..", "meshai", "central", "*_handler.py", ) - paths = sorted(glob.glob(pattern)) - assert paths, "No *_handler.py files found — check the glob path" - modules = [] - for p in paths: - name = os.path.basename(p)[:-3] # strip .py - modules.append(f"meshai.central.{name}") + central_paths = sorted(glob.glob(central_pattern)) + modules = [f"meshai.central.{os.path.basename(p)[:-3]}" for p in central_paths] + modules += ["meshai.env.fire_fusion", "meshai.env.fire_render"] + assert modules, "No handler-ish modules found — check the glob path" return modules diff --git a/work/tests/test_tail_followups.py b/work/tests/test_tail_followups.py index d2ca255..3d7c206 100644 --- a/work/tests/test_tail_followups.py +++ b/work/tests/test_tail_followups.py @@ -279,7 +279,7 @@ def test_fires_has_tombstoned_at_column(): def test_wfigs_tombstone_stamps_column(): """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() # Seed an active fire row. irwin = "TOMB-1" diff --git a/work/tests/test_tombstone_broadcast.py b/work/tests/test_tombstone_broadcast.py index 1972a3c..85544b7 100644 --- a/work/tests/test_tombstone_broadcast.py +++ b/work/tests/test_tombstone_broadcast.py @@ -23,7 +23,7 @@ import time 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.persistence import get_db diff --git a/work/tests/test_v064_guard_commit.py b/work/tests/test_v064_guard_commit.py index 6cafc39..ca9f6f1 100644 --- a/work/tests/test_v064_guard_commit.py +++ b/work/tests/test_v064_guard_commit.py @@ -169,7 +169,7 @@ def test_dedup_suffix_lets_updates_pass_and_repeats_dedup(): 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 = {} _attach_commit_handles(data, irwin_id="{X}", acres=42.0, contained_pct=15, event_log_row_id=None) diff --git a/work/tests/test_wfigs_handler.py b/work/tests/test_wfigs_handler.py index 0cb724f..dbe46bb 100644 --- a/work/tests/test_wfigs_handler.py +++ b/work/tests/test_wfigs_handler.py @@ -20,7 +20,7 @@ import time import pytest from meshai import central_normalizer as cn -from meshai.central.wfigs_handler import ( +from meshai.env.fire_render import ( WFIGS_BROADCAST_COOLDOWN_S, handle_wfigs, _render as _wfigs_render, @@ -47,7 +47,7 @@ def mem_db(monkeypatch, tmp_path): pass # Reset the stale-fire cleanup throttle so it runs deterministically. try: - from meshai.central import wfigs_handler as _wh + from meshai.env import fire_render as _wh _wh._last_cleanup = 0 except Exception: pass