spinngo-databingo
- Repo stars 0
- Author updated Live
- Author repo Spillorama-system
- Domain
- Data
- Compatible agents
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- Trust score
- 88 / 100 · community maintained
- Author / version / license
- @tobias363 · no license declared
- Token usage
- Heavy
- Setup complexity
- Manual integration
- External API key
- Not required
- Operating systems
- Docker
- Runtime requirements
- Docker
- Permissions
-
- Read-only
- Write / modify
- Shell exec
- Network behavior
- Local-only
- Install commands
- 26 variants
Profile is derived at build time from SKILL.md and install vectors. Subject to drift from author intent.
Heads up: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: spinngo-databingo
description: When the user/agent works with SpinnGo (Spill 4 / game5 / spillorama-slug), databingo classifica…
category: data
runtime: Docker
---
# spinngo-databingo output preview
## PART A: Task fit
- Use case: When the user/agent works with SpinnGo (Spill 4 / game5 / spillorama-slug), databingo classification, the 30%-til-organisasjoner-regel, the 2500 kr single-prize-cap, or legacy Unity SpinnGo's 3×3 / 1-36 / 14-draw pattern game with roulette/bonus. Also use when they mention game5, spillorama-slug, SpinnGo, Spill 4, databingo, databingo system, ruletthjul, single-prize-cap, 3x3-grid, 1-36, 14 trekk, pattern multipliers, bonusTrigger, WOF, roulette settings, ledgerGameTypeForSlug, makeHouseAccountId, themebingo deprecated, BIN-496, ComplianceLedgerOverskudd, §11 distribusjon, forhåndstrukket, player-startet. Make sure to use this skill whenever someone touches SpinnGo, databingo-paths, eller forveksler regulatorisk klassifisering mellom hovedspill og databingo — even if they don't explicitly ask for it..
- Inputs: target material, constraints, expected output, and acceptance criteria.
- Evidence boundary: follow “📚 Tier 1 context — load when skill activates / Kontekst — hvorfor er dette kritisk? / Mapping-tabell — markedsføring vs kodenavn vs slug” and do not present inference as author intent.
## PART B: Execution result
- **01** The card summarizes the use case; runtime output centers on “When the user/agent works with SpinnGo (Spill 4 / game5 / spillorama-slug), databingo classification, the 30%-til-organisasjoner-regel, the 2500 kr single-prize-cap, or legacy Unity SpinnGo's 3×3 / 1-36 / 14-draw pattern game with roulette/bonus. Also use when they mention game5, spillorama-slug, SpinnGo, Spill 4, databingo, databingo system, ruletthjul, single-prize-cap, 3x3-grid, 1-36, 14 trekk, pattern multipliers, bonusTrigger, WOF, roulette settings, ledgerGameTypeForSlug, makeHouseAccountId, themebingo deprecated, BIN-496, ComplianceLedgerOverskudd, §11 distribusjon, forhåndstrukket, player-startet. Make sure to use this skill whenever someone touches SpinnGo, databingo-paths, eller forveksler regulatorisk klassifisering mellom hovedspill og databingo — even if they don't explicitly ask for it.”.
- **02** When the source has headings, the agent prioritizes “📚 Tier 1 context — load when skill activates / Kontekst — hvorfor er dette kritisk? / Mapping-tabell — markedsføring vs kodenavn vs slug” so the result follows the author’s structure.
- **03** Typical output includes task judgment, concrete steps, required commands or file edits, validation, and follow-up options.
- **04** Risk context follows the fingerprint: read files, write/modify files, run shell commands; mostly runs locally; usually needs no extra API key.
## Running Rules
- read files, write/modify files, run shell commands; mostly runs locally; usually needs no extra API key.
- Validate with a small sample before expanding scope.
- Return the result, validation criteria, and next iteration options. The source mentions slash commands such as `/api`, `/alpha-drop`, `/wallet`, `/redis`, `/snapshot`; use them first when your agent supports command triggers.
Name target files or source material, expected output, forbidden changes, and whether network or shell access is allowed. Permission fingerprint: read files, write/modify files, run shell commands.
Start with a small task and check whether the result follows “📚 Tier 1 context — load when skill activates / Kontekst — hvorfor er dette kritisk? / Mapping-tabell — markedsføring vs kodenavn vs slug”. Inspect diffs, logs, previews, or tests before expanding scope.
Confirm the final output includes a concrete result, evidence, and next action. If it stays generic, tighten inputs, boundaries, and acceptance criteria.
---
name: spinngo-databingo
description: When the user/agent works with SpinnGo (Spill 4 / game5 / spillorama-slug), databingo classifica…
category: data
source: tobias363/Spillorama-system
---
# spinngo-databingo
## When to use
- When the user/agent works with SpinnGo (Spill 4 / game5 / spillorama-slug), databingo classification, the 30%-til-orga…
- Use it when the task has clear inputs, repeatable steps, and validation criteria.
## What to provide
- Target material, scope, expected result, and forbidden changes.
- Whether network, commands, file writes, or external services are allowed.
## Execution rules
- Organize steps around “📚 Tier 1 context — load when skill activates / Kontekst — hvorfor er dette kritisk? / Mapping-tabell — markedsføring vs kodenavn vs slug” and keep inference separate from source facts.
- read files, write/modify files, run shell commands; mostly runs locally; usually needs no extra API key.
- Validate with a small sample before expanding the task.
## Output requirements
- Return the deliverable, key evidence, validation method, and next action.
- Mark missing information as unknown; do not invent commands, platforms, or dependencies. The author source anchors workflow facts; repository files anchor sources and commands; Fluxly only adds fit, limitations, and quality judgment.
skill "spinngo-databingo" {
input -> user goal + target files + boundaries + acceptance criteria
context -> 📚 Tier 1 context — load when skill activates / Kontekst — hvorfor er dette kritisk? / Mapping-tabell — markedsføring vs kodenavn vs slug
rules -> SKILL.md triggers / order / output contract
runtime -> Docker | read files, write/modify files, run shell commands | mostly runs locally
guardrails -> usually needs no extra API key + small-sample validation + diff/log review
output -> copyable result + checklist + next iteration
} 📚 Tier 1 context — load when skill activates
Per docs/CONTEXT_LOADING_STRATEGY.md, this skill auto-references:
Hard rules: Se "Mandate" / "Hard rules" / "Kontekst" seksjonene under for domene-spesifikke regler. Bryter på disse = pilot-blokker eller regresjon.
ADRs (Read on activation):
Pitfalls kategori-filer (Tier 1, fra BIN-968-split):
docs/engineering/pitfalls/pitfalls-01-compliance.mddocs/engineering/pitfalls/pitfalls-03-spill-arkitektur.mddocs/engineering/pitfalls/pitfalls-05-git-pr-flyt.md(§5.19 — animasjons-fix forward-port-protokoll)docs/engineering/pitfalls/pitfalls-07b-master-lobby.md
Master pitfalls INDEX: docs/engineering/PITFALLS_LOG.md
NB: Denne headeren ble auto-generert av BIN-967 Fase 2-script for å etablere mønsteret på tvers av alle 26 skills. Dypere
hard rules,linje-spesifikke pitfallsogspesifikke kode-eksemplerbør utvides per skill ved neste touch (følgwallet-outbox-pattern+pengespillforskriften-compliancesom template).
SpinnGo / Spill 4 / game5 — Databingo
SpinnGo (markedsført som "Spill 4", kode-navn game5, slug spillorama) er det eneste databingo-spillet i Spillorama-systemet. Alle andre interne spill (Spill 1, 2, 3) er hovedspill (MAIN_GAME). Denne distinksjonen er regulatorisk kritisk — MAIN_GAME har 15% min til organisasjoner og ingen single-prize-cap; DATABINGO har 30% min til organisasjoner og 2500 kr cap per single-prize.
Kontekst — hvorfor er dette kritisk?
Lese-først-doc:
docs/architecture/SPILLKATALOG.md— kanonisk klassifisering. Korrigert 2026-04-25 av Tobias.docs/architecture/SPILL_REGLER_OG_PAYOUT.md§4 — single-prize-cap håndhevelsedocs/architecture/SPINNGO_ENTRY_SESSION_FLOW.md— private single-player entry/session contractapps/backend/src/game/ledgerGameTypeForSlug.ts— slug→gameType mapping
Direktiv (Tobias 2026-04-25):
Spill 1-3 = live hovedspill (15%), SpinnGo (Spill 4/game5) = databingo (30%), Candy = ekstern iframe. Korrigerer feil 2026-04-23-spikring.
Den 2026-04-23-spikringen som hevdet "alle interne spill var hovedspill" var feil og er korrigert.
Mapping-tabell — markedsføring vs kodenavn vs slug
Spill 1 = game1 = bingo = Hovedspill (MAIN_GAME), 75-ball 5×5
Spill 2 = game2 = rocket = Hovedspill (MAIN_GAME), 21-ball 3×3
Spill 3 = game3 = monsterbingo = Hovedspill (MAIN_GAME), 75-ball 5×5 uten free
SpinnGo = game5 = spillorama = Databingo (DATABINGO), 3×3 / 1-36 / 14 trekk + rulett/bonus (player-startet)
Candy = — = candy = Ekstern iframe (tredjeparts) — Spillorama eier kun launch + wallet-bridge
(Game 4 = game4 = themebingo = DEPRECATED BIN-496, IKKE BRUK)
Kanonisk SpinnGo alias-set (PR #1715, 2026-05-23)
Backend, bridge, registry og lobby aksepterer 4 SpinnGo-aliases — alle som
SAMME spill. Code-DB-stabil slug er spillorama; alle andre aliases skal
canonicaliseres til den ved boundary (lobby).
const SPINNGO_SLUG_ALIASES = ["spillorama", "game_5", "game5", "spinngo"];
Slug alias consolidation (BIN-NEW, 2026-05-25)
Backend har ÉN kanonisk isSpinngoSlug helper: Eksportert fra
apps/backend/src/game/ticket.ts sammen med SPINNGO_SLUGS set. ALLE
backend-call-sites MÅ importere denne helperen — IKKE duplisere alias-sjekken
lokalt.
import { isSpinngoSlug } from "../game/ticket.js"; // canonical
// NEVER write a local copy like:
// const SPINNGO = new Set(["spillorama", ...]); // ❌
// function isSpinngoSlug(s) { ... } // ❌
Hvorfor: Pre-BIN-NEW hadde 4 separate kopier av isSpinngoSlug/SPINNGO_SLUGS
i backend med UKONSISTENT alias-dekning — kun noen inkluderte game5 (uten
underscore), source-of-truth gjorde det ikke. Lobby-canonicalisering maskerte
bug-en, men enhver code-path som omgikk lobby-en (integration-tests, direkte
API-kall, alternate-klienter) brøt SpinnGo silently. Latent pilot-risk.
Konsoliderte call-sites (PR BIN-NEW):
apps/backend/src/util/roomHelpers.ts— replaced local copy with importapps/backend/src/routes/publicGameHealth.ts— replaced localSPINNGO_SLUGSconst with re-export of canonical setapps/backend/src/sockets/gameEvents/roomEvents.ts— replaced local copy with importapps/backend/src/sockets/gameEvents/ticketEvents.ts— replaced hardcodedgameSlug !== "spillorama"med!isSpinngoSlug(...)(kritisk bug-fix; tidligere avviste handler 3 av 4 aliases)
Oppdatert E7 (BIN-1099, 2026-05-28): Etter SpinngoRoomService-ekstraksjonen importerer
roomEvents.tsogroomHelpers.tsIKKE lengerisSpinngoSlugdirekte. De når den kanoniske helperen viaSpinngoRoomService(isPrivateSessionRoom/assertJoinAllowed) ogspinngoDisplayCache(isSpinngoDisplayRoom/resolveSpinngoDisplayPlan). Begge nye moduler importerer den kanoniskeisSpinngoSlugfra@spillorama/shared-types/slugs(D1, BIN-1095) — fortsatt ingen lokal kopi. Grep mot de tre rom-mega-filene forisSpinngoSlug|isSpinngoRoom= 0.
Regresjons-tester:
apps/backend/src/game/ticket.test.ts— pins 4 alias-aksept + alle negative cases (null, undefined, "", case-insensitive, whitespace) +SPINNGO_SLUGSset-lengde-lockapps/backend/src/sockets/gameEvents/ticketEvents.spinngoSwap.test.ts— wire-up test som beviserticket:swapaksepterer alle 4 aliases mot reell handler
Hvis du legger til et 5. alias (backend): Oppdater KUN SPINNGO_SLUGS i ticket.ts.
Alle 4 backend-call-sites over arver automatisk endringen. Frontend/lobby
(packages/game-client/src/games/game5/Game5Controller.ts,
apps/backend/public/web/lobby.js) må fortsatt oppdateres separat — de er ikke
i backend-modulen og kan ikke importere fra apps/backend/src/.
Alle 4 aliases må holdes synkronisert i (frontend/registry — fortsatt fragmentert):
| Lokasjon | Funksjon/Symbol |
|---|---|
apps/backend/src/util/roomState.ts |
isSpinngoGameType (bindVariantConfigForRoom) |
apps/backend/src/admin/GameManagementService.ts |
gameTypeIdsForRuntimeSlug |
apps/backend/src/admin/reports/HallSpecificReport.ts |
deriveGameSlotFromSlug |
packages/game-client/src/bridge/GameBridge.ts |
isSpinngoGameType (spinngoPatterns/Bonus) |
packages/game-client/src/games/game5/Game5Controller.ts |
registerGame(...) ×4 |
apps/backend/public/web/lobby.js |
isSpinngoCatalogGame + canonicalize slug→spillorama |
Lobby canonicalization-regel: Uansett om /api/games returnerer
game5/game_5/spinngo/spillorama (eller route: "/spillorama",
gameNumber: 5), lobbyen omskriver slug-feltet til spillorama før
render. data-slug="spillorama" er garantert for SpinnGo-rader.
Hvis du legger til et 5. alias (frontend): Oppdater backend SPINNGO_SLUGS + alle 6 lokasjoner over + tester:
apps/backend/src/game/ticket.test.ts(legg til alias-assertion)packages/game-client/src/games/registry.game5.test.tspackages/game-client/src/bridge/lobbyGameList.test.tsapps/backend/src/admin/GameManagementService.test.ts
Ikke spred over flere PR-er; alias-drift forrige gang (game5 manglet) gjorde
at game-registry returnerte null, lobby renderet feil data-slug, og
admin-config lookup ikke fant aktiv config.spinngo. Se PITFALLS §7b.30.
D1 implementert (BIN-1095, E3 — 2026-05-28):
- Canonical kilde flyttet til
packages/shared-types/src/slugs.ts(SPINNGO_SLUGS,isSpinngoSlug,usesSpinngoTicket) - 17-pattern:
packages/shared-types/src/spinngo/patterns.ts(DEFAULT_SPINNGO_PATTERNS) - 4 consumers refactorert: backend
ticket.ts(re-export), GameBridge.ts, Game5Controller.ts, SpinngoConfigForm.ts - Backward-compat bevart for downstream backend
- Lobby.js fortsatt lokal kopi (legacy HTML — INTENTIONAL per skill)
E2 implementert (BIN-1094, 2026-05-28):
apps/backend/src/admin/GameManagementService.ts:247-253hardkodet tuple erstattet med[...SPINNGO_SLUGS]import- 13 dead-export candidates i game5/** listet (follow-up — krever consumer-trace)
- T2.5 (DEFAULT_SPINNGO_CONFIG fail-soft) DEFERRED til E5
D3 implementert (BIN-1096, E4 — 2026-05-28) via has-a composition:
Game5Engine extends BingoEnginedirekte (3-nivå arve-kjede brutt → 2-nivå)private readonly game3Hook: Game3Engine— composition for Spill 1/2/3 dispatchrunGame2DrawHook+runGame3DrawHookshared (BIN-1068 + BIN-1108 prereq)- Spill 1/2/3 runtime bevart, hash-chain byte-paritet bevart
- Discovery: 1 false start (Micro-B revertet pga single-instance dispatch problem) → reformulering → has-a komposisjon (Option C / pragmatisk)
- POST-E4 inheritance-tester:
apps/backend/src/game/Game5Engine.postE4Inheritance.test.ts
D2 implementert (BIN-1097, E5 — 2026-05-28):
apps/backend/src/game/spinngo/SpinngoConfigBinder.ts(105 LOC) — hard-failSPINNGO_CONFIG_MISSING- Game5Engine + roomState bruker
configBinder.resolve()istedenfor?? DEFAULT_SPINNGO_CONFIG - Error propageres via BingoEngine.toPublicError som
{ code: "SPINNGO_CONFIG_MISSING" } - T1.5 admin PATCH → next round integration-test aktivert (2/2 GRØNN)
- Helpers (resolveSpinngoWheelOfFortunePrizes/Weights) beholder static fallback siden de kjører etter binder allerede har validert config
D9 implementert (BIN-1098, E6 — 2026-05-28):
apps/backend/src/game/spinngo/SpinngoPayoutCommitter.ts(521 LOC) — single committer for pattern-prize + WOF- 3 methods:
commitPaidPrize/commitZeroPrize/commitHouseDeficit(typed Args + 5 deps ports) apps/backend/src/game/Game5Engine.tsslimmed 692 → 470 LOC (-32%,commitSpinngoPayout232 → 12 LOC dispatcher)- WOF (
BingoEngineMiniGames.ts:194-346spinJackpot) ALLEREDE §3.34-compliant — ingen endring nødvendig- Inline
applySinglePrizeCapcap-applikasjon ledgerGameTypeForSlug(room.gameSlug)korrekt DATABINGO-mapping- Game 1 ad-hoc jackpot path uberørt (
reason="ADHOC_JACKPOT_PRIZE")
- Inline
- Hash-chain byte-paritet bevart (committer bodies byte-identical lift)
- Property-test (fast-check) verifiserer cap-invariant for stake × multiplier ∈ [1, 10000] × [0, 500]
- Tests: 9/9 unit + 2/2 active property + 5/5 Game5Engine + 1+3 skip end-to-end
- E10 R10 pilot-gate nå avblokket (E4 + E5 + E6 alle done)
- T6.5 WOF E2E-integration:
.skipmed TODO follow-up — coverage bevart via property + unit + inline cap
E7 implementert (BIN-1099, 2026-05-28) — SpinngoRoomService (private-session ownership):
apps/backend/src/game/spinngo/SpinngoRoomService.ts(~400 LOC) — eier private-session-livssyklusen. Public API:isPrivateSessionRoom({ gameSlug }, variantGameType?)— single source of truth for alias-sjekken (isSpinngoSlug(gameSlug) || isSpinngoSlug(variantGameType))assertJoinAllowed({ gameSlug })— kasterSPINNGO_PRIVATE_SESSIONfor SpinnGo-rom; no-op for live-spill (F-05 owner-reattach skjer FØR dette kallet)async applyStake(args)— 1:1 ekstraksjon avbet:armper-bong stake-blokken (RUM-007 GAME_RUNNING, RUM-009 per-bong over maxBetAmount, 4-slot display-cache, loss-limit partial-buy, reserve/release pre-round-delta, arm/disarm). Returnerer{ armed, lossLimitInfo?, partialBuyHappened }- Singleton
spinngoRoomService+ typede ports (SpinngoStakeDeps,SpinngoStakeEngine,SpinngoStakeReservationHelpers)
apps/backend/src/game/spinngo/spinngoDisplayCache.ts(~85 LOC) — eier 4-slot display-cache-pathen ut avroomHelpers.ts:isSpinngoDisplayRoom(gameSlug, effectiveGameType)resolveSpinngoVisibleTicketCount(config)(faller tilbake til 4)resolveSpinngoDisplayPlan(config)→{ count, colorAssignments }der count =visibleTicketCount(§3.CC-invariant: ALDRIarmedCount)
- DONE-condition oppnådd:
grep -nE "isSpinngoSlug|isSpinngoRoom"motroomState.ts+roomHelpers.ts+roomEvents.tsreturnerer nå 0 (12 → 0). roomEvents.ts+roomHelpers.tsimporterer IKKE lengerisSpinngoSlugdirekte — de går viaSpinngoRoomService/spinngoDisplayCache. (Se oppdatert import-note i "Slug alias consolidation" over.)- T7.4 bevisst NO-OP:
roomState.tshadde allerede 0 matches; §3.62 immutability-guard (bindVariantConfigForRoomidempotent, linje ~835) urørt. - F-05 bevart i ALLE room-join-paths: SPINNGO_PRIVATE_SESSION-rejecten kjører
ETTER eksisterende
findPlayerInRoomByWallet-reattach-guard, så owner-wallet kan alltid reconnecte. - Behaviour-preserving — null funksjonell endring på live-rom.
- Tests:
SpinngoRoomService.test.ts(11) +spinngoDisplayCache.test.ts(4)- F-05 (28) + integration/chaos/immutability (45) — alle 0 fail.
- T7.6 (100-parallell load-test) deferred; T7.7 (§3.CC pin) dekket av nye unit-tester.
E9 implementert (BIN-1101, 2026-05-28) — admin-web SpinngoConfigForm split:
- 756-LOC monolitt → tynn orchestrator + 5 moduler under
apps/admin-web/src/pages/games/gameManagement/spinngo/SpinngoConfigDerivers.ts(data-laget: typer + konstanter + allederive*+ parse-helpers)SpinngoStakeEditor.ts(basics + bong-multipliers) ·SpinngoPatternEditor.ts(17-pattern + bonusTrigger) ·SpinngoBonusEditor.ts(WOF priser + distribution) ·SpinngoRouletteEditor.ts(37 segmenter)
- Orchestrator (
SpinngoConfigForm.ts, 302 LOC) beholder public API (isSpinngoGameType,renderSpinngoConfig{Add,Edit}Page) + eier §3.26-spread og §3.30 kr→øre-konvertering - BEHAVIOR-PRESERVING: rendret markup, data-testids, valideringsmeldinger,
derive*-output og PATCH-payload byte-identiske. Pinnet av ny karakteriserings-test (19) + 44 modul-tester = 63 grønne;tsc --noEmitEXIT 0 - Pre-eksisterende (urelatert): vite/LightningCSS build-fail på vendored
legacy-skin/iCheckstar-hack +physicalCashoutPatternModalcwd-sensitive ENOENT — begge bevist via baseline-rerun
E8 implementert (BIN-1100, 2026-05-28) — game-client PlayScreen split (behavior-preserving):
packages/game-client/src/games/game5/screens/PlayScreen.ts1303 → 851 LOC (-35%) forblir orchestrator: eier Pixi-scene-graph (bg/table/frame-sprites, roulette, layers, play-button), layout (computeLayout/applyLayout), asset-loaders og draw-tick-rendering (renderDrawnNumbers) — per proposal §F8 "kun running-state + roulette-orchestrasjon + draw-tick + layout".- 7 nye moduler (alle PURE / view-helpers, ingen forretningslogikk endret):
logic/SpinngoStateBinding.ts(84) —resolveTotalDraws,resolveVisibleDrawnNumbers,resolvePhaseProjection,resolveStatusText(GameBridge → screen-state, Spill 1LobbyStateBinding-mønster)logic/SpinngoStakeCalculator.ts(86) —resolveVisibleStake,buildStakeSnapshot(syntheticspinngo-slot-${i}→tkt-${i}translasjon + 4-slot snapshot; §3.31/§3.BB)logic/SpinngoPatternEvaluator.ts(122) —computeOtgPerPattern/computeOtgTicketIndices/countActiveCells+filterMyClaims/filterMyPatternResults/resolveMyRoundSummary(klient-side OTG-hint; backend forblir sannhet per §3.27)components/SpinngoStakeBar.ts(349) — chip-strip (PNG + procedural fallback), chip- selection-state, drag-drop-wiring; chips legges fortsatt i screen'scontrolsLayercomponents/SpinngoSwapButton.ts(47) —canSwapTicket/buildSwapHandler(swap-kontrakt; selve knappen rendres fortsatt avSpinngoTicketBoard.drawSwapButton)screens/PreRoundScreen.ts(180) —renderTickets(alle 4 boards + drop-targets) lift 1:1screens/PostRoundScreen.ts(82) —renderRoundResult(running-info + ended win-recap) lift 1:1
- G3/§3.41/§3.BB IMMUTABLE bevart: play-button forblir SISTE child av
tableLayer(pinnet avPlayScreen.playButtonInteractive.test.tsvia privat-felt-assertion),eventMode="static"/cursor="pointer"/pointerdown→onPlayPressed/alpha-drop uendret. - G6 bevart:
RouletteWheel.ts+ 12 ballPop-tester URØRT (E8 er strukturell split, ingen animasjon). - Public API + scene-graph 100% uendret →
Game5Controller.ts+visual-harness.tskrevde INGEN endring.adjustTicketStake/findDropTarget/dropTargets/getChipLocalCenter/getDropTargetLocalCenter/localToGlobalbeholdt på PlayScreen (test-bridge + chip-drop). - Bevis: scene-graph-snapshot byte-identisk (4 states: NONE/pre-stake/RUNNING/ENDED) via
throwaway-probe stash-diff; 56/56 game5-tester grønne (4 safety-net + ballPop + Game5Controller);
tsc --noEmitEXIT 0;build:visual-harnessEXIT 0. 12 pre-eksisterende WinPopup/premieTable- failures er Spill 1 (bevist via stash-rerun), urelatert. - LOC-note: ~400-målet i proposal under-estimerte orchestrator-gulvet — konstruktor-scene-graph
- layout + sprite-loaders + play-button + draw-tick + host-getters er IMMUTABLE-bundet til PlayScreen (kan ikke flyttes uten å bryte scene-graph-invarianten). 851 er reelt gulv.
Spill 4 ≠ Game 4. Det er en historisk arv:
- Markedsføringsnavn: "Spill 4"
- Kodenavn:
game5 - Slug:
spillorama(historisk arv — forvirrer fordi det er navnet på systemet også)
Game 4 / game4 / themebingo ble permanent avviklet per BIN-496 (2026-04-17). Nye PR-er skal IKKE referere game4 eller themebingo.
Forhåndstrukket player-startet (forskjell fra Spill 1-3)
Spill 1-3 er live hovedspill — server trekker baller i sanntid mens spillere ser på. Master eller perpetual-loop styrer trekninger.
SpinnGo er forhåndstrukket og player-startet:
- Spilleren klikker "Start spill" når de er klare
- Server genererer hele draw-sekvensen før visning (forhåndstrukket)
- Klient animerer trekninger lokalt med ruletthjul-effekt
- Sekvenser med 30s minimums-mellomrom (per pengespillforskriften databingo-regler)
room:createoppretter privat session; ikke canonical per-hall live-rom.room:joiner kun owner reattach/resume. Andre wallets skal avvises medSPINNGO_PRIVATE_SESSION.
Dette er fundamentalt forskjellig fra live bingo og krever andre regulatoriske kontroller.
Spill-mekanikk
Kanonisk oppdatert 2026-05-20 fra legacy Unity + Tobias-avklaringer: Stale
3×5 / 1-60 docs var feil for SpinnGo-porten. Legacy Unity SpinnGo har 3×3
bonger, 1-36 ruletthjul/trekkgrunnlag og 14 tall trekkes. Det spilles om
17 toppmønstre med multipliers vist øverst i spillet. Jackpot_1 og
Jackpot_2 er vanlige toppmønstre med høye multipliers, ikke ekstra
jackpot-payouts.
| Aspekt | SpinnGo |
|---|---|
| Slug | spillorama (historisk arv) |
| Kodenavn | game5 |
| Grid | 3×3 (9 ruter) |
| Ball-range | 1-36 |
| Maks ball-trekk | 14 default, backend-konfigurerbar via maxDrawsPerRound |
| Spille-modell | Player-startet, forhåndstrukket |
| Spesiell mekanikk | Ruletthjul, WOF/bonusspill, backend-konfigurerbare bonus-triggere |
| Min mellomrom | 30s mellom sekvenser (databingo-regel) |
| Bonger | Legacy-visning bruker 4 samtidige 3×3-bonger; BG-012 er fortsatt øvre databingo-sikkerhetsregel |
| Salgskanaler | Kun online (ikke fysiske bonger) |
| Vinning | 17 top-patterns med stake-multiplier, DATABINGO single-prize cap 2500 kr |
Top-patterns og bonus
DEFAULT_SPINNGO_PATTERNS er autoritativ backend-default for ny port:
- 17 row-major 3×3 masks med multipliers: x200, x160, x50, x30, x30, x20, x15, x9, x7, x7, x5, x5, x4, x3, x3, x2, x1.
- Payout = innsats/bongpris × multiplier, deretter DATABINGO single-prize cap 2500 kr.
bonusTriggerer backend/admin-konfigurerbar per pattern ("none"eller"bonus").- Bonger/innsats er backend/admin-konfigurerbart via
config.spinngo.entryFee(kroner) ogconfig.spinngo.ticketTypes[](name,type,priceMultiplier,ticketCount).GameManagement.ticketPriceer kun liste-/legacy-felt og lagres i øre/cents; ikke bruk det som runtimeentryFeei kroner. - Socket
bet:arm/wallet reservation må bruke faktisk selection-total (entryFee × ticketType.priceMultiplier × qty) når SpinnGo ticket tiers er aktive. Ikke reserver kunweightedTicketCount × entryFee; SpinnGo Purple er ett 3×3-brett, men kan være 4× innsats. - Socket-integrasjonstester for SpinnGo-prising må bruke en test-wallet som
faktisk implementerer
reserve/increaseReservationog må assert'e aktiv reservation amount. En grønnbet:armack uten reservasjonsoverflate beviser ikke at wallet-beløpet er riktig. - Admin edit av SpinnGo må pre-fylle fra
GameManagement.config.spinngo, PATCHe samme row-id, og bevare andreconfig-nøkler rundtspinngo. - WOF/roulette-settings ligger i
spinngoBonusog skal være admin-konfigurerbare, ikke hardkodet klientlogikk. spinngoBonus.wheelOfFortuneDistributioner ikke bare admin-display; backend WOF-spin skal bruke vektene. HviswheelOfFortunePrizeshar gjentatte beløp, fordel beløpsvekten over segmentene slik at total sannsynlighet per beløp matcher admin-konfigen.- Bonus runtime/UX må lese backend-konfig og
bonusTriggeredmetadata fra engine-resultatet.jackpot:activatedskal bære source metadata (sourcePatternId,sourcePatternName,sourceClaimId) når WOF åpnes fra et SpinnGo-bonusmønster, slik at overlay, support og audit kan vise hva som utløste bonusen uten å gjette fra lokal state. - WOF/Free Spin Jackpot-utbetaling er også en SpinnGo/DATABINGO-premie.
jackpot:spinmå cappe faktisk utbetalt beløp viaPrizePolicyManager.applySinglePrizeCap({ gameType: ledgerGameTypeForSlug(room.gameSlug) })før wallet-transfer, og ledger-metadata må merke SpinnGo-spinn medSPINNGO_FREE_SPIN_JACKPOT_PRIZE,freeSpinJackpot,rouletteOutcome, source pattern og source claim. - Gratis SpinnGo
ticket:swap/Bytt taller ikke wallet-innsats, men skal likevel være audit-/rapport-synlig som 0 krSTAKEmedmetadata.reason = "SPINNGO_SWAP_TICKET",gameSlug,swapTicketogticketId. GameSpecific-rapporten tellerswapTicketUsesfra denne metadataen. - SpinnGo 30%-til-organisasjoner-regelen skal regressjonstestes med
runtime-generert Game5Engine-ledger når ledger/cap/rapport endres, ikke bare
seeded ComplianceLedger-fixtures. Testen må gå fra faktisk
spillorama-runde til DATABINGO-dagsrapport ogcreateOverskuddDistributionBatch, slik atgameType,channelog house-account-kilde bevises samlet. - SpinnGo observability er en del av spillkontrakten:
startGameskal persisteretraceIdfra eksplisitt input eller ALS-context iGameState/Redis/shared snapshot, og sammetraceIdskal følge STAKE, PRIZE, WOF/Free Spin Jackpot, house-deficit, Spillvett player report og rapport-export. - SpinnGo STAKE-ledger metadata må minimum bære
traceId,roundId,ticketCount,lossAmount,walletTransactionId,walletTransactionIdsoggameSlug. Uten dette kan ikke support/compliance korrelere player-startet runde, wallet-reservasjon og rapportert tap. - SpinnGo public health skal være
/api/games/spinngo/healthmedauthority: "player-started-engine"ogschedulerOwner: "player-started"; ikke gjenbruk Spill 1 schedule eller Spill 2/3 perpetual health som sannhet. - SpinnGo rejection-logging må bruke stabile RUM-koder: BIN-RUM-006 cooldown, BIN-RUM-007 aktiv runde, BIN-RUM-008 max tickets og BIN-RUM-009 max stake.
Runtime-binding av admin-konfig
GameManagement lagrer SpinnGo-oppsett under config.spinngo. Player-startede
SpinnGo-rom har normalt ikke en konkret gameManagementId, så runtime må kunne
slå opp aktiv GameManagement-rad via slug (spillorama, game_5, spinngo)
før den faller tilbake til DEFAULT_SPINNGO_CONFIG.
Ikke anta at admin-formen alene er nok. Hvis roomState.bindVariantConfigForRoom
ikke får aktiv config.spinngo, vil engine bruke default 14 draws og default
innsats/ticket multipliers/bonus/WOF selv om admin har satt egne
entryFee, ticketTypes, maxDrawsPerRound, bonusTrigger eller
spinngoBonus.wheelOfFortunePrizes.
Frontend/runtime payload-kontrakt
SpinnGo-klienten skal ikke rekonstruere toppmønstre, bonusflagging eller
roulette/WOF-oppsett fra lokale constants når en room:update inneholder
gameVariant.spinngoPatterns og gameVariant.spinngoBonus. Backend payloaden er
den autoritative presentasjonskontrakten for GameBridge og Game5 PlayScreen.
Prevention:
RoomUpdatePayloadSchemaogGameVariantSchemamå eksponerespinngoPatternsogspinngoBonusnår nye admin-felter skal nå klienten.GameBridgemå bevare SpinnGo pattern-results motspinngoPatterns; ikke fallback til generiskecurrentGame.patternshvis de mangler SpinnGo-masker.- Game5 PlayScreen skal vise 17 toppmønstre, 3×3-bonger og
x/maxDrawsPerRoundfra state. Ikke gjeninnfør LINE/BINGO/lucky-number claim UI i SpinnGo. - Player-lobby skal vise markedsføringsnavnet
SpinnGofor legacy slugspillorama/game_5, selv om/api/gamesreturnerer en gammel titleSpillorama. Ikke migrer sluggen; normaliser display-navnet ved lobby/catalog-boundary og verifiser registry-aliasene. - Game5 bonusoverlay skal vise source-pattern fra
jackpot:activatednår feltet finnes. Ikke rekonstruer bonuskilde ved å lete i lokale pattern arrays hvis backend allerede har sendtsourcePatternId/sourceClaimId. - SpinnGo
PatternResult/snapshot skal bevarebonusTriggerfra backend/admin-konfigen for både vunne og ikke-vunne mønstre, slik at replay/support forklarer rundeoppsettet selv om admin endrer konfig senere. - Legacy/manual socket-path
claim:submitmå ikke åpne SpinnGo WOF bare fordi en BINGO-claim er valid. Den må kreveclaim.bonusTriggered === true, slik at backend/admin-konfigurertbonusTriggerer autoritativ også mot direkte socket-kall. Bruk alle SpinnGo-aliasene (spillorama,game_5,spinngo), ikke bare literalspillorama. - Visual harness må ha
spinngo-runningog pre-round/betting-scenario når layout/runtime wire endres, slik at desktop overlap og roulette/ticket-presentasjon verifiseres. Browser-smoke bør assert'e 17 toppmønstre, 37 roulette-segmenter,maxDrawsPerRound=14default og runtime-konfigurerte tier-priser (for eksempelentryFee=25med x1/x2/x3/x4 → 25/50/75/100 kr), ikke bare at harness-siden laster. - Release-/testmatrise-closeout må bevise alle 17 legacy toppmønstre med eksakt
3×3 row-major mask, multiplier og
bonusTrigger, og i tillegg evaluere hvert mønster isolert via faktisk SpinnGo rule engine. Slice-tester for cap, WOF, stake og visuell smoke er nødvendige, men de erstatter ikke full pattern-tabellsporbarhet.
Per-bong stake og pre-round bonger
Kanonisk oppdatert 2026-05-22 fra legacy Unity: SpinnGo har fire synlige
3×3-bonger i pre-round. Hver bong har egen kr-innsats, ikke en generisk
ticket-tier. Legacy-klienten sendte { id, price } per bong; bonger med
price = 0 ble vist, men var ikke kjøpt og skulle ikke markeres eller telle
inn i wallet/round-resultat.
Implementasjonsregler:
- Bruk
spinngoTicketStakes/ explicitticketId + pricenår pre-round stake endres. - Summer faktisk kr-beløp per bong for wallet-reservasjon. Ikke regn
entryFee × ticketType.priceMultiplierfor denne flyten. SwapTicketfør runden skal bytte tall, men bevareticketId, farge/type og stake-pris.- Når runden starter, adopter bare pre-round bonger med positiv stake som ekte tickets. 0 kr-bonger blir igjen som visning/valg, ikke live tickets.
- Stake UI-konfig leses fra
gameVariant.spinngoStakemed legacy defaults: chips 1/5/10/20/50, fire synlige bonger, max 50 kr per bong.
Ikke gjør: Ikke "rydde" dette tilbake til gammel per-type ticketSelections
for SpinnGo. Den pathen er fortsatt gyldig for andre spill/legacy-kall, men
SpinnGo-porten trenger per-bong kr-stake for å matche Unity.
Display-cache size-invariant (BIN-NEW, 2026-05-25)
Kanonisk regel: SpinnGo bruker en 4-slot display-cache som
populeres av bet:arm-handleren med ALLE fire fargene (Blue/Green/Red/Purple),
selv om kun én har stake>0. Dette MÅ bevares gjennom hele snapshot-flyten —
ellers regenererer cachen og priser nullstilles → UI viser "0 kr" etter
første "+".
Cache-write (bet:arm):
// roomEvents.ts:1130 (skjematisk)
getOrCreateDisplayTickets(code, pid, spinngoVisibleTicketCount, slug, 4-slot-colors);
updateDisplayTicketPrices(code, pid, [{Blue:10},{Green:0},{Red:0},{Purple:0}]);
armedTicketCounts[pid] = 1; // kun antall med stake>0
Cache-read (snapshot — buildRoomUpdatePayload):
// roomHelpers.ts:457-486 (post-fix)
if (isSpinngoRoom) {
displayCount = spinngoVisibleTicketCount; // ALWAYS 4
displayColorAssignments = effectiveConfig.ticketTypes
.slice(0, spinngoVisibleTicketCount)
.map((t) => ({ color: t.name, type: t.type }));
}
getOrCreateDisplayTickets(code, pid, displayCount, slug, displayColorAssignments);
Hvorfor 4 og ikke armedCount?
armedCount= antall tickets med stake>0 (kan være 1)spinngoVisibleTicketCount= antall synlige stake-chips (alltid 4)- Cache er sized til SISTNEVNTE, ikke FØRSTNEVNTE
- Mismatch →
colorsMatch(cached, undefined)returnererfalse→ cache regenererer → priser settes til?? 0→ alle priser blir 0
Ikke gjør:
- Ikke bruk
armedCountsomcount-argument tilgetOrCreateDisplayTicketsfor SpinnGo.armedCounter en LOGISK telling (paid slots), IKKE en VISUELL telling (visible slots). - Ikke send
colorAssignments=undefinedfor SpinnGo — det matcher ikke det cachede shape-et og trigger regenerering. - Ikke endre
spinngoVisibleTicketCountruntime-default uten å oppdatere BÅDE bet:arm-handler OG snapshot-pathen.
Test-bind: apps/backend/src/util/roomHelpers.spinngoArmedTickets.test.ts
(5 tester) — pinner cache-hit-invariant. Pre-fix faller alle.
Related pitfall: §3.CC
Regulatorisk klassifisering — KRITISK
MAIN_GAME vs DATABINGO
Pengespillforskriften definerer to relevante kategorier for Spillorama:
| Kategori | Min organisasjon | Maks enkeltpremie | Trekkingsmodus |
|---|---|---|---|
| Hovedspill (MAIN_GAME) | 15% | INGEN cap | Live, server-trukket |
| Databingo (DATABINGO) | 30% | 2 500 kr | Player-startet, forhåndstrukket |
Slug → gameType mapping
apps/backend/src/game/ledgerGameTypeForSlug.ts:
// MAIN_GAME for Spill 1-3 (hovedspill, 15%)
"bingo" → MAIN_GAME
"rocket" → MAIN_GAME
"monsterbingo" → MAIN_GAME
// DATABINGO for SpinnGo (30%, 2500 kr cap)
"spillorama" → DATABINGO (default fallback også)
"game_5" → DATABINGO
§11-distribusjon (kode)
apps/backend/src/game/ComplianceLedgerOverskudd.ts:75:
const orgPercent = row.gameType === "DATABINGO" ? 0.3 : 0.15;
Strukturelt korrekt mapping, men hardkoding i call-sites er en pågående bug — pre-fix returnerte DATABINGO for ALLE call-sites (Spill 1-3 inkludert), som er regulatorisk feil. Fixed via Wave 2 PR-er for Spill 1-3.
Single-prize-cap (kun databingo)
apps/backend/src/adapters/PrizePolicyPort.ts.applySinglePrizeCap:
gameType === "DATABINGO"→ cap 2500 kr per single-prizegameType === "MAIN_GAME"→ ingen cap
Eksempel: Lilla-bong får 3000 kr på Innsatsen Fullt Hus, 4500 kr på Oddsen-HIGH. Det er forventet og regulatorisk OK fordi Spill 1 er MAIN_GAME. SpinnGo har derimot 2500 kr-cap på alle premier.
Wallet-konto-ID-format
makeHouseAccountId genererer house-account-id basert på (hallId, gameType, channel):
- Spill 1-3 hovedspill:
house-{hallId}-main_game-INTERNETellerhouse-{hallId}-main_game-HALL - SpinnGo databingo:
house-{hallId}-databingo-{channel}
NB: GameType lekker inn i konto-IDer. Kjent gap, dokumentert i SPILLKATALOG.md §6.
Tre regulatoriske ledger-dimensjoner
ComplianceLedger må skille mellom tre dimensjoner per pengespillforskriften §11:
- Hall main game — Spill 1, 2, 3 spilt fysisk i hall (kontant + agent-cashout)
- Internet main game — Spill 1, 2, 3 spilt over internett (digital wallet)
- Databingo — SpinnGo (kun internett, player-startet)
Backend-implementasjon:
app_rg_compliance_ledger.game_type∈ {MAIN_GAME,DATABINGO}channel∈ {HALL,INTERNET} (kun MAIN_GAME bruker HALL)- §11-prosent leses fra
gameTypeved overskudd-fordeling
TECHNICAL_BACKLOG-regler som gjelder KUN SpinnGo
| Regel | Gjelder for |
|---|---|
| BG-011: Min 30s mellom databingo-sekvenser | KUN SpinnGo (ikke Spill 1-3) |
| BG-012: Maks 5 databingo-tickets per spiller | KUN SpinnGo |
| BG-013: Én aktiv databingo-runde per spiller | KUN SpinnGo |
Backlog dokumenterer reglene som "databingo-regler" — verifiser at de håndheves KUN i SpinnGo-paths.
Game 4 / themebingo deprecated (BIN-496)
Game 4 (kodenavn game4, slug themebingo) ble permanent avviklet 2026-04-17 per BIN-496:
- Slug
game4/themebingoskal ikke brukes - Kode-stier som referer dem er legacy — verifiser om de kan slettes
- Ny kode skal IKKE introdusere referanser til Game 4
Kommentar fra historisk forvirring:
- Markedsføring nummer 4 (Spill 4) ≠ Game 4 / game4 / themebingo
- "Spill 4" markedsføring = SpinnGo = game5 = databingo
- Game 4 finnes ikke som aktivt spill
Vanlige feil og hvordan unngå dem
1. Antar at SpinnGo er hovedspill
Symptom: Single-prize-cap (2500 kr) ikke håndhevet, eller §11-prosent settes til 15%.
Fix: Verifiser via ledgerGameTypeForSlug("spillorama") returnerer DATABINGO.
2. Antar at Spill 1-3 er databingo
Symptom: Hardkodet gameType: "DATABINGO" i Spill 1-3-call-sites (BIN-769 / Wave 2 fix).
Fix: Bruk alltid ledgerGameTypeForSlug(room.gameSlug) — aldri hardkodet streng.
3. Refererer Game 4 / themebingo
Symptom: Ny kode importerer themebingo-konstanter eller game4-paths.
Fix: Game 4 er deprecated (BIN-496). Slug themebingo skal ikke brukes.
4. Forvirrer Spill 4 og Game 4
Symptom: Antar at "Spill 4" markedsføring betyr game4-kodebasis.
Fix: "Spill 4" markedsføring = SpinnGo = game5 = spillorama-slug. Game 4 er en deprecated annet spill.
5. Bryter forhåndstrukket-regelen
Symptom: SpinnGo trekker live i sanntid (kopiert fra Spill 1-3-flyt). Fix: SpinnGo MÅ være forhåndstrukket. Server genererer hele draw-sekvensen før visning.
6. Skipper 30s minimums-mellomrom
Symptom: Spiller kan starte ny SpinnGo-runde umiddelbart etter forrige. Fix: Håndhev 30s mellom sekvenser (BG-011).
7. Glemmer ticket-grenser
Symptom: Spiller kan kjøpe eller vise flere SpinnGo-bonger enn legacy/UI-regelen tillater. Fix: Legacy SpinnGo-porten viser 4 samtidige 3×3-bonger. Behold BG-012 som øvre databingo-sikkerhetsregel, men ikke øk SpinnGo UI-kjøp over 4 uten eksplisitt Tobias-avklaring.
8. Bytter spillorama-slug til spinngo
Symptom: Tror at slug skal følge markedsføringsnavn. Fix: Slug er stabil (historisk arv). Ikke endre — det krever DB-migrasjon av alle eksisterende rader.
9. Glemmer 2500 kr-cap håndhevelse
Symptom: SpinnGo-vinner får > 2500 kr per single-prize.
Fix: applySinglePrizeCap MÅ kjøres for DATABINGO-paths. Verifiser i tester.
10. Åpner WOF på alle SpinnGo BINGO-claims
Symptom: Direkte claim:submit på spillorama aktiverer jackpot:activated
selv om vinnermønsteret ikke har bonusTrigger: "bonus".
Fix: WOF/bonus åpnes kun når engine/claim-recorden har
bonusTriggered === true; vanlige toppmønstre, inkludert Jackpot_1 og
Jackpot_2, er bare høye multipliers. Sjekken må bruke SpinnGo-aliaser
(spillorama, game_5, spinngo) så fallback-/legacy-slugs oppfører seg likt.
11. Behandler SpinnGo WOF-utbetaling som generisk uncapped jackpot
Symptom: Admin kan konfigurere WOF-premie over 2500 kr, og jackpot:spin
utbetaler hele beløpet eller logger det uten freeSpinJackpot/roulette-metadata.
Fix: WOF-spinn for SpinnGo er DATABINGO-premie. Cap faktisk payout til
2500 kr via per-spill prize policy før transfer, og logg source pattern/claim
og rapportflagg i compliance-ledger.
12. Glemmer ledger-rad for gratis SpinnGo ticket:swap
Symptom: Bytt tall virker i klienten og koster 0 kr, men GameSpecific-rapport
viser swapTicketUses = 0 fordi ingen compliance-ledger-rad bærer
metadata.swapTicket.
Fix: Etter vellykket SpinnGo swap, logg en 0 kr STAKE med
SPINNGO_SWAP_TICKET, gameSlug, swapTicket, ticketId og round metadata
når runden finnes. Dette skal være fail-soft for UX, men regressjonstestes i
socket-integrasjonen og rapport-testes via buildGameSpecificReport.
13. Tester SpinnGo 30% kun med seeded ledger-fixtures
Symptom: ledgerGameTypeForSlug.distribution.test.ts er grønn, men en faktisk
SpinnGo-runde kan fortsatt skrive feil gameType, channel eller
house-account-kilde før overskudd-distribusjonen kjøres.
Fix: Ved endringer i SpinnGo ledger/compliance/rapport må minst én test
starte en faktisk spillorama Game5Engine-runde, generere DATABINGO-rapporten
og kjøre createOverskuddDistributionBatch. Assert 30% minimum og
house-{hallId}-databingo-internet source-account.
14. Stoler på gammel katalogtitle for SpinnGo i lobby
Symptom: Sluggen er riktig (spillorama), men player-lobby viser fortsatt
Spillorama fordi /api/games returnerer gammel DB-title fra legacy/admin.
Fix: Normaliser spillorama/game_5/spinngo til display-title
SpinnGo ved lobby/catalog-boundary, filtrer deprecated themebingo/game_4,
og test at spillorama + game_5 fortsatt mapper til Game5Controller.
15. Kaller SpinnGo E2E ferdig uten full 17-pattern-regresjon
Symptom: Backend har grønne tester for cap, WOF, per-bong stake og noen
representative resultater, men ingen test som låser hele legacy Unity-premieraden
øverst i spillet.
Fix: Hold en table-driven test for alle 17 default-mønstre: exact 3×3 mask,
multiplier, bonusTrigger og faktisk evaluator-resultat. Jackpot_1 og
Jackpot_2 er vanlige høye toppmønstre, ikke ekstra jackpot-payouts.
Implementert (SpinnGo Refactor E1 Wave 1, BIN-1093, PR #2020):
apps/backend/src/game/SpinngoRules.spinngo17PatternTable.test.ts (166 tester) +
apps/backend/src/game/SpinngoRules.mutation.test.ts (47 mutation-killing tester)
apps/backend/stryker.SpinngoRules.config.json(Stryker baseline). Pinner pitfall §3.39 IMMUTABLE: eksakt 3×3 row-major mask + multiplier + bonusTrigger per pattern × stake-sweep [1,5,10,20,50] kr × DATABINGO 2500 kr cap-verifikasjon (85 kombinasjoner). Jackpot_1/2 eksplisitt pinned sombonusTrigger='none'. Endring iDEFAULT_SPINNGO_PATTERNS(apps/backend/src/game/variantConfig.ts:848-866) breker test → krever Tobias-godkjenning per IMMUTABLE-direktiv.
Implementert i Wave 2 (PR #2025) — partial:
- T1.1 end-to-end integration test:
apps/backend/src/sockets/__tests__/spinngoEndToEnd.integration.test.ts(493 LOC, 1 pass + 3.skipmed TODO BIN-1093) - T1.6 visual-harness:
packages/game-client/visual-harness/scenarios/spinngo-{pre-round,roulette-spinning,pattern-win,wof-activated,post-round}.html(5 wrappers, 149 LOC totalt) - Follow-up: 4 issues for
.skip-rotsårsaker (display-cache viagenerateTicketForGameikke bingoAdapter,testServer.ts:432simplifiedbuildRoomUpdatePayload, socket-handlergameLifecycleEvents.ts:53handleStartGame()dropperpayload.traceIdtilengine.startGame()— sistnevnte bryter MED-1 trace-propagation) - Wave 2 ble splittet i 3 micro-agents (debug/visual/evidence, max 15 tool-calls hver) etter 2 prior full-Wave-2 crashes (API Overload + stuck). PM-AI orchestrerte sekvensielt.
Implementert i Wave 3 (BIN-1106) — utvidet sikkerhetsnett:
- T1.3:
packages/game-client/src/games/game5/Game5Controller.test.ts(8 orchestration-tester pinner G3 IMMUTABLE play-button + 4-alias registry + RUM-006..009 filtering) - T1.8:
apps/backend/src/__tests__/chaos/spinngoOwnerReconnect.test.ts(R3-I1..I4 chaos invariants for owner-reattach på privat session; G4 enforcement)
Implementert i Wave 4 (BIN-1093 T1.2) — R10 chaos sikkerhetsnett:
infra/chaos-tests/r10-spinngo-chaos-test.sh(217 LOC,--scenario=<name>)apps/backend/src/__tests__/chaos/r10SpinngoInvariants.test.ts(520 LOC, 18 invariants).github/workflows/spinngo-r10-chaos.yml(workflow_dispatch initielt per Spill 3-pattern, matrix over 3 scenarios)- Coverage: 6/18 in-process (scenario 3 owner-reconnect via
createTestServer), 12/18.skip(Docker chaos-infra påkrevd for scenarios 1+2) - Cross-link: scenario 3 deler G4 anti-hijack-mønster med Wave 3 R3-I4 (owner-reattach standard for SpinnGo)
E10 implementert (BIN-1102, 2026-05-28) — 12 Docker-chaos-invariants snapshot-backed:
De 12 tidligere {skip:true}-stubbene (scenario 1 mid-draw-redis-restart +
scenario 2 wof-spinn-postgres-failover) er erstattet med ekte snapshot-backed
assertions, speilende Spill 3 R10-mønsteret (BIN-1109 hard-pass):
loadSnapshot(envVar)leserPRE_KILL_SPINNGO_SNAPSHOT/POST_RECOVERY_SPINNGO_SNAPSHOTfra fil-stier i env.HAS_SNAPSHOTS = PRE !== null && POST !== null. Hver invariant er{ skip: !HAS_SNAPSHOTS }— KJØRER når snapshots finnes (IKKE{skip:true}).infra/chaos-tests/spinngo-room-bootstrap.mjs(NY): bootstrapper en privat SpinnGo-session FØR kill (pitfall §6.37). room:create → bet:arm → game:start → draw:next. Skriver den RANDOM room-koden tilROOM_CODE_FILE(SpinnGo har INGEN fixed room-code — ulikt MONSTERBINGO/ROCKET).- Chaos-script
snapshot_state()queryergame_recovery_snapshots WHERE room_code = <captured>(BIN-1109 lærdom: IKKEapp_room_states; brukjsonb_array_lengthIKKEjson_array_length). Claims-dedup viaCOUNT(*)vsCOUNT(DISTINCT id); cap viaMAX(payoutAmount). - NODE_ENV=production-felle: Chaos-stacken kjører
NODE_ENV=production(docker-compose.chaos.yml). I prod hard-failerSpinngoConfigBinder.resolve()medSPINNGO_CONFIG_MISSINGhvis ingen aktivapp_game_management-rad finnes, og demo-seed lager INGEN SpinnGo-config. Chaos-script §3.5 seeder derfor en minimal aktivrunningSpinnGo-config (game_type_id='spillorama') + topper opp bootstrap-spillerens wallet før runden. - Invariant→snapshot-felt-mapping:
- S1-I1 drawnNumbers monoton + ≤14 · S1-I2 tickets_owner_count ≤4 (display-cache) · S1-I3 prize_pool_remaining monoton (bet:arm overlevde) · S1-I4 claim-id unike (no dup ClaimRecord) · S1-I5 audit_log_count append-only (G1) · S1-I6 traceId (advisory)
- S2-I1 drawnNumbers monoton · S2-I2 max_claim_payout ≤2500 (G2 cap) · S2-I3 prize_pool monoton (atomisk transfer) · S2-I4 compliance-ledger append-only + house_deficit ≥0 · S2-I5 payoutTransactionIds globalt unike (no double-spend) · S2-I6 traceId (advisory)
- R10 HARD-PASS: GH run 26599872419
(workflow_dispatch scenario=all, conclusion success). Begge Docker-jobs +
scenario-3 viser
# tests 19 # pass 19 # fail 0 # skipped 0— alle 18 invariants RUN mot ekte snapshots. Snapshots pre==post byte-identisk → ingen robusthet-bug, SpinnGo overlever redis-restart + postgres-failover. Drill-log:docs/operations/dr-drill-log/2026-05-DF-spinngo.md. - Anti-theater verifisert: med healthy snapshots → 19/19 pass / 0 skip; med broken snapshots (cap-bypass 3000>2500, double-spend, prize-pool-rollback, hash-chain-truncation, lost-draws) → 10 invariants FAIL. Assertions er ekte verifisering, ikke placeholders.
16. Player-lobby viser "Stengt" for SpinnGo
Symptom: SpinnGo-tilen i player-lobby (apps/backend/public/web/lobby.js)
viser "Stengt" når /api/games/status returnerer CLOSED. Spillere tror
SpinnGo er nede selv om det er forhåndstrukket og player-startet — første
klikk spawner ny session.
Root-cause: PERPETUAL_LOOP_SLUGS (rocket + monsterbingo) får speciell
"Klar til start"-badge, men SpinnGo (spillorama/game_5/game5/spinngo)
fall-through til Stengt-default. SpinnGo har INGEN hall-opening-gate
(ulikt Spill 1) og INGEN perpetual-loop (ulikt Spill 2/3).
Fix: Legg til PLAYER_STARTED_SLUGS = { spillorama, game_5, game5, spinngo }
i buildStatusBadge() og returner samme "Klar til start"-badge som
perpetual-spillene. SpinnGo skal ALDRI vise "Stengt" basert på status-feed.
File: apps/backend/public/web/lobby.js:864-970 (PERPETUAL_LOOP_SLUGS-region).
Referanse: Tobias-direktiv 2026-04-25 (SpinnGo = databingo, player-startet).
17. Pixi pattern-strip mangler OTG-pulse + premie-kr + JACKPOT-shine
Symptom: SpinnGo SpinngoPatternStrip (Pixi) viser kun multiplier x200,
ingen kr-beløp, ingen pulse når en kupong er one-to-go, og ingen synlig
JACKPOT-merking for de 3 første mønstrene. Mockup spinngo-anim-test.html
linje 360-480 + 545-565 + 962-979 har alt dette via CSS-animation.
Root-cause: SpinngoPatternStrip.update() tar bare (patterns, results, myPlayerId) — ingen OTG-state, ingen entryFee for kr-beregning. Pixi-rendering
har heller ingen ticker-driven alpha-pulse.
Fix:
- Compute OTG i
PlayScreenved å sjekke hver pattern × hver ticket: hvis pattern krever 1 tall til og ticket har 1 udrukket required-tall → OTG på den ticket-indeksen. - Pass
otgPerPattern+entryFeeKr+singlePrizeCapKrtilSpinngoPatternStrip.update(). - Bruk
Ticker.shared.add()til å drive alpha-pulse på OTG-kort (0.9s sinus 0.65→1.0) og shine på JACKPOT-labels (1.6s sinus 0.7→1.0 + scale 1→1.05). - Compute prize =
entryFeeKr × multiplier, cap til 2500 kr per DATABINGO. File:packages/game-client/src/games/game5/components/SpinngoPatternStrip.ts
packages/game-client/src/games/game5/screens/PlayScreen.ts(computeOtgPerPattern+computeOtgTicketIndices).
18. SpinnGo ticket-board mangler OTG-glow og glass-effekt
Symptom: Spillerens 4 SpinnGo-bonger mangler den karakteristiske glass-glow
i kupong-fargen (blå/grønn/rød/lilla) og pulser ikke når en bong er
one-to-go. Mockup linje 313-358 har backdrop-filter: blur(4px) +
box-shadow: 0 0 15px rgba(color, 0.6) som visuelt definerer kupong-fargen.
Root-cause: SpinngoTicketBoard tegner kun grid + header — ingen
outer glow, ingen OTG-pulse-ramme. Pixi har ikke ekte backdrop-filter
eller box-shadow, så vi må simulere med flere lag av semi-transparent
Graphics-rektangler.
Fix:
- Tegn 3-lags "glow"-rektangler bak gridet med stigende padding (3/6/10px) og avtakende alpha (0.34/0.22/0.12) i kupong-fargen.
- Tegn et fadet "inset"-rektangel inni gridet med 0.14 alpha.
- Når
isOneToGo=true: tegn en ekstra ramme i kupong-fargen + brukTicker.sharedtil å pulse alpha 0.7→1.0 over 0.9s. File:packages/game-client/src/games/game5/components/SpinngoTicketBoard.ts(drawGlassGlow+drawOtgPulseFrame).
19. Ball-pop-animasjon kollapser til én ease (Gap 4 regresjon)
Symptom: SpinnGo ball-trill-fra-senter-til-pocket føles "robotisk" — alle
trekkene har identisk physical-feel. Mockup-en (spinngo-anim-test.html
linje 796-873) plukker bevisst en av 4 cubic-bezier roll-easings + en av 4
sweep-easings per spin pluss randomiserer durations (1.60-1.75s roll, 1.80-1.95s
sweep) for å bryte perfekt-determinisme. Hvis en fremtidig agent forenkler
ROLL_EASINGS/SWEEP_EASINGS til én ease + fast duration, mister vi den
mekaniske distinksjonen mockup-en var designet for.
Root-cause: "Bare én ease er nok, animasjonen ser greit ut" — ikke
nok visuell sammenligning mot mockup-en med 14 trekk i rad.
Fix:
- Behold
ROLL_EASINGSogSWEEP_EASINGSsom 4-arrays. Det er en testet invariant —RouletteWheel.ballPop.test.tsfeiler hvis arrayet har annen lengde enn 4. - Behold per-spin random duration (1.60-1.75s roll, 1.80-1.95s sweep). Sweep er ALLTID lengre enn roll så rotator-containeren settler etter ballen lander (mockup linje 853).
cubicBezierEase()er Newton-Raphson + bisection-hybrid — IKKE bytt til polynomial-approximation. Newton-Raphson kan returnere NaN for degenerate control-points; bisection-fallback fanger det. TestencubicBezierEase never returns NaNfinite-sjekker 101 sampling-punkter.POP_EASEMÅ værecubic-bezier(0.175, 0.885, 0.32, 1.275). TestenPOP_EASE overshoots past 1.0 mid-animationfeiler hvis kurven ikke overskytter — fordi da ser pop ut som fade-in, ikke pop.Math.random()iprocessSpinQueueer PURELY VISUAL (ease/duration- valg, ikke payout-bestemmende). BIN-745 CSPRNG-policy gjelder bareapps/backend/src/{game,agent,payments}— denne fil er ipackages/game-client/og er utenfor scope. Files:packages/game-client/src/games/game5/components/RouletteWheel.ts(cubicBezierEase,POP_EASE,ROLL_EASINGS,SWEEP_EASINGS,processSpinQueue) +RouletteWheel.ballPop.test.ts(12 invariant-tester).
20. Draw-tallet "popper" inn i stripen før hjul-spinnet lander (timer-drevet vs animasjons-drevet draw)
Symptom: SpinnGo viser det neste trukne tallet i draw-historikk-stripen
umiddelbart når backend sender det, mens roulette-hjulet fortsatt spinner mot
samme tall. Resultatet er at tallet "avsløres" før ballen lander — animasjonen
føles desynkronisert og fusket. Dette var den GAMLE (pre-PR-#1942) atferden:
draw-reveal var koblet til backend-broadcast, ikke til at spinnet faktisk landet.
Root-cause: To uavhengige tidslinjer fikk lov til å drive draw-reveal:
(a) backendens generiske DrawScheduler auto-draw-tick, og (b) klientens
hjul-animasjon. Når begge er aktive samtidig dobbelt-trekker man / avslører for
tidlig.
Fix (animasjons-drevet draw-pump — PR #1942 forward-port):
- Backend bypass:
DrawScheduler.isPlayerDrivenRoom(summary)returnererisSpinngoSlug(summary.gameSlug)→bypassesGenericScheduler()hindrer at den generiske auto-draw-tick-en trekker for SpinnGo-rom. Klienten pacer draws selv via draw-pump-en iGame5Controllerslik at hvert tall avsløres i takt med spinnet. Backend forblir sannhet — bypass-en endrer KUN HVEM som trigger neste draw-tick, ikke hvem som autoriserer/validerer trekket. ALDRI dupliser SpinnGo-alias-settet lokalt i DrawScheduler — importerisSpinngoSlugfra@spillorama/shared-types/slugs(D1/BIN-1095). - Reveal-window-clamp:
resolveVisibleDrawnNumbers(state, revealedDrawCount)ilogic/SpinngoStateBinding.tsklamper synlig draw-historikk tilrevealedDrawCountREGARDLESS avgameStatus— så det siste tallet ikke poppes inn i stripen før spinnet lander (holder også vinduet hvis runden flipper til ENDED midt i et spin).revealedDrawCount-cursoren avanseres avonNumberDrawnnårroulette.spinTo(n)-Promise resolver. onNumberDrawnreturnererPromise<void>: PlayScreen suspenderer dekorativ motion (setDecorativeMotionSuspended(true)), kjørerroulette.spinTo(number), og gjør tung state-sync FØRST etter at spinnet lander.Game5Controllerawait-er/.then()-er denne Promise og gater påplayScreen.isRoundAnimationActive()(=roulette.isAnimating()) før neste draw pumpes — dette er det som pacer pumpen til animasjonen.- Heavy-sync-deferral:
shouldDeferHeavySync = !forceHeavy && !isNewGame && drawnNumbers.length > 0 && roulette.isAnimating()— mens hjulet spinner rendres kun lett (result/info), ikke full scene-rebuild, så frame-loopen ikke stammer midt i spinnet. Files:apps/backend/src/draw-engine/DrawScheduler.ts(isPlayerDrivenRoom/bypassesGenericScheduler),packages/game-client/src/games/game5/logic/SpinngoStateBinding.ts(resolveVisibleDrawnNumbersclamp),packages/game-client/src/games/game5/screens/PlayScreen.ts(onNumberDrawn/syncFromState-deferral/setDecorativeMotionSuspended),packages/game-client/src/games/game5/Game5Controller.ts(draw-pump som gater påisRoundAnimationActive()). Tester:DrawScheduler.test.ts(2 SpinnGo player-driven bypass-tester),SpinngoAnimationProfiler.test.ts,assetPreload.test.ts.
Forward-port-protokoll (lærdom fra §5.19): Denne fixen lå i en åpen, Tobias-bekreftet PR (#1942) i ~63 commits mens E1-E10-refactoren landet på main — prod kjørte derfor den gamle ødelagte animasjonen helt til forward-porten. Når en bekreftet brukervendt fix ikke kan merges umiddelbart: spor den via Linear (ikke "husk" den åpne PR-en), og når du forward-porter en branch som er eldre enn en refactor, kjør
tsc --noEmit+ build + tester per commit — "applies cleanly" via cherry-pick betyr IKKE at den kompilerer mot den nye strukturen. Sepitfalls-05-git-pr-flyt.md§5.19.
Player-started round trigger (BIN-NEW, 2026-05-25)
SpinnGo is fundamentally different from Spill 1/2/3 in how rounds start:
| Spill | Round-start trigger |
|---|---|
| Spill 1 | Master presses "Start" in master-konsoll → backend emits game:start |
| Spill 2/3 | PerpetualRoundService auto-spawns next round after roundPauseMs |
| SpinnGo | Player presses the yellow play-button → client emits game:start |
The play-button IS the trigger — never make it decorative
packages/game-client/src/games/game5/screens/PlayScreen.ts exposes the
yellow play-button at bottom-right of the table. It MUST be wired with:
button.eventMode = "static";
button.cursor = "pointer";
button.on("pointerdown", () => { this.onPlayPressed?.(); });
Without these, the button renders fine but does nothing → user cannot
start a round → SpinnGo is unplayable. This bug shipped to prod 2026-05-25
because a previous refactor stripped the interactivity. The fix wires
PlayScreen.setOnPlayPressed(cb) and Game5Controller.handlePlayPressed()
which calls socket.startGame({ roomCode }).
Server contract for game:start
Verified in apps/backend/src/sockets/gameEvents/gameLifecycleEvents.ts:53-174:
requireAuthenticatedPlayerAction→ resolvesplayerIdfrom socket authassertGenericLifecycleAllowed→ ALLOWS SpinnGo (only blocks perpetual)engine.startGame({ actorPlayerId: playerId, ... })→ calls:assertHost(room, playerId)→ SpinnGo player IS the host (created room viaroom:create→RoomLifecycleServicesethostPlayerId = playerId). PASSES.minPlayersToStart = 1forusesSpinngoTicket(room.gameSlug)(seeBingoEngine.ts:1129-1131). Single-player rounds are valid.- Uses
getArmedPlayerSelections(roomCode)[playerId]to load the player's armed stake snapshot.
Pre-round 4 boards are server-cached
apps/backend/src/util/roomHelpers.ts:413-428 calls
getOrCreateDisplayTickets(roomCode, playerId, visibleTicketCount=4, gameSlug, colorAssignments) for UNARMED SpinnGo players. The cache in
RoomState.getOrCreateDisplayTickets (apps/backend/src/util/roomState.ts: 487-532) generates 4 tickets with id: tkt-${i} for i in [0, 4) and a
full 3×3 grid via generateSpinngoTicket() (apps/backend/src/game/ticket.ts: 254-265).
Implication: tkt-0 through tkt-3 are ALWAYS valid ticketIds for a
SpinnGo player's bet:arm payload, even before the player has placed any
stake. The client can build stake-payloads using these canonical ids even
when the local preRoundTickets snapshot is incomplete.
Chicken-and-egg fix: synthetic ticket-ids in client
Pre-fix: client PlayScreen.renderTickets gated +/− stake controls on
Boolean(ticket?.id). If state.preRoundTickets[index] was undefined, the
controls were disabled. User then couldn't add stake → never armed → server
never generated tickets → permanent deadlock.
Post-fix: client computes effectiveTicketId = ticket?.id ?? \spinngo-slot-
${index}`and passes it to the +/− callbacks.adjustTicketStaketranslatesspinngo-slot-${i}→tkt-${i}before forwarding to the server. Since server-side cache always assignstkt-${i}for sloti`,
the bet:arm payload is always valid.
Code locations:
packages/game-client/src/games/game5/screens/PlayScreen.ts:357-413(renderTicketssynthetic-id assignment)packages/game-client/src/games/game5/screens/PlayScreen.ts:850-895(adjustTicketStakeslot→tkt translation + full 4-slot snapshot)packages/game-client/src/games/game5/Game5Controller.ts:241-313(handlePlayPressed→socket.startGame)
Anti-pattern: DO NOT remove play-button interactivity
If a future agent "simplifies" createPlayButton by removing
eventMode/cursor/pointerdown, the round trigger is gone. There is no
fallback — perpetual-loop doesn't apply to SpinnGo, master doesn't exist
for SpinnGo. The play-button is the only way to start a round.
Test invariant: PlayScreen.playButton.test.ts asserts
button.eventMode === "static" and button.cursor === "pointer" and
that the onPlayPressed callback fires on pointerdown. Do not
disable these tests without replacing with equivalent invariant proof.
Når denne skill-en er aktiv
Gjør:
- Les
SPILLKATALOG.mdFØRST for klassifisering - Verifiser slug→gameType-mapping via
ledgerGameTypeForSlug - Bruk markedsføring-navn ("SpinnGo") i UI, slug ("spillorama") i kode
- Normaliser player-lobby/katalogvisning slik at legacy
spillorama-slug vises somSpinnGo, og beholdspillorama/game_5som tekniske aliases - Håndhev 2500 kr-cap KUN for
DATABINGO-paths - Sjekk §11-prosent (30% for databingo) ved overskudd-fordeling
- For ledger/compliance-endringer: bevis SpinnGo 30%-pathen med en runtime-generert Game5Engine-ledger når scope kan påvirke rapport eller overskudd-distribusjon
- Sørg for at SpinnGo-only handlinger som ikke flytter penger fortsatt har
audit-/rapportmetadata når rapporten forventer dem, spesielt
ticket:swap - Behandle SpinnGo som privat player-session i socket-flow:
room:createlager egen romkode,room:joinfor andre wallets er ikke tillatt. - Ved release-/testmatrise-closeout: map Linear-akseptanse til konkrete tester, og inkluder eksplisitt 17-pattern/multiplier/bonusTrigger-dekning.
- Verifiser at endringer på SpinnGo IKKE påvirker Spill 1-3 (forskjellige services)
Ikke gjør:
- IKKE introduser hardkodet
gameType: "DATABINGO"i Spill 1-3-paths - IKKE bruk
themebingo/game4-slug (deprecated BIN-496) - IKKE forveksle Spill 4 (SpinnGo) med Game 4 (deprecated)
- IKKE endre
spillorama-slug — det er stabil DB-koding - IKKE vis
themebingo/game_4som aktivt lobbyspill - IKKE canonicaliser SpinnGo til ett delt
SPILLORAMAper hall eller gjenbruk spectator/late-join-live-room fra Spill 1-3. - IKKE introduser live-trekkning for SpinnGo (det er forhåndstrukket)
- IKKE skip 30s mellomrom-regel eller 5-tickets-grense for SpinnGo
- IKKE fjern 2500 kr-cap for SpinnGo
- IKKE la gratis
Bytt tallvære usynlig i GameSpecific-rapportering - IKKE legg til 2500 kr-cap for Spill 1-3 (de er hovedspill — ingen cap)
- IKKE kall SpinnGo E2E komplett uten eksplisitt testdekning for alle 17 legacy toppmønstre, cooldown og per-bong ticket-/stake-grenser
Kanonisk referanse
docs/architecture/SPILLKATALOG.md er autoritativ. Hvis kode motsier doc-en, fix koden. Dokumentet er korrigert 2026-04-25 etter at 2026-04-23-spikringen feilaktig hevdet alle interne spill var hovedspill.
Hvis du finner andre dokumenter (SPILL1_GAMETYPE_INVESTIGATION, KRITISK1_RNG_*) som motsier dette: oppdater dem til ny klassifisering.
Decide Fit First
Design Intent
How To Use It
Boundaries And Review