Math Handoff Protocol — hold-and-win draft-v0.2

This protocol is the manifest submitted at AWAITING_USER_APPROVAL — FREEZE & HANDOFF. Approval applies only to the identities and scope recorded here.

This manifest is APPROVED and SEALED. Every sealed at the gate placeholder has been replaced with the SHA-256 of the artifact as it stood at approval. Those hashes are the manifest: if any one of them stops matching, this approval no longer describes the thing it approved, and a new Draft and protocol version are required.

Current position: FREEZE & HANDOFF approved 2026-08-09. The immutable Final hold-and-win-math-final-v1.0.md was created from the approved draft-v0.2 after that answer, not before. Backend cd4c093 is merged and pushed to main. The mechanic is developer-ready and dormant in production.

Approval decision

  • Allowed decision: APPROVE FREEZE & HANDOFF or CHANGES REQUESTED.
  • Decision given: APPROVE FREEZE & HANDOFF (user, 2026-08-09), with the instruction to merge and push main first; the seal below is computed against the post-merge state.
  • Approved scope: hold_and_win mechanic final-v1.0 (verbatim from draft-v0.2) and the hold-and-win-reference pack, at backend cd4c093 on main.
  • Conditions/exclusions: excludes collector, multiplier, feature buy and any additional-wager behavior; excludes every other game and the existing partial hold_and_spin feature. Approval covers the mathematics, the runtime that executes it and the wire contract binding them — it does not enable the feature for any operator, which is a separate explicit decision.
  • Approver and timestamp: user (Bogdan), 2026-08-09.

Silence, DESIGN approval and DRAFT MATH approval do not approve this handoff. This approval is not external certification.

Bound artifacts

ArtifactVersionPathSHA-256Status
Approved Finalfinal-v1.0math/mechanics/hold-and-win/hold-and-win-math-final-v1.0.md6cf5418485bf8f999afc7326f135e0b0de4b5dc2af01bd5ec2d054321b112e55created at the gate, immutable
Approved source Draftdraft-v0.2math/mechanics/hold-and-win/hold-and-win-math-draft-v0.2.md48ef927fcab327f0ddc614c09dff94fca8c05b6f157f11daa62115dc9406c46cpreserved unchanged; the Final is a verbatim copy
Superseded Draftdraft-v0.1math/mechanics/hold-and-win/hold-and-win-math-draft-v0.1.md58fa7140428c2e7ecc3aecc1df013f4c0abeef4a5e8deb6cf738f577a32de37dpreserved, not bound; retained as the reason v0.2 exists
Game Math Specfinalmath/hold-and-win-reference-math-spec.mdbba211ac022e3f633007c534d12699bfe5e629378116c3a865f1c1b6f5f69289reconciled
GDDdraft-v0.1gdd/hold-and-win-reference-gdd.md16077d3eb9183b71e3663f88b85cc0384f8d54c9c43bc73d3743a30318dea150reconciled
Backend response documentationfinal-v1.0gdd/hold-and-win-backend-response.mde5e709e81e93bdbd9727726acb0b965e2855b3ecf7a5d8680720bcfdf0ae52b3generated from LIVE orchestrator fixtures
Canonical backend configfinal-v1.0libs/mathengine/games/hold_and_win_reference/hold-and-win-reference.yamla22129d99d21eb14acdb303efdf666f4065925fa461012d8e99f2144538d0508unchanged since draft-v0.2
Wire contract (code)final-v1.0apps/game-engine/internal/domain/round/hold_and_win_response.gobf2bfced628ee81c093d94a4204661d3d83f3d5db4fd39dd4d8074296ba42059the single client-facing projection
OpenAPI schemafinal-v1.0apps/game-engine/api/openapi.yamld1004d2316bab3be5a1eb32d32a686e7e5da52d5a5a3dd760f2401515abea6354 HoldAndWin schemas + the request field
Live response fixturesfinal-v1.0apps/game-engine/internal/app/service/testdata/hold_and_win/see the fixture table belowcaptured from SpinOrchestrator.Spin
Simulation evidencedraft-v0.2evidence/simulation/2026-08-09-draft-v0.2-confirmation-2m.mdf5fa5ccb13346112d739ab568b7f6b70e8124cad45f967ebaefb2759b709ec0e2,000,000 rounds, independent seeds
Full-grid confirmationdraft-v0.2evidence/simulation/2026-08-09-draft-v0.2-fullgrid-confirmation.md03453d96b390f0c08e9fbd957e2d74c36ee8d17821974e168639df81076319552,200,000 triggered features
Tuning record — pass 1pass 1evidence/simulation/2026-08-09-tuning-pass-1-pareto.md244d9428e68e255ca5815451ac31b27d160165a4ef2f9e04252e9e4626a364a9why single-parameter tuning was rejected
Tuning record — pass 2draft-v0.1evidence/simulation/2026-08-09-draft-v0.1-2m.md070d2ce5439c7f0bb66b899a5ce353459763ae47631f5e8c5162e744ebe16bdcthe deviation that forced v0.2

Living documents are deliberately not hashed, because they must keep growing after the gate and a hash on them would be invalid the moment the next entry lands: the mechanic changelog, the game changelog, the game README and this protocol itself. Their integrity comes from Git history, not from a seal.

Live response fixtures

FixtureSHA-256
trigger.json4ad78baac4ba8e9012d0b9e0dc2ea10abdccec3fe3ffa067309c6fce78701400
respin.json78062872daafd118adcd5e5dfeeb64aa26ad71ac7443aabf768f0ac274fd236f
reconnect.json8b50b8f1e7c6da18efebce5cd339c84391c9a9f1ff4a0d346f393ee8f60c47a6
settlement-no-respins.jsone36ef1b8321cec85021c6a35eb13e78825b20fd3017db3ffe8b767643e6d2e74
settlement-full-grid.jsonf09da0a02948aa13b31f84fecab428bcf3e94a3bc4c1b4d7235a66e022dc1221

Runtime identity

FieldValue
Backend Git SHA — sealcd4c093c6b875376e547cd31123de254d2cb2f93 — the commit every bound hash below was computed at
Backend Git SHA — shipped buildf80cb60 on main — cd4c093 plus two CI lint fixes (a test's YAML parser declared in apps/game-engine/go.mod; //nolint:unused on pre-existing retained dead code the cold cache surfaced). No bound artifact differs, proven file by file; no math, rule, RNG mapping, evaluation order, rounding or cap moved, so this protocol stands
Previous mainbde8c0539f8b621c4d6d13e5834be56741a2862f
Go versiongo1.26.2 darwin/arm64 (local); CI builds on golang:1.23-alpine
Math/config versionhold-and-win-reference final-v1.0 (config byte-identical to draft-v0.2)
RNG algorithm/versionsha256-trunc64-be/go1-alfg#1 — unchanged by this work
Evaluator/draw-order versionlines evaluator, unchanged; mechanic draw order defined in the Draft's RNG contract
Changed Go packageslibs/mathengine, libs/mathengine/games/hold_and_win_reference, apps/math-studio/internal/domain, apps/game-engine

Approved math contract

  • Locked parameters and rules: 3 columns × 5 rows, 15 cells; normative reel-major cell order; trigger at ≥6 BONUS; 3 initial respins; reset to 3 on any newly landed prize; decrement by 1 on a miss-only step; locked cells never redrawn and consuming no RNG; Mini 10×, Minor 25×, Major 100×, Grand 500×; Grand not a landed type; full grid awards exactly 500× replacing the sum; cap applied once at settlement; no collector, multiplier, feature buy or additional wager.
  • Parameters closed by draft-v0.2: LandingChancePPM = 43000 and regular-value weights 30/25/18/15/10/2, both selected under the approved tuning direction and confirmed on independent seeds. Still open: prize-type weights and base trigger weights — neither was needed, and neither was touched.
  • Payout unit, evaluation order, rounding and cap: multiplier of total bet; base evaluation then feature then a single round cap; multipliers stay float64 and money crosses to exact integer minor units only at the wallet boundary; 500× applies once as a whole-round bound.
  • Trigger/state/termination contract: as in the Draft's state machine — Idle → Triggered → Respinning → Settled, terminating at zero respins or a full grid.
  • Declared max win and validation type: 500× total bet, reachable (a full grid awards it exactly), not merely an upper bound. Validation must observe it and confirm no round exceeds it.
  • Deviations from GDD/targets: RESOLVED at draft-v0.2. Total RTP 96.0031% ±0.1189pp against a 96.0% ±0.3pp target, feature contribution 20.8871% against 20.884% required, conditional mean 32.4593× against 32.4545×, full-grid 0.090053% of triggers (1 in 1,110 features, 1 in 172,570 base rounds) inside the 0.05%–0.25% band. Base RTP 75.1160% and trigger 1 in 155.4035 are exact and unchanged from draft-v0.1. The draft-v0.1 deviation (mechanic RTP ≈4× over, an 18.71% full-grid rate) is retained in the changelog as the reason this revision exists.

Verification and evidence

Gate/evidenceCommand or pathResultSHA-256
Config validationgo test ./libs/mathengine -run TestHoldAndWinConfig -count=1pass — 27 rejection casesn/a
Go tests/regressionmake test; go test -race ./apps/... ./libs/... -count=1pass — 37 ok, exit 0 twicen/a
Golden/replay vectorslibs/mathengine/games/hold_and_win_reference/golden_test.gopass — 8 pinned vectors, seeds discovered by searchbound by backend SHA cd4c093
Reference/runtime paritymake test-parity; TestHAWOrch_MatchesTheReferenceDriverStepForSteppass — existing corpora unchanged, and the live orchestrator is compared to RunHoldAndWinToCompletion over complete state (every cell, prize, newly_landed delta, respin counter, step index, raw and awarded multiplier, full-grid flag) at 0, 43,000 and 1,000,000 PPM. Reference-side step==RunToCompletion remains proven on 6 vectorsbound by backend SHA cd4c093
Exact scopenot enumerable — RNG-driven per empty celln/an/a
Seeded simulation manifestevidence/simulation/2026-08-09-draft-v0.1-2m.md (v0.1) and …-draft-v0.2-confirmation-2m.md (accepted)pass — 2,000,000 rounds each, independent seeds, SEs and 95% CIs reportedhashed in Bound artifacts
Max-win validationsame simulation runspass — 500.0000× observed (reachable) and never exceeded; a full grid awards it exactlyhashed in Bound artifacts
RNG identitymake test-rng; rng.AlgorithmID()passsha256-trunc64-be/go1-alfg#1, unchangedn/a
Durable runtime persistencego test -tags integration -race ./apps/game-engine/...pass against real PostgreSQL — concurrency, replay, quarantine, rollbackn/a
Live routinggo test -tags integration ./apps/game-engine/internal/app/service -run TestHAWOrchpass — trigger opens the feature inside the paid transaction; a respin takes no wager and advances exactly one step; a duplicate step replays byte-identically; ahead/stale steps return typed reconciliation errors; a reconnect after a cache flush resumes from PostgreSQL; settlement writes exactly one credit intent; a full grid settles at the Grandn/a
Live response serializationgo test ./apps/game-engine/internal/app/handlers -run 'HoldAndWin|OpenAPI'passnext_state reaches the wire unchanged for all five captured moments; the OpenAPI example equals the live trigger fixture; every field the runtime sends is described by the scheman/a
Engine capabilitygo test ./libs/mathengine -run 'Capabilit|TestCapabilities_HoldAndWin'passhold_and_win promoted to SupportFull in the same commit as the routing and parity evidence; hold_and_spin detail unchangedn/a

Theoretical/exact and Monte Carlo results are reported separately. Total, base and mechanic RTP are reported with confidence intervals alongside trigger frequency, volatility and mechanic-specific metrics. Monte Carlo is never labelled exact.

Developer delivery

  • Final Math, changelog, game Math Spec and GDD.
  • Canonical config identity (path + SHA-256) and the backend commit/build that executes it.
  • Config schema, API contract and the persisted-state contract, including the typed next_state of kind hold_and_win and the durable sequence/step-operation schema.
  • Golden examples with expected outcomes, including trigger, miss decrement, reset, each jackpot tier, the 500× cap and the full-grid Grand.
  • Commands required to reproduce tests and simulation in a clean environment.
  • Migration and version-routing instructions: migration 023 is additive; an unknown persisted mechanic version fails closed and never falls back to hold_and_spin.
  • Known product, parity, compliance and certification blockers.

Seal

  • Protocol SHA-256: recorded in ../../../changelog.md, not here. A file cannot contain its own hash, and writing one that was computed over a different version of the file would be worse than writing none.

  • Final decision: APPROVE FREEZE & HANDOFF — user (Bogdan), 2026-08-09.

  • Developer-ready: YES.

  • Outstanding blockers: none.

    1. four open parameters — CLEARED at draft-v0.2: landing chance and regular weights closed.
    2. the mechanic RTP deviation — CLEARED: all acceptance bands met on independent seeds.
    3. no reference/runtime parity — CLEARED: the live orchestrator is compared with RunHoldAndWinToCompletion over complete state at three landing chances, and the comparison was mutation-checked so a green result is evidence rather than a tautology.
    4. the spin orchestrator does not route to the durable runtime — CLEARED: a paid trigger opens the feature inside the paid gameplay transaction and each respin is its own wager-free request. Engine capability is SupportFull, promoted in the same commit as that evidence.
    5. the backend response contract — CLEARED: one projection, bound to live fixtures, OpenAPI and the handler's serialized output.
  • Scope statement, not a blocker: the durable path is dormant in production by construction. hold_and_win_enabled and hold_and_win:<operator> both default to DENY and a nil gate denies, so this approval enables play for nobody. Turning it on for an operator is a separate, explicit decision, and the dev deploy triggered by the main push ships the code inert.

  • Certification statement: internal handoff approval is not external certification. No external laboratory has reviewed this mechanic. The mechanic is not enumerable, so its RTP contribution is a seeded Monte Carlo estimate reported with a standard error and interval, and it must never be quoted as exact.

Changing any bound path, version, hash, locked rule, RNG mapping, evaluation order, rounding or cap invalidates this protocol. Start a new Draft and a new protocol version as appropriate; never edit final-v1.0 in place.