diff options
Diffstat (limited to 'AGENTS.md')
| -rw-r--r-- | AGENTS.md | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c5139d5 --- /dev/null +++ b/AGENTS.md | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | # Project Conventions for zagan-bet | ||
| 2 | |||
| 3 | ## Type Safety | ||
| 4 | - SQLite `INTEGER` is `i64`. Never cast to `u64` without a bounds check. | ||
| 5 | - Balance, bet, and winnings fields must agree on `i64` or `u64` across the | ||
| 6 | entire codebase — pick one and enforce it. | ||
| 7 | - Negative balance is a state the DB can represent; the application must reject | ||
| 8 | plays that would produce one, not silently wrap via `as u64`. | ||
| 9 | |||
| 10 | ## Game Logic | ||
| 11 | - Never compute the same payout/result in two places. `process_action` and | ||
| 12 | `calculate_result` must not both independently derive winnings. | ||
| 13 | The canonical split: `process_action` mutates state and stores raw data | ||
| 14 | (reels, win_type); `calculate_result` reads that data and produces the | ||
| 15 | `GameResult`. One path, one formula. | ||
| 16 | - When checking symmetry (e.g. two-of-a-kind), enumerate **all** pairs, not | ||
| 17 | just adjacent indices. `[a, b, a]` is a valid match. | ||
| 18 | |||
| 19 | ## Session Tracking | ||
| 20 | - Every `PLAY_START` must produce a session entry before the balance is | ||
| 21 | touched. The session is the audit trail for the deduction; without it the | ||
| 22 | deduction is orphaned. | ||
| 23 | - Clean up sessions on client disconnect. If a game is in progress when the | ||
| 24 | connection drops, the server must roll back the bet. | ||
| 25 | |||
| 26 | ## Server Handler | ||
| 27 | - Never hold a lock, drop it, then re-acquire it in the same function to | ||
| 28 | mutate the same resource. Acquire once, scope the lock to the operation. | ||
| 29 | - All balance mutations (`SET balance = balance ± ?`) must happen in a | ||
| 30 | transaction. | ||
| 31 | |||
| 32 | ## Client TUI | ||
| 33 | - `render_lines()` and `render()` must agree. If `render()` uses `\r\n`, | ||
| 34 | count lines against the same string the cursor will erase. | ||
| 35 | Prefer returning a fixed known count for the game's static layout and | ||
| 36 | letting dynamic content fill it, rather than inferring the count. | ||
| 37 | |||
| 38 | ## Error Paths | ||
| 39 | - When a game action fails after a balance deduction, the refund must happen | ||
| 40 | in the same transaction scope as the deduction, not as a separate | ||
| 41 | `execute()` call. | ||
| 42 | - Never silently ignore `Err` on balance mutations. If the refund fails, | ||
| 43 | log the username, balance, and operation; surface to the operator. | ||
| 44 | |||
| 45 | ## Tests | ||
| 46 | - Every `calculate_payout` / `calculate_result` edge case gets a unit test | ||
| 47 | in `tests/`. Tests that exercise game logic use a deterministic seed or | ||
| 48 | a test helper that calls the function directly, not the network. | ||
| 49 | - A change to game logic that breaks an existing payout pattern must fail | ||
| 50 | a test before it reaches the server. | ||
| 51 | |||
| 52 | ## TLS / TOFU | ||
| 53 | - See `src/tofu.rs` for the trust model. The fingerprint is over the | ||
| 54 | certificate's SPKI, not the whole certificate. | ||
| 55 | - `save_host` must not wipe the fingerprint column. Use `ON CONFLICT ... | ||
| 56 | DO UPDATE SET last_used` — never `INSERT OR REPLACE`. | ||
