diff --git a/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/.openspec.yaml b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/.openspec.yaml
new file mode 100644
index 0000000..66da1ae
--- /dev/null
+++ b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-05-17
diff --git a/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/design.md b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/design.md
new file mode 100644
index 0000000..580812f
--- /dev/null
+++ b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/design.md
@@ -0,0 +1,153 @@
+## Context
+
+The current PokeR app implements Texas Hold'em poker but contains 10 critical bugs in its game flow logic. The core issues stem from three architectural problems: (1) ambiguous `currentBet` semantics that conflate "player contribution" with "bet matched," (2) lack of last-aggressor tracking for betting round completion, and (3) no side pot support for all-in scenarios. The hand evaluator and card systems are solid; the game orchestration layer needs complete reconstruction.
+
+## Goals / Non-Goals
+
+**Goals:**
+- Implement correct Texas Hold'em betting mechanics per official rules
+- Fix all identified bugs without introducing new ones
+- Support side pots for multi-player all-in scenarios
+- Prevent race conditions between human input and AI turns
+- Maintain existing Svelte 5 runes-based architecture and component structure
+
+**Non-Goals:**
+- Adding new game variants (Omaha, Stud, etc.)
+- Implementing GTO or strategic AI improvements
+- Multiplayer networking
+- Educational features (pot odds, hand ranges)
+- UI/visual redesign
+
+## Decisions
+
+### Decision 1: Separate `betMatched` flag from `currentBet`
+
+**Current:** `PlayerSeat.currentBet` serves dual purpose — tracks actual chips contributed AND whether player has matched the current bet level. This causes `applyCheck` to corrupt state by setting `currentBet = state.currentBet`.
+
+**Decision:** Add explicit `betMatched: boolean` field to `PlayerSeat`. `currentBet` becomes purely "chips contributed this round." Check simply sets `betMatched = true` without modifying chip counts.
+
+```
+PlayerSeat {
+ // ... existing fields
+ currentBet: number // actual chips contributed this round
+ betMatched: boolean // has player matched or checked this round?
+}
+```
+
+**Rationale:** Eliminates the fundamental semantic confusion that causes cascading bugs. Call amount becomes `state.currentBet - player.currentBet` with no corruption from check actions.
+
+### Decision 2: Last-aggressor betting round completion
+
+**Current:** `completeBettingRound` checks if all active players have `currentBet >= currentMaxBet`. This fails when all-in players are included and doesn't handle the "return to last aggressor" rule.
+
+**Decision:** Track `lastAggressorIndex` in GameState. A betting round ends when:
+- Action returns to `lastAggressorIndex` AND that player has matched, OR
+- All active (non-all-in) players have `betMatched = true`, OR
+- Only one active player remains
+
+```
+// Round completion logic
+function isRoundComplete(state): boolean {
+ const activePlayers = state.players.filter(p => p.status === 'active');
+
+ // Single active player — round over
+ if (activePlayers.length <= 1) return true;
+
+ // No raises occurred — everyone checked/called
+ if (state.lastAggressorIndex === -1) {
+ return activePlayers.every(p => p.betMatched);
+ }
+
+ // Raises occurred — must return to last aggressor who matches
+ const lastAgg = state.players[state.lastAggressorIndex];
+ return lastAgg.status === 'active' &&
+ state.currentTurn === state.lastAggressorIndex &&
+ lastAgg.betMatched;
+}
+```
+
+**Rationale:** Texas Hold'em rules require action to return to the last aggressor. This is the standard implementation pattern used by poker software.
+
+### Decision 3: Side pot algorithm
+
+**Current:** No side pot support. All chips go into a single pot, causing incorrect distributions when players go all-in at different levels.
+
+**Decision:** Implement standard side pot calculation:
+1. Sort all-in players by their bet amount (ascending)
+2. Create pots from bottom up — each pot contains chips above the next lower all-in level
+3. Track which players are eligible for each pot (active or all-in at that level or above)
+
+```
+SidePot {
+ amount: number
+ eligiblePlayerIds: string[]
+}
+
+// Algorithm sketch:
+function calculatePots(players, currentPot): { mainPot: number, sidePots: SidePot[] }
+ // Sort players by bet amount
+ // Create nested pots from smallest all-in to largest
+ // Main pot = chips matched by all remaining active/all-in players
+```
+
+**Rationale:** Standard poker software approach. Keeps implementation clean and matches real-world rules exactly.
+
+### Decision 4: Unified game state machine in `+page.svelte`
+
+**Current:** `processGame()` has complex branching logic with async AI turns, state discarding, and multiple exit paths. Race conditions occur when human actions interrupt AI chains.
+
+**Decision:** Replace with a clear state machine pattern:
+- `aiActing: boolean` flag disables controls during AI turns
+- Single `processTurn()` function handles all post-action processing
+- State transitions are always applied atomically (`gameState = newState`)
+- AI turns use sequential promise chains, not nested timeouts
+
+```
+function processTurn() {
+ // 1. Check for early win (single active player)
+ // 2. Check if betting round is complete
+ // 3. If complete: advance to next stage or showdown
+ // 4. If not complete: check if next player is AI, schedule turn
+}
+
+// In handleAction:
+if (aiActing) return; // Guard against race conditions
+gameState = applyAction(gameState, action);
+processTurn();
+```
+
+**Rationale:** Eliminates race conditions and state discarding bugs. Single source of truth for game flow logic.
+
+### Decision 5: Validation enforcement
+
+**Current:** `validateAction` exists but is never called by `applyRaise`, `applyFold`, etc. The AI and human actions bypass validation entirely.
+
+**Decision:** All action functions call `validateAction` internally and return unchanged state on invalid actions. The UI layer also validates before rendering buttons.
+
+```
+function applyRaise(state, playerId, amount) {
+ const validation = validateAction(playerId, 'raise', amount, state);
+ if (!validation.valid) return state;
+ // ... proceed with raise logic
+}
+```
+
+**Rationale:** Defense in depth — both the action layer and UI layer enforce rules. Prevents illegal states from being created.
+
+## Risks / Trade-offs
+
+[Side pot complexity] → Side pots add significant complexity to showdown logic. Mitigation: Thoroughly test with edge cases (2 all-ins, 3 all-ins, all-in on flop/turn/river).
+
+[State expansion] → Adding `betMatched`, `lastAggressorIndex`, and `sidePots` to GameState increases mutation surface. Mitigation: Use immutable state updates consistently, reset all fields in `startNewHand`.
+
+[AI timing changes] → Switching from nested timeouts to promise chains changes AI behavior timing. Mitigation: Keep 400ms delay per AI action for consistent feel.
+
+[Performance] → Side pot calculation at showdown could be expensive with many players. Mitigation: 6-max table limits complexity; algorithm is O(n log n) with n ≤ 6.
+
+[currentTurn deadlock] → `completeBettingRound` originally set `currentTurn = dealerPosition + 1`, which could land on a folded/all-in player, causing an infinite loop in the game state machine. Fixed by using `findFirstActivePlayer()` to always point to an active player.
+
+## Open Questions
+
+- Should we implement burn cards (standard in real poker, currently not implemented)?
+- How should all-in raises that don't constitute a full raise be handled (half-pot rule)?
+- Should the BB option (BB can check pre-flop if no raise) be explicitly documented in the UI?
diff --git a/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/proposal.md b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/proposal.md
new file mode 100644
index 0000000..f900744
--- /dev/null
+++ b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/proposal.md
@@ -0,0 +1,36 @@
+## Why
+
+The current poker game logic contains multiple critical bugs that violate Texas Hold'em rules, causing rounds to skip, bets to calculate incorrectly, and chips to disappear. The game needs a complete rewrite of its core game flow to produce legitimate, playable poker.
+
+## What Changes
+
+- **Fix `applyCheck`**: Remove phantom chip assignment that corrupts call calculations and round completion
+- **Fix betting round completion**: Exclude all-in players from "all matched" check so active players aren't skipped
+- **Fix game loop state management**: Always apply returned state from `completeBettingRound` instead of discarding it when rounds don't advance
+- **Implement side pot support**: Create and distribute side pots when players go all-in at different levels
+- **Enforce minimum raise validation**: Call `validateAction` before applying any action, reject illegal raises
+- **Fix fold validation**: Allow folding in all situations per Texas Hold'em rules
+- **Fix pot distribution**: Award remainder chips to first winner instead of losing them
+- **Add input guardrails**: Disable player controls during AI turns to prevent race conditions
+- **Rewrite turn advancement**: Properly track last aggressor and end betting rounds when action returns correctly
+
+## Capabilities
+
+### New Capabilities
+- `side-pots`: Handle all-in scenarios by creating main pot and side pots with correct chip distribution among eligible players
+- `betting-round-flow`: Complete rewrite of betting round completion logic with proper last-aggressor tracking, all-in handling, and turn advancement
+- `game-loop-integrity`: Unified game state machine that prevents race conditions, always applies state transitions, and disables controls during AI turns
+
+### Modified Capabilities
+
+
+## Impact
+
+- `src/lib/game/actions.ts` — Complete rewrite of check, call, raise, fold, all-in functions
+- `src/lib/game/betting-round.ts` — Rewrite completion logic with side pot awareness and last-aggressor tracking
+- `src/lib/game/state.ts` — Add last aggressor tracking, side pot structures to GameState
+- `src/lib/types/game-state.ts` — Extend with `lastAggressor`, `sidePots`, betting round metadata
+- `src/lib/game/showdown.ts` — Fix pot distribution, handle side pots at showdown
+- `src/lib/game/validation.ts` — Fix fold validation, enforce minimum raise rules
+- `src/routes/+page.svelte` — Add input disabling during AI turns, proper state machine flow
+- `src/lib/game/turn.ts` — Rewrite turn advancement logic
diff --git a/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/betting-round-flow/spec.md b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/betting-round-flow/spec.md
new file mode 100644
index 0000000..af7fdae
--- /dev/null
+++ b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/betting-round-flow/spec.md
@@ -0,0 +1,45 @@
+## ADDED Requirements
+
+### Requirement: Check action does not modify player chip count or bet contribution
+When a player checks, the system SHALL only mark the player as having matched the current bet level without deducting chips or modifying their `currentBet` value.
+
+#### Scenario: Check with zero current bet
+- **WHEN** the current bet is 0 and Player A checks
+- **THEN** Player A's `betMatched` becomes true, `currentBet` remains unchanged, and chip count remains unchanged
+
+#### Scenario: Check after matching previous bet
+- **WHEN** Player A has already called a bet of 100 (currentBet = 100) and the next player checks (no raise occurred)
+- **THEN** Player A's state remains unchanged except `betMatched` is set to true
+
+### Requirement: Betting round completes when action returns to last aggressor
+The system SHALL end a betting round when all active players have matched the current bet and action returns to the position of the last player who raised or opened betting.
+
+#### Scenario: Pre-flop BB option (no raises)
+- **WHEN** SB posts 10, BB posts 20, and all players check around back to BB
+- **THEN** BB may check without posting additional chips, and the betting round completes
+
+#### Scenario: Raise requires action to return to raiser
+- **WHEN** Player A raises to 100, Players B and C call, and Player D checks
+- **THEN** the round does not complete until Player A either checks or faces no further raises
+
+### Requirement: All-in players are excluded from betting round completion check
+When determining if a betting round is complete, the system SHALL only consider active (non-all-in) players for the "all matched" condition.
+
+#### Scenario: Active player skipped by all-in completion bug
+- **WHEN** Player A bets 100, Player B goes all-in for 50, and Player C has not yet acted
+- **THEN** the betting round does NOT complete; Player C must act before the round advances
+
+#### Scenario: Single active player ends round
+- **WHEN** all players except one have folded or gone all-in
+- **THEN** the betting round completes immediately and the game proceeds to the next stage
+
+### Requirement: Turn advancement skips non-active players correctly
+The system SHALL advance turns to the next player with `status === 'active'`, skipping folded and all-in players.
+
+#### Scenario: Folded player is skipped
+- **WHEN** Player A folds and it's their turn position
+- **THEN** the turn advances to the next active player clockwise from Player A
+
+#### Scenario: All-in player is skipped during betting
+- **WHEN** Player B goes all-in during a betting round
+- **THEN** subsequent turns skip Player B and advance to the next active player
diff --git a/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/game-loop-integrity/spec.md b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/game-loop-integrity/spec.md
new file mode 100644
index 0000000..a136f9b
--- /dev/null
+++ b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/game-loop-integrity/spec.md
@@ -0,0 +1,45 @@
+## ADDED Requirements
+
+### Requirement: Game state transitions are always applied atomically
+The system SHALL always apply the complete returned state from action functions and never discard intermediate state updates.
+
+#### Scenario: Betting round completion state is preserved
+- **WHEN** `completeBettingRound` returns a new state with advanced `currentTurn` and reset bets
+- **THEN** the game applies the entire returned state, including turn advancement and bet resets, even if the betting round stage does not change
+
+#### Scenario: Action function results are never lost
+- **WHEN** a player performs any action (check, call, raise, fold, all-in)
+- **THEN** the returned state from the action function is applied completely before any subsequent processing occurs
+
+### Requirement: Player controls are disabled during AI turns
+The system SHALL disable human player input controls while AI players are taking their turns to prevent race conditions.
+
+#### Scenario: Human clicks during AI turn
+- **WHEN** an AI player is processing their turn (400ms delay) and the human player clicks a bet control
+- **THEN** the action is rejected, the UI shows disabled controls, and no state mutation occurs
+
+#### Scenario: Controls re-enable after AI turn completes
+- **WHEN** an AI player completes their action and the turn advances to the human player
+- **THEN** the bet controls become enabled and the human player may act
+
+### Requirement: Validation is enforced before applying any action
+The system SHALL validate all actions against game rules before applying state changes, returning unchanged state for invalid actions.
+
+#### Scenario: Invalid raise is rejected
+- **WHEN** a player attempts to raise below the minimum raise amount
+- **THEN** the action is rejected, the state remains unchanged, and an error reason is returned
+
+#### Scenario: Fold always permitted
+- **WHEN** a player attempts to fold during any betting round, including pre-flop
+- **THEN** the action is accepted regardless of current bet level
+
+### Requirement: Pot distribution preserves all chips
+When awarding pots to winners, the system SHALL distribute all chips without losing remainder amounts.
+
+#### Scenario: Odd pot split between two winners
+- **WHEN** the pot contains 105 chips and two players tie for the win
+- **THEN** one winner receives 53 chips, the other receives 52 chips, and zero chips are lost
+
+#### Scenario: Single winner receives full pot
+- **WHEN** a single player wins the pot of 200 chips
+- **THEN** the winner receives exactly 200 chips and the pot is reset to 0
diff --git a/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/side-pots/spec.md b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/side-pots/spec.md
new file mode 100644
index 0000000..77f82b4
--- /dev/null
+++ b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/specs/side-pots/spec.md
@@ -0,0 +1,34 @@
+## ADDED Requirements
+
+### Requirement: Side pots are created when players go all-in at different bet levels
+When one or more players go all-in for less than the current bet level, the system SHALL create separate side pots to ensure only eligible players can win chips they contributed to.
+
+#### Scenario: Single all-in creates main pot and side pot
+- **WHEN** Player A bets 100, Player B goes all-in for 50, and Player C calls 100
+- **THEN** a main pot of 150 (50×3) is created with all three players eligible, and a side pot of 100 (25×4) is created with Players A and C eligible
+
+#### Scenario: Multiple all-ins create multiple side pots
+- **WHEN** Player A bets 200, Player B goes all-in for 100, Player C goes all-in for 150, and Player D calls 200
+- **THEN** a main pot of 400 (100×4) is created with all players eligible, a side pot of 100 (50×3) is created with Players A, C, D eligible, and a side pot of 100 (50×2) is created with Players A and D eligible
+
+### Requirement: Side pots are awarded to eligible winners only
+When determining winners for each pot, the system SHALL only award a pot to players who contributed chips to that specific pot level.
+
+#### Scenario: All-in player wins main pot only
+- **WHEN** Player B is all-in for 50 and has the best hand at showdown, Player A and C are still active with worse hands
+- **THEN** Player B receives the main pot amount proportional to their contribution, and Players A/C compete for the side pot
+
+#### Scenario: All-in player loses all pots
+- **WHEN** Player B is all-in for 50 and has the worst hand at showdown
+- **THEN** Player B receives no chips from any pot, and eligible players split the main pot proportionally
+
+### Requirement: Side pot calculation handles edge cases correctly
+The system SHALL correctly calculate side pots when all players go all-in or when an all-in does not constitute a raise.
+
+#### Scenario: All players all-in creates single pot
+- **WHEN** all remaining players go all-in during a betting round
+- **THEN** a single main pot containing all chips is created with all players eligible, and no side pots are generated
+
+#### Scenario: All-in that doesn't raise does not create side pot
+- **WHEN** the current bet is 100 and Player B goes all-in for 80 (less than a full raise)
+- **THEN** no side pot is created; the main pot contains all chips and active players may check to continue
diff --git a/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/tasks.md b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/tasks.md
new file mode 100644
index 0000000..7a34014
--- /dev/null
+++ b/openspec/changes/archive/2026-05-17-fix-texas-holdem-rules/tasks.md
@@ -0,0 +1,71 @@
+## 1. Type system updates
+
+- [x] 1.1 Add `betMatched: boolean` field to `PlayerSeat` interface in `src/lib/types/player.ts`
+- [x] 1.2 Add `lastAggressorIndex: number` field to `GameState` interface in `src/lib/types/game-state.ts`
+- [x] 1.3 Define `SidePot` interface with `amount` and `eligiblePlayerIds` fields
+- [x] 1.4 Add `sidePots: SidePot[]` field to `GameState` interface
+
+## 2. State initialization & reset
+
+- [x] 2.1 Update `createInitialState` to initialize new fields (`betMatched`, `lastAggressorIndex`, `sidePots`)
+- [x] 2.2 Update `startNewHand` to reset all per-round fields including new fields
+- [x] 2.3 Update `postBlinds` to set `betMatched` correctly for SB and BB players
+
+## 3. Action function rewrites
+
+- [x] 3.1 Rewrite `applyCheck` to set `betMatched = true` without modifying `currentBet` or chips
+- [x] 3.2 Rewrite `applyCall` to use `state.currentBet - player.currentBet` for call amount with correct chip deduction
+- [x] 3.3 Rewrite `applyRaise` to update `lastAggressorIndex`, reset other players' `betMatched`, and enforce minimum raise
+- [x] 3.4 Rewrite `applyFold` to correctly mark player as folded and advance turn
+- [x] 3.5 Rewrite `applyAllIn` to handle partial all-in raises and update `lastAggressorIndex` if applicable
+- [x] 3.6 Add `validateAction` call at the start of each action function with early return on invalid actions
+
+## 4. Validation fixes
+
+- [x] 4.1 Fix fold validation to always permit folding (remove pre-flop restriction)
+- [x] 4.2 Fix raise validation to correctly calculate minimum raise based on `lastRaiseAmount`
+- [x] 4.3 Add validation for all-in actions that don't constitute a full raise
+
+## 5. Betting round completion rewrite
+
+- [x] 5.1 Implement `isRoundComplete` function with last-aggressor tracking logic
+- [x] 5.2 Rewrite `completeBettingRound` to use `betMatched` flag and exclude all-in players from match check
+- [x] 5.3 Implement BB option: allow BB to check pre-flop when no raises occurred
+- [x] 5.4 Ensure `currentTurn` advances correctly after round completion
+
+## 6. Side pot implementation
+
+- [x] 6.1 Implement `calculatePots` function to compute main pot and side pots from player bets
+- [x] 6.2 Integrate side pot calculation into all-in action handling
+- [x] 6.3 Update `awardPot` to distribute main pot and all side pots correctly
+- [x] 6.4 Fix remainder chip distribution (award leftover chips to first winner)
+- [x] 6.5 Update showdown logic to determine winners for each pot based on eligibility
+
+## 7. Turn advancement fixes
+
+- [x] 7.1 Rewrite `advanceTurn` to skip folded and all-in players correctly
+- [x] 7.2 Ensure turn advancement respects dealer position and betting round start position
+- [x] 7.3 Handle edge case where no active players remain for turn advancement
+
+## 8. Game loop rewrite (+page.svelte)
+
+- [x] 8.1 Add `aiActing: boolean` state flag to disable controls during AI turns
+- [x] 8.2 Replace `processGame` with unified `processTurn` state machine function
+- [x] 8.3 Ensure all state transitions apply returned state atomically (no discarding)
+- [x] 8.4 Convert AI turn timing from nested setTimeout to sequential promise chain
+- [x] 8.5 Add guard clause in `handleAction` to reject actions when `aiActing` is true
+- [x] 8.6 Update BetControls component to accept and respond to `disabled` prop
+
+## 9. Bug fixes discovered during playtesting
+
+- [x] 9.1 Fix deadlock: `completeBettingRound` sets `currentTurn` to first active player instead of hardcoded `dealerPosition + 1`
+- [x] 9.2 Simplify `processTurn` to use corrected `currentTurn` directly after round completion
+
+## 10. Testing & verification
+
+- [ ] 10.1 Test pre-flop betting round with all action combinations (check, call, raise, fold)
+- [ ] 10.2 Test single all-in scenario verifies correct main pot and side pot creation
+- [ ] 10.3 Test multiple all-ins at different levels verifies correct multi-side pot distribution
+- [ ] 10.4 Test BB option pre-flop (BB checks when no raises)
+- [ ] 10.5 Test full hand flow from deal through showdown with no state corruption
+- [ ] 10.6 Test race condition prevention (rapid human clicks during AI turn are rejected)
\ No newline at end of file
diff --git a/openspec/specs/betting-round-flow/spec.md b/openspec/specs/betting-round-flow/spec.md
new file mode 100644
index 0000000..7416f0b
--- /dev/null
+++ b/openspec/specs/betting-round-flow/spec.md
@@ -0,0 +1,49 @@
+# betting-round-flow Specification
+
+## Purpose
+TBD - created by archiving change fix-texas-holdem-rules. Update Purpose after archive.
+## Requirements
+### Requirement: Check action does not modify player chip count or bet contribution
+When a player checks, the system SHALL only mark the player as having matched the current bet level without deducting chips or modifying their `currentBet` value.
+
+#### Scenario: Check with zero current bet
+- **WHEN** the current bet is 0 and Player A checks
+- **THEN** Player A's `betMatched` becomes true, `currentBet` remains unchanged, and chip count remains unchanged
+
+#### Scenario: Check after matching previous bet
+- **WHEN** Player A has already called a bet of 100 (currentBet = 100) and the next player checks (no raise occurred)
+- **THEN** Player A's state remains unchanged except `betMatched` is set to true
+
+### Requirement: Betting round completes when action returns to last aggressor
+The system SHALL end a betting round when all active players have matched the current bet and action returns to the position of the last player who raised or opened betting.
+
+#### Scenario: Pre-flop BB option (no raises)
+- **WHEN** SB posts 10, BB posts 20, and all players check around back to BB
+- **THEN** BB may check without posting additional chips, and the betting round completes
+
+#### Scenario: Raise requires action to return to raiser
+- **WHEN** Player A raises to 100, Players B and C call, and Player D checks
+- **THEN** the round does not complete until Player A either checks or faces no further raises
+
+### Requirement: All-in players are excluded from betting round completion check
+When determining if a betting round is complete, the system SHALL only consider active (non-all-in) players for the "all matched" condition.
+
+#### Scenario: Active player skipped by all-in completion bug
+- **WHEN** Player A bets 100, Player B goes all-in for 50, and Player C has not yet acted
+- **THEN** the betting round does NOT complete; Player C must act before the round advances
+
+#### Scenario: Single active player ends round
+- **WHEN** all players except one have folded or gone all-in
+- **THEN** the betting round completes immediately and the game proceeds to the next stage
+
+### Requirement: Turn advancement skips non-active players correctly
+The system SHALL advance turns to the next player with `status === 'active'`, skipping folded and all-in players.
+
+#### Scenario: Folded player is skipped
+- **WHEN** Player A folds and it's their turn position
+- **THEN** the turn advances to the next active player clockwise from Player A
+
+#### Scenario: All-in player is skipped during betting
+- **WHEN** Player B goes all-in during a betting round
+- **THEN** subsequent turns skip Player B and advance to the next active player
+
diff --git a/openspec/specs/game-loop-integrity/spec.md b/openspec/specs/game-loop-integrity/spec.md
new file mode 100644
index 0000000..1e93481
--- /dev/null
+++ b/openspec/specs/game-loop-integrity/spec.md
@@ -0,0 +1,49 @@
+# game-loop-integrity Specification
+
+## Purpose
+TBD - created by archiving change fix-texas-holdem-rules. Update Purpose after archive.
+## Requirements
+### Requirement: Game state transitions are always applied atomically
+The system SHALL always apply the complete returned state from action functions and never discard intermediate state updates.
+
+#### Scenario: Betting round completion state is preserved
+- **WHEN** `completeBettingRound` returns a new state with advanced `currentTurn` and reset bets
+- **THEN** the game applies the entire returned state, including turn advancement and bet resets, even if the betting round stage does not change
+
+#### Scenario: Action function results are never lost
+- **WHEN** a player performs any action (check, call, raise, fold, all-in)
+- **THEN** the returned state from the action function is applied completely before any subsequent processing occurs
+
+### Requirement: Player controls are disabled during AI turns
+The system SHALL disable human player input controls while AI players are taking their turns to prevent race conditions.
+
+#### Scenario: Human clicks during AI turn
+- **WHEN** an AI player is processing their turn (400ms delay) and the human player clicks a bet control
+- **THEN** the action is rejected, the UI shows disabled controls, and no state mutation occurs
+
+#### Scenario: Controls re-enable after AI turn completes
+- **WHEN** an AI player completes their action and the turn advances to the human player
+- **THEN** the bet controls become enabled and the human player may act
+
+### Requirement: Validation is enforced before applying any action
+The system SHALL validate all actions against game rules before applying state changes, returning unchanged state for invalid actions.
+
+#### Scenario: Invalid raise is rejected
+- **WHEN** a player attempts to raise below the minimum raise amount
+- **THEN** the action is rejected, the state remains unchanged, and an error reason is returned
+
+#### Scenario: Fold always permitted
+- **WHEN** a player attempts to fold during any betting round, including pre-flop
+- **THEN** the action is accepted regardless of current bet level
+
+### Requirement: Pot distribution preserves all chips
+When awarding pots to winners, the system SHALL distribute all chips without losing remainder amounts.
+
+#### Scenario: Odd pot split between two winners
+- **WHEN** the pot contains 105 chips and two players tie for the win
+- **THEN** one winner receives 53 chips, the other receives 52 chips, and zero chips are lost
+
+#### Scenario: Single winner receives full pot
+- **WHEN** a single player wins the pot of 200 chips
+- **THEN** the winner receives exactly 200 chips and the pot is reset to 0
+
diff --git a/openspec/specs/side-pots/spec.md b/openspec/specs/side-pots/spec.md
new file mode 100644
index 0000000..f0bc314
--- /dev/null
+++ b/openspec/specs/side-pots/spec.md
@@ -0,0 +1,38 @@
+# side-pots Specification
+
+## Purpose
+TBD - created by archiving change fix-texas-holdem-rules. Update Purpose after archive.
+## Requirements
+### Requirement: Side pots are created when players go all-in at different bet levels
+When one or more players go all-in for less than the current bet level, the system SHALL create separate side pots to ensure only eligible players can win chips they contributed to.
+
+#### Scenario: Single all-in creates main pot and side pot
+- **WHEN** Player A bets 100, Player B goes all-in for 50, and Player C calls 100
+- **THEN** a main pot of 150 (50×3) is created with all three players eligible, and a side pot of 100 (25×4) is created with Players A and C eligible
+
+#### Scenario: Multiple all-ins create multiple side pots
+- **WHEN** Player A bets 200, Player B goes all-in for 100, Player C goes all-in for 150, and Player D calls 200
+- **THEN** a main pot of 400 (100×4) is created with all players eligible, a side pot of 100 (50×3) is created with Players A, C, D eligible, and a side pot of 100 (50×2) is created with Players A and D eligible
+
+### Requirement: Side pots are awarded to eligible winners only
+When determining winners for each pot, the system SHALL only award a pot to players who contributed chips to that specific pot level.
+
+#### Scenario: All-in player wins main pot only
+- **WHEN** Player B is all-in for 50 and has the best hand at showdown, Player A and C are still active with worse hands
+- **THEN** Player B receives the main pot amount proportional to their contribution, and Players A/C compete for the side pot
+
+#### Scenario: All-in player loses all pots
+- **WHEN** Player B is all-in for 50 and has the worst hand at showdown
+- **THEN** Player B receives no chips from any pot, and eligible players split the main pot proportionally
+
+### Requirement: Side pot calculation handles edge cases correctly
+The system SHALL correctly calculate side pots when all players go all-in or when an all-in does not constitute a raise.
+
+#### Scenario: All players all-in creates single pot
+- **WHEN** all remaining players go all-in during a betting round
+- **THEN** a single main pot containing all chips is created with all players eligible, and no side pots are generated
+
+#### Scenario: All-in that doesn't raise does not create side pot
+- **WHEN** the current bet is 100 and Player B goes all-in for 80 (less than a full raise)
+- **THEN** no side pot is created; the main pot contains all chips and active players may check to continue
+
diff --git a/src/lib/components/BetControls.svelte b/src/lib/components/BetControls.svelte
index 0f83f65..62ce3d9 100644
--- a/src/lib/components/BetControls.svelte
+++ b/src/lib/components/BetControls.svelte
@@ -1,13 +1,15 @@
@@ -152,7 +177,7 @@