From 828eb472701af16b7bd913111273b62d22ea3627 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 24 Aug 2026 17:36:19 +0200 Subject: Fix critical game bugs: payout, sessions, balance type, render_lines; add AGENTS.md and regression tests --- AGENTS.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 AGENTS.md (limited to 'AGENTS.md') diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c5139d5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,56 @@ +# Project Conventions for zagan-bet + +## Type Safety +- SQLite `INTEGER` is `i64`. Never cast to `u64` without a bounds check. +- Balance, bet, and winnings fields must agree on `i64` or `u64` across the + entire codebase — pick one and enforce it. +- Negative balance is a state the DB can represent; the application must reject + plays that would produce one, not silently wrap via `as u64`. + +## Game Logic +- Never compute the same payout/result in two places. `process_action` and + `calculate_result` must not both independently derive winnings. + The canonical split: `process_action` mutates state and stores raw data + (reels, win_type); `calculate_result` reads that data and produces the + `GameResult`. One path, one formula. +- When checking symmetry (e.g. two-of-a-kind), enumerate **all** pairs, not + just adjacent indices. `[a, b, a]` is a valid match. + +## Session Tracking +- Every `PLAY_START` must produce a session entry before the balance is + touched. The session is the audit trail for the deduction; without it the + deduction is orphaned. +- Clean up sessions on client disconnect. If a game is in progress when the + connection drops, the server must roll back the bet. + +## Server Handler +- Never hold a lock, drop it, then re-acquire it in the same function to + mutate the same resource. Acquire once, scope the lock to the operation. +- All balance mutations (`SET balance = balance ± ?`) must happen in a + transaction. + +## Client TUI +- `render_lines()` and `render()` must agree. If `render()` uses `\r\n`, + count lines against the same string the cursor will erase. + Prefer returning a fixed known count for the game's static layout and + letting dynamic content fill it, rather than inferring the count. + +## Error Paths +- When a game action fails after a balance deduction, the refund must happen + in the same transaction scope as the deduction, not as a separate + `execute()` call. +- Never silently ignore `Err` on balance mutations. If the refund fails, + log the username, balance, and operation; surface to the operator. + +## Tests +- Every `calculate_payout` / `calculate_result` edge case gets a unit test + in `tests/`. Tests that exercise game logic use a deterministic seed or + a test helper that calls the function directly, not the network. +- A change to game logic that breaks an existing payout pattern must fail + a test before it reaches the server. + +## TLS / TOFU +- See `src/tofu.rs` for the trust model. The fingerprint is over the + certificate's SPKI, not the whole certificate. +- `save_host` must not wipe the fingerprint column. Use `ON CONFLICT ... + DO UPDATE SET last_used` — never `INSERT OR REPLACE`. -- cgit v1.2.3