Hold & Win Reference Mechanic Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Add a reusable, persistent hold_and_win mechanic to the canonical Go math runtime and deliver a separate 3-column × 5-row reference pack with versioned Draft math, reproducible simulation evidence and gated handoff artifacts.
Architecture: Implement one pure versioned state machine (Start, Step, Settle) in libs/mathengine; production persists each step while RunToCompletion drives the identical core for batch simulation. Add a new feature type instead of changing the existing partial hold_and_spin, and keep all executable parameters canonical in /Users/admin/kiro/backend.
Tech Stack: Go, libs/mathengine, deterministic libs/rng, Math Studio domain/config mapping, PostgreSQL game-engine state, YAML/JSON config, Go tests with race detector, seeded Monte Carlo evidence, Markdown GDD/Math Spec/handoff.
Global Constraints
- Approved design:
docs/superpowers/specs/2026-08-09-hold-and-win-reference-design.mdat workspace commitc44bc01. - Grid is exactly 3 columns × 5 rows; normative cell order is column 0 rows 0–4, column 1 rows 0–4, column 2 rows 0–4.
- Trigger requires at least 6 BONUS symbols; initial respins are 3; a new landed prize resets respins to 3.
- Draft landing chance is 15% per empty cell.
- Regular value/weight pairs are
1:40, 2:28, 3:16, 5:10, 10:5, 20:1. - Prize-type weights are Regular
1000, Mini 10×20, Minor 25×5, Major 100×1. - Full grid awards exactly Grand 500× instead of the prize sum; ordinary settlement is capped once at 500×.
- No collector, multiplier, feature buy or additional wager in v0.1.
hold_and_spinand frozenclassic-fruits-3x3v1 must remain unchanged.- Runtime and simulation must call the same state-transition implementation and preserve the documented draw order.
- Every backend behavior change follows RED → observed failure → minimal GREEN → regression run.
- Draft evidence is
DRAFT / NOT FOR CERTIFICATION; Monte Carlo is never labelled exact. - Stop at the three mechanic approval gates defined in the design and
skills/kiro-slot-mechanic-builder/SKILL.md.
File structure
Math workspace
- Create
games/hold-and-win-reference/README.md— artifact index and lifecycle status. - Create
games/hold-and-win-reference/gdd/hold-and-win-reference-gdd.md— player-facing and normative feature rules. - Create
games/hold-and-win-reference/gdd/hold-and-win-backend-response.md— actual serialized trigger/respin/replay/reconnect/settlement contract derived from backend fixtures. - Create
games/hold-and-win-reference/math/hold-and-win-reference-math-spec.md— whole-game targets, decomposition and backend provenance. - Create
games/hold-and-win-reference/math/mechanics/hold-and-win/hold-and-win-math-draft-v0.1.md— mechanic-owned Draft math. - Create
games/hold-and-win-reference/math/mechanics/hold-and-win/hold-and-win-math-changelog.md— immutable revision history. - Create
games/hold-and-win-reference/math/mechanics/hold-and-win/hold-and-win-math-handoff-v0.1.md— pending handoff manifest. - Create
games/hold-and-win-reference/evidence/{simulation,parity,max-win}/README.md— evidence routing. - Create
games/hold-and-win-reference/test-vectors/README.md— vector identity and generation commands. - Create
games/hold-and-win-reference/changelog.md— game-level changes. - Modify
games/README.mdand rootREADME.md— link the reference game.
Backend
- Modify
libs/mathengine/config.go— typed config and prize definitions. - Create
libs/mathengine/hold_and_win.go— pure state, transitions, validation and settlement. - Create
libs/mathengine/hold_and_win_test.go— core contract tests. - Create
libs/mathengine/hold_and_win_executor.go— feature-loop adapter and outcome mapping. - Create
libs/mathengine/hold_and_win_executor_test.go— engine integration and regression tests. - Modify
libs/mathengine/feature_def_json.goand tests — typed JSON round trip. - Modify
libs/mathengine/mathengine.go— outcome field and feature registration. - Modify
libs/mathengine/capabilities.goand tests — support status. - Modify
libs/mathengine/batch.goand tests — mechanic duration/reset/full-grid/jackpot aggregates. - Modify
apps/math-studio/internal/domain/feature.goand tests — authoring model and validation. - Modify
apps/math-studio/internal/domain/build_config.goand tests — domain-to-engine mapping. - Create
apps/math-studio/internal/domain/packs/hold_and_win_reference.goand tests — reference pack. - Create
libs/mathengine/games/hold_and_win_reference/pack.go, YAML config and tests — canonical reference config. - Create
apps/game-engine/migrations/023_create_hold_and_win_sequences.sql— durable sequence and step-operation state. - Create
apps/game-engine/internal/domain/round/hold_and_win_sequence.go— persistence interfaces and statuses. - Create
apps/game-engine/internal/infra/postgres/hold_and_win_store.goand integration tests — locking, replay and atomic advance. - Create
apps/game-engine/internal/app/service/spin_orchestrator_hold_and_win.goand tests — trigger/start/resume routing. - Modify
apps/game-engine/internal/domain/round/round.goand API schema — typednext_state.kind="hold_and_win". - Create focused handler serialization fixtures/tests — canonical 15-cell snapshots and newly-landed deltas used by OpenAPI and workspace response documentation.
- Modify
apps/game-engine/cmd/main.go— dormant dependency wiring behind explicit eligibility.
Task 1: Author Draft artifacts and record the approved design gate
Files: Math workspace files listed above.
Interfaces:
-
Consumes: approved design spec and mechanic placeholder templates.
-
Produces: versioned Draft contract paths that every backend/evidence task must cite.
-
Step 1: Copy the mechanic Draft, changelog and handoff templates to their exact target paths
Replace every placeholder with the approved contract, current backend Git SHA bde8c0539f8b621c4d6d13e5834be56741a2862f, current RNG algorithm ID read from code, and status DRAFT / NOT FOR CERTIFICATION. Record canonical config as candidate — not created rather than inventing a path/hash.
- Step 2: Write GDD, whole-game Math Spec and README index
The GDD must state 3 columns × 5 rows, 6+ trigger, 3 respins, reel-major order, Mini/Minor/Major and full-grid Grand. The Math Spec must keep targets separate from results and link the mechanic Draft.
- Step 3: Run document checks
Run:
Expected: no unresolved placeholders and no whitespace errors.
- Step 4: Record design approval and commit workspace artifacts
Use approval state DESIGN approved 2026-08-09 by user; scope bound to design spec c44bc01. Commit only workspace documents:
- Step 5: Stop at mechanic DESIGN gate if the written Draft differs from the approved spec
If it matches exactly, continue under the recorded approval. Any rule/value/RNG-order deviation requires AWAITING_USER_APPROVAL — DESIGN before backend code.
Task 2: Add typed config and strict validation
Files: libs/mathengine/config.go, feature_def_json.go, apps/math-studio/internal/domain/feature.go, build_config.go and focused tests.
Interfaces:
- Produces:
- Step 1: Write failing engine JSON/validation tests
Cover valid v0.1 and rejection of wrong dimensions, trigger outside 1..15, respins below one, probability outside 0..1_000_000, empty/duplicate/non-positive weights, unknown prize kinds, non-positive multipliers and Grand above cap.
- Step 2: Run RED
Expected: compile/test failure because HoldAndWinConfig and JSON mapping do not exist.
- Step 3: Implement the minimal types, validation and JSON mapping
Use integer PPM for the landing mapping. Reject duplicate prize kinds and require exactly one each of regular, mini, minor and major for v0.1.
- Step 4: Add Math Studio domain/build round-trip tests, observe RED, then implement mapping
Run:
- Step 5: Run GREEN and commit
Task 3: Implement Start and prize assignment
Files: Create libs/mathengine/hold_and_win.go and hold_and_win_test.go.
Interfaces:
- Step 1: Write failing tests for five/six trigger boundary and reel-major assignment
Use a scripted RNG test helper that records every Intn domain and drives exact prize choices. Assert five BONUS returns a typed no-trigger result and six locks the exact indices in column-major/reel-major order with three respins.
- Step 2: Run RED, implement the smallest Start function, then run GREEN
- Step 3: Add failing regular/jackpot weighted-boundary tests
Assert cumulative intervals exactly match configured integer weights and a regular prize consumes one additional value draw while Mini/Minor/Major do not.
- Step 4: Implement weighted selection with overflow-safe integer totals
Invalid or overflowing totals return an error; never modulo-map a raw draw.
- Step 5: Run focused and package tests, then commit
Task 4: Implement Step, replay and settlement
Files: libs/mathengine/hold_and_win.go, hold_and_win_test.go.
Interfaces:
- Step 1: RED tests for empty-only draws, reset and decrement
Assert locked cells consume no draw; misses decrement once per step; any land resets to three; iteration order is normative.
- Step 2: Implement minimal Step and run GREEN
- Step 3: RED tests for duplicate step identity
Call the same stepID twice with an RNG that panics if read on the retry. Assert identical state/result and zero retry draws. Reject an older non-last step ID with a typed reconciliation error rather than guessing history.
-
Step 4: Implement one-step replay contract and GREEN
-
Step 5: RED tests for zero-respin settlement, ordinary cap and full-grid Grand override
Assert 499× remains 499×, 520× caps to 500×, and a full grid with a raw sum below or above 500× settles at exactly 500×.
- Step 6: Implement settlement and
RunToCompletionusing documented sub-seeds
Use featureSeed + ":hold_and_win:start" and featureSeed + ":hold_and_win:step:" + stepIndex. The production adapter must use the identical strings.
- Step 7: Prove step mode equals RunToCompletion and commit
Task 5: Wire the engine executor and outcome
Files: hold_and_win_executor.go, mathengine.go, capabilities.go, their tests.
Interfaces:
- Step 1: Write failing
RunRoundtrigger/no-trigger tests
Assert FeaturesTriggered and FeatureWins["hold_and_win"] appear only for 6+ BONUS, base win remains separate and total cap applies once.
- Step 2: Run RED, add executor/registration/outcome plumbing, run GREEN
- Step 3: Add capability regression tests
The new feature reports partial until Task 8 persistence/parity is complete; the old hold_and_spin detail remains byte-for-byte unchanged.
- Step 4: Run engine regression and commit
Task 6: Add the canonical reference pack
Files: reference pack files in Math Studio and libs/mathengine/games/hold_and_win_reference.
Interfaces: Produces Load() (*mathengine.MathVersionConfig, error) and canonical YAML path libs/mathengine/games/hold_and_win_reference/hold-and-win-reference.yaml.
- Step 1: Write failing pack tests
Assert Game ID, 3 columns, 5 rows, exactly one hold_and_win feature, approved config values and absence of hold_and_spin.
- Step 2: Run RED and create the minimal pack/config
Base reels must expose BONUS and ordinary symbols; initial weights are design inputs chosen to place the trigger near 1/150 but must not be documented as achieved before simulation.
- Step 3: Validate config and generate a SHA-256 identity
- Step 4: Update Draft provenance and changelog, then commit both repositories separately
Backend commit:
Workspace commit records the resulting backend candidate path/hash/SHA without copying the YAML.
Task 7: Extend batch simulation metrics
Files: libs/mathengine/batch.go, batch_test.go, new hold_and_win_batch_test.go.
Interfaces: Add merge-safe HoldAndWinBatchAgg with trigger count, conditional award moments/histogram, total steps, reset count, full-grid count and jackpot counts.
- Step 1: Write failing controlled-outcome aggregation tests
Drive deterministic outcomes and assert every additive field and merge boundary.
- Step 2: Run RED, implement aggregation and MergeBatches support, run GREEN
- Step 3: Run same-seed single-shard versus merged-shard parity
Assert totals and mechanic aggregates are identical under the existing shard-seed contract.
- Step 4: Commit
Task 8: Add durable game-engine step persistence and replay
Files: migration 023, domain/store/service/API files listed in File structure.
Interfaces: Durable sequence contains server UUID, operator/logical-session/game/config identities, mechanic version, canonical state JSON, current step index, status open|completed|quarantined, and timestamps. Unique active/quarantined identity prevents a second sequence. Step operation key is (sequence_id, step_index).
- Step 1: Write failing real-PostgreSQL migration/store tests
Cover open, FOR UPDATE, atomic step advance, duplicate replay, concurrent requests, completion, quarantine, config mismatch, rollback and restart.
- Step 2: Run RED
-
Step 3: Implement migration/domain/store minimally and run GREEN
-
Step 4: Write failing orchestrator/API tests
Assert trigger creates the durable state in the paid round transaction; subsequent steps charge no wager; duplicate expected step replays; Redis expiry restores from PostgreSQL; quarantined/config-unavailable state blocks paid play before debit.
- Step 5: Implement service/API wiring behind default-off eligibility
Use the hardened paid-operation/latch/credit-intent infrastructure already on main. Do not create a second wallet retry or allocator.
-
Step 6: Promote capability to full only after persistence/parity tests pass
-
Step 7: Run integration/race tests and commit
Task 9: Golden vectors, parity and regression
Files: new reference pack golden tests, parity vectors/tests, workspace test-vectors index.
Interfaces: Vectors bind config hash, backend SHA, RNG algorithm/version, feature seed, incoming state/step ID and exact outcome/state.
-
Step 1: Add failing vectors for trigger, miss decrement, reset, Mini/Minor/Major, cap and full-grid Grand
-
Step 2: Generate expected results only through the approved canonical runtime, review them manually, then pin them
-
Step 3: Prove reference
RunToCompletionequals persisted step execution -
Step 4: Run full regression and verify protected artifacts
Expected: existing game vectors unchanged; new vectors additive.
- Step 5: Commit backend vectors and workspace vector manifest
Task 9A: Implement and document the real backend response
Files: apps/game-engine/internal/domain/round/round.go, Hold & Win handler/service response types, apps/game-engine/api/openapi.yaml, focused serialization/integration tests, and games/hold-and-win-reference/gdd/hold-and-win-backend-response.md.
Interfaces: The real wire response is authoritative. Documentation consumes deterministic serialized fixtures; it must not invent fields independently.
- Step 1: Write failing response-contract tests before adding response fields
Cover trigger, ordinary respin, duplicate-step replay, reconnect and both settlement reasons. Each response must serialize exactly 15 cells in normative reel-major order and include the exact newly_landed subset for the current step.
- Step 2: Verify RED, then implement typed response mapping and orchestrator routing
The response must include mechanic version, status, current/expected step index, respins remaining, locked count and accumulated multiplier. Each cell must include stable index/column/row, locked state and nullable prize. Settlement adds reason, raw/awarded multiplier, cap flag, full-grid flag and jackpot counts.
- Step 3: Add OpenAPI schemas and validate serialized fixtures against them
Do not hand-edit examples that disagree with handler output. Generate or capture examples from deterministic integration fixtures and keep internal sequence/identity secrets out of the response.
- Step 4: Create backend-response documentation from those fixtures
Write games/hold-and-win-reference/gdd/hold-and-win-backend-response.md with the actual request/response lifecycle and deterministic JSON examples. Link it from README, GDD, Math Spec and handoff protocol.
- Step 5: Add a drift guard
The test must fail when serialized backend output changes without updating the fixture/OpenAPI-bound response artifact. A changed response invalidates runtime parity and the handoff protocol.
- Step 6: Run handler, integration, OpenAPI and parity tests, then commit backend and workspace changes separately
This task is mandatory before AWAITING_USER_APPROVAL — FREEZE & HANDOFF; a math-only executor/store is not a complete mechanic delivery.
Task 10: Run Draft simulation and stop at DRAFT MATH gate
Files: generated aggregate reports under games/hold-and-win-reference/evidence/simulation/, Draft math/results/changelog.
Interfaces: Report exact base results separately from estimated stateful feature results.
- Step 1: Run a reproducible pilot
Use at least 1,000,000 rounds over recorded independent seeds/shards. Record Go version, backend SHA, config SHA, RNG version, rounds, seeds, elapsed time and commands.
- Step 2: Calculate and record uncertainty
Report RTP, standard error and 95% confidence interval; trigger rate with interval; conditional feature mean/median/percentiles; duration; reset/full-grid/jackpot rates; observed max and validated 500× bound.
- Step 3: Reconcile target versus observed
Separate base RTP, Hold & Win RTP contribution and total RTP. Do not tune yet. Mark every result estimated unless an exact method was actually implemented.
-
Step 4: Update Draft/changelog and commit evidence manifest
-
Step 5: Stop
Return AWAITING_USER_APPROVAL — DRAFT MATH with locked/open parameters and observed drift. Do not tune or prepare Final until the user approves direction.
Task 11: Tune approved open parameters
Files: new backend config revision, tuning records under games/hold-and-win-reference/math/tuning/, regenerated evidence.
Interfaces: Only landing PPM, regular weights, jackpot weights and base trigger weights are tunable without a new DESIGN approval.
-
Step 1: Apply only user-approved tuning direction in a new config revision
-
Step 2: Run identical-seed A/B plus independent final seeds
-
Step 3: Re-run full tests, parity, max validation and simulation evidence
-
Step 4: Record every old→new value and invalidated evidence in the mechanic changelog
-
Step 5: Commit each traceable tuning revision separately
Task 12: Freeze and handoff
Files: Final mechanic math, completed handoff protocol, reconciled GDD/Math Spec/changelog/evidence manifests.
- Step 1: Reconcile every bound artifact and compute SHA-256 hashes
The bound artifacts must include the implemented OpenAPI response schema, deterministic serialized response fixtures and gdd/hold-and-win-backend-response.md.
-
Step 2: Run clean-environment reproduction commands and record results
-
Step 3: Submit
AWAITING_USER_APPROVAL — FREEZE & HANDOFF
The user must respond exactly APPROVE FREEZE & HANDOFF or CHANGES REQUESTED for the stated scope.
-
Step 4: After approval only, copy the approved Draft to immutable
hold-and-win-math-final-v1.0.md -
Step 5: Seal hashes, update changelog and declare developer-ready without claiming external certification
Plan self-review
- Spec coverage: grid, trigger, values, jackpots, RNG order, state persistence, replay, reference pack, simulation, tuning and handoff each have an implementing task.
- Type consistency: config/state/function names are defined once and reused by later tasks.
- Scope: the existing
hold_and_spin, Classic Fruits and unrelated backend hardening remain outside modifications. - Evidence: exact and Monte Carlo claims remain separated; capability cannot become full before runtime parity.