Commit graph

5 commits

Author SHA1 Message Date
391a6316ad
chore: remove dead modules and orphaned fixtures (-2,952 LOC) (#142)
* chore: remove dead mesh_sources.py module

Superseded by mesh_data_store.py, whose docstring states it "replaces
mesh_sources.py with a clean three-layer architecture." No remaining
inbound imports (absolute or relative) repo-wide; the only surviving
references to "mesh_sources" are to the unrelated config YAML section
of the same name, which is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: remove dead region_tagger.py module

Only reference to "region_tagger" repo-wide was inside its own
module docstring usage example; no live imports found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: remove empty meshai/cli package

Package contained only a docstring, no code. No imports of
meshai.cli found anywhere; console entry point is meshai.main:main
(pyproject.toml), and packaging (tool.setuptools.packages.find,
include = ["meshai*"]) has no explicit reference to the cli
subpackage. No MANIFEST.in/setup.py/setup.cfg exist to update.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: remove orphaned swpc/swpc_last golden fixtures

63 files (40 in fixtures/swpc/, 23 in fixtures/swpc_last/) never
loaded by any test. harness/goldens.py's load_fixtures(hazard) is
the only generic fixture loader and is never called with "swpc" or
"swpc_last" (only "avalanche", "nws", "nws_last", "quake"). The sole
remaining textual reference, a provenance comment in
test_swpc_refactor.py:162 ("Fixture mirrors swpc_last/0003.json..."),
is left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 14:06:08 -06:00
bbe97398bc
feat(ipaws): add FEMA IPAWS-OPEN civil-alert adapter (disabled by default) (#138)
Adds a new native `ipaws` adapter for FEMA IPAWS-OPEN EAS civil alerts.

- Two-stage CAP fetch via base_url (direct FEMA or Conduit proxy): Atom
  index -> per-entry CAP 1.2 documents.
- Non-weather civil alerts only (evacuation, Civil Emergency Message,
  AMBER, 911 outage, law-enforcement, HazMat); NWS/NOAA CAP dropped so
  weather is never double-broadcast.
- Idaho + neighbour statefips scope gate applied before stage-2 fetch.
- Own `ipaws_alerts` dedup table (migration v29); reuses the NWS CAP
  severity + formatter pattern.
- Ships enabled=False (no transmit until explicitly enabled).

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:05:01 -06:00
bdc42aa341
refactor(phase2): migrate nws + incident/roads to formatter+decider (tier-a) (#30)
Behavior-preserving relocation (byte-identical goldens), behind the staged-
cutover gate.

- formatters/nws.py + gating/nws.py: move _render + all CAP parsing (HTML-strip,
  _parse_nws_description, motion/hail/wind, county scoping, short-expiry) and the
  gating (3h re-alert window, Update/Cancel/Expire tombstone, first-sighting,
  warning->immediate severity). Native env/nws.py stops truncating description +
  carries parameters/eventCode/certainty/msgType/references/geocoder. Registers
  weather_warning + weather_statement.
- formatters/incident.py + gating/incident.py: absorb the incident _render AND
  the work_zone renderer, reconciling the two sub_type vocabularies; traffic_events
  change-detection gating. Native env/{traffic,roads511}.py emit canonical data.
  Registers work_zone/road_incident/road_closure/traffic_congestion.
- formatters/_anchor.py: shared town/distance/bearing resolver (consolidates WFIGS
  _location_anchor + incident nearest_town; WFIGS fire reuses in Phase 3).
- Fixtures captured: nws x37, traffic x46.

Tier-a byte-identical goldens for all fixtures. Tests: +~150; 0 new failures
(34 baseline, 1539 passed).

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 18:16:34 -06:00
4e23fc737d
refactor(phase1): quake + swpc(Kp+flare) + avalanche, behind staged-cutover gate (#29)
* fix(fixtures): rewrite capture_fixtures.py to use ephemeral push subscribe

Previous script hardcoded stream="CENTRAL" which does not exist — Central
partitions into domain streams (CENTRAL_QUAKE, CENTRAL_SPACE, etc.).  It
also called pull_subscribe_bind() without await, making the fetch a no-op.

Fix: mirror the proven CentralConsumer.start() pattern — use
js.subscribe(subject, cb=..., AckPolicy.NONE, no durable) which auto-
discovers the correct stream via the subject filter, identical to how the
live consumer binds.  Messages are funnelled through asyncio.Queue with
an idle-timeout to detect drain completion.

Adds live captured fixtures:
- tests/fixtures/quake/  — 3 envelopes (CENTRAL_QUAKE stream, mode=all)
- tests/fixtures/swpc/   — 40 envelopes (CENTRAL_SPACE, mode=all, proton_flux history)
- tests/fixtures/swpc_last/ — 23 envelopes (mode=last: 21 alert variants + kindex + proton_flux)
Avalanche: confirmed empty off-season (CENTRAL_AVY stream, 0 messages).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(phase1): migrate quake, swpc(Kp+flare), avalanche + staged-cutover gate

First hazards on the source-agnostic formatter+decider path, behind a
staged-cutover gate so deploy = shadow-only (old path still broadcasts; new
path dry-run-diffed) until MESHAI_CUTOVER_CATEGORIES flips a category live.

- formatters/{quake,swpc,avalanche}.py + gating/{quake,swpc,avalanche}.py:
  source-agnostic format(event,*,now,budget) + decide(data,*,source,now)->GateResult.
  quake (earthquake_event, tier-b: render PAGER + live update-prefix), swpc
  (geomagnetic_storm + rf_propagation_alert, Kp+flare only; proton/solar_radiation
  _storm stays legacy; geomag 600s window re-homed off the module-global into
  gating/swpc with a deferred stamp; tier-b scale-based severity), avalanche
  (avalanche_warning/watch; centralseverity->NAADS 1-5 remap; synthetic fixtures
  off-season).
- central/{quake,swpc,avy}_handler.py bridges: cutover -> new decide()+canonical
  data; not-cutover -> exact legacy behavior. env/{usgs_quake,swpc,avalanche}.py
  emit canonical Event.data (avalanche stops precomposing). env/store.py generic
  native decider hook (cutover-gated).
- notifications/cutover.py (is_cutover via MESHAI_CUTOVER_CATEGORIES); composer
  dispatch + shadow hooks are cutover-aware (shadow no-ops once a category is live).
- scripts/capture_fixtures.py fixed (per-domain streams e.g. CENTRAL_QUAKE, await
  bind); real quake/swpc fixtures captured; avalanche synthetic.

Tests: +~150 (quake/swpc/avalanche parity+cross-source+gate-sequence+cutover);
0 new failures (34 baseline, 1426 passed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 15:58:46 -06:00
Ubuntu
2e1fb325f7 refactor: move source tree into work/, multi-stage Docker build, fix satpass
- Move all application source (meshai/, dashboard-frontend/, tests/,
  config/, docs/, Dockerfile, etc.) into work/ directory
- Add Node.js multi-stage build to Dockerfile for frontend compilation;
  remove compiled static assets from git tracking
- Fix satpass missing time windows: consolidation was splitting wire on
  newline and only putting line 1 in event.title, dropping the time
  window line that the composer uses for precomposed broadcasts
- Fix satpass burst flooding: stagger consolidation timers (+60s per
  pending pass) so Central batch publishes don't blast the mesh
- Update CI workflow build context to work/
- Anchor lib/ and data/ gitignore patterns to repo root to prevent
  false matches on nested directories
- Add dashboard-frontend/node_modules/ to .dockerignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-16 03:40:31 +00:00