Files
x0gp/AGENTS.md
x0gp 978d36c505 feat(server): initial implementation
This commit captures the full server code accumulated across
several development sessions. It includes the following layers,
applied in roughly this order during development:

1. Base infrastructure
   - cmd/poc-server HTTP+WebSocket server, /health, /stats
   - internal/transport wire types (Envelope + all message types)
   - internal/catalog, internal/realtime, internal/stats
   - internal/storage/postgres with migrations 001-004
   - .env, .env.example, docker-compose, Dockerfile, scripts/

2. Swagger documentation
   - go get github.com/swaggo/http-swagger
   - swag init produces docs/docs.go
   - /swagger/index.html UI, /swagger/doc.json spec
   - annotations on health/stats/ws and catalog handlers

3. Races API
   - internal/races/{store,service,keyset,types}.go
   - migration 005_races.sql: finished_races, race_plans, race_queue
   - GET /api/races with keyset pagination, status filter
   - GET /api/races/upcoming
   - POST /api/races/queue/join, GET, DELETE
   - POST/GET /api/races/plans, DELETE /{id}
   - lobby.RaceMeta simplification (no host_id/host_name)

4. Races seeder
   - internal/races/seed with deterministic generator
   - --seed-races / --reset CLI flags in main.go
   - 30 finished + 5 live + 5 plans + 4 queue entries

5. Drivers and clans
   - internal/drivers, internal/clans (CRUD, validation)
   - migration 008_drivers_clans.sql
   - /api/clans and /api/drivers endpoints
   - 3-letter uppercase nickname/tag validation
   - lobby.DriverMeta: Nickname, AvatarURL, ClanID, ClanTag

6. Podium
   - internal/transport.RacePodiumEntry
   - lobby.SetDriverProfile for in-memory metadata sync
   - migration 007_podium.sql (podium JSONB column)
   - seeder populates top-3 per finished race

7. Live races persistence
   - migration 009_live_persistence.sql: live_races,
     live_race_drivers, lobby_drivers
   - internal/races/live_store.go: LiveStore with write-side
     mirror for lobby.Service mutations
   - Service.collectLive and Upcoming read from Postgres
   - main.go RestoreFromDB rehydrates lobby on startup

8. Unify live + finished into one races table
   - migration 010_unify_races.sql: rename finished_races to
     races, expand status CHECK, merge live rows
   - PgStore now hosts both write paths; LiveStore is a
     thin facade implementing lobby.Persistence
   - seeder resetAll drops only finished/cancelled rows and
     race_plans / race_queue / lobby_drivers

Each layer is consistent on its own; cross-layer changes are
visible in the file history. A future refactor may split this
commit into the per-stage boundaries listed above.
2026-06-22 22:01:09 +04:00

96 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — заметки для ассистента
Этот файл — заметки, которые нужно помнить между сессиями.
## MCP-инструменты, которые здесь работают
### blender-mcp
Управляет Blender-сценой напрямую. Используй когда нужно менять/осматривать 3D-модель машинки (OpenRC_F1_2018_Halo, BodyCam, LayoutCam, Battery и др.).
Типичные задачи:
- Осмотреть сцену: `blender-mcp_get_scene_info`, `blender-mcp_get_viewport_screenshot`
- Менять геометрию: `blender-mcp_execute_blender_code` (Python в Blender)
- Скачивать ассеты: `blender-mcp_download_polyhaven_asset`, `blender-mcp_search_sketchfab_models`, `blender-mcp_download_sketchfab_model`, `blender-mcp_set_texture`
- Генерировать 3D по тексту/картинке: `blender-mcp_generate_hyper3d_model_via_text`, `blender-mcp_generate_hyper3d_model_via_images`, `blender-mcp_generate_hunyuan3d_model`
В текущей сцене 78 объектов, 49 материалов. Корневая папка модели — `D:/x0gp`. Blender-сцену открывать не надо — MCP подключён к запущенному Blender.
### chrome-mcp-server
Управляет Chrome-браузером. Удобно для проверки ссылок на товары из `CART_MVP.md` (Ozon/WB/AliExpress).
Типичные задачи:
- Список открытых вкладок: `chrome-mcp-server_get_windows_and_tabs`
- Открыть URL: `chrome-mcp_navigate`
- Прочитать страницу: `chrome-mcp_read_page` (предпочтительнее скриншота), `chrome-mcp_get_web_content`
- Клик/ввод: `chrome-mcp_click_element`, `chrome-mcp_fill_or_select`, `chrome-mcp_keyboard`
- Переключить вкладку: `chrome-mcp_switch_tab`
- Скриншот: `chrome-mcp_computer` с `action=screenshot` или `chrome-mcp_screenshot`
> Сейчас уже открыто ~20 вкладок с товарами из корзины MVP. Не закрывай их без причины.
## Ключевые файлы проекта
- `D:/x0gp/hardware/CART_MVP.md` — корзина MVP (что заказать)
- `D:/x0gp/hardware/ASSEMBLY.md` — пошаговая сборка
- `D:/x0gp/hardware/BOM_v4.json` — структурированный BOM (машины в JSON)
- `D:/x0gp/hardware/SHOPPING_LIST.md` — каталог альтернатив (бонусы)
- `D:/x0gp/hardware/PLAN.md` — общий план
- `D:/x0gp/hardware/print/stl/*.stl` — STL для печати на P1S (13 шт)
- `D:/x0gp/hardware/print/parts_manifest.md` — манифест печати
- `D:/x0gp/ARCHITECTURE.md` — общая архитектура проекта
## Сервер x0gp (`D:/x0gp/server`)
- Go 1.26, бинарь `cmd/poc-server`. Сборка: `go build ./...` или `go build -o poc-server.exe ./cmd/poc-server`.
- HTTP+WebSocket, роутер `http.NewServeMux`. Запуск требует `DATABASE_URL` (см. `.env`).
- **Swagger UI**: `GET /swagger/index.html`, спека `GET /swagger/doc.json` (14 paths, 29 definitions).
- Генерация спеки: `swag init -g cmd/poc-server/main.go -o docs --parseInternal` из `D:/x0gp/server`. CLI ставится через `go install github.com/swaggo/swag/cmd/swag@v1.16.4`.
- Аннотации swag живут над хендлерами в `cmd/poc-server/main.go`, `catalog_handlers.go`, `races_handlers.go`. При добавлении нового REST-эндпоинта — добавить `@Router`/`@Param`/`@Success` и перегенерить `docs/`.
- Не использовать `--parseDependency`: падает на stdlib (`runtime/mprof.go`).
### Хранилище гонок (гибрид)
- **Live** (status=lobby|countdown|racing): in-memory `lobby.Service`.
- **Finished**: Postgres (`finished_races`), снапшот пишется в `lobby.SetRaceLifecycleHook` при `SetRaceStatus(finished)`.
- **Race plans** (`race_plans`): расписание, материализуется в lobby шедулером раз в 5 сек.
- **Queue** (`race_queue`): подписки `(driver_id, race_id)`.
### API гонок (keyset-пагинация)
- `GET /api/races` — список (live + finished), фильтры `status,track_id`, `cursor`, `limit` (1..200, default 50). Status enum: `all|lobby|racing|finished`. **Кастомных лобби нет** — гонки привязаны к физическим трекам, фильтр только по `track_id`. Cursor — base64(`ms:id[:src]`).
- `GET /api/races/upcoming?limit=3` — ближайшие lobby|countdown.
- `POST /api/races/queue/join` — встать в очередь на следующие N (body: `driver_id,limit` или header `X-Driver-Id`).
- `GET /api/races/queue?driver_id=...` — посмотреть.
- `DELETE /api/races/queue?driver_id=...&race_id=...` — выйти.
- `POST /api/races/plans` — создать расписание (`start_at_ms, interval_s, count, ...`).
- `GET /api/races/plans` — список планов (keyset `start_at_ms ASC, id ASC`).
- `DELETE /api/races/plans/{id}` — удалить.
### Сущности drivers и clans
- Таблицы `drivers` (id, nickname [3 uppercase letters], name, avatar_url, clan_id?) и `clans` (id, tag [3 uppercase letters], name, avatar_url).
- CRUD:
- `POST/GET /api/clans`, `GET/PUT/DELETE /api/clans/{id}`, `GET /api/clans/by-tag/{tag}`
- `POST/GET /api/drivers`, `GET/PUT/DELETE /api/drivers/{id}`, `GET /api/drivers/by-nick/{nick}`
- `PUT /api/drivers/{id}` принимает `clan_id` как `*string` (nil = оставить, "" = очистить, иначе = назначить).
- Сидер (через `--seed-races`) заполняет 3 клана и 8 водителей, треки берутся из `tracks` каталога (никаких выдуманных id в `finished_races`/`race_plans`).
- Podium в `LobbyRace` заполняется только для finished-гонок (top-3: position, driver_id, name, total_time_ms).
### TODO
- Реальная сущность driver ещё не используется для **lobby.DriverMeta** при входе через WS — там пока захардкоженный driver. При первой сессии надо подтягивать профиль из таблицы `drivers` и звать `lobby.SetDriverProfile(id, nick, avatar, clanID, clanTag)`.
### Сид моковых гонок
- `poc-server.exe --seed-races` — при старте наполняет БД и lobby детерминированными моками: 30 finished, 5 live, 5 plans, 4 queue-entries.
- `poc-server.exe --seed-races --reset` — сначала `TRUNCATE finished_races, race_plans, race_queue` и очищает lobby.
- Без флага `--seed-races` сидер не запускается, даже если `X0GP_SEED_RACES=1` (флаг > env).
- Драйвер — константный, водители в lobby под именами `driver-alice``driver-heidi`.
## Важные факты о машинке (x0gp F1 1/27)
- Масштаб: 1/27, wheelbase 102мм, deck 80×135мм, толщина 1.5мм PETG
- Мотор MVP: коллекторный 370 35T (Ø24×30мм) с Ozon
- ⚠️ `motor_mount.stl` сейчас под мотор 130/180 (Ø20мм) — надо перепечатать под Ø24мм до сборки
- Кузов печатаем сами (PETG, body_shell.stl), готовый Kyosho НЕ покупаем
- Подшипники: 684ZZ 4×9×4 (перед) + 693ZZ 3×8×4 (зад)
- ESP32-S3-DevKitC-1 N16R8 + U.FL (Wi-Fi агент, не RC-приёмник!)
- MPU-6050 пины: SDA=GPIO21, SCL=GPIO22 (это уже исправлено в ASSEMBLY.md)
- FPV: AIO модуль с WB (1997₽) + антенна RUSHFPV с AliExpress (1050₽) — это **разные вещи** (передатчик vs антенна к нему)
- VRX (приёмник видео) для MVP не нужен — показываем картинку через Wi-Fi стрим ESP32 на телефон/PC