central/tests/fixtures/avalanche_snfac.json
malice e92b51c518
v0.10.10: new avalanche_org adapter — backcountry avalanche advisories (#98)
meshai-requested adapter for avalanche.org's per-center map layers (SNFAC
Sawtooth + PAC Payette by default; operator-extensible to any avalanche.org
forecast center). Pure passthrough + severity gate, no cross-source fusion,
fits Central's adapter pattern cleanly.

Adapter surface:
- Polls https://api.avalanche.org/v2/public/products/map-layer/{center_id}
  per configured center; default cadence 1800s (30 min).
- Severity gate: only danger_level >= 3 publishes. danger_level 0/1/2
  (None/Low/Moderate), -1 ('no rating'), and off_season=true all omitted at
  adapter level. Idaho summer = all 4 SNFAC + 2 PAC zones yield 0 events;
  that's correct behavior, verified by the negative-case test against the
  frozen 2026-06-08 SNFAC fixture.
- Severity mapping (corrected from meshai's inverted spec): danger_level
  3 (Considerable) → severity 2, 4 (High) → 3, 5 (Extreme) → 4. Matches
  Central's 4-most-severe convention (nws.SEVERITY_MAP).
- Subject: central.avy.advisory.us.{state_lower} — one per state; v0.10.8's
  category-discriminated Nats-Msg-Id keeps multiple zones in the same state
  from colliding in JetStream dedup.
- Stream: CENTRAL_AVY (central.avy.>); 7-day / 1 GiB retention defaults.
- Event.data fields per meshai spec: center_id, zone_name, danger_level,
  danger_name, travel_advice (truncated to 200 chars), state, valid_date,
  end_date, off_season=false, latitude/longitude (polygon centroid via
  shapely), plus geo.geometry passes through as the upstream Polygon.

Tests (38 in test_avalanche_org.py):
- Pure helpers: _slug (8 cases), _parse_iso (6 cases), _centroid (2 cases).
- Severity gate: 3 publish cases (danger 3/4/5 → severity 2/3/4),
  4 omit cases (danger -1/0/1/2), off_season=true omit, missing state omit,
  unparseable geom omit, travel_advice truncation, subject derivation.
- Real-fixture negative case: 4-zone SNFAC fixture all omitted off-season.
- Real-fixture positive case: same fixture with synthetic winter overrides
  publishes all 4 with valid centroids on actual Idaho polygons.
- End-to-end poll() with mixed severities and the new wiring (streams
  registry + supervisor family map).
- Defensive: empty center_ids list yields nothing without crashing.

Wiring + plumbing:
- src/central/streams.py: StreamEntry('CENTRAL_AVY', 'central.avy.>')
- src/central/supervisor.py: STREAM_CATEGORY_DOMAINS['CENTRAL_AVY']=('avy',)
- sql/migrations/035: seed config.streams row (mirror of 019/CENTRAL_SPACE,
  idempotent ON CONFLICT DO NOTHING). Note: migrations don't auto-run on
  supervisor restart -- see deferred ops list (schema_migrations cleanup
  blocks central-migrate from running anything cleanly).
- src/central/gui/templates/_event_rows/avalanche_org.html (8 lines)
- src/central/gui/templates/_event_summaries/avalanche_org.html (2 lines)
  Both required by the existing per-adapter template consistency tests.

Doc updates (required by existing doc-vs-registry tests):
- docs/PRODUCER-INTEGRATION.md §6.1: added 'avy' to top-level-domain list.
- docs/PRODUCER-INTEGRATION.md §8: added StreamEntry('CENTRAL_AVY',...) line
  to the verbatim snippet.
- docs/CONSUMER-INTEGRATION.md §3 stream layout table: added CENTRAL_AVY row.
- docs/CONSUMER-INTEGRATION.md §6: new '### avalanche_org' subsection with
  source, subject convention, dedup key, severity gate, Event.data field
  table, and off-season behavior note.
- tests/test_events_feed_frontend.py: added avalanche_org to _SAMPLE_INNER
  and _EXPECTED_SUBJECT (the events-JSON subject-derivation coverage tests).

Budget note: this PR is well over the ~400-line target -- the new-adapter
surface picked up downstream consistency tests (doc validators + frontend
sample coverage + template partials) I didn't anticipate at probe time.
Most of the overrun is the SNFAC fixture (1,135 lines pretty-printed JSON,
non-code) and the adapter + tests pair. Stripping the fixture and the
required doc/template edits would leave ~620 lines of code; the fixture
itself is a frozen snapshot, not a maintenance burden.

Full sweep: 1072 passed, 0 failures (+41 from this PR), ruff clean on
all new files. One PRE-EXISTING ruff violation in supervisor.py (unused
poll_start variable at line 388) surfaces when we touch supervisor.py;
confirmed not introduced by this PR via git stash check.

Deploy plan (NEW STREAM — archive restart required per
[[feedback_new_stream_needs_archive_restart]]):
1. Squash-merge -> tag v0.10.10 -> push.
2. On central: pull main -> systemctl restart central-supervisor -> ALSO
   systemctl restart central-archive (new event-bearing stream; archive
   enumerates consumers at startup and doesn't hot-reload).
3. Migration 035 deferred to morning per the schema_migrations cleanup
   task -- the stream creation itself doesn't depend on it (supervisor
   creates JetStream streams from the STREAMS registry at startup; the
   config.streams row is for operator-tunable retention only).
4. Verify: nats stream info CENTRAL_AVY (created), poll log shows
   yielded=0 / omitted=N (off-season), no positive publishes during
   summer (correct).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-08 21:57:56 -06:00

1135 lines
38 KiB
JSON

{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": 2907,
"properties": {
"name": "Banner Summit",
"center": "Sawtooth Avalanche Center",
"center_link": "https://www.sawtoothavalanche.com/",
"timezone": "America/Denver",
"center_id": "SNFAC",
"state": "ID",
"off_season": true,
"travel_advice": "Watch for signs of unstable snow such as recent avalanches, cracking in the snow, and audible collapsing. Avoid traveling on or under similar slopes.",
"danger": "no rating",
"danger_level": -1,
"color": "#888888",
"stroke": "#104efb",
"font_color": "#ffffff",
"link": "https://www.sawtoothavalanche.com/forecasts/avalanche/banner-summit",
"start_date": "2026-05-04T17:59:00",
"end_date": "2026-11-01T19:00:00",
"fillOpacity": 0.5,
"fillIncrement": 0.1,
"warning": {
"product": null
}
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-115.138874,
44.2614528
],
[
-115.1617415,
44.257419
],
[
-115.1716265,
44.2831339
],
[
-115.1947313,
44.301009
],
[
-115.2311048,
44.3055083
],
[
-115.2457084,
44.2975165
],
[
-115.2833539,
44.2947103
],
[
-115.3741316,
44.2984942
],
[
-115.4061015,
44.3011989
],
[
-115.4324143,
44.3073084
],
[
-115.446103,
44.3212728
],
[
-115.4613095,
44.3519552
],
[
-115.4727717,
44.4173564
],
[
-115.4600931,
44.493862
],
[
-115.4429455,
44.5110415
],
[
-115.4175206,
44.5179614
],
[
-115.3018712,
44.5270106
],
[
-115.2837776,
44.5246207
],
[
-115.2448394,
44.5094724
],
[
-115.2315541,
44.4991822
],
[
-115.2185013,
44.4958211
],
[
-115.1882942,
44.4964549
],
[
-115.167121,
44.5009287
],
[
-115.1178545,
44.4943391
],
[
-115.081012,
44.4933686
],
[
-115.0542813,
44.4851446
],
[
-115.0104882,
44.4901515
],
[
-114.9729699,
44.5035704
],
[
-114.9647735,
44.5007211
],
[
-114.9454779,
44.4804398
],
[
-114.9416968,
44.4613191
],
[
-114.9299942,
44.4448358
],
[
-114.9047814,
44.4313507
],
[
-114.9044588,
44.4205692
],
[
-114.8979217,
44.4078909
],
[
-114.978435,
44.3985633
],
[
-115.0232961,
44.3801264
],
[
-115.0855262,
44.2994481
],
[
-115.1194948,
44.2703503
],
[
-115.138874,
44.2614528
]
]
]
}
},
{
"type": "Feature",
"id": 2904,
"properties": {
"name": "Galena Summit & Eastern Mtns",
"center": "Sawtooth Avalanche Center",
"center_link": "https://www.sawtoothavalanche.com/",
"timezone": "America/Denver",
"center_id": "SNFAC",
"state": "ID",
"off_season": true,
"travel_advice": "Watch for signs of unstable snow such as recent avalanches, cracking in the snow, and audible collapsing. Avoid traveling on or under similar slopes.",
"danger": "no rating",
"danger_level": -1,
"color": "#888888",
"stroke": "#104efb",
"font_color": "#ffffff",
"link": "https://www.sawtoothavalanche.com/forecasts/avalanche/galena-summit-&-eastern-mtns",
"start_date": "2026-05-04T17:59:00",
"end_date": "2026-11-01T19:00:00",
"fillOpacity": 0.5,
"fillIncrement": 0.1,
"warning": {
"product": null
}
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-114.8446175,
44.2608996
],
[
-114.8111421,
44.2605064
],
[
-114.7552782,
44.2332183
],
[
-114.6983948,
44.2200041
],
[
-114.649504,
44.1995976
],
[
-114.6387335,
44.1729502
],
[
-114.5966802,
44.1296575
],
[
-114.5932842,
44.1188533
],
[
-114.5728534,
44.093356
],
[
-114.5690375,
44.0808182
],
[
-114.5794577,
44.0347533
],
[
-114.579774,
44.0112541
],
[
-114.5747383,
43.988
],
[
-114.5537203,
43.9626577
],
[
-114.5307082,
43.9532599
],
[
-114.4469656,
43.9465611
],
[
-114.3875694,
43.9335669
],
[
-114.34314,
43.9094639
],
[
-114.3092234,
43.8797561
],
[
-114.2604248,
43.8504177
],
[
-114.1809214,
43.8321735
],
[
-114.1046775,
43.8067929
],
[
-114.0791159,
43.8010874
],
[
-114.0559845,
43.79984
],
[
-114.0167393,
43.8148133
],
[
-113.9919154,
43.8206233
],
[
-113.9787403,
43.8147219
],
[
-113.9727411,
43.8035239
],
[
-113.9802692,
43.7934712
],
[
-113.9883941,
43.7704676
],
[
-113.9709411,
43.714719
],
[
-113.9719464,
43.6878206
],
[
-113.9654234,
43.6628032
],
[
-113.9664323,
43.6525663
],
[
-113.975844,
43.6481947
],
[
-114.1018316,
43.6567446
],
[
-114.1908254,
43.6767942
],
[
-114.2009513,
43.6805548
],
[
-114.2335804,
43.7063231
],
[
-114.2644999,
43.7204015
],
[
-114.2727186,
43.7335808
],
[
-114.2931726,
43.7522993
],
[
-114.366615,
43.7975177
],
[
-114.3818938,
43.8064857
],
[
-114.4111343,
43.8090447
],
[
-114.4351157,
43.7991251
],
[
-114.4787859,
43.7884726
],
[
-114.505536,
43.7714938
],
[
-114.5557865,
43.7223697
],
[
-114.5846493,
43.686673
],
[
-114.6076998,
43.6472265
],
[
-114.6291343,
43.6192913
],
[
-114.6616927,
43.6160661
],
[
-114.6802863,
43.618286
],
[
-114.6724519,
43.6492057
],
[
-114.6769643,
43.6558163
],
[
-114.7052024,
43.6775203
],
[
-114.7245645,
43.7006522
],
[
-114.7289481,
43.7196946
],
[
-114.7348271,
43.7867413
],
[
-114.7530438,
43.8271988
],
[
-114.7714002,
43.8870404
],
[
-114.7822398,
43.9045177
],
[
-114.7994125,
43.9523218
],
[
-114.8300567,
44.0204178
],
[
-114.837045,
44.0552317
],
[
-114.8849117,
44.1634596
],
[
-114.9176879,
44.1745117
],
[
-114.9308308,
44.194099
],
[
-114.9306446,
44.2134896
],
[
-114.9223221,
44.2301544
],
[
-114.8966445,
44.2426661
],
[
-114.8700231,
44.2488551
],
[
-114.8446175,
44.2608996
]
]
]
}
},
{
"type": "Feature",
"id": 2906,
"properties": {
"name": "Sawtooth & Western Smoky Mtns",
"center": "Sawtooth Avalanche Center",
"center_link": "https://www.sawtoothavalanche.com/",
"timezone": "America/Denver",
"center_id": "SNFAC",
"state": "ID",
"off_season": true,
"travel_advice": "Watch for signs of unstable snow such as recent avalanches, cracking in the snow, and audible collapsing. Avoid traveling on or under similar slopes.",
"danger": "no rating",
"danger_level": -1,
"color": "#888888",
"stroke": "#104efb",
"font_color": "#ffffff",
"link": "https://www.sawtoothavalanche.com/forecasts/avalanche/sawtooth-&-western-smoky-mtns",
"start_date": "2026-05-04T17:59:00",
"end_date": "2026-11-01T19:00:00",
"fillOpacity": 0.5,
"fillIncrement": 0.1,
"warning": {
"product": null
}
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-115.1759322,
44.1409253
],
[
-115.1798793,
44.1689253
],
[
-115.1638028,
44.2085545
],
[
-115.1631598,
44.2150458
],
[
-115.1691407,
44.2311114
],
[
-115.1617415,
44.257419
],
[
-115.1479193,
44.2580703
],
[
-115.1249402,
44.2672598
],
[
-115.0855262,
44.2994481
],
[
-115.0232961,
44.3801264
],
[
-114.978435,
44.3985633
],
[
-114.9014371,
44.4059358
],
[
-114.8932312,
44.4031277
],
[
-114.8784142,
44.3720699
],
[
-114.8512993,
44.3569823
],
[
-114.8406993,
44.3472438
],
[
-114.8412851,
44.3371303
],
[
-114.8322258,
44.3048933
],
[
-114.8292771,
44.2776243
],
[
-114.820212,
44.2658736
],
[
-114.8214223,
44.26175
],
[
-114.8446175,
44.2608996
],
[
-114.8700231,
44.2488551
],
[
-114.8966445,
44.2426661
],
[
-114.9223221,
44.2301544
],
[
-114.9306446,
44.2134896
],
[
-114.9308308,
44.194099
],
[
-114.9176879,
44.1745117
],
[
-114.8849117,
44.1634596
],
[
-114.837045,
44.0552317
],
[
-114.8300567,
44.0204178
],
[
-114.7994125,
43.9523218
],
[
-114.7822398,
43.9045177
],
[
-114.7714002,
43.8870404
],
[
-114.7530438,
43.8271988
],
[
-114.7348271,
43.7867413
],
[
-114.7289481,
43.7196946
],
[
-114.7245645,
43.7006522
],
[
-114.7052024,
43.6775203
],
[
-114.6769643,
43.6558163
],
[
-114.6724519,
43.6492057
],
[
-114.6802863,
43.618286
],
[
-114.7370655,
43.6173784
],
[
-114.7954699,
43.6045019
],
[
-114.859198,
43.6084087
],
[
-114.9046727,
43.6068248
],
[
-114.9449101,
43.6018537
],
[
-114.9814795,
43.5879138
],
[
-115.0271527,
43.5889398
],
[
-115.0504963,
43.5989683
],
[
-115.0771391,
43.604192
],
[
-115.1128025,
43.6038817
],
[
-115.1151015,
43.6390263
],
[
-115.1279352,
43.7002433
],
[
-115.1221168,
43.7697045
],
[
-115.125942,
43.8031331
],
[
-115.1501328,
43.8477941
],
[
-115.1609606,
43.8815762
],
[
-115.1692619,
43.9235166
],
[
-115.1778328,
43.9387028
],
[
-115.1967583,
43.9596468
],
[
-115.2012668,
43.9713154
],
[
-115.2020673,
43.9893653
],
[
-115.1884887,
44.0391981
],
[
-115.1844632,
44.1033017
],
[
-115.1759322,
44.1409253
]
]
]
}
},
{
"type": "Feature",
"id": 2905,
"properties": {
"name": "Soldier & Wood River Valley Mtns",
"center": "Sawtooth Avalanche Center",
"center_link": "https://www.sawtoothavalanche.com/",
"timezone": "America/Denver",
"center_id": "SNFAC",
"state": "ID",
"off_season": true,
"travel_advice": "Watch for signs of unstable snow such as recent avalanches, cracking in the snow, and audible collapsing. Avoid traveling on or under similar slopes.",
"danger": "no rating",
"danger_level": -1,
"color": "#888888",
"stroke": "#104efb",
"font_color": "#ffffff",
"link": "https://www.sawtoothavalanche.com/forecasts/avalanche/soldier-&-wood-river-valley-mtns",
"start_date": "2026-05-04T17:59:00",
"end_date": "2026-11-01T19:00:00",
"fillOpacity": 0.5,
"fillIncrement": 0.1,
"warning": {
"product": null
}
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-115.1128025,
43.6038817
],
[
-115.0771391,
43.604192
],
[
-115.0504963,
43.5989683
],
[
-115.0271527,
43.5889398
],
[
-114.9814795,
43.5879138
],
[
-114.9449101,
43.6018537
],
[
-114.9046727,
43.6068248
],
[
-114.859198,
43.6084087
],
[
-114.7954699,
43.6045019
],
[
-114.7370655,
43.6173784
],
[
-114.6616927,
43.6160661
],
[
-114.6291343,
43.6192913
],
[
-114.6076998,
43.6472265
],
[
-114.5846493,
43.686673
],
[
-114.5557865,
43.7223697
],
[
-114.505536,
43.7714938
],
[
-114.4787859,
43.7884726
],
[
-114.4351157,
43.7991251
],
[
-114.4111343,
43.8090447
],
[
-114.3818938,
43.8064857
],
[
-114.2931726,
43.7522993
],
[
-114.2727186,
43.7335808
],
[
-114.2644999,
43.7204015
],
[
-114.2335804,
43.7063231
],
[
-114.2009513,
43.6805548
],
[
-114.1170674,
43.6588235
],
[
-113.975844,
43.6481947
],
[
-113.961114,
43.6406346
],
[
-113.9496878,
43.6211143
],
[
-113.9371012,
43.5843687
],
[
-113.9420904,
43.5652506
],
[
-113.9807011,
43.5143163
],
[
-113.9736104,
43.4641059
],
[
-113.952297,
43.4324763
],
[
-113.9112702,
43.3912372
],
[
-113.9100808,
43.3669776
],
[
-113.9235922,
43.3290544
],
[
-113.9426459,
43.3074879
],
[
-114.0050062,
43.2953495
],
[
-114.0285364,
43.3000146
],
[
-114.0627546,
43.313214
],
[
-114.1792198,
43.3642322
],
[
-114.2103143,
43.3866
],
[
-114.2389029,
43.3884312
],
[
-114.2695614,
43.3714135
],
[
-114.3130839,
43.3626042
],
[
-114.3709392,
43.3602081
],
[
-114.4407513,
43.3500491
],
[
-114.4786831,
43.3480787
],
[
-114.5128729,
43.3630498
],
[
-114.5821792,
43.3712775
],
[
-114.6336501,
43.3817452
],
[
-114.7047,
43.3884593
],
[
-114.7230237,
43.3964327
],
[
-114.7886945,
43.4009317
],
[
-114.8412706,
43.3794947
],
[
-114.8974125,
43.3750631
],
[
-114.9234448,
43.3650553
],
[
-114.9569957,
43.3580404
],
[
-114.9839125,
43.3573755
],
[
-115.0343696,
43.3492485
],
[
-115.0590769,
43.353868
],
[
-115.0926835,
43.4033243
],
[
-115.1009177,
43.4397244
],
[
-115.1042305,
43.4709534
],
[
-115.1003414,
43.5035081
],
[
-115.1117719,
43.5590744
],
[
-115.1128025,
43.6038817
]
]
]
}
}
],
"start_time": null,
"end_time": null
}