Files
enduro-trails/docs/work-items/ET-007/10-tech-risks.md
claude-bot 1984b0bde6
All checks were successful
CI / lint (push) Successful in 4s
CI / test (push) Successful in 6s
CI / lint (pull_request) Successful in 4s
CI / test (pull_request) Successful in 5s
CI / build (push) Successful in 4s
CI / build (pull_request) Successful in 2s
fix(ET-007): address 6 P1 findings from review (docs + code)
12-review.md (REQUEST_CHANGES, attempt 2/3) flagged 6 must-fix items
in the analysis/architecture artefacts plus matching bugs that had
already leaked into the committed implementation. This patch lands
both: documents corrected, code aligned with corrected specs, tests
updated.

P1-1: TRZ/ADR/Data/Risks referenced fictional layer ids
(`trails-grade1..5-halo-satellite`, `paths-bridleway-halo-satellite`).
Actual style*.json has only `trails-track-halo-satellite` and
`trails-path-bridleway-halo-satellite`; grade differentiation lives
inside one `match` expression on `tracktype` within `trails-track`.
Docs rewritten to operate on real ids.

P1-2: POI labels contrast was broken — spec changed only halo-color
to black, leaving `text-color: #333333` (light theme baseline)
unreadable over the new black halo. Code+docs now switch BOTH
`text-color` (-> `#ffffff` on satellite) AND halo together, with
per-theme baselines (`#333333` light / `#e0e0e0` dark) restored on
return to Schematic.

P1-3: BRD §5 hillshade risk said «hillshade auto-disabled on
satellite», contradicting TRZ/ADR/AC. BRD wording aligned: hillshade
keeps working over satellite; visual check is AC-04.

P1-4: background-color had four divergent sources (`#1a1a1a`,
`#2a2a2a`, `#1a1a2e`, `#f0ede6`), incl. an inverted-theme typo and a
baseline `#1a1a1a` that didn't match the actual `style-dark.json:28`
value `#1a1a2e`. Settled on ADR-004's single-constant model: `#2a2a2a`
on satellite for both themes; on Schematic restore per-theme baselines
`#f0ede6` (light) / `#1a1a2e` (dark). `_applyBackgroundForSatellite`
fixed accordingly.

P1-5: app.js already had `layerState.basemap` and `toggleLayer
('basemap')` (legacy «Базовая карта» switch). Neither TRZ nor ADR
specified the interaction. Added save&restore contract: on entering
Satellite save `layerState.basemap` to `_savedBasemapState` and
force-hide `osm-base`; on returning to Schematic restore osm-base
visibility from the saved value. CSS hook `body.satellite-active
#btn-basemap { display:none }` keeps the user from trying to enable
a hybrid mode (out of scope, BRD §3). TRZ §5.6, ADR-004 §8.

P1-6: `restoreTrailsState()` and `onTrailsCheckbox()` only managed
visibility of `trails-track` / `trails-path-bridleway`, leaving
their halo-underlay siblings as «phantom» halos when the user
unchecked grunты/тропы under Satellite. Introduced
`_applyTrailHaloVisibility(map, base)` reading checkbox state from
DOM; called from `onTrailsCheckbox`, `restoreTrailsState`, and both
branches of `applyBaseLayer`. Rule: halo visible ⇔ (base ===
satellite) AND (checkbox ON). TRZ §5.7, ADR-004 §9.

Docs bumped: BRD v2, TRZ v2, AC v2, Data v2, Risks v2; ADR-004
получает «Ревизии»-секцию (status remains accepted — only editorial
fixes, no decision change).

Tests:
- tests/unit/base_layer.test.js: rewritten 2 background-color
  assertions (#1a1a1a expectation removed), added 6 new tests for
  P1-2 / P1-4 (POI text-color per-theme baselines, single satellite
  bg #2a2a2a, baseline restore on Schematic).
- All 33 JS unit tests + 22 pytest static checks green.
- Full pytest suite: 76 passed (excluding pre-existing
  shapely-import skipped collection in tests/unit/test_health.py).

Refs: ET-007
Review: docs/work-items/ET-007/12-review.md (P1-1..P1-6)
ADR: docs/work-items/ET-007/06-adr/ADR-004-satellite-base-layer.md (rev. 2026-05-31)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 21:05:49 +00:00

215 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: tech-risks
work_item_id: ET-007
title: "Технические риски — ET-007: Спутниковая карта (Схема / Спутник)"
version: 2
status: approved
created_at: 2026-05-31
changelog:
- "v2 (2026-05-31): code-review fix (12-review.md P1-1) — R-1 переписан под реальные halo-id (trails-track-halo-satellite, trails-path-bridleway-halo-satellite); исключён фиктивный массив grade1..5."
authors:
- "agent:architect"
---
# Технические риски — ET-007
Технические риски этапа разработки. Бизнес-риски — в BRD §5
(пересечение есть, здесь акцент на техническую митигацию).
Шкала: вероятность (Н/С/В) × влияние (Н/С/В).
## R-1 — Дрейф halo-слоёв в `style.json` / `style-dark.json`
- **Описание:** ADR-004 §5 решает читаемость линий грунтовок и троп
на спутнике через парные «underlay»-слои с `visibility: none` в
обоих файлах стилей. Реальные id (подтверждены кодом
`style.json:5670`, `93107` и `style-dark.json:5670`, `93107`):
`trails-track-halo-satellite`, `trails-path-bridleway-halo-satellite`.
Любая будущая правка основных trails-слоёв (цвет, ширина, фильтр)
требует **согласованной правки halo-слоёв** в обоих файлах. Без
явной проверки легко забыть один из четырёх случаев (2 темы × 2
рода слоёв).
- **Вероятность / Влияние:** С / Н.
- **Митигация:**
- При разработке завести единый список затрагиваемых пар в
`applyBaseLayer()`: массив пар `[('trails-track',
'trails-track-halo-satellite'), ('trails-path-bridleway',
'trails-path-bridleway-halo-satellite')]`. Производное правило
«`<base>-halo-satellite`» допустимо, но только для **этих двух**
base-id; массив `['trails-grade1..5']` (как в более раннем
черновике, см. 12-review.md P1-1) **не использовать** — таких
слоёв в `style.json` нет, дифференциация grade хранится внутри
одного `match`-выражения по `tracktype` в `trails-track`.
- Code review-чеклист: при правке `trails-track`, `trails-path-
bridleway` в `style*.json` — обязательная сверка соответствующего
`*-halo-satellite` в том же файле.
- UI-тест AC-04 проверяет видимость линий поверх спутника в обеих
темах.
## R-2 — Провайдер Esri меняет условия / URL / вводит API-ключ
- **Описание:** Esri World Imagery — единственная внешняя зависимость
фичи, выбранная без формального соглашения; в перспективе Esri
может ограничить бесплатный доступ, изменить URL-схему или ввести
обязательный API-ключ (BRD §5 риск №1). Тогда «Спутник» перестаёт
работать у всех пользователей одновременно.
- **Вероятность / Влияние:** С / В.
- **Митигация:**
- Точка расширения локализована: единственный объект source-spec
в `applyBaseLayer()` (ADR-004 §1).
- При деградации провайдера выполняется одна правка JS-фронтенда
(новый URL-шаблон + новая атрибуция), без миграций и серверных
изменений; откат прежнего поведения — обратный коммит.
- Альтернативные провайдеры предварительно рассмотрены в ADR-004
§«Вариант P»; быстрый switch на следующего по приоритету —
Mapbox или MapTiler — потребует только введения переменной
окружения для API-ключа (это уже инфра-изменение, выходящее за
scope ET-007).
- Регулярная smoke-проверка дос­тупности через UI-тест AC-02.
## R-3 — Утечка IP клиента на серверы Esri
- **Описание:** при активном «Спутник» браузер обращается напрямую
к `server.arcgisonline.com`; IP пользователя и User-Agent видны
Esri. Это **не регрессия** (OSM tile уже работает аналогично), но
расширение списка третьих сторон, к которым обращается клиент.
- **Вероятность / Влияние:** В (т.е. произойдёт всегда при включении
спутника, дизайн именно такой) / Н.
- **Митигация:**
- **Ленивое создание source** (ADR-004 §3): пользователь,
никогда не включающий «Спутник», ни одного запроса в Esri не
отправляет. Это обеспечивает «приватный по умолчанию» режим.
- Документировано в `08-data-requirements.md` §7.
- В отдельный политический документ выноситься не требуется —
приватность фичи на уровне рейзанса вынесена в ADR-004 §«Последствия».
## R-4 — Корпсеть / DNS-блокировка `arcgisonline.com`
- **Описание:** часть пользователей работает в сетях, блокирующих
arcgisonline.com (анти-трекинг-DNS типа NextDNS/Pi-hole,
корпоративные firewall). MapLibre будет показывать прозрачные
плитки поверх фона `#2a2a2a`; пользователь увидит «дыры».
- **Вероятность / Влияние:** Н / С.
- **Митигация:**
- TRZ §1 REQ-F-08 явно фиксирует: автоматический fallback на
«Схему» не закладывается — пользователь возвращается на схему
вручную; это сознательное проектное решение.
- В виду фона `#2a2a2a` пустота визуально опознаётся как ошибка
подложки, а не «лёг" сайт.
- Эскалация / альтернативный провайдер при единичных жалобах не
требуется; при системных — переход к R-2.
## R-5 — Дублирование `background-color` между `style*.json` и `app.js`
- **Описание:** ADR-004 §6 требует менять `background-color` на
единую satellite-константу `#2a2a2a` (обе темы) при включении
«Спутник» и возвращать к исходному при возврате на «Схему».
«Исходные» значения (`#f0ede6` для светлой, `#1a1a2e` для тёмной —
именно `#1a1a2e`, как в `style-dark.json:28`, а не `#1a1a1a` из
более раннего черновика, см. 12-review.md P1-4 / P2-3)
дублируются в `applyBaseLayer()` и в `style*.json` — при смене
палитры тем легко забыть один из двух.
- **Вероятность / Влияние:** Н / Н.
- **Митигация:**
- Альтернатива — при возврате на «Схему» **читать** актуальное
значение через `getPaintProperty('background', 'background-color')`
непосредственно перед мутацией в «Спутник», и кэшировать его в
замыкании. Однако `setStyle()` сбрасывает кэш, что усложняет
логику. Принято: задублировать в коде с явным комментарием
в `app.js` и code review-чеклистом.
- Покрытие AC-06 (смена темы при активном «Спутник») косвенно
проверяет согласованность.
## R-6 — Накопление обработчиков и source/layer после `map.setStyle()`
- **Описание:** при `map.setStyle()` (переключение тёмной/светлой
темы) спутниковый source/layer удаляются вместе со стилем.
`restoreBaseLayerState()` пересоздаёт их в `rebuildMapOverlays()`.
Аналогичный риск зафиксирован для GPX (ET-006, R-4: «дублирование
обработчиков»). Спутник, в отличие от GPX, **не вешает свои
`map.on('click', ...)`** обработчиков на свои слои (он —
невзаимодействующий растр), поэтому дублирования обработчиков
здесь не возникает.
- **Вероятность / Влияние:** Н / Н.
- **Митигация:** проверка перед `addSource` — `if
(!map.getSource('satellite-raster')) map.addSource(...)`; то же для
layer. Это идемпотентный паттерн, уже используемый в проекте для
terrain/trails.
## R-7 — Несовместимость z-order при `restoreBaseLayerState()` после terrain
- **Описание:** если разработчик случайно вызовет
`restoreBaseLayerState()` **после** `restoreTerrainState()` в
`rebuildMapOverlays()`, спутник окажется поверх hillshade и
перекроет его. Это нарушит AC-04 («Hillshade поверх спутника»).
- **Вероятность / Влияние:** Н / С.
- **Митигация:**
- ADR-004 §4 явно фиксирует: `restoreBaseLayerState()` вызывается
**ПЕРВЫМ** в `rebuildMapOverlays()`.
- Комментарий в коде `app.js` непосредственно у вызова —
`// ET-007/ADR-004: ПЕРВЫМ, чтобы trails/terrain легли поверх`.
- UI-тест AC-04 «Hillshade поверх спутника» отлавливает регрессию.
## R-8 — Производительность переключения «Схема → Спутник» > 500 мс
- **Описание:** НФТ ТЗ — ≤ 500 мс до первой видимой плитки. При
холодном переключении в одном кадре происходит: чтение
`localStorage`, `addSource`, `addLayer`, `setLayoutProperty`,
`setPaintProperty` ×N для POI, `setLayoutProperty` ×K для halo-
underlays. Главная неопределённость — сетевая задержка до Esri.
- **Вероятность / Влияние:** Н / С.
- **Митигация:**
- Все операции стиля MapLibre — синхронные O(1) на source/layer;
суммарно < 50 мс.
- Сетевая задержка для PNG 3080 КБ из Esri CDN на канале
≥ 5 Мбит/с укладывается в 200300 мс на тайл (по практике
Leaflet/OpenLayers с этим же провайдером).
- Тест НФТ TP-Performance в `04-test-plan.yaml` проверяет
верхнюю границу.
## R-9 — Конфликт mobile-вёрстки попапа
- **Описание:** новая строка `terrain-base-row` добавляется в
`#terrain-popup` сверху. На узких экранах (375 px, ET-005 TP-05)
возможен выход за пределы попапа или перекрытие смежных строк.
- **Вероятность / Влияние:** Н / Н.
- **Митигация:**
- Переиспользуется готовый компонент `.seg-control` (адаптивен по
ширине), без введения нового CSS-компонента.
- UI-тест AC-09 (mobile viewport 375 × 812) — обязательный.
## R-10 — Включение спутника после рестарта при отсутствии сети у Esri
- **Описание:** пользователь сохранил `map-base-layer = "satellite"`,
затем при следующем визите Esri недоступен. `restoreBaseLayerState()`
вызовет `applyBaseLayer('satellite')`, source создастся, плиток не
будет — пользователь увидит пустой тёмный фон вместо привычной
карты.
- **Вероятность / Влияние:** Н / Н.
- **Митигация:**
- Поведение явно соответствует TRZ §1 REQ-F-08; на mobile/desktop
пользователь нажмёт «Схема» и продолжит работу.
- Авто-fallback на «Схему» при сетевой ошибке провайдера —
**не закладывается** (см. ADR-004 §«Последствия»). Введение
fallback возможно в будущей итерации без изменения внешнего
контракта `applyBaseLayer()`.
## Сводная таблица
| ID | Риск | Вер. | Влияние | Класс | Статус |
|-----|------|------|---------|-------|--------|
| R-1 | Дрейф halo-слоёв в обоих style.json | С | Н | Средний | внимание разработки + review |
| R-2 | Esri меняет условия / URL / вводит ключ | С | В | Высокий | митигация — точка расширения |
| R-3 | Утечка IP на Esri при активном спутнике | В | Н | Средний | приватный-по-умолчанию (lazy) |
| R-4 | DNS-блокировка `arcgisonline.com` | Н | С | Низкий | принят (TRZ REQ-F-08) |
| R-5 | Дубль background-color в style/app.js | Н | Н | Низкий | принят + комментарий в коде |
| R-6 | Source/layer после setStyle | Н | Н | Низкий | идемпотентные `if (!getSource)` |
| R-7 | Неверный порядок restoreBaseLayerState | Н | С | Низкий | ADR явно + комментарий + AC-04 |
| R-8 | Переключение > 500 мс | Н | С | Низкий | покрыто НФТ-тестом |
| R-9 | Mobile-вёрстка попапа | Н | Н | Низкий | AC-09 |
| R-10 | Restore satellite при недоступности Esri | Н | Н | Низкий | принят, fallback не закладываем |
Блокирующих рисков нет. R-2 — единственный «высокий» класс, но
вероятность средняя и митигация (локализация точки расширения)
делает реакцию операционно простой. Эскалация `arch:major-change`
или возврат в Анализ не требуются.