meshai/tests
K7ZVX a4f23c226e fix(dashboard): v0.4 C.2.1 -- route PUT /config to multi-file save_section (Rule 17 persistence unblocked)
C.2 surfaced that GUI config saves were broken in the prod multi-file
layout. This fixes it. Pre-existing v0.3-era bug (predates C.2; affected
EVERY config section, not just environmental).

Save flow (before -> after):
  before: PUT /api/config/{section} -> config.py::load_config(config.yaml)
          [monolithic, vanilla YAML] -> blows up on the !include orchestrator
          ("could not determine a constructor for the tag '!include'"),
          then config.py::save_config (same !include-blind path). Every save
          500'd; nothing persisted.
  after:  PUT validates the body by coercing to the section dataclass (runs
          __post_init__ validators, e.g. feed_source), then persists via
          config_loader.py::save_section(section, dict, config_dir) -- the
          multi-file / !include-aware writer. It writes ONLY the section's
          target file (env_feeds.yaml for environmental, notifications.yaml,
          llm.yaml, ...), strips SECRET_FIELDS (traffic.api_key, firms.map_key)
          and extracts LOCAL_FIELDS (ducting lat/lon -> local.yaml). The
          orchestrator config.yaml and its !include directives are never
          re-parsed. Live app.state.config is kept in sync via setattr when
          the section isn't restart-required (no disk reload needed).

Also: save_section now tolerates a top-level LIST section (mesh_sources) --
it cleans each item for secrets and writes the list directly instead of
assuming a dict (which would have crashed). Other callers of save_config are
untouched (it remains valid for the monolithic single-file path).

Files: meshai/dashboard/api/config_routes.py (PUT handler + import),
meshai/config_loader.py (save_section list guard),
tests/test_dashboard_config_save.py (new).

Verification:
- (A) py_compile clean on config_routes.py + config_loader.py.
- (C) pytest -q: 272 passed (269 + 3 new -- save_section writes env_feeds,
  strips secret fields, handles the mesh_sources list section).
- (D) Rebuilt prod; ran the C.2 round-trip again, now SUCCESS: backup
  env_feeds.yaml (md5 dde5d634...), GET then PUT /api/config/environmental ->
  {"saved":true,"restart_required":false} (NO !include error); disk reflected
  it (feed_source on all 10 adapters + central block written); restored from
  backup -> md5 matches original -> DISK_PRISTINE_RESTORED.
- (E) Rule 17 round-trip confirmed: the GUI can now SAVE config that
  round-trips to disk in the multi-file !include layout, secrets staying in
  .env and local fields in local.yaml.

C.3 (quake -> central flip) is now unblocked: feed_source can be flipped and
saved from the GUI.

Follow-up (non-blocking): mesh_sources per-item secret stripping
(mesh_sources.*.api_token) isn't matched by the section-relative check in the
new list path; mesh_sources files are volume-only (not git) and this was no
worse before, but worth tightening when mesh_sources GUI save is exercised.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:17:30 +00:00
..
test_adapter_avalanche.py feat(notifications): Phase 2.10 avalanche adapter pipeline integration 2026-05-27 23:08:24 +00:00
test_adapter_ducting.py feat(notifications): Phase 2.13 ducting adapter threshold-crossing emission (severity-tiered, Option C) 2026-05-28 00:01:40 +00:00
test_adapter_fires.py feat(notifications): Phase 2.11 NIFC fires adapter pipeline integration 2026-05-27 23:33:48 +00:00
test_adapter_firms.py feat(env): Phase 2.6 FIRMS adapter emits Events to pipeline bus 2026-05-15 05:23:00 +00:00
test_adapter_nws.py feat(notifications): Phase 2.6 NWS adapter pipeline integration 2026-05-15 04:47:31 +00:00
test_adapter_roads511.py feat(notifications): Phase 2.8 roads511 adapter pipeline integration 2026-05-27 21:18:21 +00:00
test_adapter_swpc.py feat(notifications): Phase 2.12 SWPC space weather adapter + dedup fix 2026-05-27 23:41:30 +00:00
test_adapter_traffic.py feat(notifications): Phase 2.7 traffic adapter pipeline integration 2026-05-27 19:17:27 +00:00
test_adapter_usgs.py feat(notifications): Phase 2.9 usgs water adapter pipeline integration 2026-05-27 21:58:13 +00:00
test_adapter_usgs_quake.py feat(notifications): Phase 2.14 USGS earthquake adapter (new) -- closes Rule 16 Seismic standalone path 2026-05-28 00:10:39 +00:00
test_central_consumer.py feat(central): v0.4 C.1 Central connector backend (no-op until adapter source flipped) 2026-05-28 02:28:19 +00:00
test_channel_rendering.py feat(notifications): Phase 2.5b per-channel-type renderers 2026-05-15 04:25:44 +00:00
test_config_loader.py fix(notifications): Phase 2.16.1 unblock pipeline -- grouper flush + rules coercion + toggle warning 2026-05-28 00:36:13 +00:00
test_config_source_field.py feat(central): v0.4 C.1 Central connector backend (no-op until adapter source flipped) 2026-05-28 02:28:19 +00:00
test_dashboard_config_save.py fix(dashboard): v0.4 C.2.1 -- route PUT /config to multi-file save_section (Rule 17 persistence unblocked) 2026-05-28 03:17:30 +00:00
test_pipeline_digest.py fix(notifications): inject llm_backend into build_pipeline 2026-05-15 03:08:31 +00:00
test_pipeline_grouper.py fix(notifications): Phase 2.16.1 unblock pipeline -- grouper flush + rules coercion + toggle warning 2026-05-28 00:36:13 +00:00
test_pipeline_inhibitor_grouper.py fix(notifications): Phase 2.16.1 unblock pipeline -- grouper flush + rules coercion + toggle warning 2026-05-28 00:36:13 +00:00
test_pipeline_scheduler.py feat(notifications): Phase 2.5a channel interface unification 2026-05-15 03:45:27 +00:00
test_pipeline_skeleton.py feat(notifications): Phase 2.5a channel interface unification 2026-05-15 03:45:27 +00:00
test_pipeline_toggle_filter.py fix(notifications): inject llm_backend into build_pipeline 2026-05-15 03:08:31 +00:00
test_renderers.py feat(notifications): Phase 2.5b per-channel-type renderers 2026-05-15 04:25:44 +00:00