From 316bb0d1a61d6e0dc7278c2fb8fb63888349912b Mon Sep 17 00:00:00 2001 From: claude-bot Date: Thu, 4 Jun 2026 10:10:25 +0000 Subject: [PATCH] tester(ET): auto-commit from tester run_id=85 --- docs/work-items/ET-013/13-test-report.md | 462 +++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 docs/work-items/ET-013/13-test-report.md diff --git a/docs/work-items/ET-013/13-test-report.md b/docs/work-items/ET-013/13-test-report.md new file mode 100644 index 0000000..b9e4304 --- /dev/null +++ b/docs/work-items/ET-013/13-test-report.md @@ -0,0 +1,462 @@ +--- +type: test-report +work_item_id: ET-013 +title: "Test Report: Перепады высот на z9-z11 — zoom-aware paint" +version: 1 +status: blocked +verdict: BLOCKED +created_at: 2026-06-04 +updated_at: 2026-06-04 +authors: + - "agent:tester" +related: + - "ET-007" + - "PH-6.terrain" +adr_refs: + - "ADR-017" +--- + +# Test Report — ET-013 + +## TL;DR + +- `make lint` ✅, прицельный прогон unit/integration ET-013 ✅ + (23 passed, 7 skipped — skip ожидаемы: нет PNG-fixtures в sandbox). +- Полный `make test` падает на этапе collection из-за **внешней** + проблемы (`ModuleNotFoundError: No module named 'lxml'` в тестах + `tests/api/test_gps_tracks_download.py` / `_gpx_builder.py`) — это + наследие ET-011, не имеет отношения к ET-013. После исключения + этих двух файлов: **191 passed, 46 skipped, 0 failed**, регрессий + ET-007/008/009/011/012 нет. +- Код в ветке `feature/ET-013-z9-z11-z8` 1:1 соответствует TRZ + (REQ-F-01..F-21) и ADR-017 (подтверждено Review v2, **APPROVED**). +- **❌ Pre-deploy gate AC-19 — FAIL (P1):** на test-среде отсутствуют + тайлы `hillshade/9/*` (а также `hillshade/8/*`). Проверка по + координатам `[37.6, 54.5]` (юг МО / Кашира — основная зона UI-тестов): + `hillshade/z9/309/348.png → 404`. Тайлы `hillshade/z10`, + `hillshade/z11`, `tri/z8..z11` присутствуют (200 OK). Это блокирует + основную пользовательскую ценность ET-013: после деплоя на z=9 + чекбокс «Тени рельефа» станет активным, но карта 404'нется на каждом + hillshade-запросе, и пользователь увидит включённый слой **без теней** + (хуже, чем до ET-013, где чекбокс был disabled с честным hint'ом + «Зум 10+»). +- **UI Playwright (TC-UI-01..12) — NOT EXECUTED:** раннер + `/home/slin/tools/ui-test/run_tests.js` и `playwright`/`npx` + недоступны в этом контейнере. Дополнительно: test-среда сейчас + держит **до-ET-013** код (`if (zoom < 10)`, `HILLSHADE_PAINT` нет), + поэтому даже при наличии раннера большинство TC дали бы PASS «по + старому контракту» — нерелевантный сигнал. Визуальные TC должны + выполниться **после** деплоя. + +**Вердикт: BLOCKED.** Реализация ET-013 в коде корректна и готова, +но деплой остановлен по TRZ REQ-F-20 §1: «При 404 — задача +останавливается, тайлы z9 нужно догенерировать в рамках PH-6 +follow-up». Следующий шаг — открыть PH-6 follow-up +(«generate hillshade tiles z8-z9 для CFO») и после генерации тайлов +повторно прогнать pre-deploy probe + Playwright UI suite. + +--- + +## 1. Окружение прогона + +| Параметр | Значение | +|-------------------------|-------------------------------------------------------------------------| +| Ветка | `feature/ET-013-z9-z11-z8` | +| HEAD | `397dc60 reviewer(ET): auto-commit from reviewer run_id=84` | +| Содержательные коммиты | `5be81f9 feat(terrain): zoom-aware paint для hillshade/TRI на z9-z11 (ET-013)`
`099669d fix(terrain): расширить whitelist endpoint'а на `tri` (ET-013 review F-1)` | +| Python | 3.12.13 | +| pytest | 8.3.3 | +| Ruff | через `python -m ruff check src/api/` | +| Test-среда (HTTP) | https://openclaw.mva154.duckdns.org/enduro/ | +| Состояние test-среды | **до-ET-013** (фронт ещё с `if (zoom < 10)`, без `HILLSHADE_PAINT`/`TRI_PAINT`). Это ожидаемо: деплой ET-013 — следующий этап пайплайна. | +| `curl` в sandbox | отсутствует; HTTP-проверки выполнены через `urllib.request` (Python). | + +Сетевая проверка `/health`: +``` +GET /enduro/api/health → 200 +{"status":"ok","db_path":"/app/data/centralfederal.sqlite","db_exists":true} +``` + +--- + +## 2. Шаг 1 — `make lint` + +``` +python -m ruff check src/api/ +All checks passed! +``` +**Результат:** ✅ PASS (часть AC-18). + +--- + +## 3. Шаг 2 — `make test` (целевой gate) + +### 3.1 Прицельный прогон ET-013 + +``` +python -m pytest tests/unit/test_terrain_paint.py \ + tests/integration/test_terrain_z9_tiles.py -v + +collected 30 items +… +=================== 23 passed, 7 skipped, 1 warning in 0.46s =================== +``` + +| Suite | Кейсов | PASS | SKIP | Покрытие AC | +|-----------------------------------------------|--------|------|------|----------------------------| +| `tests/unit/test_terrain_paint.py` | 17 | 17 | 0 | AC-01, AC-04, AC-05, AC-15, AC-22 | +| `tests/integration/test_terrain_z9_tiles.py` | 13 | 6 | 7 | AC-16 | + +Что покрывают unit-тесты (выборка): +- `test_hillshade_paint_defined`, `test_hillshade_opacity_is_interpolate_by_zoom`, + `test_hillshade_opacity_stops`, `test_hillshade_contrast_peak_z9`, + `test_hillshade_resampling_nearest` — структура `HILLSHADE_PAINT`, + stops 9/10/11/12/14 → 0.65/0.60/0.55/0.50/0.40, contrast пик z9 ≥0.30 / z14 ≤0.10. +- `test_tri_paint_defined`, `test_tri_opacity_z8_regression` («8, 0.70» + ровно, защита AC-06), `test_tri_opacity_peak_z9_z11` (z10/z11 ≥ 0.80), + `test_tri_resampling_nearest`. +- `test_apply_terrain_layer_signature_uses_opacity_or_paint`, + `test_apply_terrain_layer_normalizes_number_to_legacy_paint`, + `test_apply_terrain_layer_uses_paint_variable` — обратная + совместимость `applyTerrainLayer` (AC-22). +- `test_minzoom_threshold_lowered_to_9` (`if (zoom < 9)` найден, + `< 10` отсутствует), `test_hint_text_updated_to_z9` («Зум 9+»), + `test_apply_terrain_layer_caller_count` (ровно 2 вызова), + `test_hillshade_call_uses_paint_constant_and_minzoom_9`, + `test_tri_call_uses_paint_constant_and_minzoom_5`. + +Что покрывают integration-тесты: +- **PASS:** `test_known_terrain_layer_accepted_by_whitelist[hypso|hillshade|tri]` + (доказывает фикс F-1 review v1), `test_unknown_terrain_layer_returns_404`, + `test_missing_terrain_tile_returns_404`, `test_invalid_zoom_returns_404`. +- **SKIP:** `test_terrain_tile_available_z9_z10_z11[*]` ×6, + `test_terrain_tile_cache_control_immutable` — требуют PNG-fixtures + в `data/terrain/`, которых нет в sandbox-репо. Skip — корректный + механизм через `_maybe_skip`; AC-16 говорит «при отсутствии тайлов + в CI — тесты skipped с reason», что в точности и наблюдается. + +### 3.2 Полный регресс (`pytest tests/`) + +Полный прогон падает на collection из-за **внешней** проблемы: + +``` +ERROR tests/api/test_gps_tracks_download.py +ERROR tests/api/test_gps_tracks_gpx_builder.py + from lxml import etree as lxml_et +E ModuleNotFoundError: No module named 'lxml' +!!! Interrupted: 2 errors during collection !!! +``` + +`lxml` не установлен в этом контейнере. Это **наследие ET-011 / GPX +download**, не связано с ET-013 (ветка не трогает `gps_tracks/`). +В CI-окружении проекта `lxml` устанавливается через +`src/api/requirements.txt`, и эти тесты зелёные. + +Прогон без этих двух файлов: +``` +python -m pytest tests/ \ + --ignore=tests/api/test_gps_tracks_download.py \ + --ignore=tests/api/test_gps_tracks_gpx_builder.py +… +========== 191 passed, 46 skipped, 4 deselected, 79 warnings in 3.47s ========== +``` + +- `4 deselected` — perf/network маркеры (стандартный exclude). +- `46 skipped` — async-тесты `gps_tracks` (нет pytest-asyncio в + sandbox) + integration без fixtures. Не относится к ET-013. +- **Регрессий ET-007 / ET-008 / ET-009 / ET-011 / ET-012 — НЕТ.** + +**Результат:** ✅ PASS (AC-15, AC-16 в части автоматики, AC-17, AC-18). + +--- + +## 4. Шаг 3 — E2E (контракт API на test-среде) + +### 4.1 IT-TILE-* «вживую» против test-среды + +Поскольку sandbox без data fixtures даёт SKIP, я выполнил эквивалент +IT-TILE-* напрямую HTTP-запросом к test-среде. Координата +`[37.6, 54.5]` (юг МО / Кашира) — основная для UI-тестов (см. +04b-ui-test-cases.md §«Координаты»). Тайлы под TMS-схемой (как +объявлено в `addSource(... scheme: 'tms' ...)`): + +| z | hillshade (x, y_tms) | hillshade status | tri (x, y_tms) | tri status | +|----|---------------------------|------------------|---------------------------|------------| +| 8 | `8/154/174` | **❌ 404** | `8/154/174` | ✅ 200 | +| 9 | `9/309/348` | **❌ 404** | `9/309/348` | ✅ 200 | +| 10 | `10/618/697` | ✅ 200 | `10/618/697` | ✅ 200 | +| 11 | `11/1237/1395` | ✅ 200 | `11/1237/1395` | ✅ 200 | +| 14 | `14/9903/11162` | ✅ 200 | `14/9903/11162` | ❌ 404 ¹ | + +¹ TRI z=14 404 — за пределами TRI-стека (TRI генерится до z11 в +PH-6, регрессия известная, в скоупе ET-013 не трогается). Чекбокс TRI +на z=14 включит источник с minzoom=5/maxzoom=15, но реально тайлы +отдадутся только до z=11; визуально на z>11 — пусто. Это **не** +новая регрессия ET-013, такое же поведение было до ET-013. Фиксирую +как P3 для PH-6 follow-up. + +Дополнительная проверка покрытия hillshade z=9 — wide grid 5×5 вокруг +центра `(309, 348)`: +``` +hillshade z=9 found: 0 tiles around (309,348) +hillshade z=10 found: 9 tiles around (618,697) +``` +То есть на z=9 нет ни одного hillshade-тайла, не только «целевого»; +данных просто нет в pipeline. + +### 4.2 Заголовок Cache-Control + +``` +hillshade z=10 → Cache-Control: max-age=31536000 +hillshade z=11 → Cache-Control: max-age=31536000 +tri z=8 → Cache-Control: max-age=31536000 +… +``` + +Только `max-age=31536000`; `immutable`-флаг **отсутствует** в ответах +nginx-перед-fastapi на test-среде. Это **предсуществующая** ситуация +(не введена ET-013): backend FastAPI отдаёт `Cache-Control: max-age=…, +immutable`, но nginx-конфиг на test-среде стрипает `immutable`. На +бизнес-логику это не влияет (`max-age=1y` достаточен), но формальная +формулировка REQ-F-18 / IT-TILE-CACHE-HEADER «immutable сохраняется» +выполняется только на backend-уровне (см. integration-тест +`test_terrain_tile_cache_control_immutable`, корректно SKIPPED здесь). +**Не блокер ET-013.** Фиксирую как P3 (известная инфра-косметика, +не в скоупе). + +### 4.3 `/health` стабилен +См. раздел 1. ✅ + +--- + +## 5. Шаг 4 — UI / Visual тесты + +### 5.1 Состояние раннера + +``` +ls /home/slin/tools/ui-test/ → No such file or directory +which playwright / npx → not found +find / -name run_tests.js -type f → (нет результатов) +``` + +UI-test раннер, Playwright и `npx` в этом контейнере отсутствуют. +Запустить TC-UI-01..12 невозможно. + +### 5.2 Состояние test-среды (до-ET-013) + +``` +GET https://openclaw.mva154.duckdns.org/enduro/app.js +HILLSHADE_PAINT in body: False +TRI_PAINT in body: False +'if (zoom < 9)' in body: False +'if (zoom < 10)' in body: True +``` + +На test-среде сейчас выкатан **до-ET-013** код. Это **ожидаемо**: +деплой ET-013 — следующий этап пайплайна (deployer → `14-deploy-log.md`). +Визуальную регрессию TC-UI-01..12 имеет смысл прогонять только +ПОСЛЕ деплоя. + +### 5.3 План постдеплойного прогона (DEFERRED) + +| TC | Тип | viewport | Зум | Что проверяем | Severity | Статус | +|-------------------------|--------------------|----------|-----|-------------------------------------------------------|----------|--------------| +| TC-UI-01-Z9 | functional+visual | desktop | 9 | Чекбокс активен, hint скрыт, hillshade виден | **P1** | DEFERRED ¹ | +| TC-UI-02-Z8-REGRESS | regression+visual | desktop | 8 | TRI выглядит как до ET-013 | P2 | DEFERRED | +| TC-UI-03-Z9-Q | visual (qual.) | desktop | 9 | Перепады читаются ≥ z=8 | **P1** | DEFERRED ¹ | +| TC-UI-04-Z10-Q | visual (qual.) | desktop | 10 | Перепады читаются | P2 | DEFERRED | +| TC-UI-05-Z11-Q | visual (qual.) | desktop | 11 | Перепады читаются | P2 | DEFERRED | +| TC-UI-06-Z14-REGRESS | regression+visual | desktop | 14 | Hillshade не «перегрет» (opacity 0.40, contrast 0) | P2 | DEFERRED | +| TC-UI-07-Z9-MOBILE | visual | mobile | 9 | Чекбокс/hint работают, нет H-scroll | **P1** | DEFERRED ¹ | +| TC-UI-08-Z10-SAT-Q | visual (qual.) | desktop | 10 | Hillshade поверх спутника не «глушит» | P2 | DEFERRED | +| TC-UI-09-Z10-DARK-Q | visual (qual.) | desktop | 10 | Hillshade на тёмной теме читается | P2 | DEFERRED | +| TC-UI-10-PERSIST | functional+visual | desktop | 10 | F5 не теряет состояние, оба слоя восстановлены | P2 | DEFERRED | +| TC-UI-11-NETWORK-Q | perf (network) | desktop | 8-11 | Σ traffic ≤ 135% baseline | P2 | DEFERRED | +| TC-UI-12-Z9-PAN | perf+visual | desktop | 9 | Pan без «белых дыр» в hillshade/TRI | P3 | DEFERRED | + +¹ **TC-UI-01, TC-UI-03, TC-UI-07 — заблокированы pre-deploy gate +(см. §4.1):** даже после деплоя ET-013 эти три кейса дадут FAIL, +потому что `/terrain/hillshade/9/*` отдаёт 404 → MapLibre нарисует +hillshade-слой пустым (или с «белыми дырами»), что не соответствует +AC-03 «На карте видны тени рельефа». + +**DEFERRED** = тест не запущен в текущем окружении и должен быть +выполнен оператором/Playwright против test-среды **после**: +(a) генерации hillshade z8-z9 тайлов (PH-6 follow-up); +(b) деплоя ET-013. + +Результаты приколоть к `14-deploy-log.md`. + +--- + +## 6. Матрица Acceptance Criteria → Test + +| AC | Покрытие | Результат | +|---------|-------------------------------------------------------------------------------------------|------------------------| +| AC-01 | `test_minzoom_threshold_lowered_to_9`, `test_hint_text_updated_to_z9` | ✅ PASS | +| AC-02 | DevTools на test-среде | ⏳ DEFER → deploy log | +| AC-03 | TC-UI-01-Z9 + видимость hillshade-слоя | **❌ BLOCKED** (нет тайлов z9) | +| AC-04 | `test_hillshade_opacity_is_interpolate_by_zoom`, `…contrast_peak_z9`, `…resampling_nearest` | ✅ PASS | +| AC-05 | `test_tri_opacity_z8_regression`, `test_tri_opacity_peak_z9_z11`, `…resampling_nearest` | ✅ PASS | +| AC-06 | `test_tri_opacity_z8_regression` (z8 = 0.70 ровно) + TC-UI-02-Z8-REGRESS | ✅ PASS (код) / ⏳ DEFER (visual) | +| AC-07 | TC-UI-03-Z9-Q | **❌ BLOCKED** (нет тайлов z9) | +| AC-08 | TC-UI-04-Z10-Q | ⏳ DEFER → deploy log | +| AC-09 | TC-UI-05-Z11-Q | ⏳ DEFER → deploy log | +| AC-10 | TC-UI-06-Z14-REGRESS | ⏳ DEFER → deploy log | +| AC-11 | TC-UI-09-Z10-DARK-Q | ⏳ DEFER → deploy log | +| AC-12 | TC-UI-08-Z10-SAT-Q | ⏳ DEFER → deploy log | +| AC-13 | TC-UI-07-Z9-MOBILE | **❌ BLOCKED** (нет тайлов z9) | +| AC-14 | TC-UI-10-PERSIST | ⏳ DEFER → deploy log | +| AC-15 | `pytest tests/unit/test_terrain_paint.py` — 17/17 | ✅ PASS | +| AC-16 | `pytest tests/integration/test_terrain_z9_tiles.py` — 6 pass / 7 skip (по плану) | ✅ PASS | +| AC-17 | Полный `pytest tests/` (исключая lxml-зависимые) — 191 passed, 46 skipped | ✅ PASS | +| AC-18 | `make lint` (✅) + `make test` (✅ модуль ET-013; полный — внешняя lxml-проблема) | ✅ PASS | +| AC-19 | Pre-deploy `curl -sI .../hillshade/{9,10,11}/X/Y.png` — `hillshade/9` отдаёт **404** | **❌ FAIL (P1)** | +| AC-20 | Документация work item (см. §8) | ✅ PASS (12+ файлов) | +| AC-21 | TC-UI-11-NETWORK-Q (требует baseline + Playwright) | ⏳ DEFER → deploy log | +| AC-22 | `test_apply_terrain_layer_normalizes_number_to_legacy_paint` + `…uses_paint_variable` | ✅ PASS | + +**Итого:** 10/22 AC закрыты автоматически зелёные · 1 AC **FAIL +(блокер P1)** · 3 AC **BLOCKED** (зависят от AC-19) · 8 AC +делегированы Deployer-агенту. + +--- + +## 7. Findings + +### P0 +Нет. + +### P1 + +#### P1-01 — Pre-deploy gate AC-19: hillshade z=9 тайлы отсутствуют + +**Где.** Test-среда `https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/9/*.png`. + +**Симптом.** Все запросы вида `GET /terrain/hillshade/9/X/Y.png` (и +`hillshade/8/…`) возвращают 404. Покрытие отсутствует на всю +изученную область юга МО / ЦФО (проверено grid'ом 5×5 вокруг +ожидаемой целевой плитки `(309, 348)` под TMS). + +**Почему блокер.** После деплоя ET-013 фронт: +- понизит UI-минзум hillshade до 9 → чекбокс «Тени рельефа» станет + активным на z=9; +- понизит `source.minzoom` до 9 → MapLibre начнёт запрашивать + `/terrain/hillshade/9/X/Y.png`; +- получит 404 → слой нарисуется пустым. + +Пользователь увидит **включённый** слой **без теней**. Это хуже, чем +до ET-013, где чекбокс был disabled с честным hint'ом «Зум 10+». +**Регрессия UX**, явно противоречащая AC-03 / AC-07 / AC-13 / BRD-цели +ET-013 («перепады читаются на z9-z11»). + +**Что делать.** TRZ REQ-F-20 §1 и AC-19 однозначно говорят: +> Если 404 — задача останавливается, тайлы z9 нужно догенерировать в +> рамках PH-6 follow-up. + +Действия: +1. Открыть PH-6 follow-up: «Generate hillshade tiles z8-z9 for CFO + coverage» (как минимум область, покрываемая текущим + `data/terrain/hillshade/10..14/`). +2. После генерации повторно прогнать probe из §4.1. +3. После 200 OK на z=9 — повторный запуск Tester'а + переход на + Deployer. + +**Severity = P1, не P0** только потому, что: (a) код ET-013 корректен +и proven unit/integration-тестами; (b) рег-серверная UI-страница +сейчас работает (тестовая среда держит до-ET-013, чекбокс правомерно +disabled); (c) рабочий процесс PH-6 follow-up — стандартная процедура +для такого класса проблем. + +### P2 +Нет. + +### P3 + +#### P3-01 — TRI z=14 отдаёт 404 (предсуществующая регрессия PH-6, не в скоупе ET-013) +`GET .../tri/14/X/Y.png → 404`. ET-013 не трогает TRI pipeline, +но при включённом TRI и z>11 пользователь видит пустой слой. Покрыть +follow-up'ом «extend TRI tiles to z14». + +#### P3-02 — Cache-Control `immutable` стрипается nginx-проксей на test +Backend FastAPI отдаёт `max-age=31536000, immutable`, на проде через +nginx остаётся только `max-age=31536000`. Формально REQ-F-18 нарушен +на edge-слое, но `max-age=1y` функционально достаточен. Не в скоупе +ET-013. + +#### P3-03 — `from __future__ import annotations` в unit-тесте не используется +`tests/unit/test_terrain_paint.py:15` — косметика (унаследовано из +review v2 F-5). + +#### P3-04 — Комментарий в `HILLSHADE_PAINT` не учитывает MapLibre clamping ниже z9 +`src/web/app.js:2728-2733` — унаследовано из review v2 F-3. Не блокер; +актуально только если UI-минзум hillshade когда-нибудь понизят до z<9. + +--- + +## 8. Документация work item (AC-20) + +``` +docs/work-items/ET-013/ + 00-business-request.md ✅ + 01-brd.md ✅ + 02-trz.md ✅ + 03-acceptance-criteria.md ✅ + 04-test-plan.yaml ✅ + 04b-ui-test-cases.md ✅ + 06-adr/ADR-017-zoom-aware-terrain-paint.md ✅ + 07-infra-requirements.md ✅ + 08-data-requirements.md ✅ + 10-tech-risks.md ✅ + 12-review.md ✅ + 13-test-report.md ← этот файл + 14-deploy-log.md ⏳ ожидается после устранения P1-01 +``` + +--- + +## 9. Вердикт + +**BLOCKED.** Реализация ET-013 в коде корректна и готова к деплою: +- `make lint` и прицельный `make test` (ET-013 модуль) — зелёные. +- 23/23 PASS unit/integration ET-013 (7 SKIP — ожидаемые без data + fixtures), 0 регрессий на 191 кейсе остальных тестов. +- Соответствие TRZ / ADR-017 — 1:1 (подтверждено Review v2). +- Контракт API на test-среде — стабилен. + +Однако **pre-deploy gate AC-19 не пройден** (P1-01): на test-среде +отсутствуют `hillshade/z9/*` (и `z8`) тайлы. Деплой остановлен +согласно TRZ REQ-F-20 §1 и BRD-приоритету «UX-regression > frontend-fix +ready». + +### Что должно произойти дальше + +1. **Открыть PH-6 follow-up:** «Generate hillshade tiles z8..z9 for + CFO coverage area» (≈ область, покрытая `data/terrain/hillshade/10/`, + расширенная вверх по zoom-иерархии). +2. **После генерации тайлов:** + - повторный пробинг по §4.1 — все 6 ячеек (hillshade/tri × z=9..11) + должны вернуть 200; + - повторный запуск Tester'а (изменения отчёта — в виде патча версии + v2 этого файла, без `back-to:dev` для самого ET-013); + - переход на Deployer. +3. **Deployer:** + - накатить ветку `feature/ET-013-z9-z11-z8` в test; + - выполнить ручные шаги REQ-F-20 §2: открыть карту, `setZoom(9)`, + включить hillshade, скриншот → визуальная приёмка AC-03..AC-05; + - прогнать Playwright TC-UI-01..12 (или хотя бы P1: TC-UI-01, + TC-UI-03, TC-UI-07); + - замерить network-объём (TC-UI-11/AC-21) против baseline; + - зафиксировать всё в `14-deploy-log.md`. +4. **Если визуальная приёмка AC-07..AC-09 «перепады недостаточно + выразительны»** — корректировка stops в HILLSHADE_PAINT/TRI_PAINT + (это калибровка, не баг — см. BRD §6 «известная итеративность + калибровки»). + +### Что НЕ нужно делать + +- **Не back-to:dev для ET-013-frontend.** Код ETM-013 правильный, тесты + зелёные, ревью пройдено. Изменения в `src/web/app.js` / `src/web/index.html` + не требуются. +- **Не закрывать ET-013 без устранения P1-01.** Деплой без z9-тайлов + даст регрессию UX (включённый, но пустой hillshade на z=9).