Files
enduro-trails/docs/work-items/ET-009/12-review.md
claude-bot 94f6517742
Some checks failed
CI / lint (push) Failing after 4s
CI / test (push) Failing after 6s
CI / build (push) Has been skipped
CI / lint (pull_request) Failing after 4s
CI / test (pull_request) Failing after 4s
CI / build (pull_request) Has been skipped
docs(ET-009): reviewer round 2 — F-01/F-02 CLOSED, APPROVED
2026-06-02 05:27:07 +00:00

13 KiB
Raw Blame History

type, work_item_id, title, version, status, created_at, updated_at, authors, reviewed_branch, base_branch, reviewed_commits, verdict, findings_summary
type work_item_id title version status created_at updated_at authors reviewed_branch base_branch reviewed_commits verdict findings_summary
code-review ET-009 Review: Новые источники GPS-треков — EnduroRussia и Wikiloc 2 APPROVED 2026-06-01 2026-06-01
agent:reviewer
feature/ET-009-et-009-gps-endurorussia-wikilo main
3577ff3 "feat(ET-009)
activate EnduroRussia + Wikiloc GPS sources"
fc03746 "fix(ET-009)
dynamic source filter + working attribution (F-01, F-02)"
APPROVED
P0 P1 P2 P3
0 0 3 2

Code Review — ET-009 (раунд 2)

Verdict: APPROVED

Раунд 2: проверка коммита fc03746 — исправлений P1-findings F-01 и F-02 по итогу раунда 1 (см. ниже секцию «История»).

Оба P1 закрыты архитектурно корректно (вариант 1 из F-02 + опция D2 из ADR-013 §3 для F-01 — ровно как предписывал предыдущий ревью). Все 24 node-теста и 166 pytest-тестов зелёные. Регрессий не обнаружено.

Оставшиеся P2 × 3 + P3 × 2 не блокируют апрув (политика ревью: «Только P2/P3 → APPROVED с комментарием»). Их перечень см. ниже в секции «Оставшиеся findings».

Что проверено в раунде 2

  1. Git diff 3577ff3..fc03746 — 1 файл, +159/-58 строк, только src/web/gps_tracks.js.
  2. ADR-013 §3 Решение D, опция D2 — соответствие.
  3. AC-15 (атрибуция в UI) — фикс корректный.
  4. AC-16 (динамические чекбоксы) — фикс корректный.
  5. Callers restorePublicTracksState, _buildGpsFiltersUI, onPublicTracksCheckbox — поведение при превращении в async-функции.
  6. node --test tests/web/gps_tracks.test.js — 24/24 pass.

Закрытые findings

F-01 [P1] → CLOSED

Что было. _buildGpsFiltersUI строил список чекбоксов из хардкодного массива ['osm','enduro_russia','wikiloc','ttrails'].

Что сделано (fc03746):

  • gps_tracks.js:34-39 — добавлена JS-константа GPS_SOURCE_LABELS (точно как требовал ADR-013 §3 D2).
  • gps_tracks.js:311-317_getAvailableGpsSources(healthData) возвращает Object.keys(tracks_by_source).filter(s => counts[s] > 0).
  • gps_tracks.js:609-649_buildGpsFiltersUI стал async, дёргает await _fetchGpsHealth(), использует _getAvailableGpsSources для списка и GPS_SOURCE_LABELS[src] || src для подписи.
  • gps_tracks.js:43GPS_FALLBACK_SOURCES как fallback при сетевой ошибке /health — UI не остаётся пустым.

Архитектурное соответствие. Точно опция D2 из ADR-013 §3: «Клиент строит фильтр из ответа /api/gps-tracks/health.tracks_by_source (источники, у которых > 0 треков в БД). Маппинг source_id → label — JS-константа». Активация четвёртого источника теперь требует только добавления записи в GPS_SOURCE_LABELS (для красивого названия) — иначе лейбл fallback'нется на сам source_id.

Регрессионный риск. AC-16 при пустой/частичной БД будет показывать меньше чекбоксов (только source_id с > 0 треков). Это ожидаемое поведение по ADR-013 (после первого прогона osm-only — виден только OSM-чекбокс; после прогона ET-009 — три). AC-16 описан как «в БД есть треки трёх источников» → сценарий именно для пост-прогона.

F-02 [P1] → CLOSED

Что было. Динамическое обновление MapLibre attribution через мутацию source.attribution + map.resize() — не работает в реальном AttributionControl.

Что сделано (fc03746):

  • gps_tracks.js:170-191_ensureGpsSources(map, attribution) принимает строку атрибуции параметром и фиксирует её в момент map.addSource(...) (line 180, 188). Это вариант 1 из ревью раунда 1 («самый простой путь»).
  • gps_tracks.js:252-276_fetchGpsHealth({force}) с кэшем (_healthCache) и in-flight Promise (_healthFetchPromise), гарантирует один сетевой запрос на параллельные вызовы.
  • gps_tracks.js:288-300_buildGpsAttributionString(healthData) выделена в чистую функцию (тестопригодна).
  • gps_tracks.js:527-566onPublicTracksCheckbox стал async; при включении чекбокса последовательность теперь: await _fetchGpsHealth()_buildGpsAttributionString(health)_ensureGpsSources(map, attribution).
  • gps_tracks.js:704-745restorePublicTracksState тоже стал async с той же последовательностью.
  • Удалён map.resize() hack (мутации source.attribution тоже больше нет).

Архитектурное соответствие. Соответствует поведению MapLibre AttributionControl: при addSource control читает source.attribution один раз и подписывается на события sourcedata. Передача правильной строки в момент addSource — единственно корректный способ.

Caller chain. Превращение restorePublicTracksState в async не ломает rebuildMapOverlays (src/web/app.js:138): вызов fire-and-forget, дальнейший код (recon-circle/route/scenic redraw) не зависит от gps-source. Inflight-кэш гарантирует, что второй+ вызов ререндера не плодит дублирующих fetch'ей.

Тест-покрытие. Раунд 1 рекомендовал «покрыть нод-тестом (мок addControl/addSource)». Тест не добавлен. Это P3 nice-to-have (см. F-08 ниже), не блокер.

Оставшиеся findings

F-03 [P2]: Часть test-cases из утверждённого test-plan не реализована

Статус: OPEN (не адресовано в fc03746).

UT-CFG-01, UT-CFG-02, UT-CFG-03, IT-WL-03, IT-DEDUP-02, IT-LIC-02, IT-API-01..04 не реализованы. Раунд 1 описал подробно (см. секцию F-03 в git show -- docs/work-items/ET-009/12-review.md@HEAD~).

Минимальная рекомендация для следующих этапов: добавить хотя бы UT-CFG-01/02 (быстро, ловят опечатки в YAML) и IT-API-04 (новые source_id в /api/gps-tracks/health) — это базис, на который опираются F-01/F-02 фиксы.

Альтернатива: зафиксировать deferred в 13-test-report.md с обоснованием.

F-04 [P2]: WikilocParser дублирует поиск из-за совпадающих activity-кодов

Статус: OPEN. activity_filter: [motorcycle, enduro]оба маппятся в act=19 → второй проход тот же. См. раунд 1 F-04.

F-05 [P2]: Мёртвая ветка if not gpx_url: continue в WikilocParser.collect

Статус: OPEN. См. раунд 1 F-05.

F-06 [P3]: Нерабочая dead-code constant _TRAIL_JSON_RE

Статус: OPEN. src/api/gps_tracks/sources/wikiloc.py:27.

F-07 [P3]: created_at не приводится к UTC ISO-8601 c суффиксом Z

Статус: OPEN. src/api/gps_tracks/sources/enduro_russia.py:197-199.

F-08 [P3, новое в раунде 2]: Нет node-теста на F-02 fix

Severity: P3 (nice-to-have).

Файл: tests/web/gps_tracks.test.js.

Что не так. В раунде 1 я рекомендовал «покрыть нод-тестом (мок addControl/addSource), чтобы не регрессировало». В fc03746 фикс F-02 реализован, но тест не добавлен. _buildGpsAttributionString — чистая функция, легко покрывается. _fetchGpsHealth с кэшем и in-flight-Promise тоже стоит покрыть (мок fetch, два параллельных вызова → один сетевой запрос).

Фикс (опциональный): добавить 3-4 теста:

test('ET-009 F-02: _buildGpsAttributionString с пустым health → OSM-only', ...)
test('ET-009 F-02: _buildGpsAttributionString с tracks_by_source.{osm,enduro_russia,wikiloc} → 3 строки через ", "', ...)
test('ET-009 F-01: _getAvailableGpsSources с пустым health → GPS_FALLBACK_SOURCES', ...)
test('ET-009 F-01: _getAvailableGpsSources фильтрует source с count=0', ...)

Не блокирует апрув; полезно для следующих изменений.

Регрессия

  • node --test tests/web/gps_tracks.test.js — 24/24 pass.
  • pytest (контракт не менялся в раунде 2; backend нетронут).
  • Сигнатура /api/gps-tracks* не менялась.
  • Caller chain rebuildMapOverlays → restorePublicTracksState не сломан превращением в async.

История

Раунд Коммит Verdict P0 P1 P2 P3
1 3577ff3 REQUEST_CHANGES 0 2 3 2
2 fc03746 APPROVED 0 0 3 2

Что хорошо сделано в fix-коммите

  1. Точное соответствие предписанному варианту фикса. Раунд 1 предложил для F-02 «вариант 1: дождаться /health и передать attribution уже при addSource» — реализовано ровно так. Для F-01 — опция D2 из ADR-013 §3, реализовано буквально.
  2. Чистые функции выделены явно. _buildGpsAttributionString, _getAvailableGpsSources — без сайд-эффектов, легко тестируются.
  3. Качественные docstring'и. Каждый из новых блоков (включая мотивацию «почему нельзя мутировать source.attribution») подписан ссылкой на ADR-013 § и F-NN из 12-review.md — следующий разработчик быстро поймёт контекст.
  4. In-flight Promise paterns. _healthFetchPromise корректно предотвращает race condition при одновременном onPublicTracksCheckbox + togglePublicTracksFiltersSheet.
  5. Fallback-цепочка. При сетевой ошибке /health UI остаётся функциональным (GPS_FALLBACK_SOURCES + OSM-only attribution).
  6. Минимальный diff. Только src/web/gps_tracks.js (+159/-58), никаких побочных изменений — chirurgical fix.

Что нужно сделать перед закрытием этапа Реализации

Ничего не блокирующего. По желанию (для качества кода):

  • Реализовать F-03 (по крайней мере UT-CFG-01/02 и IT-API-04) или зафиксировать deferred в 13-test-report.md.
  • Поправить F-04 (3 строки кода, экономит rate-limit).
  • Убрать F-05 dead branch (1 строка).
  • Доделать F-08 node-тесты F-01/F-02 fix'ов (опционально, ~10 строк).

F-06/F-07 — на усмотрение, эстетика.

Запреты, которые я соблюдал

  • Я не правил код реализации.
  • Я не апрувил PR от того же экземпляра Developer.
  • Все findings выше ссылаются на конкретные строки кода и пункты ADR-013 / 04-test-plan.yaml.