feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
import { useEffect , useState , type ReactNode } from 'react'
2026-05-12 14:59:54 -06:00
import {
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
Cloud , Flame , Radio , Car , Mountain , Satellite , Activity ,
Save , RotateCcw , RefreshCw , AlertCircle , AlertTriangle , Info ,
2026-05-12 14:59:54 -06:00
} from 'lucide-react'
import {
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
Toggle , TextInput , NumberInput , SelectInput , ListInput , NumberListInput ,
US_STATES ,
} from './Config'
import {
fetchEnvStatus , fetchEnvActive ,
type EnvStatus , type EnvEvent ,
2026-05-12 14:59:54 -06:00
} from '@/lib/api'
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
type FeedSource = 'native' | 'central'
interface EnvConfig {
enabled : boolean
nws_zones : string [ ]
nws : { enabled : boolean ; user_agent : string ; tick_seconds : number ; severity_min : string ; feed_source? : FeedSource }
swpc : { enabled : boolean ; feed_source? : FeedSource }
ducting : { enabled : boolean ; tick_seconds : number ; latitude : number ; longitude : number ; feed_source? : FeedSource }
fires : { enabled : boolean ; tick_seconds : number ; state : string ; feed_source? : FeedSource }
avalanche : { enabled : boolean ; tick_seconds : number ; center_ids : string [ ] ; season_months : number [ ] ; feed_source? : FeedSource }
usgs : { enabled : boolean ; tick_seconds : number ; sites : string [ ] ; feed_source? : FeedSource }
usgs_quake : { enabled : boolean ; tick_seconds : number ; feed_url : string ; min_magnitude : number ; bbox : number [ ] ; region : string ; feed_source? : FeedSource }
traffic : { enabled : boolean ; tick_seconds : number ; api_key : string ; corridors : { name : string ; lat : number ; lon : number } [ ] ; feed_source? : FeedSource }
roads511 : { enabled : boolean ; tick_seconds : number ; api_key : string ; base_url : string ; endpoints : string [ ] ; bbox : number [ ] ; feed_source? : FeedSource }
firms : { enabled : boolean ; tick_seconds : number ; map_key : string ; source : string ; bbox : number [ ] ; day_range : number ; confidence_min : string ; proximity_km : number ; feed_source? : FeedSource }
central ? : { enabled : boolean ; url : string ; durable : string }
}
2026-05-12 14:59:54 -06:00
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
type FeedHealth = EnvStatus [ 'feeds' ] [ number ]
2026-05-12 14:59:54 -06:00
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
// ---------------------------------------------------------------- status cards
function FeedStatusCard ( { feed } : { feed : FeedHealth } ) {
const color = ! feed . is_loaded ? 'bg-red-500' : feed . consecutive_errors > 0 ? 'bg-amber-500' : 'bg-green-500'
const text = ! feed . is_loaded ? 'Not loaded' : feed . consecutive_errors > 0 ? ` ${ feed . consecutive_errors } errors ` : 'Healthy'
const lastFetch = feed . last_fetch ? new Date ( feed . last_fetch * 1000 ) . toLocaleTimeString ( ) : 'Never'
2026-05-12 14:59:54 -06:00
return (
< div className = "bg-bg-hover rounded-lg p-4" >
< div className = "flex items-center justify-between mb-2" >
< div className = "flex items-center gap-2" >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = { ` w-2 h-2 rounded-full ${ color } ` } / >
< span className = "text-sm font-medium text-slate-200 uppercase" > { feed . source } < / span >
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< span className = "text-xs text-slate-400" > { text } < / span >
2026-05-12 14:59:54 -06:00
< / div >
< div className = "text-xs text-slate-500 space-y-1" >
< div > Events : { feed . event_count } < / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div > Last fetch : { lastFetch } < / div >
{ feed . last_error && < div className = "text-amber-500 truncate" > { feed . last_error } < / div > }
2026-05-12 14:59:54 -06:00
< / div >
< / div >
)
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
function EventCard ( { event } : { event : EnvEvent } ) {
const sev = event . severity . toLowerCase ( )
const styles = ( sev === 'extreme' || sev === 'severe' || sev === 'immediate' )
? { bg : 'bg-red-500/10' , border : 'border-red-500' , Icon : AlertCircle , color : 'text-red-500' }
: ( sev === 'moderate' || sev === 'warning' || sev === 'priority' )
? { bg : 'bg-amber-500/10' , border : 'border-amber-500' , Icon : AlertTriangle , color : 'text-amber-500' }
: { bg : 'bg-blue-500/10' , border : 'border-blue-500' , Icon : Info , color : 'text-blue-500' }
const Icon = styles . Icon
2026-05-12 14:59:54 -06:00
return (
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = { ` p-3 rounded-lg ${ styles . bg } border-l-2 ${ styles . border } ` } >
2026-05-12 14:59:54 -06:00
< div className = "flex items-start gap-3" >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< Icon size = { 16 } className = { styles . color } / >
2026-05-12 14:59:54 -06:00
< div className = "flex-1 min-w-0" >
< div className = "flex items-center gap-2 mb-1" >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< span className = "text-sm font-medium text-slate-200" > { event . event_type } < / span >
< span className = { ` text-xs px-1.5 py-0.5 rounded ${ styles . bg } ${ styles . color } ` } > { event . severity } < / span >
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = "text-sm text-slate-300" > { event . headline } < / div >
2026-05-12 14:59:54 -06:00
< / div >
< / div >
< / div >
)
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
// ---------------------------------------------------------------- feed_source toggle
function FeedSourceToggle ( { value , onChange , disabled , centralDisabled } : {
value : FeedSource ; onChange : ( v : FeedSource ) = > void ; disabled : boolean ; centralDisabled : boolean
} ) {
const base = 'px-2 py-1 text-xs transition-colors'
2026-05-12 14:59:54 -06:00
return (
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = { ` flex rounded border border-[#1e2a3a] overflow-hidden ${ disabled ? 'opacity-40' : '' } ` } >
< button
type = "button"
disabled = { disabled }
onClick = { ( ) = > onChange ( 'native' ) }
className = { ` ${ base } ${ value === 'native' ? 'bg-accent text-white' : 'text-slate-400 hover:text-slate-200' } ` }
> native < / button >
< button
type = "button"
disabled = { disabled || centralDisabled }
title = { centralDisabled ? 'Central not available for this adapter' : '' }
onClick = { ( ) = > { if ( ! centralDisabled ) onChange ( 'central' ) } }
className = { ` ${ base } ${ centralDisabled ? 'text-slate-600 cursor-not-allowed' : value === 'central' ? 'bg-accent text-white' : 'text-slate-400 hover:text-slate-200' } ` }
> central < / button >
2026-05-12 14:59:54 -06:00
< / div >
)
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
// ---------------------------------------------------------------- adapter panel
function AdapterPanel ( { title , subtitle , enabled , onEnabled , feedSource , onFeedSource , hasCentral , nativeOnly , hasKey , health , events , children } : {
title : string ; subtitle? : string
enabled : boolean ; onEnabled : ( v : boolean ) = > void
feedSource : FeedSource ; onFeedSource : ( v : FeedSource ) = > void
hasCentral : boolean ; nativeOnly : boolean ; hasKey : boolean
health? : FeedHealth ; events? : EnvEvent [ ] ; children? : ReactNode
} ) {
const centralDisabled = nativeOnly || ! hasCentral
2026-05-12 14:59:54 -06:00
return (
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = "border border-[#1e2a3a] rounded-lg p-4 space-y-3" >
< div className = "flex items-center justify-between" >
< div >
< span className = "text-sm font-medium text-slate-300" > { title } < / span >
{ subtitle && < p className = "text-xs text-slate-600" > { subtitle } < / p > }
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = "flex items-center gap-4" >
< div className = "flex items-center gap-1" >
< span className = "text-[10px] uppercase tracking-wide text-slate-600" > source < / span >
< FeedSourceToggle value = { feedSource } onChange = { onFeedSource } disabled = { ! enabled } centralDisabled = { centralDisabled } / >
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< Toggle label = "" checked = { enabled } onChange = { onEnabled } / >
2026-05-12 14:59:54 -06:00
< / div >
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{ ! hasKey && (
< div className = "text-xs text-amber-400 bg-amber-500/10 rounded p-2" >
API key not configured — contact admin
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
) }
{ nativeOnly && (
< div className = "text-[11px] text-slate-600" > Central not available for this adapter — native only < / div >
) }
< div className = { enabled ? 'space-y-3' : 'space-y-3 opacity-40 pointer-events-none select-none' } >
{ children }
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{ ( health || ( events && events . length > 0 ) ) && (
< div className = "pt-2 border-t border-[#1e2a3a] space-y-3" >
< div className = "text-[10px] uppercase tracking-wide text-slate-600" > Live status < / div >
{ health ? < FeedStatusCard feed = { health } / > : < div className = "text-xs text-slate-600" > No status reported . < / div > }
{ events && events . length > 0 && (
< div className = "space-y-2" >
{ events . slice ( 0 , 5 ) . map ( ( e , i ) = > < EventCard key = { i } event = { e } / > ) }
< / div >
) }
2026-05-12 14:59:54 -06:00
< / div >
) }
< / div >
)
}
2026-05-12 10:28:12 -06:00
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
// ---------------------------------------------------------------- families
type AdapterKey = 'nws' | 'fires' | 'firms' | 'swpc' | 'ducting' | 'traffic' | 'roads511' | 'usgs_quake' | 'usgs' | 'avalanche'
interface AdapterMeta { label : string ; subtitle : string ; health : string ; hasCentral : boolean ; nativeOnly : boolean ; hasKey : boolean }
const META : Record < AdapterKey , AdapterMeta > = {
nws : { label : 'NWS Weather Alerts' , subtitle : 'National Weather Service alerts' , health : 'nws' , hasCentral : true , nativeOnly : false , hasKey : true } ,
fires : { label : 'NIFC Fire Perimeters' , subtitle : 'Active wildfires (National Interagency Fire Center)' , health : 'nifc' , hasCentral : true , nativeOnly : false , hasKey : true } ,
firms : { label : 'NASA FIRMS Hotspots' , subtitle : 'Satellite thermal-anomaly detections' , health : 'firms' , hasCentral : true , nativeOnly : false , hasKey : false } ,
swpc : { label : 'NOAA Space Weather (SWPC)' , subtitle : 'Solar indices, geomagnetic storms' , health : 'swpc' , hasCentral : true , nativeOnly : false , hasKey : true } ,
ducting : { label : 'Tropospheric Ducting' , subtitle : 'VHF/UHF extended-range conditions' , health : 'ducting' , hasCentral : false , nativeOnly : true , hasKey : true } ,
traffic : { label : 'TomTom Traffic' , subtitle : 'Traffic flow on monitored corridors' , health : 'traffic' , hasCentral : true , nativeOnly : false , hasKey : true } ,
roads511 : { label : '511 Road Conditions' , subtitle : 'State DOT road events and closures' , health : 'roads511' , hasCentral : false , nativeOnly : true , hasKey : false } ,
usgs_quake : { label : 'USGS Earthquakes' , subtitle : 'Seismic events from the USGS feed' , health : 'usgs_quake' , hasCentral : true , nativeOnly : false , hasKey : true } ,
usgs : { label : 'USGS Stream Gauges' , subtitle : 'River and stream water levels' , health : 'usgs' , hasCentral : true , nativeOnly : false , hasKey : true } ,
avalanche : { label : 'Avalanche Advisories' , subtitle : 'Backcountry avalanche danger ratings' , health : 'avalanche' , hasCentral : false , nativeOnly : true , hasKey : true } ,
}
const FAMILIES : { key : string ; label : string ; icon : typeof Cloud ; adapters : AdapterKey [ ] } [ ] = [
{ key : 'weather' , label : 'Weather' , icon : Cloud , adapters : [ 'nws' ] } ,
{ key : 'fire' , label : 'Fire' , icon : Flame , adapters : [ 'fires' , 'firms' ] } ,
{ key : 'rf' , label : 'RF Propagation' , icon : Radio , adapters : [ 'swpc' , 'ducting' ] } ,
{ key : 'roads' , label : 'Roads' , icon : Car , adapters : [ 'traffic' , 'roads511' ] } ,
{ key : 'geohazards' , label : 'Geohazards' , icon : Mountain , adapters : [ 'usgs_quake' , 'usgs' , 'avalanche' ] } ,
{ key : 'tracking' , label : 'Tracking' , icon : Satellite , adapters : [ ] } ,
{ key : 'mesh' , label : 'Mesh Health' , icon : Activity , adapters : [ ] } ,
]
// ---------------------------------------------------------------- main page
2026-05-12 10:28:12 -06:00
export default function Environment() {
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const [ env , setEnv ] = useState < EnvConfig | null > ( null )
const [ original , setOriginal ] = useState < string > ( '' )
const [ status , setStatus ] = useState < EnvStatus | null > ( null )
2026-05-12 14:59:54 -06:00
const [ events , setEvents ] = useState < EnvEvent [ ] > ( [ ] )
const [ loading , setLoading ] = useState ( true )
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const [ saving , setSaving ] = useState ( false )
2026-05-12 14:59:54 -06:00
const [ error , setError ] = useState < string | null > ( null )
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const [ success , setSuccess ] = useState < string | null > ( null )
const [ restartRequired , setRestartRequired ] = useState ( false )
const [ family , setFamily ] = useState ( 'weather' )
const [ adapter , setAdapter ] = useState < AdapterKey | null > ( 'nws' )
2026-05-12 14:59:54 -06:00
useEffect ( ( ) = > {
2026-05-13 01:36:31 +00:00
document . title = 'Environment — MeshAI'
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
; ( async ( ) = > {
try {
const res = await fetch ( '/api/config/environmental' )
const data = await res . json ( )
setEnv ( data )
setOriginal ( JSON . stringify ( data ) )
} catch ( e ) {
setError ( e instanceof Error ? e . message : 'Failed to load config' )
} finally {
2026-05-12 14:59:54 -06:00
setLoading ( false )
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
}
} ) ( )
2026-05-12 14:59:54 -06:00
} , [ ] )
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
useEffect ( ( ) = > {
const load = async ( ) = > {
try {
setStatus ( await fetchEnvStatus ( ) )
setEvents ( await fetchEnvActive ( ) )
} catch { /* status is best-effort */ }
}
load ( )
const t = setInterval ( load , 30000 )
return ( ) = > clearInterval ( t )
} , [ ] )
const hasChanges = env !== null && JSON . stringify ( env ) !== original
const save = async ( ) = > {
if ( ! env ) return
setSaving ( true ) ; setError ( null ) ; setSuccess ( null )
try {
const res = await fetch ( '/api/config/environmental' , {
method : 'PUT' ,
headers : { 'Content-Type' : 'application/json' } ,
body : JSON.stringify ( env ) ,
} )
const result = await res . json ( )
if ( ! res . ok ) throw new Error ( result . detail || 'Save failed' )
setOriginal ( JSON . stringify ( env ) )
setSuccess ( 'Environmental config saved' )
if ( result . restart_required ) setRestartRequired ( true )
setTimeout ( ( ) = > setSuccess ( null ) , 3000 )
} catch ( e ) {
setError ( e instanceof Error ? e . message : 'Save failed' )
} finally {
setSaving ( false )
}
2026-05-12 14:59:54 -06:00
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const discard = ( ) = > { if ( env ) setEnv ( JSON . parse ( original ) ) }
const restart = async ( ) = > {
try { await fetch ( '/api/restart' , { method : 'POST' } ) ; setRestartRequired ( false ) ; setSuccess ( 'Restart initiated' ) }
catch { setError ( 'Restart failed' ) }
2026-05-12 14:59:54 -06:00
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const up = ( patch : Partial < EnvConfig > ) = > env && setEnv ( { . . . env , . . . patch } )
if ( loading ) return < div className = "flex items-center justify-center h-64 text-slate-400" > Loading environmental config … < / div >
if ( ! env ) return < div className = "flex items-center justify-center h-64 text-red-400" > { error || 'No config' } < / div >
const healthFor = ( key : AdapterKey ) : FeedHealth | undefined = >
status ? . feeds . find ( ( f ) = > f . source === META [ key ] . health )
const eventsFor = ( key : AdapterKey ) : EnvEvent [ ] = >
events . filter ( ( e ) = > e . source === META [ key ] . health )
const fam = FAMILIES . find ( ( f ) = > f . key === family ) !
const activeAdapter : AdapterKey | null =
fam . adapters . length === 0 ? null : ( adapter && fam . adapters . includes ( adapter ) ? adapter : fam.adapters [ 0 ] )
// -- per-adapter settings forms (preserve all existing settings) --
const renderSettings = ( key : AdapterKey ) = > {
switch ( key ) {
case 'nws' : return ( < >
< ListInput label = "NWS Zones" value = { env . nws_zones } onChange = { ( v ) = > up ( { nws_zones : v } ) } helper = "Zone IDs like IDZ016, IDZ030" infoLink = "https://www.weather.gov/pimar/PubZone" / >
< TextInput label = "User Agent" value = { env . nws . user_agent } onChange = { ( v ) = > up ( { nws : { . . . env . nws , user_agent : v } } ) } placeholder = "(MeshAI, you@email.com)" helper = "Format: (app_name, contact_email)" / >
< div className = "grid grid-cols-2 gap-4" >
< NumberInput label = "Tick Seconds" value = { env . nws . tick_seconds } onChange = { ( v ) = > up ( { nws : { . . . env . nws , tick_seconds : v } } ) } min = { 30 } / >
< SelectInput label = "Min Severity" value = { env . nws . severity_min } onChange = { ( v ) = > up ( { nws : { . . . env . nws , severity_min : v } } ) } options = { [ { value : 'minor' , label : 'Minor' } , { value : 'moderate' , label : 'Moderate' } , { value : 'severe' , label : 'Severe' } , { value : 'extreme' , label : 'Extreme' } ] } / >
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< / > )
case 'swpc' : return < div className = "text-xs text-slate-500" > No additional settings . < / div >
case 'ducting' : return (
< div className = "grid grid-cols-3 gap-4" >
< NumberInput label = "Tick Seconds" value = { env . ducting . tick_seconds } onChange = { ( v ) = > up ( { ducting : { . . . env . ducting , tick_seconds : v } } ) } min = { 60 } / >
< NumberInput label = "Latitude" value = { env . ducting . latitude } onChange = { ( v ) = > up ( { ducting : { . . . env . ducting , latitude : v } } ) } step = { 0.01 } / >
< NumberInput label = "Longitude" value = { env . ducting . longitude } onChange = { ( v ) = > up ( { ducting : { . . . env . ducting , longitude : v } } ) } step = { 0.01 } / >
< / div > )
case 'fires' : return (
< div className = "grid grid-cols-2 gap-4" >
< NumberInput label = "Tick Seconds" value = { env . fires . tick_seconds } onChange = { ( v ) = > up ( { fires : { . . . env . fires , tick_seconds : v } } ) } min = { 60 } / >
< SelectInput label = "State" value = { env . fires . state } onChange = { ( v ) = > up ( { fires : { . . . env . fires , state : v } } ) } options = { US_STATES } / >
< / div > )
case 'avalanche' : return ( < >
< NumberInput label = "Tick Seconds" value = { env . avalanche . tick_seconds } onChange = { ( v ) = > up ( { avalanche : { . . . env . avalanche , tick_seconds : v } } ) } min = { 60 } / >
< ListInput label = "Center IDs" value = { env . avalanche . center_ids } onChange = { ( v ) = > up ( { avalanche : { . . . env . avalanche , center_ids : v } } ) } helper = "e.g., SNFAC" infoLink = "https://avalanche.org/avalanche-centers/" / >
< NumberListInput label = "Season Months" value = { env . avalanche . season_months } onChange = { ( v ) = > up ( { avalanche : { . . . env . avalanche , season_months : v } } ) } helper = "e.g., 12, 1, 2, 3, 4" / >
< / > )
case 'usgs' : return ( < >
< NumberInput label = "Tick Seconds" value = { env . usgs . tick_seconds } onChange = { ( v ) = > up ( { usgs : { . . . env . usgs , tick_seconds : v } } ) } min = { 900 } helper = "Minimum 15 min (900s)" / >
< ListInput label = "Site IDs" value = { env . usgs . sites } onChange = { ( v ) = > up ( { usgs : { . . . env . usgs , sites : v } } ) } helper = "USGS gauge site numbers" infoLink = "https://waterdata.usgs.gov/nwis" / >
< / > )
case 'usgs_quake' : return ( < >
< NumberInput label = "Tick Seconds" value = { env . usgs_quake . tick_seconds } onChange = { ( v ) = > up ( { usgs_quake : { . . . env . usgs_quake , tick_seconds : v } } ) } min = { 60 } / >
< NumberInput label = "Min Magnitude" value = { env . usgs_quake . min_magnitude } onChange = { ( v ) = > up ( { usgs_quake : { . . . env . usgs_quake , min_magnitude : v } } ) } step = { 0.1 } min = { 0 } / >
< TextInput label = "Region Tag" value = { env . usgs_quake . region } onChange = { ( v ) = > up ( { usgs_quake : { . . . env . usgs_quake , region : v } } ) } / >
< div className = "grid grid-cols-4 gap-2" >
{ ( [ 'West' , 'South' , 'East' , 'North' ] as const ) . map ( ( lbl , i ) = > (
< NumberInput key = { lbl } label = { lbl } value = { env . usgs_quake . bbox ? . [ i ] ? ? 0 } onChange = { ( v ) = > { const b = [ . . . ( env . usgs_quake . bbox || [ 0 , 0 , 0 , 0 ] ) ] ; b [ i ] = v ; up ( { usgs_quake : { . . . env . usgs_quake , bbox : b } } ) } } step = { 0.01 } / >
) ) }
< / div >
< div className = "text-xs text-slate-500" > Bounding box [ W , S , E , N ] geographic filter < / div >
< / > )
case 'traffic' : return ( < >
< TextInput label = "API Key" value = { env . traffic . api_key } onChange = { ( v ) = > up ( { traffic : { . . . env . traffic , api_key : v } } ) } type = "password" helper = "developer.tomtom.com" / >
< NumberInput label = "Tick Seconds" value = { env . traffic . tick_seconds } onChange = { ( v ) = > up ( { traffic : { . . . env . traffic , tick_seconds : v } } ) } min = { 60 } / >
< div className = "text-xs text-slate-500 mt-2" > Corridors : < / div >
{ ( env . traffic . corridors || [ ] ) . map ( ( c , i ) = > (
< div key = { i } className = "grid grid-cols-4 gap-2 items-end" >
< TextInput label = "Name" value = { c . name } onChange = { ( v ) = > { const n = [ . . . env . traffic . corridors ] ; n [ i ] = { . . . c , name : v } ; up ( { traffic : { . . . env . traffic , corridors : n } } ) } } / >
< NumberInput label = "Lat" value = { c . lat } onChange = { ( v ) = > { const n = [ . . . env . traffic . corridors ] ; n [ i ] = { . . . c , lat : v } ; up ( { traffic : { . . . env . traffic , corridors : n } } ) } } step = { 0.01 } / >
< NumberInput label = "Lon" value = { c . lon } onChange = { ( v ) = > { const n = [ . . . env . traffic . corridors ] ; n [ i ] = { . . . c , lon : v } ; up ( { traffic : { . . . env . traffic , corridors : n } } ) } } step = { 0.01 } / >
< button onClick = { ( ) = > up ( { traffic : { . . . env . traffic , corridors : env.traffic.corridors.filter ( ( _ , j ) = > j !== i ) } } ) } className = "px-2 py-2 text-xs text-red-400 hover:text-red-300 border border-red-400/30 rounded" > Remove < / button >
< / div >
) ) }
< button onClick = { ( ) = > up ( { traffic : { . . . env . traffic , corridors : [ . . . ( env . traffic . corridors || [ ] ) , { name : '' , lat : 0 , lon : 0 } ] } } ) } className = "text-xs text-accent hover:underline" > + Add Corridor < / button >
< / > )
case 'roads511' : return ( < >
< TextInput label = "Base URL" value = { env . roads511 . base_url } onChange = { ( v ) = > up ( { roads511 : { . . . env . roads511 , base_url : v } } ) } placeholder = "https://511.yourstate.gov/api/v2" / >
< TextInput label = "API Key" value = { env . roads511 . api_key } onChange = { ( v ) = > up ( { roads511 : { . . . env . roads511 , api_key : v } } ) } type = "password" helper = "Leave empty if not required" / >
< NumberInput label = "Tick Seconds" value = { env . roads511 . tick_seconds } onChange = { ( v ) = > up ( { roads511 : { . . . env . roads511 , tick_seconds : v } } ) } min = { 60 } / >
< ListInput label = "Endpoints" value = { env . roads511 . endpoints } onChange = { ( v ) = > up ( { roads511 : { . . . env . roads511 , endpoints : v } } ) } helper = "e.g., /get/event" / >
< div className = "grid grid-cols-4 gap-2" >
{ ( [ 'West' , 'South' , 'East' , 'North' ] as const ) . map ( ( lbl , i ) = > (
< NumberInput key = { lbl } label = { lbl } value = { env . roads511 . bbox ? . [ i ] ? ? 0 } onChange = { ( v ) = > { const b = [ . . . ( env . roads511 . bbox || [ 0 , 0 , 0 , 0 ] ) ] ; b [ i ] = v ; up ( { roads511 : { . . . env . roads511 , bbox : b } } ) } } step = { 0.01 } / >
) ) }
< / div >
< / > )
case 'firms' : return ( < >
< TextInput label = "MAP Key" value = { env . firms . map_key } onChange = { ( v ) = > up ( { firms : { . . . env . firms , map_key : v } } ) } type = "password" helper = "firms.modaps.eosdis.nasa.gov/api/area/" infoLink = "https://firms.modaps.eosdis.nasa.gov/api/area/" / >
< NumberInput label = "Tick Seconds" value = { env . firms . tick_seconds } onChange = { ( v ) = > up ( { firms : { . . . env . firms , tick_seconds : v } } ) } min = { 300 } / >
< SelectInput label = "Satellite Source" value = { env . firms . source } onChange = { ( v ) = > up ( { firms : { . . . env . firms , source : v } } ) } options = { [ { value : 'VIIRS_SNPP_NRT' , label : 'VIIRS SNPP (NRT)' } , { value : 'VIIRS_NOAA20_NRT' , label : 'VIIRS NOAA-20 (NRT)' } , { value : 'MODIS_NRT' , label : 'MODIS (NRT)' } ] } / >
< div className = "grid grid-cols-3 gap-4" >
< NumberInput label = "Day Range" value = { env . firms . day_range } onChange = { ( v ) = > up ( { firms : { . . . env . firms , day_range : v } } ) } min = { 1 } max = { 10 } / >
< SelectInput label = "Min Confidence" value = { env . firms . confidence_min } onChange = { ( v ) = > up ( { firms : { . . . env . firms , confidence_min : v } } ) } options = { [ { value : 'low' , label : 'Low' } , { value : 'nominal' , label : 'Nominal' } , { value : 'high' , label : 'High' } ] } / >
< NumberInput label = "Proximity (km)" value = { env . firms . proximity_km } onChange = { ( v ) = > up ( { firms : { . . . env . firms , proximity_km : v } } ) } step = { 0.5 } / >
< / div >
< div className = "grid grid-cols-4 gap-2" >
{ ( [ 'West' , 'South' , 'East' , 'North' ] as const ) . map ( ( lbl , i ) = > (
< NumberInput key = { lbl } label = { lbl } value = { env . firms . bbox ? . [ i ] ? ? 0 } onChange = { ( v ) = > { const b = [ . . . ( env . firms . bbox || [ 0 , 0 , 0 , 0 ] ) ] ; b [ i ] = v ; up ( { firms : { . . . env . firms , bbox : b } } ) } } step = { 0.01 } / >
) ) }
< / div >
< / > )
}
}
const a = env as unknown as Record < AdapterKey , { enabled : boolean ; feed_source ? : FeedSource } >
const setAdapterField = ( key : AdapterKey , patch : { enabled? : boolean ; feed_source? : FeedSource } ) = > {
const cur = ( env as any ) [ key ] || { }
up ( { [ key ] : { . . . cur , . . . patch } } as unknown as Partial < EnvConfig > )
2026-05-12 14:59:54 -06:00
}
2026-05-12 10:28:12 -06:00
return (
2026-05-12 14:59:54 -06:00
< div className = "space-y-6" >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{ /* Header + master enable + save bar */ }
2026-05-12 14:59:54 -06:00
< div className = "flex items-center justify-between" >
< h1 className = "text-xl font-semibold text-slate-200" > Environment < / h1 >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = "flex items-center gap-3" >
< Toggle label = "Feeds Enabled" checked = { env . enabled } onChange = { ( v ) = > up ( { enabled : v } ) } / >
{ hasChanges && (
< >
< button onClick = { discard } className = "flex items-center gap-1 px-3 py-1.5 text-sm text-slate-400 hover:text-slate-200 border border-border rounded" >
< RotateCcw size = { 14 } / > Discard
< / button >
< button onClick = { save } disabled = { saving } className = "flex items-center gap-1 px-3 py-1.5 text-sm bg-accent text-white rounded disabled:opacity-50" >
< Save size = { 14 } / > { saving ? 'Saving…' : 'Save' }
< / button >
< / >
) }
2026-05-12 14:59:54 -06:00
< / div >
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{ error && < div className = "text-sm text-red-400 bg-red-500/10 rounded p-3" > { error } < / div > }
{ success && < div className = "text-sm text-green-400 bg-green-500/10 rounded p-3" > { success } < / div > }
{ restartRequired && (
< div className = "flex items-center justify-between text-sm text-amber-400 bg-amber-500/10 border border-amber-500/30 rounded p-3" >
< span className = "flex items-center gap-2" > < RefreshCw size = { 14 } / > A restart is required for some changes to take effect . < / span >
< button onClick = { restart } className = "px-3 py-1 bg-amber-500/20 hover:bg-amber-500/30 rounded" > Restart now < / button >
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
) }
2026-05-12 14:59:54 -06:00
feat(notifications): v0.5.0 -- Master Toggles UX redesign + Central Connection GUI + grouped categories + region scoping
Per-family notification policy (PagerDuty/Grafana-style): each family gets a
severity threshold + region scope + a severity->channel routing matrix, so an
operator opts in per family rather than hand-writing rules.
SECTION 1 -- BACKEND
- config.py: new NotificationToggle dataclass (enabled, min_severity, regions,
severity_channels{severity->[channel types]}, quiet_hours_override, + per-channel
delivery config: broadcast_channel/node_ids/smtp_*/recipients/webhook_*).
notifications.toggles is now a dict[family]->NotificationToggle with 8 family
defaults (mesh_health, weather, fire, rf_propagation, roads, avalanche, seismic,
tracking), all enabled=false (opt-in), min_severity=priority,
severity_channels={priority:[mesh_broadcast], immediate:[mesh_broadcast, mesh_dm]},
quiet_hours_override=true. (Old TogglesConfig.enabled was only read by
build_pipeline via getattr -> degrades to ToggleFilter no-op, so the pipeline
filter is unchanged; toggles now drive the Dispatcher instead.)
- region_scope:list added to NotificationRuleConfig; _matching_rules filters by
event.region/regions ([] = all).
- Dispatcher: _dispatch_toggles runs IN PARALLEL to rule matching -- looks up
get_toggle(event.category), checks enabled + region scope + severity threshold,
then for each channel in severity_channels[event.severity] builds a synthetic
rule (override_quiet set only for immediate when quiet_hours_override) and
delivers. 'digest' channel is skipped in live dispatch (handled by accumulator).
- categories.py: get_toggle() prefix fallback maps the live phases-2.7-2.14
categories (weather_warning, wildfire_incident, earthquake_event,
traffic_congestion, geomagnetic/rf_*, stream_*, ...) to their family, fixing the
v0.4 "category -> other" gap.
- config_loader.py: SECRET_FIELDS += notifications.toggles.*.smtp_password.
- _dataclass_to_dict now recurses dict-of-dataclasses, and the loader coerces the
toggles dict -> NotificationToggle on both the full-load and section-PUT paths
(so GUI save round-trips correctly).
- tests/test_notification_toggles.py (11): enabled/disabled, region filter
(empty+populated+regions-list), severity threshold, per-severity channel routing,
digest-skipped-live, quiet-hours-override immediate-only, category->family,
rules+toggles both fire. Full suite: 294 passed (283 + 11).
SECTION 2 -- FRONTEND
- Notifications.tsx: MasterToggles component above the rules section -- 8 family
cards (icon + enable toggle; collapsed summary 'OFF' or 'N regions, M channels at
<sev>+'; expanded: severity threshold, severity x channel checkbox matrix,
region list, quiet-hours-override toggle, per-channel config:
broadcast_channel/DM node IDs/recipients/SMTP host+port/webhook URL).
- Environment.tsx: CentralConnectionPanel above the family tabs (url, durable,
enabled) wired to environmental.central.
- npm run build clean (tsc strict); rebuilt static committed (index-CfYlhn4e.js).
SECTION 3 -- VERIFICATION
- py_compile + tsc strict clean; pytest 294 passed.
- Rebuilt prod: /notifications serves Master Toggles, /environment serves Central
Connection (strings confirmed in the served bundle); 8 adapters, pipeline
started, no tracebacks, healthy.
- GUI round-trip: enable weather toggle (min_severity=priority,
regions=[Magic Valley], severity_channels.priority=[mesh_broadcast]) -> PUT
{saved:true} -> notifications.yaml reflects it; env_feeds traffic.api_key stayed
${TOMTOM_API_KEY} (C.3.1 secret preservation holds). Restored to clean opt-in
baseline.
- Synthetic NWS weather_warning/priority/Magic Valley -> routes through the weather
toggle to mesh_broadcast; out-of-region and below-threshold events correctly
dropped.
DEFERRED (noted for a follow-up, not blocking Matt's morning config): Section 2B
rules-editor polish -- grouped-by-family category checkboxes, region_scope
multi-select in the rule editor (backend field + filtering ARE in), tooltips, and
the fire-count Active/No-activity badge -- were not built tonight to keep the build
shippable and verified; the Advanced Rules section is otherwise unchanged and
still functional.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 07:00:10 +00:00
{ /* Central Connection (v0.5) -- NATS source for adapters set to central */ }
{ env . central && (
< div className = "border border-[#1e2a3a] rounded-lg p-4 space-y-3" >
< div className = "flex items-center justify-between" >
< div >
< span className = "text-sm font-medium text-slate-300" > Central Connection < / span >
< p className = "text-xs text-slate-600" > NATS JetStream source for any adapter set to "central" < / p >
< / div >
< Toggle label = "" checked = { ! ! env . central . enabled }
onChange = { ( v ) = > up ( { central : { . . . env . central ! , enabled : v } } ) } / >
< / div >
< div className = { env . central . enabled ? 'space-y-3' : 'space-y-3 opacity-40 pointer-events-none select-none' } >
< TextInput label = "URL" value = { env . central . url || '' }
onChange = { ( v ) = > up ( { central : { . . . env . central ! , url : v } } ) }
placeholder = "nats://central.echo6.mesh:4222" / >
< TextInput label = "Durable" value = { env . central . durable || '' }
onChange = { ( v ) = > up ( { central : { . . . env . central ! , durable : v } } ) }
placeholder = "meshai-v04" / >
< / div >
< / div >
) }
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{ /* Family tabs */ }
< div className = "flex gap-1 border-b border-border overflow-x-auto" >
{ FAMILIES . map ( ( { key , label , icon : Icon } ) = > (
< button key = { key } onClick = { ( ) = > { setFamily ( key ) ; const f = FAMILIES . find ( ( x ) = > x . key === key ) ! ; setAdapter ( f . adapters [ 0 ] ? ? null ) } }
className = { ` flex items-center gap-2 px-4 py-2 text-sm whitespace-nowrap border-b-2 -mb-px transition-colors ${ family === key ? 'border-accent text-accent' : 'border-transparent text-slate-400 hover:text-slate-200' } ` } >
< Icon size = { 15 } / > { label }
< / button >
) ) }
2026-05-12 14:59:54 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{ /* Tracking placeholder */ }
{ family === 'tracking' && (
< div className = "flex flex-col items-center justify-center h-[40vh] text-center" >
< Satellite size = { 32 } className = "text-slate-600 mb-4" / >
< p className = "text-slate-500 max-w-md" > No adapters yet . ADS - B / AIS / satellite passes are planned for v0 . 5 . < / p >
2026-05-12 15:22:07 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
) }
2026-05-12 15:22:07 -06:00
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{ /* Mesh Health (no env adapters; central greyed for future migration) */ }
{ family === 'mesh' && (
< div className = "border border-[#1e2a3a] rounded-lg p-4 space-y-3" >
< div className = "flex items-center justify-between" >
< div >
< span className = "text-sm font-medium text-slate-300" > Mesh Health < / span >
< p className = "text-xs text-slate-600" > Node / infra telemetry — sourced from the mesh , not an environmental feed . < / p >
2026-05-12 15:22:07 -06:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = "flex items-center gap-1" >
< span className = "text-[10px] uppercase tracking-wide text-slate-600" > source < / span >
< FeedSourceToggle value = "native" onChange = { ( ) = > { } } disabled = { false } centralDisabled = { true } / >
2026-05-12 15:22:07 -06:00
< / div >
2026-05-12 22:22:57 +00:00
< / div >
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< div className = "text-[11px] text-slate-600" > Central not available — reserved for a future migration . < / div >
2026-05-12 22:22:57 +00:00
< / div >
) }
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{ /* Adapter sub-tabs + panel */ }
{ fam . adapters . length > 0 && activeAdapter && (
< >
{ fam . adapters . length > 1 && (
< div className = "flex gap-1" >
{ fam . adapters . map ( ( k ) = > (
< button key = { k } onClick = { ( ) = > setAdapter ( k ) }
className = { ` px-3 py-1.5 text-sm rounded ${ activeAdapter === k ? 'bg-bg-hover text-slate-100' : 'text-slate-400 hover:text-slate-200' } ` } >
{ META [ k ] . label }
< / button >
) ) }
2026-05-12 22:22:57 +00:00
< / div >
) }
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle
Restructures the environmental config UI into the 7-family taxonomy on the
/environment page (Matt-approved Option C: unified families = config +
live status per adapter). The editable adapter config moves out of the
Config page's "Environmental" tab (now deprecated) onto /environment, where
each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings)
together with its live status (feed health + active events).
Frontend (dashboard-frontend/src):
- pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF
Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family
adapter sub-tab strip -> AdapterPanel.
* AdapterPanel: header row = on/off Toggle + feed_source toggle
(native|central). When OFF, feed_source + all settings grey out
(disabled, not hidden).
* Native-only adapters (ducting, avalanche, roads511 -- no Central stream
per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central'
disabled + a 'Central not available for this adapter' tooltip/label.
* Missing-key adapters (firms, roads511) show an 'API key not configured
-- contact admin' notice; toggles still operate.
* Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes
are planned for v0.5.'). Mesh Health = no env adapters; a disabled
feed_source toggle with 'central' greyed for future migration.
* All existing per-adapter settings preserved verbatim (NWS zones/user_agent/
severity, ducting lat/lon, fires state, avalanche centers/season, USGS
sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS
map_key/source/confidence/bbox). ALSO adds a usgs_quake panel
(tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in
the old GUI. feed_source field (C.1) now surfaced per adapter.
* Per-adapter live status: reuses /api/env/status feed health + /api/env/active
events (filtered by source; fires->nifc mapping). Refreshes every 30s.
- pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS
entry + render case + EnvironmentalSection function + unused Thermometer
import); exported the shared form primitives (Toggle, TextInput, NumberInput,
SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx.
- Reuses the existing restart_required banner pattern.
- Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js,
index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle).
Rule 17 / no backend change: config is wired to the existing schema-driven
GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake
fields ride the generic dataclass coercion). No backend edited this phase.
Verification: (A) `npm run build` clean -- tsc strict + vite, only the
pre-existing >500kB single-chunk advisory (not introduced here). (B) static
committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell
(index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards,
RF Propagation, ADS-B, 'Central not available', 'API key not configured').
(D) GET /api/config/environmental returns all adapters with feed_source=native,
usgs_quake present, central{enabled:false} -- toggles bind to real data; all
native, nothing flipped. Rebuilt prod healthy.
*** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 ***
The save half of gate D fails: PUT /api/config/environmental returns
{"detail":"could not determine a constructor for the tag '!include' ..."}.
Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py)
calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a
loader lacking the !include constructor) instead of the multi-file-aware
meshai/config_loader.py::save_section that exists for exactly the !include
layout. This breaks ALL GUI config saves in prod (every section, not just
environmental) and predates C.2 -- the old Config 'Environmental' tab had the
same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind
works; only persistence is blocked. Verified disk pristine (idempotent PUT
errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup).
FIX (one line, but prod-wide blast radius -> wants its own phase + verification):
config_routes PUT should call config_loader.save_section(section, data, config_dir)
instead of config.save_config(...). Recommend C.2.1 backend fix before C.3.
C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save,
since flipping feed_source from the GUI is the whole point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
< AdapterPanel
title = { META [ activeAdapter ] . label }
subtitle = { META [ activeAdapter ] . subtitle }
enabled = { a [ activeAdapter ] ? . enabled ? ? false }
onEnabled = { ( v ) = > setAdapterField ( activeAdapter , { enabled : v } ) }
feedSource = { ( a [ activeAdapter ] ? . feed_source ? ? 'native' ) }
onFeedSource = { ( v ) = > setAdapterField ( activeAdapter , { feed_source : v } ) }
hasCentral = { META [ activeAdapter ] . hasCentral }
nativeOnly = { META [ activeAdapter ] . nativeOnly }
hasKey = { META [ activeAdapter ] . hasKey }
health = { healthFor ( activeAdapter ) }
events = { eventsFor ( activeAdapter ) }
>
{ renderSettings ( activeAdapter ) }
< / AdapterPanel >
< / >
2026-05-12 22:22:57 +00:00
) }
2026-05-12 10:28:12 -06:00
< / div >
)
}