Files
enduro-trails/docs/work-items/ET-009/10-tech-risks.md
claude-bot 4be7fbf3de
Some checks failed
CI / lint (push) Failing after 4s
CI / test (push) Failing after 6s
CI / build (push) Has been skipped
feat(ET-009): architect deliverables — ADR, infra requirements, data requirements, tech risks, wikiloc parser stub
2026-06-01 19:20:15 +00:00

338 lines
22 KiB
Markdown
Raw 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-009
title: "Технические риски — ET-009: Активация EnduroRussia + Wikiloc"
version: 1
status: approved
created_at: 2026-06-01
authors:
- "agent:architect"
---
# Технические риски — ET-009
Технические риски этапа активации двух новых GPS-источников. Бизнес-риски —
в BRD §6 ET-009. Многие риски наследуются от ET-008 (R-1, R-5, R-9 из
`docs/work-items/ET-008/10-tech-risks.md`); здесь — специфика ET-009.
Шкала: вероятность (Н/С/В) × влияние (Н/С/В).
## R-1 — Wikiloc меняет HTML → парсер возвращает 0 треков
- **Описание:** Парсер Wikiloc опирается на regex-извлечение
`<a href="/trails/…/<id>">` и `<h1>` для названия. Wikiloc может в
любой момент изменить разметку (новый шаблон, JS-rendering) → парсер
вернёт 0 треков.
- **Вероятность / Влияние:** В / С.
- **Митигация:**
- Парсер уже спроектирован **graceful**: `return` без `raise` при
отсутствии match'ей regex (см. `wikiloc.py::_extract_track_paths`).
- Health-эндпоинт показывает `tracks_by_source.wikiloc = 0` после
прогона → видимый сигнал оператору.
- Unit-тест UT-WL-01 на снимке `wikiloc-search-page1.html` — при
смене разметки CI зелёным быть не сможет, разработчик обновит
фикстуру + парсер за 1 итерацию.
- `gps_sources.yaml::wikiloc.enabled: false` — мгновенное отключение
без deploy при критической поломке.
- **Наследник от:** ET-008 R-1 (general).
## R-2 — Wikiloc банит IP mva154
- **Описание:** Скрейпер с фиксированного IP может попасть в чёрный
список Wikiloc'а (особенно при ошибках rate-limit или
накоплении 1000+ запросов в сутки). Pipeline начнёт получать 403/429
на все запросы → новых треков не будет.
- **Вероятность / Влияние:** С / В.
- **Митигация:**
- `rate_limit_sec: 10` — самый консервативный rate в проекте.
- `max_tracks_per_run: 50` — soft-cap на первом прогоне; ≤ 150
запросов на одну активацию.
- `User-Agent` с контактным URL — платформа может связаться
через email до бана.
- **Graceful-stop** на 403/429 — не агрессивный retry, не вызывает
дополнительных запросов.
- **Мониторинг первых 3 прогонов** оператором; при систематических
403 → `enabled: false` + новый ADR-update «Wikiloc deprecated».
- Запрет использования прокси через сторонний IP (нарушает дух
прозрачности; см. ET-008 R-5).
## R-3 — EnduroRussia API меняет схему ответа
- **Описание:** `enduro_russia.py::_parse_gpx` ожидает поля
`id`, `name`, `difficulty`, `created_at` в JSON-items. Платформа
может добавить/переименовать поля.
- **Вероятность / Влияние:** Н / С.
- **Митигация:**
- Парсер использует `.get()` с дефолтами — отсутствие необязательных
полей не валит.
- Отсутствие `id` → запись пропускается (`continue`), не валит весь
прогон.
- Контрактный smoke-тест `tests/contract/test_endurorussia_api_smoke.py`
с маркером `@pytest.mark.network` — запускается nightly или вручную,
сигнализирует о поломке внешнего API до пропущенного cron-прогона.
- Pipeline-error не лоадит всю БД: `errors_json` фиксирует, оператор
видит через `/health`.
## R-4 — Расхождение конфига `enduro-russia.ru` (с дефисом) vs
реального `endurorussia.ru` (без дефиса)
- **Описание:** До ET-009 `gps_sources.yaml::enduro_russia.base_url`
содержит `https://enduro-russia.ru` (с дефисом), но реальный
домен — `https://endurorussia.ru` (без дефиса; парсер по default
использует именно его). При активации `enabled: true` без фикса URL
парсер использовал бы default из кода, но `external_url` сохранённых
треков опирался бы на `base_url` из конфига → fragmentation
external_url'ов между «корректным» и «дефис-вариантом».
- **Вероятность / Влияние:** Случилось (известный bug в конфиге) /
В (при активации).
- **Митигация:**
- **F-01 в BRD/TRZ** — фикс URL в одно изменение.
- **Регрессионный тест UT-ER-05** — проверяет, что парсер
сохраняет URL без дефиса при передаче `base_url` без дефиса.
- One-shot UPDATE для существующих треков (опционально, см.
`07-infra-requirements.md` §4.1).
## R-5 — EnduroRussia и Wikiloc — двойник одного и того же трека → массовые дубли
- **Описание:** Авторы часто публикуют одну и ту же поездку и на
Wikiloc, и на EnduroRussia (Wikiloc даже сохраняет `creator=Wikiloc`
в GPX мета-теге, что подтверждается на практике). Без правильно
работающего dedup'а в БД появятся два трека с одинаковой геометрией.
- **Вероятность / Влияние:** В / С.
- **Митигация:**
- `compute_dedup_key` (ADR-006) основан на `bbox+length+date`, который
при достаточно похожих координатах и одной дате попадает в один
bucket → upsert ON CONFLICT мержит.
- **Интеграционный тест IT-DEDUP-01** — задаёт фикстуру `wikiloc-track.gpx`
с координатами, совпадающими с одним из EnduroRussia-треков; проверяет
итоговое объединение `sources_json=['enduro_russia','wikiloc']`.
- Метаданные при merge — берутся от source с большим `source_priority`
(`enduro_russia=80 > wikiloc=70`); `external_urls`оба сохраняются.
- Если на практике dedup пропускает (например, точное время / точный
bbox slightly off): план отступления ADR-006 §8 (сузить
length-bucket, добавить activity).
## R-6 — Cron первого прогона превышает окно из-за rate-limit Wikiloc
- **Описание:** При больших cap'ах `max_tracks_per_run` и rate-limit
10 сек × 3 запроса/трек первый прогон Wikiloc может занять часы.
- **Вероятность / Влияние:** С / Н.
- **Митигация:**
- `max_tracks_per_run: 50` — soft-cap → ≤ 25 минут на прогон Wikiloc.
- EnduroRussia при rate-limit 5 сек × 305 треков ≈ 25 минут — окей.
- Cron автоматизация **отложена** до отдельного DevOps-task'а
после двух успешных ручных прогонов; оператор контролирует
длительность.
- Опционально: `timeout 21600 docker compose ...` в cron (ET-008
R-11 уже фиксирует).
## R-7 — UI-фильтр «Источник» не подхватывает новые ID
- **Описание:** Если в ET-008 UI-фильтр (`#gps-source-grid`) построен
с захардкоженным списком `[osm]`, новые источники не появятся как
чекбоксы.
- **Вероятность / Влияние:** Н / С.
- **Митигация:**
- **Дизайн ET-008**: UI строит фильтр **динамически** из ответа
`/api/gps-tracks/health.tracks_by_source` (источники с count > 0).
После первого прогона ET-009 — фильтр сам покажет 3 чекбокса.
- UI-тест TC-UI-04 (в `04b-ui-test-cases.md` ET-008) расширен для
ET-009: проверяет наличие 3 чекбоксов после двух прогонов.
- Маппинг `SOURCE_LABELS``gps_tracks.js`) расширяется явно
в ET-009 — даёт корректные читаемые названия.
## R-8 — Цветовая палитра в `style.json` / `style-dark.json` не содержит новых ID → линии серые
- **Описание:** В ET-008 match-expression `line-color` может содержать
только `osm`; новые источники получат fallback-серый.
- **Вероятность / Влияние:** В / Н.
- **Митигация:**
- **REQ-F-13** явно требует обновить match-expression с тремя
источниками + fallback.
- Code-review-чеклист: проверить наличие `enduro_russia`, `wikiloc`
в `paint.line-color` обоих стилей.
- При пропуске: визуальный регресс легко заметен в smoke-тесте
(TC-UI-05).
## R-9 — Дамп БД (резервная копия с старым URL) — orphan записи
- **Описание:** Если на test-сервере есть резервная копия БД, в которой
`external_urls_json` содержит `enduro-russia.ru` (с дефисом),
то после фикса URL новые treki будут иметь `endurorussia.ru` (без
дефиса), а старые — `enduro-russia.ru`. Это не криминал, но
фрагментация атрибуции.
- **Вероятность / Влияние:** Н / Н.
- **Митигация:**
- На практике `enduro_russia` до ET-009 был `enabled: false`
таких записей **нет**. Риск гипотетический.
- Опциональный one-shot `UPDATE tracks SET external_urls_json = REPLACE(...)`
— фиксируется в `14-deploy-log.md` если применяется.
## R-10 — ADR-010 / ADR-012 регрессировали в `proposed`
- **Описание:** Между моментом написания BRD/TRZ ET-009 и моментом
активации (merge → deploy) кто-то откатил статус ADR в `proposed`.
Pipeline-guard заблокирует source с `skipped_license`.
- **Вероятность / Влияние:** Н / В.
- **Митигация:**
- **F-03 / REQ-F-05** — pre-check перед активацией:
```bash
grep -E "^status:" docs/work-items/ET-008/06-adr/ADR-010-enduro-russia-licensing.md
grep -E "^status:" docs/work-items/ET-008/06-adr/ADR-012-wikiloc-licensing.md
```
Оба должны вернуть `accepted`. Иначе — STOP и эскалация архитектору.
- Интеграционный тест IT-LIC-01 проверяет работу pipeline-guard'а:
подменяет `accepted → proposed` в копии ADR-010 и убеждается, что
pipeline скипает source с `status='skipped_license'`.
- **Наследник от:** ET-008 R-9.
## R-11 — Пользовательский opt-in для новых источников
- **Описание:** Пользователи с уже сохранённым `localStorage['gps-tracks-sources']
= ["osm"]` после ET-009 **не увидят** треки EnduroRussia/Wikiloc на
своих устройствах — клиент сохраняет старое значение, новые источники
по умолчанию не enabled в UI.
- **Вероятность / Влияние:** В / Н.
- **Митигация:**
- Это **сознательное решение** UX (см. `08-data-requirements.md` §4.2):
добавление источников без согласия пользователя — нарушение
принципа без сюрпризов.
- Чекбоксы новых источников появятся в `#sheet-gps-filters`
automatically (через health-endpoint), пользователь может включить
их вручную.
- В release-notes (если они есть в проекте) — фиксируем «появились
два новых источника, активация в фильтре».
## R-12 — Wikiloc-парсер сохраняет описание / автора несмотря на ADR-012
- **Описание:** ADR-012 §3 явно запрещает сохранять `description` и
`user` для Wikiloc. Если реализация парсера не уважает этот запрет
(например, `TrackInsert.description` заполняется), нарушение
licensing-условий.
- **Вероятность / Влияние:** Н / В.
- **Митигация:**
- **Текущая реализация:** `wikiloc.py::_parse_gpx` возвращает
`TrackInsert(description=None, user=None)` (зашито в коде).
- Unit-тест UT-WL-05 проверяет, что `description=None` и `user=None`
в возвращаемом `TrackInsert`.
- Code-review checklist в `12-review.md`: при любом изменении
парсера Wikiloc убедиться, что эти поля остаются null.
## R-13 — Тестовые фикстуры устаревают
- **Описание:** Снимки HTML/JSON, использованные в unit-тестах,
отражают состояние API/HTML **на момент снятия**. Через 6-12
месяцев платформа может изменить разметку, и фикстуры станут
неактуальны. Тесты пройдут (фикстура соответствует тесту), но
парсер **не будет работать** в production.
- **Вероятность / Влияние:** С / С.
- **Митигация:**
- Контрактный smoke-тест `test_endurorussia_api_smoke.py`
(`@pytest.mark.network`, nightly) — проверяет реальную схему
API, ловит расхождение.
- Аналогичный smoke для Wikiloc **не** делаем (риск бана IP при
регулярных запросах; ETC-009 §«REQ-F-16»).
- Health-эндпоинт показывает `tracks_by_source.wikiloc` после
каждого продакшн-прогона; устойчивое 0 — сигнал.
- При устаревании фикстуры — снимаем заново (1 час работы), парсер
обновляем (1-3 часа).
## R-14 — Производительность endpoint деградирует при росте кол-ва треков
- **Описание:** REQ-NF-02 ET-008 фиксирует p95 ≤ 300 мс на bbox с
≤ 500 треков. После ET-009 в БД появятся ещё ≤ 250 треков —
пренебрежимо относительно 5000 OSM.
- **Вероятность / Влияние:** Н / Н.
- **Митигация:**
- R-tree индекс по `geom` (ADR-005) → O(log n) bbox-prefetch.
- AC-20 — нагрузочный тест 100 запросов после первого прогона.
- При деградации — анализ EXPLAIN QUERY PLAN; добавление индекса
`idx_tracks_source` опционально (out of scope ET-009).
## R-15 — Конфликт MAPPING-таблиц для одной активности
- **Описание:** EnduroRussia маппит `motorcycle → moto`, Wikiloc
тоже `motorcycle → moto` — корректно. Но: EnduroRussia при
отсутствии match'а в MAPPING возвращает `enduro` (fallback),
Wikiloc — `moto`. Для одного и того же трека (попавшего в оба
источника) при merge получим `activity_type` от source с большим
`source_priority` = `enduro_russia` → `enduro`. Это **OK**: priority
делает выбор детерминированным.
- **Вероятность / Влияние:** Н / Н.
- **Митигация:**
- Принято as is. Уточнение в `08-data-requirements.md` §3.4.
- Unit-тесты UT-ER-04 и UT-WL-06 проверяют отдельные MAPPING'и.
## R-16 — Регрессия e2e-тестов ET-008
- **Описание:** Расширение `style.json` / `gps_tracks.js`
атрибуцией и цветами может случайно сломать существующие
selectors / визуальные тесты ET-008.
- **Вероятность / Влияние:** Н / С.
- **Митигация:**
- **AC-19** — все e2e ET-008 (E-01..E-41) должны пройти после
мерджа ET-009.
- Регрессионный прогон `pytest tests/e2e/ -v` — обязательный
шаг CI.
## R-17 — Pipeline скипает source из-за неправильного `license_adr` path
- **Описание:** Pipeline-guard `_check_license_adr` читает YAML
front-matter файла по пути из `license_adr`. Если путь опечатан
(например, `ADR-12-...` вместо `ADR-012-...`), guard вернёт false →
`status='skipped_license'`.
- **Вероятность / Влияние:** Н / В.
- **Митигация:**
- Pre-deploy check: убедиться, что `license_adr` указывает на
реально существующий файл с `status: accepted`.
- При первом запуске pipeline в test-среде оператор смотрит
`pipeline_runs[-1].status`; если `skipped_license` —
диагностирует и исправляет до merge в main.
- Pydantic-валидация `gps_sources.yaml` в pipeline ET-008 уже
требует обязательное `license_adr` поле; отсутствие — exception
при старте.
- **Наследник от:** ET-008 R-9.
## Сводная таблица
| ID | Риск | Вер. | Влияние | Класс | Статус |
|---|---|---|---|---|---|
| R-1 | Wikiloc меняет HTML | В | С | Высокий | принят + graceful + быстрое отключение |
| R-2 | Wikiloc банит IP mva154 | С | В | Высокий | rate-limit 10s + cap 50 + UA + monitor |
| R-3 | EnduroRussia API меняет схему | Н | С | Низкий | smoke-тест + graceful + health |
| R-4 | Расхождение URL `enduro-russia` vs `endurorussia` | Случилось | В | Высокий | F-01 фикс + UT-ER-05 |
| R-5 | Дубли EnduroRussia/Wikiloc | В | С | Средний | dedup-key + IT-DEDUP-01 |
| R-6 | Cron первого прогона долго | С | Н | Низкий | `max_tracks_per_run=50` + ручной прогон |
| R-7 | UI-фильтр не подхватит | Н | С | Низкий | динамика из health + SOURCE_LABELS |
| R-8 | Стили без новых цветов | В | Н | Низкий | REQ-F-13 + review + smoke |
| R-9 | Orphan записи с старым URL | Н | Н | Низкий | гипотетический (БД чистая); опц UPDATE |
| R-10 | ADR-010/012 регрессировали в proposed | Н | В | Высокий | pre-check + IT-LIC-01 |
| R-11 | Пользовательский opt-in для новых источников | В | Н | Низкий | сознательный UX-compromise |
| R-12 | Wikiloc сохраняет description/user | Н | В | Высокий | parser-design + UT-WL-05 + review |
| R-13 | Фикстуры устаревают | С | С | Средний | smoke-test + health + ручной refresh |
| R-14 | Деградация endpoint | Н | Н | Низкий | R-tree + AC-20 |
| R-15 | Конфликт MAPPING | Н | Н | Низкий | source_priority детерминирует |
| R-16 | Регрессия ET-008 e2e | Н | С | Низкий | AC-19 + pytest e2e |
| R-17 | Неправильный `license_adr` path | Н | В | Высокий | pre-deploy check + Pydantic |
**Высокие классы:**
- R-1, R-2 — операционные, ожидаемые для скрейп-источника Wikiloc;
митигация — multi-layer (graceful + monitor + конфиг-kill-switch).
- R-4 — known bug в конфиге, прямо адресован REQ-F-01.
- R-10, R-17 — критичны для legal compliance; митигация многослойная
(pre-check + integration-тест + Pydantic).
- R-12 — критичен для соблюдения ADR-012; митигация через design +
UT-WL-05 + review.
**Блокирующих рисков нет.** Высокие классы требуют внимания на этапе
разработки и code review.
## Эскалация
- **arch:major-change** — **не выставляется** (см. ADR-013
§«Классификация»). Изменение не вводит новых архитектурных компонентов.
- **back-to:analysis** — не требуется. ТЗ полное, BRD ясный,
open-questions в `TRZ §6` закрыты дефолтными решениями.
- Эскалация архитектору требуется **только** при срабатывании R-10
(ADR в `proposed` на момент активации). Тогда задача останавливается
до повторного апрува ADR.