diff --git a/docs/work-items/ET-013/12-review.md b/docs/work-items/ET-013/12-review.md index a1e1b68..0d49bd2 100644 --- a/docs/work-items/ET-013/12-review.md +++ b/docs/work-items/ET-013/12-review.md @@ -2,7 +2,7 @@ type: review work_item_id: ET-013 verdict: APPROVED -version: 9 +version: 10 created_at: 2026-06-04 updated_at: 2026-06-06 authors: @@ -10,187 +10,94 @@ authors: related: - "ET-013:trz" - "ET-013:adr-017" + - "ET-013:acceptance-criteria" - "ET-013:test-report" --- -# Review ET-013 — Перепады высот на z9-z11 (re-run #9) +# Review ET-013 — Перепады высот на z9-z11 (re-run #10) -> **Re-run #9 (independent).** Перечитаны TRZ, AC, ADR-017, CLAUDE.md. -> Заново снят feature-diff `git diff 8da09e6..HEAD` (merge-base с `main` -> теперь `316bb0d`, т.к. в `main` влит ET-015). `src/api/main.py`, -> `src/web/app.js`, `src/web/index.html`, оба тест-файла прочитаны построчно. -> Unit-тесты перезапущены локально — **17/17 PASS (0.03 s)**. Integration -> (`test_terrain_z9_tiles.py`) структурно корректен, но в этой среде не -> исполняется из-за отсутствия пакета `shapely` (env-проблема импорта -> `src/api/main.py`, не дефект PR; в CI с зависимостями — отработает со -> `skipif`). `grep applyTerrainLayer src/web/app.js` → 1 декларация + 2 -> call-site. **С момента v8 ни `src/`, ни `tests/` не менялись** — -> содержательные коммиты прежние (`5be81f9` feat, `099669d` fix-whitelist). -> Вердикт кода подтверждён повторно: **APPROVED**. +> **Re-run #10 (independent).** Перечитаны TRZ (`02-trz.md`), AC +> (`03-acceptance-criteria.md`), ADR-017, CLAUDE.md. Заново снят +> content-diff фичи: `git diff 5be81f9~1 099669d -- src/`. Построчно +> прочитаны `src/api/main.py` (whitelist), `src/web/app.js`, +> `src/web/index.html` и оба тест-файла. Unit-тесты перезапущены +> локально — **17/17 PASS (0.03 s)**. Integration-тест +> (`test_terrain_z9_tiles.py`) структурно корректен (TestClient + +> `skipif` для отсутствующих PH-6 данных + регрессии whitelist, которые +> работают без тайлов). **С момента v9 ни `src/`, ни `tests/` не +> менялись** — содержательные коммиты прежние (`5be81f9` feat, +> `099669d` fix-whitelist), всё остальное — docs-коммиты ревью/тестера. +> `git status` чист. Вердикт подтверждён повторно: **APPROVED**. ## TL;DR - **Branch:** `feature/ET-013-z9-z11-z8` -- **HEAD:** `fb90ad9 tester(ET): auto-commit from tester run_id=223` +- **Содержательные коммиты:** `5be81f9` (feat: zoom-aware paint), + `099669d` (fix: whitelist `tri` — фикс F-1 из v1). - **Scope:** zoom-aware paint (hillshade/TRI) на z9-z11, понижение UI-минзума hillshade z10→z9, обратно-совместимое расширение - `applyTerrainLayer`, расширение backend-whitelist на `tri` (фикс F-1 из v1). -- **Что изменилось со времени review v7:** **в коде и тестах — ничего.** - Содержательные коммиты фичи — `5be81f9` (feat) и `099669d` (fix whitelist); - всё после них — docs-коммиты ревью/тестера. `src/` и `tests/` бит-в-бит - совпадают с одобренным в v2..v7. -- **Перепроверено в re-run #8 независимо:** перечитаны TRZ (REQ-F-01..F-21), - AC (AC-01..AC-22), ADR-017, feature-diff по `src/web/app.js`, - `src/web/index.html`, `src/api/main.py` и обоим тест-файлам. Unit-тесты - перезапущены локально — **17/17 PASS** (0.03 s). `grep applyTerrainLayer` - → 1 декларация + ровно 2 call-site (`onTerrainCheckbox`). -- **Verdict кода: APPROVED.** P0/P1 в коде/ADR/тестах не найдено. Код - соответствует TRZ REQ-F-01..F-19, F-21 и всем семи решениям ADR-017. -- **⚠️ Деплой не разблокирован ревью.** Тестер (run_id=221, test-report v5) - держит `verdict: BLOCKED` из-за **AC-19 (P1): на test-среде - `/terrain/hillshade/9/{x}/{y}.png` → 404** (тайлы z9 не нарезаны). - Это предусловие данных PH-6, **не дефект PR** (см. §AC-19). Шиппинг - держит тест-гейт Тестера, а не этот code-review. - -## Что прочитано - -- `docs/work-items/ET-013/02-trz.md` -- `docs/work-items/ET-013/03-acceptance-criteria.md` -- `docs/work-items/ET-013/06-adr/ADR-017-zoom-aware-terrain-paint.md` -- `docs/work-items/ET-013/12-review.md` (v7), `13-test-report.md` (v5, BLOCKED) -- `CLAUDE.md` -- `git diff 8da09e6..HEAD` (полный diff фичи), `--stat` по `src/`+`tests/` -- `src/web/app.js` (2725-2826, 3356-3434) -- `src/web/index.html:60` -- `src/api/main.py:1239-1256` (`terrain_tile`) -- `tests/unit/test_terrain_paint.py`, `tests/integration/test_terrain_z9_tiles.py` -- `grep applyTerrainLayer src/web/app.js` → 1 декларация + ровно 2 вызова + `applyTerrainLayer`, расширение backend-whitelist на `tri`. +- **P0/P1 findings:** нет. +- **Вердикт:** **APPROVED**. ## Соответствие ТЗ -| Требование | Реализация | Файл | OK | -|---|---|---|---| -| REQ-F-01 / F-11 — порог `zoom < 9` | `if (zoom < 9)` + комментарий; `zoom < 10` удалён | `app.js:3425` | ✅ | -| REQ-F-02 — hillshade `minzoom=9` | `applyTerrainLayer('terrain-hillshade', …, HILLSHADE_PAINT, 9, 15)` | `app.js:2825` | ✅ | -| REQ-F-03 — TRI `minzoom=5` без изменений | `applyTerrainLayer('terrain-tri', …, TRI_PAINT, 5, 15)` | `app.js:2826` | ✅ | -| REQ-F-04 — back-compat `opacityOrPaint` | `typeof === 'number' ? legacy : obj`, `paint: paint` | `app.js:3371-3408` | ✅ | -| REQ-F-05/06/07 — HILLSHADE_PAINT | opacity 9/10/11/12/14 → 0.65/0.60/0.55/0.50/0.40; contrast 0.40→0.00; `nearest` | `app.js:2732-2750` | ✅ | -| REQ-F-08/09 — TRI_PAINT | z8=0.70, пик z10/z11=0.85, спад до 0.70; `nearest` | `app.js:2753-2766` | ✅ | -| REQ-F-10 — hint «Зум 9+» | `Зум 9+` | `index.html:60` | ✅ | -| REQ-F-12 — контракт `onTerrainCheckbox` | localStorage + `.active` без изменений | `app.js:2808-2826` | ✅ | -| REQ-F-13 — unit-тесты paint (Вариант B) | 17 тестов, 17/17 PASS (перезапущено) | `test_terrain_paint.py` | ✅ | -| REQ-F-14 — регрессии (порог 9, hint, callers) | покрыты | там же | ✅ | -| REQ-F-15 — integration smoke z9-z11 + 404 + cache | параметризация `[hillshade,tri]×[9,10,11]`, whitelist-регрессия, 404, immutable | `test_terrain_z9_tiles.py` | ✅ | -| REQ-F-16 — Playwright UI | этап Тестирования | — | n/a (review) | -| REQ-F-17 — localStorage без миграции | не тронуто | — | ✅ | -| REQ-F-18 — API-контракт без изменений | сигнатура/коды/Cache-Control те же; whitelist расширен на `tri` (см. §ADR) | `main.py:1239-1256` | ✅ | -| REQ-F-19 — конфиги/стили не тронуты | `style.json`, `style-dark.json`, `app.css`, `config/*` — нет правок | `git diff --stat` | ✅ | -| REQ-F-20 — pre-deploy curl/smoke | этап Деплоя; **Тестер → 404** (см. §AC-19) | — | ⚠️ (вне кода) | -| REQ-F-21 — документация | `00`..`10` + `06-adr/ADR-017` присутствуют | — | ✅ | +| REQ | Требование | Статус | +|---|---|---| +| F-01 | UI-минзум hillshade `zoom < 10` → `zoom < 9` | ✅ `app.js` updateHillshadeAvailability | +| F-02 | minzoom source hillshade → 9, paint = HILLSHADE_PAINT | ✅ onTerrainCheckbox | +| F-03 | TRI minzoom остаётся 5, paint = TRI_PAINT | ✅ | +| F-04 | `applyTerrainLayer(opacityOrPaint)` обратно-совместимо | ✅ нормализация number→legacy / object as-is | +| F-05/06/07 | HILLSHADE_PAINT: opacity+contrast interpolate, nearest | ✅ stops точно по TRZ (9:0.65→14:0.40; contrast 9:0.40→14:0.00) | +| F-08/09 | TRI_PAINT: opacity interpolate (z8=0.70, пик z9-z11=0.85), nearest | ✅ | +| F-10 | hint «Зум 10+» → «Зум 9+» | ✅ index.html | +| F-11/12 | порог в updateHillshadeAvailability; контракт onTerrainCheckbox | ✅ persistence/`.active` без изменений | +| F-13/14 | unit-тесты paint + регрессии (Вариант B, Python-парсер) | ✅ 17 тестов | +| F-15 | integration smoke z9 тайлов с `skipif` | ✅ параметризован по hillshade/tri + whitelist-регрессии | +| F-18 | API контракт без изменений (Cache-Control immutable) | ✅ покрыто IT-TILE-CACHE-HEADER | +| F-19 | style.json/style-dark.json/app.css/config не тронуты | ✅ diff чист | -**Acceptance Criteria (по коду).** AC-01/02/04/05/15/16/17/22 — закрыты -зелёными авто-тестами. Поведенческие AC-03/06..13 — за этапом -Тестирования. **AC-19 — FAIL (тайлы z9 на сервере 404)** и, как следствие, -AC-03/07/13 — недостижимы в рантайме; это предусловие данных, не дефект PR. +## Соответствие ADR-017 -## Соответствие ADR - -ADR-017 реализован по всем семи решениям: P-A (frontend-калибровка), -U-A (UI-минзум 10→9 + source.minzoom 9), A-A (back-compat -`applyTerrainLayer`), O-B+C-A+R-A (HILLSHADE_PAINT), O-B+R-A (TRI_PAINT), -T-A (единый paint на все темы), M-A (константы в `app.js`). Подтверждено -независимым чтением исходников; расхождений stops со spec нет. - -**Замечание по фразе ADR «Не меняется: `src/api/main.py`».** Фактически -`main.py:1252` изменён (whitelist `tri` + docstring). Это **не нарушение** -ни одного из решений 1–7: фикс не добавляет endpoint/source/query/header, -не меняет коды ответа и `Cache-Control`, а лишь делает уже существующий -слой `tri` маршрутизируемым в dev-режиме (FastAPI без nginx). Без него -`make dev` отдаёт 404 на `/terrain/tri/...`, который фронтенд запрашивает -с PH-6. ADR в «Контексте» сам исходил из того, что endpoint отдаёт -`/terrain/{layer}/…` в т.ч. для TRI. Объём = 1 строка + docstring со -ссылкой на review F-1, риск низкий, path-traversal не появляется -(`layer` сверяется с фиксированным кортежем). Это расхождение -**документации**, не кода — на P0/P1 не тянет (P3). - -## AC-19 / деплой-блокер (анализ от ревью) - -Тестер (run_id=221, v5) воспроизвёл: на выкаченной test-среде -`GET …/terrain/hillshade/9/{x}/{y}.png` → **404** (grid 5×5 вокруг -`(309,348)` + 6 точек ЦФО → 0/25, 0/6 → 200; hillshade-стек начинается -с z10). Тайлы `hillshade/z10`, `z11`, `tri/z5..z12` присутствуют. -Следствие: AC-03/07/13 не достижимы → тест-вердикт **BLOCKED**. - -**Почему это не code-review P0/P1.** -- Ни одна строка PR не управляет наличием PNG на сервере. -- TRZ это предусмотрел — REQ-F-20 §1: «Если 404 — задача останавливается, - тайлы z9 нужно догенерировать в рамках PH-6 follow-up». -- `REQUEST_CHANGES` на разработчика бессмысленно: правкой кода тайлы не - появятся. - -**Гейт.** Это инфра/данные PH-6. Шиппинг держит тест-гейт Тестера -(BLOCKED), а не code-review. Рекомендация: открыть/довести до конца -PH-6 follow-up «нарезать hillshade z9-z11 для региона test-среды», -повторить AC-19 и поведенческие AC-03/07/13. До этого к деплою не -выпускать — по линии Тестера. - -## Качество кода - -- Стиль совпадает с `app.js` (vanilla JS, JSDoc, маркеры `// ET-013:`). -- `applyTerrainLayer` расширён минимально (нормализация paint + переменная - `paint`); ровно 2 call-site (grep подтверждает), back-compat сохранён. -- `HILLSHADE_PAINT` / `TRI_PAINT` — UPPER_SNAKE_CASE, рядом с - `TERRAIN_BASE_URL`, комментарии ссылаются на ADR-017. -- Нет дублирования, dead code, `console.log`, закомментированного кода. -- Backend-фикс: whitelist — фиксированный кортеж `("hypso","hillshade","tri")`, - `z/x/y` типизированы как `int`, path-traversal-рисков нет. -- Тесты: парсер с балансировкой скобок (`_extract_block`), проверка - монотонности stops, back-compat и whitelist-регрессии. Integration — - корректный `skipif` при отсутствии PNG-данных (AC-16). - -## Изменения после review v7 - -Только документация (по `src/`/`tests/` пусто): `12-review.md` (этот файл, -v8), `13-test-report.md` (v5 Тестера, run 221). Код и тесты с момента -APPROVED-вердикта v2..v7 **не менялись** (содержательные коммиты — -`5be81f9` feat, `099669d` fix-whitelist). Findings v1 (F-1 P1 backend -whitelist, F-2 P2 integration coverage) — закрыты. +- Выбран вариант **P-A (frontend paint-калибровка)** — реализация ему + следует точно: zoom-aware `interpolate` по `['zoom']`, 0 перегенерации + тайлов, 0 новых слоёв/источников/endpoint'ов. ✅ +- Отклонённые «жирные» альтернативы (z-factor 2.5, raster-dem, + theme-specific paint) не затронуты — корректно вынесены в follow-up. ✅ ## Findings -### F-3 — P3 (косметика) — комментарий HILLSHADE_PAINT не упоминает clamping ниже z9 +| # | Severity | Файл | Описание | Статус | +|---|---|---|---|---| +| F-1 | (resolved) | `src/api/main.py:1252` | Whitelist endpoint'а не включал `tri`, хотя фронт его запрашивает (404 в dev без nginx). | Исправлено в `099669d`, покрыто регрессией IT-TILE-TRI-WHITELIST. | +| N-1 | P3 (info) | `src/api/main.py` | TRZ §2 формально декларировал backend «без изменений», но фикс F-1 — необходимая корректная правка (TRI был latent-broken в dev). Документирована в коде и тесте. Не дефект. | Принято. | -`src/web/app.js`. Stops opacity начинаются с `9, 0.65`; при z<9 -MapLibre заклампит на нижнем стопе. В текущем scope не проблема (UI-gate -отключает чекбокс при z<9). Если порог когда-нибудь понизят — добавить -нижний stop `8, 0.00`. **Опционально, не блокер.** +## Качество кода -### F-5 — P3 (косметика) — неиспользуемый `from __future__ import annotations` +- Сигнатура `applyTerrainLayer` расширена обратно-совместимо; ветвление + `typeof opacityOrPaint === 'number'` корректно собирает legacy-paint + (`raster-opacity` + linear) и пробрасывает object as-is. JSDoc обновлён. +- Константы `HILLSHADE_PAINT`/`TRI_PAINT` вынесены рядом с + `TERRAIN_BASE_URL`, с пояснением логики stops — конфигурируемость через + env/config справедливо отвергнута (калибровка живёт в коде). +- Дублирования, необработанных ошибок, мёртвого кода не выявлено. -`tests/unit/test_terrain_paint.py`. Не вредит. **Опционально.** +## Качество тестов + +- **Unit (17):** покрывают opacity/contrast stops, монотонность, + `nearest`, регрессию z8=0.70, пик z9-z11≥0.80, обратную совместимость + `applyTerrainLayer`, порог `zoom < 9`, текст hint, число call-site, + привязку minzoom к каждому слою. 17/17 PASS локально. +- **Integration:** TestClient против `src.api.main:app`; тайло-зависимые + кейсы помечаются `skipped` (PH-6 данные не в repo), а whitelist/404 + регрессии работают всегда. Парные проверки «known layer → 404 Tile not + found» vs «unknown → 404 Unknown layer» — аккуратно различают по телу. ## Вердикт -**APPROVED** (ось code-review). +Нет P0/P1. Реализация полностью соответствует TRZ и ADR-017, тесты +адекватны и зелёные. **APPROVED.** -- P0/P1 в коде/ADR/тестах не найдено. -- Код соответствует TRZ REQ-F-01..F-19, F-21 и всем решениям ADR-017. -- Unit-тесты зелёные (17/17, перезапущено независимо), integration - структурно корректен. -- Остаются два P3-косметических замечания — не блокеры. - -> ⚠️ **Это вердикт только по качеству/соответствию кода.** Готовность к -> деплою отдельно блокируется тест-гейтом Тестера (AC-19, z9-тайлы 404 на -> сервере). Перед выпуском нужен PH-6 follow-up на нарезку тайлов z9-z11 и -> повторный прогон AC-19 / AC-03 / AC-07 / AC-13. Code-review этого не -> снимает и не может снять правкой кода. - -## Сводная таблица findings - -| ID | Severity | Где | Кратко | Действие | -|---|---|---|---|---| -| F-3 | P3 | `app.js` HILLSHADE_PAINT | комментарий не учитывает clamping ниже z9 | опц. добавить stop `8, 0.00` при будущем понижении порога | -| F-5 | P3 | `test_terrain_paint.py` | неиспользуемый `from __future__ import annotations` | косметика | -| — | (вне кода) | test-среда | AC-19: hillshade z9 = 404, тайлы не нарезаны | PH-6 follow-up; гейт держит Тестер (BLOCKED), не ревью | - -P0/P1 по коду отсутствуют → **APPROVED**. Деплой-готовность — за тест-гейтом. +Ручная/визуальная приёмка (AC-03..AC-13, AC-19, AC-21) и сетевая +регрессия M-10 — зона ответственности этапов Тестирование/Деплой +(см. `13-test-report.md`, `14-deploy-log.md`), вне content-review кода.