Classic Fruits 3×3 — Documentation Changelog

2026-08-23 — Draft Math v0.1 (v2, SEVENS jackpot)

  • Authored a new canonical math version v2-draft-0.1 implementing GDD 0.2.0, in a new backend pack libs/mathengine/games/classic_fruits_3x3_v2/. Frozen v1 is byte-for-byte unchanged.
  • Recorded config SHA-256 f429ff05…, canonical hash f66827ec…, backend commit f4cfa83a….
  • Added Draft v2 Math Spec with exact figures: RTP 96.068513%, hit 11.748980%, σ 4.323568, max win 1300× reachable, jackpot 1 in 19,056 / 57,167 / 343,000.
  • Ran and recorded a 10,000,000-round simulation: RTP 95.97038%, 95% CI [95.70550%, 96.23526%], 1300× observed, cap never truncated. Measurement only — not balancing, not certification.
  • Added six portable golden vectors covering no-win, one-line, multi-line and all three jackpot tiers, with payouts derived by hand from the GDD.
  • Recorded open items: hit frequency and σ unaccepted by Product, no parity vectors, no PAR sheet, pack not registered or published.

Runtime wiring (same day, after review)

  • Registered v2-draft-0.1 in the canonical pack-hash gate and the reference↔runtime parity corpus, and added a math-studio authoring preset that canonicalizes to the identical config hash.
  • Added six parity vectors (no win, one line, two simultaneous lines, 7/8/9 SEVENS) executed from both the reference engine and game-engine's spin orchestrator, plus runtime tests for payout composition, replay identity, the forced-grid path and no double crediting.
  • Config SHA-256 re-recorded 770f7a53…f429ff05…. Comment-only change: the gdd: header pointed at a path that resolved on one machine. The math did not move — the canonical config hash is still f66827ec…, which is the value the runtime gate compares and the value pinned in three separate tests. Exact RTP, hit rate, σ, max win and every golden vector are unchanged; the 10M report was regenerated only so it names the current bytes.
  • Wire contract, decision applied. wins[].type for the 7/8/9 SEVENS award is now jackpot, not scatter; jackpot added to the OpenAPI enum, scatter kept and documented as legacy for replaying rounds persisted before the rename. No config byte moved — the config SHA-256 and the canonical hash are both unchanged, because ScatterPayouts and IsScatter keep their names on purpose: they are hashed into every pack's identity.
  • openapi.yaml now states that wins[].payout on a line entry is the award for ONE payline and must never be summed; the round total has exactly one source, spin.payout. Left as documentation rather than repriced, because the units are the same for every line game in the catalogue.
  • Config contract split, decision applied. GET /config/{game_id} now publishes jackpot_symbols / jackpot_payouts beside the scatter pair; for this game SEVEN appears only in the jackpot half and scatter_symbols is empty. The split is decided by mathengine.JackpotSymbolIDs — a count-anywhere award on a symbol that opens no feature is a jackpot; one that triggers free spins stays a Scatter — and it fails closed when a config declares free spins whose trigger cannot be resolved. IsScatter, ScatterPayouts and every canonical hash are unchanged. Both new fields omit when empty, so config_hash for a game without a jackpot is byte-for-byte what it was: measured at 7437ab59e0a480af with the fields removed and restored, and pinned. This closes GDD 0.2.1 §17.4.
  • Focused review caught two HIGH defects in the first cut of these fixes, both now closed: the win-type rename was unconditional, so a real Scatter's pay also said "jackpot" while its config still called it a scatter; and the classifier knew only free_spins, so a symbol opening hold_and_win, bonus or super_scatter would have been published as a jackpot. The emitter now reads the same per-symbol verdict as the config contract, and the classifier answers all thirteen feature types with deny-by-default for anything it has not been taught. Also fixed: the legacy exchange payload dropped the jackpot table entirely, the session-service wins[].type enum was stale, and evaluateScatterWins ranged a map — so a game with two paying count-anywhere symbols emitted them in random order, against the determinism requirement.
  • GDD §5 narrowed rather than the line wire repriced: the 60× rule now scopes to what the player is SHOWN, wins[].multiplier is named a legacy per-line field that is not a display value, and spin.payout is named the single source of the round total.
  • GDD moved to 0.2.1 (docs(classic-fruits): clarify paytable rules, authored outside this work). It ratifies the ×5 per-line encoding and the 1300× composition as normative rather than inferred; no rule changed and no figure in the Math Spec moved. Its new §5 sentence "expose 60×, never 300×" does conflict with wins[].multiplier on a line entry — recorded in Math Spec §10.6 as an open decision, not silently resolved.

2026-08-23

  • Replaced GDD 0.1.0 with draft target 0.2.0 for the same classic-fruits-3x3 Game ID.
  • Changed the target paylines to three horizontals plus two diagonals.
  • Replaced Wild-based legacy rules with nine regular symbols and the target paytable 60× / 40× / 20× / 15× / 7×.
  • Added fixed 7/8/9 SEVENS-anywhere jackpot targets of 50× / 250× / 1000×.
  • Recorded the inferred 1300× maximum as 1000× + 5 × 60×, pending live payout verification.
  • Recorded 96.08% as a reference target, not as measured or certified math.
  • Explicitly excluded FreeBets until a complete contract is available.
  • Added the game-specific SEVENS-jackpot presentation flow and recorded its Visual Refs and DEV scenarios as open Unique Mechanic Presentation Gate requirements.
  • Preserved frozen v1 and its exact evidence as a superseded legacy baseline; backend math was not changed.

2026-08-08

  • Created initial GDD 0.1.0 around frozen math pack v1.
  • Created Math Spec from exact backend evidence.
  • Recorded canonical config hash and backend commit.
  • Recorded the current low-tier certification rejection without changing frozen math.