summaryrefslogtreecommitdiffstats
path: root/AGENTS.md
blob: c5139d556ebe0e014cc5711abeaffbd18696d302 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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`.