Reviewer'ом найден pre-existing P1: backend `terrain_tile` whitelist
не пропускал слой `tri`, хотя фронтенд (`onTerrainCheckbox`) шлёт
запросы на `/terrain/tri/{z}/{x}/{y}.png` для слоя «Перепады высот».
На test/prod-среде эти запросы перехватывает nginx (подтверждено
эмпирически — 404 идёт с сигнатурой `nginx/1.18.0 (Ubuntu)`, а не
с FastAPI JSON-detail), но в dev-режиме (`make dev` → FastAPI на
:5556 напрямую) endpoint обязан поддерживать `tri` нативно.
Изменения:
- `src/api/main.py:1252`: whitelist `("hypso", "hillshade")` →
`("hypso", "hillshade", "tri")`. Ответ-контракт и заголовки
идентичны существующим слоям; REQ-F-18 «API contract без изменений»
не нарушен (поведение для уже-известных layer'ов не меняется,
добавляется только поддержка нового layer'а).
- `tests/integration/test_terrain_z9_tiles.py`: новый параметризованный
тест `test_known_terrain_layer_accepted_by_whitelist[hypso|hillshade|tri]`,
фиксирующий регрессию F-1 (не требует локальных PNG-данных:
для несуществующего файла ожидает `detail: "Tile not found"`,
а не `"Unknown layer"`).
- `tests/integration/test_terrain_z9_tiles.py`: параметризация
`test_terrain_tile_available_z9_z10_z11` по `(layer × zoom)` —
6 кейсов вместо 3 (review F-2).
- `tests/integration/test_terrain_z9_tiles.py`: убран неиспользуемый
`from __future__ import annotations` (review F-4); type-аннотации
упрощены (Python 3.10+ нативно).
- `tests/integration/test_terrain_z9_tiles.py`: `test_unknown_terrain_layer_returns_404`
усилен ассертом `detail == "Unknown layer"` (парность с whitelist-тестом).
Тесты: 17/17 unit PASS, 6/6 non-data-зависимых integration PASS,
6 layer×zoom кейсов SKIPPED (нет PH-6 данных в sandbox — корректное
поведение `_maybe_skip`).
Refs: ET-013, review F-1/F-2/F-4 (`docs/work-items/ET-013/12-review.md`).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Понижаем UI-минзум hillshade с 10 до 9 и переводим raster-paint
обоих terrain-слоёв в zoom-aware форму через MapLibre interpolate.
На z9-z11 — пик opacity/contrast, чтобы рельеф читался как на z8;
на z12-z14 — возврат к исходным значениям (регрессия по AC-10).
TRI на z8 остаётся 0.70 (регрессия по AC-06), пик 0.80-0.85 на z9-z11.
Изменения:
- src/web/app.js: добавлены HILLSHADE_PAINT и TRI_PAINT; applyTerrainLayer
расширена для поддержки object-paint (обратно-совместимо); порог
updateHillshadeAvailability понижен до 9; вызовы для hillshade переведены
на minzoom=9.
- src/web/index.html: hint обновлён с «Зум 10+» на «Зум 9+».
- tests/unit/test_terrain_paint.py: 17 тестов покрытия zoom-stops, контракта
applyTerrainLayer и регрессий (UT-PAINT-*, UT-REG-*).
- tests/integration/test_terrain_z9_tiles.py: smoke /terrain endpoint на
z9-z11 + кэш-заголовки (IT-TILE-*).
Backend, тайлы на диске, конфиги, стили — без изменений.
Refs: ET-013
ADR: ADR-017
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Калибровка существующих tier-таблиц `build_gps_mvt` /
`_simplify_coords` (ADR-016), чтобы при первом открытии карты
пользователь видел общее покрытие сети треков, а не пустую подложку.
Backend (src/api/gps_tracks/mvt.py):
- build_gps_mvt: добавлены тиры z<=5 (min_length=10 км, limit=1500)
и z=6 (5 км / 2000); z=7+ — без изменений (регрессия).
- _simplify_coords: tolerance для z=6 = 0.018° (~2 км),
для z<=5 = 0.04° (~4 км); z=7+ не меняется.
Frontend:
- GPS_TRACKS_MIN_ZOOM понижен с 8 до 5; vector-source.minzoom
подхватывает константу автоматически.
- line-width / halo получили stop на z=5 (0.8 / 1.8 CSS-px),
чтобы линия была читаема на любом DPR.
- Hint #public-tracks-zoom-hint: «Зум 8+» → «Зум 5+».
Тесты:
- 8 unit zoom-tier (UT-Z5/6/7/8/12) — REQ-F-09.
- 10 unit simplify (UT-SIMP-*) — REQ-F-10.
- 9 integration endpoint z5-z7 (IT-Z5/6/7, CACHE, REGRESS) — REQ-F-11/12.
- 2 perf (PERF-Z5-01/02; avg ~64 ms, p95 ~89 ms при 500 треках —
ниже бюджета 200/500 ms по M-6) — REQ-F-13.
Маркер @pytest.mark.perf, не в основном CI-gate.
Контракт API /api/gps-tracks* не меняется (REQ-F-15);
localStorage-ключи и конфиги тоже (REQ-F-16, F-18).
Refs: ET-012
ADR: docs/work-items/ET-012/06-adr/ADR-016-z5-tiling-policy.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #21 merged to main and tag v0.0.3 pushed, but docker-compose roll on
test host did not happen: /home/slin/bin/enduro-deploy-hook.sh exits with
"Permission denied" on /var/log/enduro-trails/deploy-hook.log
(root-owned, no NOPASSWD sudo for slin). Healthcheck/smoke/rollback all
skipped — new code is on main but old image still serves traffic.
Action for ops: see docs/work-items/ET-011/14-deploy-log.md
("Что нужно от ops, чтобы доехать"). After fix, re-run deploy hook —
PR/tag do not need to be redone.
CI failures на feature/ET-011 были вызваны двумя проблемами:
1. ruff `E402 Module level import not at top of file` × 10 в src/api/main.py:
- 9 ошибок от ET-008 (GPS_TRACKS_DB_PATH между импортами) +
1 новая от ET-011 (`from src.api.gps_tracks.endpoint import ...` после
определения `app`). Перенёс все импорты наверх; константы
GPS_TRACKS_DB_PATH и GPS_SOURCES_CONFIG_PATH теперь сразу после import-блока,
а создание router-а остаётся в нижней части файла (зависит от `app`).
2. pyproject.toml не объявлял runtime-deps, которые реально импортируются
в src/ (defusedxml, pyyaml) и в тестах (lxml). Dockerfile брал их из
src/api/requirements.txt, но CI jobs `lint`/`test` ставят `.[dev]` —
поэтому `pytest tests/` падал на ModuleNotFoundError при коллекции
тестов из ET-008/ET-009/ET-011. Добавил недостающие пины в pyproject
(defusedxml/pyyaml в основные deps, lxml — только в dev, нужен для
XSD-валидации в test_gps_tracks_download/_gpx_builder).
Проверено локально в чистом venv после `pip install .[dev]`:
- `ruff check src/` → All checks passed
- `pytest tests/` → 200 passed, 2 deselected
Refs: ET-011
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove dangerous git checkout $LAST_TAG from deployer prompt: it left the shared working copy in detached HEAD (breaking the next git pull) and did not roll back prod at all. Rollback now goes through the deploy hook (ssh ... bash ${HOOK} --rollback), which restores the app container to the previously running image. Narrow tools to Bash (git, curl) since the deployer no longer invokes docker directly.
Add .task*.md to .gitignore and remove already-tracked task files from
the index. These are orchestrator runtime artifacts (B-3) and should not
be committed.
- Tag v0.0.2 cut from main b5ba7b2 (PR #16 merged).
- enduro_russia pipeline run: ok, 5 new + 36 updated, 0 errors (39 tracks in DB).
- wikiloc: 403 from WAF on first request, graceful stop (config-complete, scrape-blocked).
- Public URL returns 502 due to pre-existing nginx config bug
(sites-enabled pointed to :5558, app listens on :5556). Patched the
config file in place; awaits operator-side `systemctl reload nginx`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
F-01 (P1): _buildGpsFiltersUI больше не хардкодит список источников —
подтягивает source_id из /api/gps-tracks/health.tracks_by_source
(ADR-013 §3 Решение D, опция D2). Маппинг source_id → label вынесен
в JS-константу GPS_SOURCE_LABELS. Активация четвёртого источника
теперь не требует изменений в этом коде.
F-02 (P1): attribution фиксируется в момент addSource, а не мутацией
src.attribution после. MapLibre AttributionControl не реагирует на
прямое присвоение — потому до этого фикса AC-15 проваливался бы в
UI-тестах. Теперь onPublicTracksCheckbox / restorePublicTracksState
сначала await _fetchGpsHealth() → _buildGpsAttributionString(),
потом _ensureGpsSources(map, attribution).
Добавлен кэш + in-flight Promise (window.gpsTracksLayer._healthCache /
_healthFetchPromise) — переоткрытие sheet'а фильтров не плодит
дублирующих сетевых запросов.
Все 24 node-теста gps_tracks.test.js зелёные.
Refs: ET-009
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>