Magic Vault 5×3 — Frontend DEV API
DEV ONLY · DEMO ONLY · DRAFT MATH · NOT FOR PRODUCTION · NOT CERTIFIED
Це короткий handoff для нового frontend engine/store. Канонічний і детальніший API contract:
backend/doc/dev-api.md, §5.
Якщо цей файл і backend-документація розходяться, пріоритет має backend-документація та
актуальна OpenAPI schema.
Поточна доступність
Stage і production не публікують цю гру. Наявність гри в catalog не є авторизацією: backend повторно перевіряє environment, session, wallet, Game ID, config identity та DEV flags перед будь-яким рухом грошей.
Джерела істини
- Backend DEV contract:
backend/doc/dev-api.md, §5. - OpenAPI: Game Engine OpenAPI.
- GDD: magic-vault-5x3-gdd.md.
- Math Spec: ../math/magic-vault-5x3-math-spec.md.
- Hold & Win response contract: ../../hold-and-win-reference/gdd/hold-and-win-backend-response.md.
1. Bootstrap і повторне використання токена
Перший успішний виклик повертає новий session_bearer і reused:false. Після reload frontend
повторно надсилає кешований bearer; reused:true означає, що сервер продовжив ту саму demo
session. Не створювати новий токен на кожне перезавантаження сторінки. Bearer — credential: не
логувати, не комітити, не вставляти в telemetry.
Якщо кешований bearer отримав 403 DEMO_SESSION_UNAVAILABLE, видалити його та один раз повторити
bootstrap без Authorization.
Початковий store заповнює:
sessionBearerзsession_bearer;balanceзbalance.real;gameIdзgame.game_id;debugAllowedзcapabilities.debug_allowed;feature = null.
2. Base spin
Важливі поля відповіді:
spin.gridмає порядок[reel][row]: 5 зовнішніх масивів по 3 символи.spin.layout— ті самі клітинки в порядку[row][reel].- Store має обрати один формат; рекомендовано зберігати canonical
spin.grid. - Символи:
WILD,COIN,H1–H4,L1–L4. СимволуBONUSнемає. - Загальний виграш — тільки
spin.payout. Не сумуватиspin.wins[].payout. - Баланс оновлювати з
wallet.balance_after, а не обчислювати локально.
3. COIN trigger
- 6 або більше видимих
COINзапускають Hold & Win. COINне має line payout;WILDне замінюєCOIN.- Початкові COIN переходять у feature як locked cells у тих самих позиціях.
- Trigger spin може мати
spin.payout: 0; активну feature визначаєnext_state, а не payout. - Debug
mockможе показати 6+ COIN, але на поточному mock route не відкриває feature. Lifecycle тестувати через звичайний RNG абоdebug.force_seed.
Перевірений DEV seed: mv-runtime-357. Він відкриває feature із locked indexes
[6,7,8,9,10,13,14].
4. Cell model
Backend повертає всі 15 cells на trigger, кожному step, replay і settlement. Це authoritative
feature snapshot. newly_landed — лише список індексів для анімації; frontend не повинен
відновлювати game state через накопичення animation deltas.
5. Feature step
bet потрібен для validation, але feature step не робить нової ставки: відповідь має
wallet.bet: 0.
Правила:
- Брати
expected_hold_and_win_step_indexтільки з попередньогоnext_state.expected_step_index. - Не інкрементувати index локально до підтвердженого
200. - Один логічний step — один
Idempotency-Key. - Retry того самого step повторює той самий key, body та expected index.
- Одночасно дозволений тільки один feature request.
Feature response містить:
Multiplier fields — множники total bet. wallet.* і spin.payout — currency major units.
6. Store contract
Store actions повинні бути атомарними на рівні response:
bootstrap()— створити/відновити demo session;spin(bet)— один paid base request;applyBaseResponse(response)— оновити grid, wins, payout, balance, round і feature;stepFeature()— створити pending operation з поточним expected index;retryPendingFeatureStep()— повторити ті самі key/body/index;applyFeatureResponse(response)— замінити повний snapshot і balance;settleFeature(response)— показати award один раз, очистити pending operation;clearSettledFeature()— перед наступним base spin видалити feature та step index;stopAmbiguousMock()— зупинити queue і показати останній підтвердженийround_id.
7. Animation contract
- Отримати повну відповідь.
- Анімувати тільки indexes із
newly_landed. - Після анімації замінити UI state повним
cellssnapshot. - Якщо
newly_landed.length > 0, показати reset respins до 3; інакше показати decrement. - Не надсилати наступний step до завершення animation.
- На
settlementодин раз анімуватиaward_multiplier. - Баланс завжди брати з
wallet.balance_after.
Animation state ніколи не є game state. Якщо animation і backend snapshot розходяться, правильний backend snapshot.
8. Replay, reconnect і відомі ризики
Paid runtime route
Feature replay idempotent: той самий logical request із тим самим key/index повертає той самий
round_id і state без нового RNG, wager, payout intent або credit.
Debug mock route
Idempotency-Key приймається, але ігнорується. Після timeout не робити auto-retry: зупинити
scenario queue та показати останній підтверджений round_id.
Reconnect
restore.next_state зараз null навіть при відкритій feature. Після reload:
- повторно використати cached bearer;
- повторити останній підтверджений feature request із тим самим key та expected index;
- отримати PostgreSQL-backed replay;
- перемалювати всі 15 клітинок;
- продовжити з повернутого
expected_step_index.
Stale index — money warning
Якщо feature вже завершена, але request builder залишив
expected_hold_and_win_step_index, backend наразі може виконати звичайний paid base spin. Тому
при отриманні settlement store зобов'язаний очистити feature state, pending operation і step
index до дозволу наступної ставки.
Error messages
Частина orchestrator errors містить internal diagnostics, включно з parent round ID. Не показувати
message гравцю. Мапити відомі code/prefix на локалізоване frontend повідомлення, а повну деталь
залишати тільки в захищеному DEV logging.
9. Observed lifecycle
Seed mv-runtime-357:
Money reconciliation from live DEV smoke:
Replay і reconnect не створили нових rounds, intents або credits. Settlement створив рівно один credit intent і один wallet credit.
10. Debug inputs
Deterministic runtime trigger:
Mock grid — тільки grid/payout test, не feature trigger:
Не використовувати неіснуючі поля force_grid, force_feature або force_win_band.
11. Publication update
Стандартний Math Studio from-pack flow виправлено: internal storage ID лишається version-scoped,
а executable runtime ID зберігає канонічні WILD, COIN, H1–H4, L1–L4.
Рекомендований DEV publication command:
mvpublish deprecated і зберігається тимчасово як recovery tool. Frontend не викликає жоден із
publication endpoints.
Три різні identities не можна змішувати:
a42ae358…— SHA-256 canonical YAML bytes у Math Spec/evidence;ee67c5ee…— recursively canonicalized config для Studio evidence;61dc7d68…— runtime gate comparison identity.
12. Definition of done для frontend store
- Cached demo bearer переживає reload.
- Base spin блокує повторний click, поки request pending.
- Grid рендериться як 5×3 без transposition bug.
- Total win береться з
spin.payout. - 6+ COIN відкривають feature через runtime response.
- Trigger locked positions збігаються з base grid.
- Store зберігає повний 15-cell snapshot.
newly_landedвикористовується тільки для animation.- Feature steps виконуються строго послідовно.
- Runtime retry використовує той самий Idempotency-Key.
- Mock timeout зупиняє queue без retry.
- Reload відновлює feature через replay останнього step.
- Settlement очищає stale step index до наступного base spin.
- Wallet оновлюється з
wallet.balance_after. - Raw backend diagnostics не показуються гравцю.