feat(game): canonical gameId in POST /api/v1/admin/init #72

Merged
developer merged 1 commits from feature/canonical-game-id-init into development 2026-05-29 11:29:20 +00:00
Owner

Summary

  • POST /api/v1/admin/init теперь принимает обязательное поле gameId. Движок больше не генерирует свой собственный UUID — каноническую идентичность партии выдаёт оркестратор (бэкенд), который и так использует её для имени контейнера, host-bind-mount-директории и games.game_id в БД.
  • Защитные ветки в движке: gameId == uuid.Nil → 400, попытка init поверх существующего state.json (с любым gameId) → 409. Идемпотентный повтор пока не поддерживается — это вне scope-а PR.
  • Бэкенд передаёт gameID в rest.InitRequest{GameID: gameID, Races: races}; engine-client Init не меняется (шлёт DTO как есть).
  • Документация и тесты подведены под новый инвариант: state.json всегда содержит тот же UUID, что и games.game_id бэкенда.

Затронутые контракты

  • game/openapi.yamlgameId обязателен в InitRequest, новый response 409 ConflictError для adminInitGame.
  • pkg/model/rest/init.go — поле GameID uuid.UUID с binding:"required".

Чего нет в этом PR (намеренно)

  • race UUIDs (движковый raceID vs бэкендный EnginePlayerUUID) — отдельная история; backend/engine сегодня сравниваются по RaceName, расхождения UUID-ов не ломают рантайм.
  • Полная идемпотентность повторного init — нужен сценарий-обоснование.
  • Squash старых state.json или миграция формата — не требуется, поле id там и так есть.

Test plan

  • go test ./game/... локально — зелёный
  • go test ./backend/internal/{engineclient,runtime,lobby,server}/... локально — зелёный
  • go-unit.yaml на gitea (run #528) — зелёный
  • ui-test.yaml на gitea — должен запуститься на PR (правки только в Go, должен быть нерелевантен)
  • integration.yaml на gitea — testcontainers-сюита; runtime_lifecycle_test поднимает реальный движок и бэкенд, должен пройти без правок
  • Defensive-smoke на dev-стенде после мёржа: curl POST /api/v1/admin/init с тем же storage-путём, но другим gameId → 409; с gameId = 00000000-… → 400
## Summary - `POST /api/v1/admin/init` теперь принимает обязательное поле `gameId`. Движок больше не генерирует свой собственный UUID — каноническую идентичность партии выдаёт оркестратор (бэкенд), который и так использует её для имени контейнера, host-bind-mount-директории и `games.game_id` в БД. - Защитные ветки в движке: `gameId == uuid.Nil` → 400, попытка `init` поверх существующего `state.json` (с любым `gameId`) → 409. Идемпотентный повтор пока не поддерживается — это вне scope-а PR. - Бэкенд передаёт `gameID` в `rest.InitRequest{GameID: gameID, Races: races}`; engine-client `Init` не меняется (шлёт DTO как есть). - Документация и тесты подведены под новый инвариант: `state.json` всегда содержит тот же UUID, что и `games.game_id` бэкенда. ### Затронутые контракты - `game/openapi.yaml` — `gameId` обязателен в `InitRequest`, новый response `409 ConflictError` для `adminInitGame`. - `pkg/model/rest/init.go` — поле `GameID uuid.UUID` с `binding:"required"`. ### Чего нет в этом PR (намеренно) - race UUIDs (движковый `raceID` vs бэкендный `EnginePlayerUUID`) — отдельная история; backend/engine сегодня сравниваются по `RaceName`, расхождения UUID-ов не ломают рантайм. - Полная идемпотентность повторного `init` — нужен сценарий-обоснование. - Squash старых `state.json` или миграция формата — не требуется, поле `id` там и так есть. ## Test plan - [x] `go test ./game/...` локально — зелёный - [x] `go test ./backend/internal/{engineclient,runtime,lobby,server}/...` локально — зелёный - [x] `go-unit.yaml` на gitea (run #528) — зелёный - [ ] `ui-test.yaml` на gitea — должен запуститься на PR (правки только в Go, должен быть нерелевантен) - [ ] `integration.yaml` на gitea — testcontainers-сюита; `runtime_lifecycle_test` поднимает реальный движок и бэкенд, должен пройти без правок - [ ] Defensive-smoke на dev-стенде после мёржа: `curl POST /api/v1/admin/init` с тем же storage-путём, но другим `gameId` → 409; с `gameId = 00000000-…` → 400
developer added 1 commit 2026-05-29 11:21:12 +00:00
feat(game): canonical gameId in POST /api/v1/admin/init
Tests · Go / test (push) Successful in 1m57s
Tests · Integration / integration (pull_request) Successful in 1m48s
Tests · Go / test (pull_request) Successful in 2m0s
15d35f6f1f
Engine no longer mints its own game UUID. The orchestrator (backend)
generates the game UUID at game-create time and passes it in the
admin/init request body as the required `gameId` field, so the value
that names the engine container and host bind-mount directory also
ends up inside the engine's state.json.

The engine rejects the zero UUID with 400 and any init that conflicts
with an existing state.json with 409 (a second init on the same gameId
is also a conflict; full idempotency is not part of the contract).

Updates rest.InitRequest, openapi.yaml (schema + 409 response),
controller.GenerateGame/NewGame/buildGameOnMap signatures, the engine
HTTP handler/executor, the backend runtime worker, and the relevant
unit and contract tests. Documentation in game/README.md,
docs/ARCHITECTURE.md, backend/README.md, and backend/docs/{runtime,flows}.md
is updated in the same patch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
owner approved these changes 2026-05-29 11:27:14 +00:00
developer merged commit e36d33482f into development 2026-05-29 11:29:20 +00:00
developer deleted branch feature/canonical-game-id-init 2026-05-29 11:29:20 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/galaxy-game#72