358 lines
26 KiB
Markdown
358 lines
26 KiB
Markdown
---
|
||
type: tech-risks
|
||
work_item_id: ET-013
|
||
title: "Технические риски — ET-013: Zoom-aware paint для terrain-слоёв на z9-z11"
|
||
version: 1
|
||
status: approved
|
||
created_at: 2026-06-04
|
||
authors:
|
||
- "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 (все гейты)
|