# 2026-05-02 — Дневник

## Enduro Trails — отладка прототипа (утро)

### Что делали
Долгая отладка карты грунтовок ЦФО на http://82.22.50.71:5558

### Проблемы и решения

#### 1. style.json не грузился (404 на /static/style.json)
- Путь в index.html был `/static/style.json`, а сервер отдаёт `/style.json`
- Исправлено: путь исправлен на `/style.json`

#### 2. Шрифты — demotiles.maplibre.org отдавал 404
- MapLibre зависал при инициализации
- Исправлено: переключили на `https://fonts.openmaptiles.org/{fontstack}/{range}.pbf`

#### 3. Невалидный фильтр в style.json
- `trails-path-bridleway` использовал expression синтаксис: `["in", ["get", "highway"], "literal", [...]]`
- MapLibre 4.x падал с ошибкой "Expected 2 arguments, but found 3"
- Исправлено: заменили на legacy синтаксис `["in", "highway", "path", "bridleway", "footway"]`

#### 4. Самописный MVT энкодер давал пустые тайлы
- WKB геометрия имеет тип `0x20000002` (SRID+LineString), не входил в список `(2, 1000002, 2147483650)`
- Исправлено: заменили самописный энкодер на `mapbox-vector-tile` v2.2.0 + `shapely`

#### 5. Треки смещались при зуме
- `quantize_bounds` передавался с буфером, координаты выходили за [0,4096]
- Исправлено: клиппинг строго по границе тайла через `shapely.ops.clip_by_rect`, `quantize_bounds=(west, south, east, north)` без буфера

#### 6. Y-координата перевёрнута
- `mapbox-vector-tile` v2.2.0 НЕ флипает Y по умолчанию
- MVT spec: Y=0 = север (верх)
- Исправлено: `y_coord_down=True` в encode

#### 7. index.html — дублирование window._map и незакрытые теги
- При рефакторинге initMap() файл был обрезан без `</script></body></html>`
- Исправлено: добавлены закрывающие теги

### Текущее состояние (10:09 UTC)
- Треки отображаются ✅
- OSM подложка работает ✅  
- Смещение при зуме — частично исправлено, Слава говорит "стало лучше, но ещё сохраняется"
- `y_coord_down=True` применён, ждём подтверждения от Славы

### Технические детали

#### Стек на сервере (mva154, 82.22.50.71:5558)
- Docker контейнер `enduro-trails`
- FastAPI + uvicorn
- SQLite с индексами min_lon/max_lon/min_lat/max_lat
- `mapbox-vector-tile` v2.2.0, `shapely`
- БД: `/data/centralfederal.sqlite`, 1.1М треков

#### Доступ к серверу
- SSH: `sshpass -p 'пароль' ssh node@82.22.50.71` (пароль в .env как MVA154_PASSWORD)
- Контейнер: `docker exec enduro-trails ...`
- app.py на сервере: `/app/app.py` внутри контейнера
- Локальная копия: `/tmp/app_new.py` (актуальная версия)

#### Ключевые файлы
- `tasks/enduro-trails/prototype/static/style.json` — стили карты
- `tasks/enduro-trails/prototype/static/index.html` — фронтенд
- `/tmp/app_new.py` — актуальный app.py (нужно синхронизировать с workspace)

### Итоговое исправление (11:49 UTC) ✅
- Убран `clip_by_rect` в `build_mvt()` — треки отдаются целиком, MapLibre клипит на клиенте
- `y_coord_down=True` в `mapbox_vector_tile.encode` сохранён
- Слава подтвердил: треки перестали скакать при зуме
- TODO обновить PROJECT.md, TASK.md, онтологию

### TODO
- [ ] Синхронизировать `/tmp/app_new.py` с `tasks/enduro-trails/prototype/app.py`
- [ ] Обновить PROJECT.md и TASK.md для enduro-trails
- [ ] Обновить онтологию для проекта enduro-trails

### Обновлённый vibecode API key (11:27 UTC)
- Слава прислал новый ключ для провайдера vibecode: `sk-81bb8312c879e5c3aec7d02d671edcb4eab4b28cebcb32cb`
- Нужно обновить в `~/.openclaw/.env` (переменная `VIBECODE_API_KEY`)


## Enduro Trails — продолжение отладки (11:40-12:09 UTC)

### Скриншоты от Славы
- Прислал 2 пары скринов: z12.4 vs z11.8/12.0 — треки всё ещё смещаются при зуме
- "Опять пляшут" / "Ещё как прыгают!!!" — подтверждение что проблема не решена
- Ключевое наблюдение Славы: **"Треки же отражают дорогу, почему они смещаются"** — треки из OSM должны идеально ложиться на OSM подложку

### Диагностика
- Удаление `clip_by_rect` НЕ помогло — треки продолжали прыгать
- Причина: без клиппинга геометрия выходит за `quantize_bounds`, координаты >4096 ломают MapLibre
- **Найден корневой баг**: в WKB геометрии из БД координаты хранятся как `(lat, lon)`, а `wkb_to_coords()` читала их как `(lon, lat)`. Широта и долгота были перепутаны!

### Фикс (12:07 UTC)
- В `wkb_to_coords()`: `lat, lon = struct.unpack_from('<dd', data, offset)` → `coords.append((lon, lat))`
- Раньше было: `lon, lat = struct.unpack_from(...)` — неправильно, потому что БД хранит lat-first
- Файл обновлён в контейнере через docker cp + docker restart

### Статус: ожидает подтверждения
- Слава написал "Test" — видимо проверяет карту
- Ждём подтверждения что треки теперь ложатся на дороги OSM

### TODO
- [ ] Подтвердить визуально: треки совпадают с дорогами OSM при всех зумах
- [ ] Синхронизировать app.py с workspace: `tasks/enduro-trails/prototype/app.py`
- [ ] Обновить PROJECT.md и TASK.md для enduro-trails
- [ ] Обновить онтологию для проекта enduro-trails


## Enduro Trails — ревью прототипа (12:53 UTC)
- Слава попросил ревью — после серии горячих фиксов (clip_by_rect, y_coord_down, swap lat/lon) код запутан
- TODO: провести полное ревью app.py и index.html
# 2026-05-02 — Дневник

## Enduro Trails — отладка прототипа (утро)

### Что делали
Долгая отладка карты грунтовок ЦФО на http://82.22.50.71:5558

### Проблемы и решения

#### 1. style.json не грузился (404 на /static/style.json)
- Путь в index.html был `/static/style.json`, а сервер отдаёт `/style.json`
- Исправлено: путь исправлен на `/style.json`

#### 2. Шрифты — demotiles.maplibre.org отдавал 404
- MapLibre зависал при инициализации
- Исправлено: переключили на `https://fonts.openmaptiles.org/{fontstack}/{range}.pbf`

#### 3. Невалидный фильтр в style.json
- `trails-path-bridleway` использовал expression синтаксис: `["in", ["get", "highway"], "literal", [...]]`
- MapLibre 4.x падал с ошибкой "Expected 2 arguments, but found 3"
- Исправлено: заменили на legacy синтаксис `["in", "highway", "path", "bridleway", "footway"]`

#### 4. Самописный MVT энкодер давал пустые тайлы
- WKB геометрия имеет тип `0x20000002` (SRID+LineString), не входил в список `(2, 1000002, 2147483650)`
- Исправлено: заменили самописный энкодер на `mapbox-vector-tile` v2.2.0 + `shapely`

#### 5. Треки смещались при зуме
- `quantize_bounds` передавался с буфером, координаты выходили за [0,4096]
- Исправлено: клиппинг строго по границе тайла через `shapely.ops.clip_by_rect`, `quantize_bounds=(west, south, east, north)` без буфера

#### 6. Y-координата перевёрнута
- `mapbox-vector-tile` v2.2.0 НЕ флипает Y по умолчанию
- MVT spec: Y=0 = север (верх)
- Исправлено: `y_coord_down=True` в encode

#### 7. index.html — дублирование window._map и незакрытые теги
- При рефакторинге initMap() файл был обрезан без `</script></body></html>`
- Исправлено: добавлены закрывающие теги

### Текущее состояние (10:09 UTC)
- Треки отображаются ✅
- OSM подложка работает ✅  
- Смещение при зуме — частично исправлено, Слава говорит "стало лучше, но ещё сохраняется"
- `y_coord_down=True` применён, ждём подтверждения от Славы

### Технические детали

#### Стек на сервере (mva154, 82.22.50.71:5558)
- Docker контейнер `enduro-trails`
- FastAPI + uvicorn
- SQLite с индексами min_lon/max_lon/min_lat/max_lat
- `mapbox-vector-tile` v2.2.0, `shapely`
- БД: `/data/centralfederal.sqlite`, 1.1М треков

#### Доступ к серверу
- SSH: `sshpass -p 'пароль' ssh node@82.22.50.71` (пароль в .env как MVA154_PASSWORD)
- Контейнер: `docker exec enduro-trails ...`
- app.py на сервере: `/app/app.py` внутри контейнера
- Локальная копия: `/tmp/app_new.py` (актуальная версия)

#### Ключевые файлы
- `tasks/enduro-trails/prototype/static/style.json` — стили карты
- `tasks/enduro-trails/prototype/static/index.html` — фронтенд
- `/tmp/app_new.py` — актуальный app.py (нужно синхронизировать с workspace)

### Итоговое исправление (11:49 UTC) ✅
- Убран `clip_by_rect` в `build_mvt()` — треки отдаются целиком, MapLibre клипит на клиенте
- `y_coord_down=True` в `mapbox_vector_tile.encode` сохранён
- Слава подтвердил: треки перестали скакать при зуме
- TODO обновить PROJECT.md, TASK.md, онтологию

### TODO
- [ ] Синхронизировать `/tmp/app_new.py` с `tasks/enduro-trails/prototype/app.py`
- [ ] Обновить PROJECT.md и TASK.md для enduro-trails
- [ ] Обновить онтологию для проекта enduro-trails

### Обновлённый vibecode API key (11:27 UTC)
- Слава прислал новый ключ для провайдера vibecode: `sk-81bb8312c879e5c3aec7d02d671edcb4eab4b28cebcb32cb`
- Нужно обновить в `~/.openclaw/.env` (переменная `VIBECODE_API_KEY`)


## Enduro Trails — продолжение отладки (11:40-12:09 UTC)

### Скриншоты от Славы
- Прислал 2 пары скринов: z12.4 vs z11.8/12.0 — треки всё ещё смещаются при зуме
- "Опять пляшут" / "Ещё как прыгают!!!" — подтверждение что проблема не решена
- Ключевое наблюдение Славы: **"Треки же отражают дорогу, почему они смещаются"** — треки из OSM должны идеально ложиться на OSM подложку

### Диагностика
- Удаление `clip_by_rect` НЕ помогло — треки продолжали прыгать
- Причина: без клиппинга геометрия выходит за `quantize_bounds`, координаты >4096 ломают MapLibre
- **Найден корневой баг**: в WKB геометрии из БД координаты хранятся как `(lat, lon)`, а `wkb_to_coords()` читала их как `(lon, lat)`. Широта и долгота были перепутаны!

### Фикс (12:07 UTC)
- В `wkb_to_coords()`: `lat, lon = struct.unpack_from('<dd', data, offset)` → `coords.append((lon, lat))`
- Раньше было: `lon, lat = struct.unpack_from(...)` — неправильно, потому что БД хранит lat-first
- Файл обновлён в контейнере через docker cp + docker restart

### Статус: ожидает подтверждения
- Слава написал "Test" — видимо проверяет карту
- Ждём подтверждения что треки теперь ложатся на дороги OSM

### TODO
- [ ] Подтвердить визуально: треки совпадают с дорогами OSM при всех зумах
- [ ] Синхронизировать app.py с workspace: `tasks/enduro-trails/prototype/app.py`
- [ ] Обновить PROJECT.md и TASK.md для enduro-trails
- [ ] Обновить онтологию для проекта enduro-trails


## Enduro Trails — ревью прототипа (12:53 UTC)
- Слава попросил ревью — после серии горячих фиксов (clip_by_rect, y_coord_down, swap lat/lon) код запутан
- TODO: провести полное ревью app.py и index.html

---

## Enduro Trails — полное ревью и план восстановления (13:07–13:57 UTC)

### Что сделано
- Прочитаны все файлы прототипа: `app.py` (из контейнера), `docker-compose.yml`, `static/index.html`, `static/style.json`, `scripts/parse.py`, `scripts/download.sh`
- Прочитаны требования: `TECHNICAL_SPEC.md`, `CONCEPT.md`, `PROJECT.md`, `TASK.md`
- Проведено полное ревью с сопоставлением требований и реализации
- Составлен план восстановления прототипа
- Составлено жёсткое ТЗ для Dev-агента

### Ключевые находки ревью

#### CRITICAL — workspace vs container расхождение
- workspace `app.py` был в мусорном состоянии: дубликаты функций, синтаксические ошибки
- контейнер содержал чистую рабочую версию
- **ИСПРАВЛЕНО**: workspace синхронизирован из контейнера

#### CRITICAL — parse.py не соответствует реальной схеме БД
- `parse.py` создаёт таблицу `trails` БЕЗ колонок `min_lon/max_lon/min_lat/max_lat`
- реальная БД имеет эти колонки + индекс `idx_trails_bbox`
- если запустить `parse.py` заново — получим несовместимую БД
- **НЕ ИСПРАВЛЕНО** — передаётся Dev-агенту

#### HIGH — layer toggles сломаны
- `layerGroups` в `index.html` ссылается на `trails-grade12`, `trails-grade345`
- в `style.json` таких слоёв нет — есть `trails-track`, `trails-asphalt`
- **НЕ ИСПРАВЛЕНО** — передаётся Dev-агенту

#### HIGH — popup не соответствует ТЗ
- `build_mvt()` в `app.py` не передаёт `length_m` и `mtb_scale` в props
- popup показывает неполные данные
- **НЕ ИСПРАВЛЕНО** — передаётся Dev-агенту

#### HIGH — POI без bbox-фильтра в SQL
- `SELECT ... FROM poi LIMIT 2000` без пространственного фильтра
- фильтрация в Python — неэффективно
- **НЕ ИСПРАВЛЕНО** — передаётся Dev-агенту

#### MEDIUM — нет валидации z/x/y
- tile endpoint принимает любые значения без проверки
- **НЕ ИСПРАВЛЕНО** — передаётся Dev-агенту

#### MEDIUM — docker-compose тяжёлый
- apt-get + pip install при каждом старте
- нет Dockerfile
- **НЕ ИСПРАВЛЕНО** — передаётся Dev-агенту

#### MEDIUM — весь фронт в одном index.html
- нет отдельных app.js / app.css как требует ТЗ
- **НЕ ИСПРАВЛЕНО** — передаётся Dev-агенту

### Состояние БД (реальное)
- 1,141,926 trails
- 14,882 POI
- колонки min_lon/max_lon/min_lat/max_lat есть
- индекс idx_trails_bbox есть
- Spatialite НЕ загружен — работает обычный SQLite + WKB + bbox columns

### Принятые решения по архитектуре тайлов
- без `clip_by_rect` — MapLibre клипит на клиенте
- `quantize_bounds` с буфером 10%
- `y_coord_down=True`
- WKB хранит координаты как (lat, lon) — читать нужно соответственно

### ТЗ для Dev-агента
Составлено полное ТЗ на стабилизацию прототипа v0.1:
- синхронизация source of truth
- исправление parse.py под реальную схему
- стабилизация app.py (валидация, props, POI bbox)
- починка frontend (toggles, popup, split js/css)
- smoke/regression checks
- обновление документации

**Definition of Done:**
1. `prototype/` — канонический рабочий код
2. `parse.py` создаёт БД совместимую с `app.py`
3. runtime не зависит от ручных правок в контейнере
4. tile endpoint стабильно работает
5. trail popup показывает нужные поля
6. layer toggles реально работают
7. есть минимальный regression/smoke набор
8. документация обновлена

### Следующий шаг
Слава подтвердил план и ТЗ. Нужно передать ТЗ Dev-агенту через `sessions_spawn`.
