F-01 (P1): _buildGpsFiltersUI больше не хардкодит список источников —
подтягивает source_id из /api/gps-tracks/health.tracks_by_source
(ADR-013 §3 Решение D, опция D2). Маппинг source_id → label вынесен
в JS-константу GPS_SOURCE_LABELS. Активация четвёртого источника
теперь не требует изменений в этом коде.
F-02 (P1): attribution фиксируется в момент addSource, а не мутацией
src.attribution после. MapLibre AttributionControl не реагирует на
прямое присвоение — потому до этого фикса AC-15 проваливался бы в
UI-тестах. Теперь onPublicTracksCheckbox / restorePublicTracksState
сначала await _fetchGpsHealth() → _buildGpsAttributionString(),
потом _ensureGpsSources(map, attribution).
Добавлен кэш + in-flight Promise (window.gpsTracksLayer._healthCache /
_healthFetchPromise) — переоткрытие sheet'а фильтров не плодит
дублирующих сетевых запросов.
Все 24 node-теста gps_tracks.test.js зелёные.
Refs: ET-009
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>