Files
enduro-trails/docs/work-items/ET-013/10-tech-risks.md
claude-bot 6b88bcee28
Some checks failed
CI / lint (push) Successful in 5s
CI / test (push) Failing after 5s
CI / build (push) Has been skipped
architect(ET): auto-commit from architect run_id=79
2026-06-04 09:40:50 +00:00

26 KiB
Raw Blame History

type, work_item_id, title, version, status, created_at, authors
type work_item_id title version status created_at authors
tech-risks ET-013 Технические риски — ET-013: Zoom-aware paint для terrain-слоёв на z9-z11 1 approved 2026-06-04
agent:architect

Технические риски — ET-013

Технические риски этапа калибровки клиентского paint для растровых terrain-слоёв. Бизнес-риски — в BRD §5 (R-1..R-11). Шкала: вероятность (Н/С/В) × влияние (Н/С/В).

R-T-1 — Тайлы hillshade z9-z11 отсутствуют на test-среде

  • Описание: BRD §2.1 утверждает, что PH-6 нарезала hillshade z8-z14. Если реальная нарезка на mva154 отличается (например, z10-z14), при включении hillshade на z9 пользователь увидит 404-шахматную доску, а в DevTools — череду failed requests.
  • Вероятность / Влияние: Н / В.
  • Митигация:
    • Архитектурное решение (ADR-017 §U-A): pre-deploy smoke curl -I на 3 разных тайла (z9/z10/z11) над ЦФО — обязателен перед merge (07-infra-requirements.md §6.2 шаг 1, AC-19).
    • Эскалация: при 404 — задача останавливается, открывается PH-6 follow-up «hillshade-z9-z14 backfill». ET-013 не мержится.
    • Acceptance гейт: AC-19 в 03-acceptance-criteria.md.

R-T-2 — raster-contrast 0.40 даёт «пересвет» / черноту на тёмных тайлах

  • Описание: На z9-z11 hillshade-тайлы из тёмных лесных зон (низкая средняя яркость PNG) при raster-contrast: 0.40 могут «провалиться в черноту» — пиксели clipping'уются к 0, тени превращаются в чёрные кляксы, теряя информацию.
  • Вероятность / Влияние: С / С.
  • Митигация:
    • Архитектурное решение (ADR-017 §C-A): stops контраста подобраны консервативно (0.40 на z9 → быстрый спад к 0 на z14); значения калибруются по результатам визуальной приёмки.
    • Acceptance гейт: TC-UI-04-Z10-Q (BRD R-1, AC-07..AC-09) — оператор смотрит скриншоты на холмистом районе. При «пересвете» — снижаем contrast в stops до 0.25-0.30 итеративно.
    • Принцип: stops живут в коде, правка — одна строка, не ADR.

R-T-3 — 'nearest'-resampling на overzoom z12-z14 даёт пикселизацию

  • Описание: При overzoom (когда MapLibre тянет тайл z14 для z15-z18) 'nearest'-resampling показывает крупные квадраты вместо плавных теней. Это особенно заметно на hillshade.
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение (ADR-017 §R-A): MapLibre не поддерживает interpolate для raster-resampling, поэтому глобальное 'nearest' — единственный простой путь. Альтернатива (два layer'а) отклонена как overkill.
    • Контекст использования: на z12+ пользователь обычно отключает hillshade в пользу подложки (для города нужны улицы, а не тени). Это вторичный сценарий.
    • Acceptance гейт: AC-10 (TC-UI-06-Z14-Q) — оператор подтверждает «не темнее и не контрастнее, чем до ET-013» (т.к. opacity и contrast уже вернулись к baseline). Пикселизация допустима, если не нарушает читаемость.
    • Fallback: если визуально неприемлемо — отдельным минорным патчем вводится второй layer hillshade с 'linear' для z12+, переключаемый по getZoom(). Это не часть ET-013.

R-T-4 — Сетевой трафик растёт > +35% при активной zoom-сессии

  • Описание: Снижение UI-минзума hillshade с 10 до 9 добавляет +1 zoom-уровень. На активной сессии (пользователь крутит зум z8→z11→z8→z11 много раз) первая загрузка z9 тайлов даёт заметную дельту трафика. BRD M-10 = ≤ +35%.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение: Cache-Control: public, max-age=31536000, immutable (src/api/main.py:1252) + браузерный кэш + nginx-кэш. После первого визита повторные запросы дают 304 If-Modified-Since (или вовсе не доходят до сервера — browser hits memory cache).
    • Acceptance гейт: AC-21 в 03-acceptance-criteria.md — network-traffic ≤ 135% от baseline на сценарии zoom-петли z=8→9→10→11→10→9→8.
    • Мониторинг: см. 07-infra-requirements.md §7.1 — первая неделя оператор смотрит nginx access.log на аномалии.

R-T-5 — На тёмной теме (ET-007 theme-dark) hillshade с opacity 0.65 + contrast 0.40 сливается в кашу

  • Описание: Тёмная подложка + полупрозрачный тёмный hillshade с усиленным контрастом → визуально неразличимая «грязь». BRD R-2.
  • Вероятность / Влияние: С / С.
  • Митигация:
    • Архитектурное решение (ADR-017 §T-A): в MVP — один paint для всех тем. Если AC-11 проваливается — открывается ADR-018 «theme-specific terrain paint» с отдельной таблицей stops для theme-dark (через подписку на theme-change event и setPaintProperty).
    • Acceptance гейт: AC-11 (TC-UI-09-Z10-DARK-Q) — оператор проверяет на dark + holmistom районе. Если провал — фиксируется в 13-test-report.md и открывается follow-up.
    • Принцип: не плодим сложность пока не доказана необходимость.

R-T-6 — На спутниковой подложке (ET-007) hillshade «глушит» снимок

  • Описание: Esri World Imagery уже содержит визуальный рельеф (тени снимков). Поверх него полупрозрачный hillshade с opacity 0.65 → снимок превращается в «серую плёнку», пользователь теряет цвета поверхности. BRD R-3.
  • Вероятность / Влияние: Н / С.
  • Митигация:
    • Архитектурное решение (ADR-017 §T-A): UX-нота: на спутнике пользователь обычно отключает hillshade — снимок и так «показывает» рельеф. Если AC-12 проваливается — open ADR-018 с правилом «на satellite layer'е opacity hillshade = старые 0.40» (через подписку на applyBaseLayer).
    • Acceptance гейт: AC-12 (TC-UI-08-Z10-SAT-Q).
    • Принцип: не плодим сложность пока не доказана необходимость.

R-T-7 — TRI с opacity 0.85 на z9-z11 перекрывает грунтовки/тропы

  • Описание: Слой trails-* (грунтовки, тропы) рисуется тонкими линиями. Если TRI поднять до opacity 0.85, цветные пятна категориальной палитры могут визуально «убить» линии трасс.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение: существующая логика в applyTerrainLayer (src/web/app.js:3337-3339) вставляет terrain-слои перед первым trails-* или poi-* слоем — z-order корректный. TRI рисуется ПОД линиями трасс, не НАД.
    • Тесты: AC-07..AC-09 (визуальная приёмка на холмистом районе с грунтовками).

R-T-8 — MapLibre 4.7.0 не поддерживает interpolate для raster-contrast

  • Описание: Если документация MapLibre врёт или версия 4.7.0 имеет regression на raster-contrast с zoom-выражением, paint не применится, в DevTools будет warning, hillshade покажется с default contrast = 0.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение (NFR-04 в TRZ §4): MapLibre 4.7.0 официально поддерживает interpolate для всех raster paint properties, кроме raster-resampling. Проверка — публичная документация maplibre.org.
    • Smoke-проверка после деплоя: DevTools window._map.getPaintProperty('terrain-hillshade', 'raster-contrast') должен вернуть массив ['interpolate', ...] (AC-04).
    • Fallback: если фактически не работает — заменить на case-step выражение (грубое stepwise) или просто оставить числовую константу 0.30 для z9-z11 (одно значение, без zoom-плавности).

R-T-9 — Регрессия z8: после правки TRI_PAINT на z8 перепады выглядят иначе

  • Описание: В новой TRI_PAINT для z=8 стоит 0.70 — точно как было. Но если при правке нечаянно поставить 8, 0.75 (или пропустить стоп для z8 — тогда interpolate между 7→0.65 и 9→0.80 даст на z8 значение ~0.72), регрессия z8 нарушится.
  • Вероятность / Влияние: С / С.
  • Митигация:
    • Архитектурное решение (ADR-017 §O-B): в TRI_PAINT явно указан стоп 8, 0.70 (не полагаемся на интерполяцию между соседними стопами).
    • Acceptance гейт: AC-06 (TC-UI-02-Z8-REGR) — скриншот сравнивается с до-ET-013 baseline.
    • Unit-тест: REQ-F-13 проверяет наличие 8, 0.70 в исходнике TRI_PAINT через regex.

R-T-10 — Регрессия z14: hillshade «не возвращается» к baseline

  • Описание: Если stops HILLSHADE_PAINT не закрываются явным стопом на z14 (например, 14, 0.40, 14, 0.00), MapLibre экстраполирует за пределами последнего стопа, и на z14-z15 hillshade может остаться «перегретым» (opacity 0.55, contrast 0.20).
  • Вероятность / Влияние: Н / С.
  • Митигация:
    • Архитектурное решение (ADR-017 §O-B / §C-A): interpolate у MapLibre clamp'ит значения за пределами крайних stops (clamping behavior). Явные стопы 14, 0.40 для opacity и 14, 0.00 для contrast обеспечивают регрессию z14.
    • Acceptance гейт: AC-10 (TC-UI-06-Z14-Q) — скриншот сравнивается с до-ET-013 baseline.
    • Unit-тест: REQ-F-13 проверяет наличие 14, 0.40 и 14, 0 в исходнике HILLSHADE_PAINT.

R-T-11 — applyTerrainLayer ломает обратную совместимость

  • Описание: При расширении сигнатуры opacity → opacityOrPaint: number | object существующая логика (если есть где-то ещё в src/web/) может сломаться при передаче числа.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение (ADR-017 §A-A): внутри функции — нормализация (typeof opacityOrPaint === 'number') ? {…linear…} : opacityOrPaint. Старый контракт работает без изменений.
    • Acceptance гейт: AC-22, UT-COMPAT-01 (REQ-F-14) — статический grep по src/web/*.js: подтверждает, что вызовов applyTerrainLayer только два (оба в onTerrainCheckbox), оба переведены на новые константы.
    • Принцип: unit-тест на нормализацию + явный комментарий // ET-013: backwards-compat shim в коде.

R-T-12 — Старый клиент (закэшированный в браузере) не подхватывает новый app.js

  • Описание: Пользователь с открытой вкладкой неделю назад имеет закэшированный старый app.js со старым applyTerrainLayer без paint-нормализации. При reload браузер должен дёрнуть свежий app.js. Service worker — не настроен в MVP (PH-9 не реализована).
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение: src/web/index.html загружает app.js напрямую (без SW). nginx + Cache-Control на *.js — стандартные (не immutable; If-Modified-Since работает). При reload браузер делает conditional GET → 200 (если файл изменился) или 304.
    • Backwards compat: старый клиент с minzoom=10 для hillshade продолжает работать; он просто не запрашивает hillshade z=9. Никаких 4xx-ответов нет (REQ-F-18 — контракт неизменен).
    • Митигация в долгую: PWA / SW (PH-9) введёт правильную inval-стратегию.

R-T-13 — Hint «Зум 10+» забыт в HTML → расхождение с фактическим порогом

  • Описание: В src/web/index.html строка <span id="terrain-hillshade-hint">Зум 10+</span>. Если правка REQ-F-10 потеряется (например, мердж-конфликт), у пользователя на z<9 будет hint «Зум 10+», который противоречит фактическому порогу 9.
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение (REQ-F-10): в HTML текст явно меняется на «Зум 9+». Это атомарная правка, проверяется grep'ом.
    • Acceptance гейт: AC-01 — проверяет «Зум 9+» в исходнике index.html. AC-03 — проверяет hint.style.display === 'none' на z=9.
    • Unit-тест: REQ-F-14 (UT-REG-02) — grep по строке zoom < 9 в app.js и «Зум 9+» в index.html.

R-T-14 — nearest-resampling на TRI делает «зернистую» картинку, пользователю не нравится

  • Описание: TRI — категориальная палитра (5 уровней). На 'nearest' ясно видны 30-метровые SRTM-клетки, картинка выглядит «зернистой». BRD R-10 классифицирует это как «желаемое поведение» (показ «реальных» границ перепадов), но возможен субъективный негативный отзыв.
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение (ADR-017 §R-A): на TRI «зернистость» — спецификация. Категориальные данные требуют резких границ, 'linear' их размывает.
    • Fallback: если AC-07..AC-09 проваливаются с пометкой «зернисто» — откатывается F-09 (TRI → 'linear'), hillshade остаётся на 'nearest'. Это одна строка кода в TRI_PAINT.
    • Acceptance гейт: AC-07..AC-09 — оператор подтверждает качественную приёмку.

R-T-15 — Performance деградация из-за interpolate в paint

  • Описание: Если MapLibre на каждом zoom-tick пересчитывает interpolate-выражение без кэширования, на слабых устройствах (mobile, low-end) может появиться jank при зуме.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение (NFR-01 в TRZ §4): MapLibre кэширует скомпилированные interpolate-выражения; вычисление при смене zoom — < 1 мс на frame.
    • Эмпирически: существующие слои gps_tracks.js, trails-* уже используют interpolate по zoom без жалоб.
    • Тест: AC-13 (TC-UI-07-Z9-MOBILE) — Playwright mobile viewport, проверяет работоспособность; не measure'ит FPS, но регрессия проявится визуально.

R-T-16 — Pre-deploy smoke не покрывает все регионы (тайлы z9 могут отсутствовать вне ЦФО)

  • Описание: Pre-deploy curl проверяет 3 тайла над ЦФО. Если нарезка z9 ограничена только ЦФО, пользователь над Уралом / Алтаем увидит 404. По BRD §6 это OOS (MVP покрывает только ЦФО), но риск стоит явно зафиксировать.
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение: в MVP test-среда обслуживает ЦФО (centralfederal.sqlite). Тайлы вне ЦФО — out of scope.
    • Принцип: если пользователь панорамирует за пределы ЦФО, на z9-z14 он увидит «шахматку» из 404 и для terrain, и для trails — это известная граница MVP, не баг ET-013.
    • Документация: зафиксировать в 14-deploy-log.md как «known limitation».

R-T-17 — eslint падает на новых interpolate-массивах

  • Описание: Если в проекте настроен eslint с правилами no-magic-numbers или жёстким max-len, длинные массивы ['interpolate', ['linear'], ['zoom'], 9, 0.65, …] могут завалить линтер.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение: существующие JS-файлы (gps_tracks.js) уже используют похожие массивы — значит, eslint их пропускает.
    • Acceptance гейт: AC-18 (make lint зелёный). При проблеме — добавить // eslint-disable-next-line точечно.

R-T-18 — Калибровка stops «не угадывает» желаемую читаемость с первого раза

  • Описание: Значения 9→0.65, 10→0.60, 11→0.55 для hillshade выбраны архитектором по эстимейту из BRD. На реальных данных оператор может сказать «на z9 ещё мало, на z10 уже слишком темно». Это итеративный процесс, не «упало».
  • Вероятность / Влияние: В / Н.
  • Митигация:
    • Архитектурное решение: stops живут в JS-константах HILLSHADE_PAINT / TRI_PAINT. Правка одной цифры — одна строка кода + новый коммит. Не требует архитектурного re-decide (ADR-017 §«Технический долг» TD-1).
    • Процесс: после первого деплоя — фикс stops по фидбеку оператора без новой задачи. Учитывать в bandwidth-плане до закрытия ET-013.
    • Гейт: AC-07..AC-09 — качественные, оператор-driven. Они и есть «точка калибровки».

Сводная таблица

# Риск Вер Влиян Митигация (тип)
R-T-1 Тайлы z9-z11 отсутствуют Н В Pre-deploy smoke + AC-19; STOP на 404
R-T-2 raster-contrast 0.40 — пересвет/чернота С С Итеративная калибровка stops; AC-07..AC-09
R-T-3 'nearest' пикселизация на z12+ С Н Принимается; fallback — двойной layer
R-T-4 Трафик +35% превышает гейт M-10 Н Н immutable кэш; AC-21
R-T-5 Hillshade на тёмной теме — каша С С AC-11; follow-up ADR-018 при провале
R-T-6 Hillshade «глушит» спутник Н С AC-12; follow-up ADR-018 при провале
R-T-7 TRI 0.85 перекрывает trails Н Н Existing z-order (terrain ПОД trails)
R-T-8 MapLibre 4.7.0 не поддерживает interpolate для raster-contrast Н Н Документация подтверждает; fallback — case-step
R-T-9 Регрессия z8 TRI С С Явный стоп 8, 0.70; AC-06; unit-тест
R-T-10 Регрессия z14 hillshade Н С Явные стопы 14, 0.40 и 14, 0; AC-10
R-T-11 applyTerrainLayer обратная совместимость Н Н Нормализация внутри функции; UT-COMPAT-01
R-T-12 Старый клиент в кэше браузера С Н Backwards-compat контракта
R-T-13 Hint «Зум 10+» забыт С Н grep-проверка + AC-01
R-T-14 TRI 'nearest' — зернисто С Н Specified behavior; fallback — откат F-09
R-T-15 interpolate deg performance Н Н MapLibre кэширует expr; NFR-01
R-T-16 Pre-deploy smoke ≠ покрытие региона С Н Known MVP limitation; deploy-log
R-T-17 eslint падает на длинных массивах Н Н Существующий код уже использует такие массивы
R-T-18 Stops не угадывают с первого раза В Н Итеративная калибровка; AC-07..AC-09 — qualitative

Связанные документы

  • 01-brd.md §5 Бизнес-риски R-1..R-11 (часть пересекается)
  • 02-trz.md §3 REQ-F-04..REQ-F-15 (paint, тесты), §4 NFR-01..NFR-07
  • 06-adr/ADR-017-zoom-aware-terrain-paint.md §«Решение», §«Последствия», §«Технический долг»
  • 07-infra-requirements.md §3 (network), §6 (deploy procedure), §7 (мониторинг)
  • 08-data-requirements.md §3.3 (pre-deploy validation), §5 (API contracts)
  • 03-acceptance-criteria.md AC-01..AC-22 (все гейты)