Commit graph

61 commits

Author SHA1 Message Date
ca6a3df500
fix(rete): inject clock into EventLog; use tsc for DTS; fix cycle.test.ts private access; add Playwright worker limit 2026-04-16 18:25:49 -06:00
103f2bd0a6
test(root): E2E multiplayer with reconnect; tag Phase 4 (P4.12) 2026-04-16 18:12:44 -06:00
6fb67c5026
feat(chess): add lobby UI for create/join rooms (P4.11) 2026-04-16 18:01:31 -06:00
721cc5484d
feat(chess): add client prediction + server reconciliation (P4.10) 2026-04-16 17:48:33 -06:00
39d91b6356
feat(chess): add WebSocket client library with reconnect (P4.9)
GameClient provides typed event-driven access to the chess server:

- Envelope management: auto v/seq/ts, token captured from room.created/joined
- Event emitter: on/off for game.state, game.delta, game.end, room.created,
  room.joined, error, connected, disconnected(willReconnect)
- Exponential backoff reconnect: 1s, 2s, 4s, ... capped at 30s, max 10 attempts
- Sequence-ack tracking via currentSeq (highest server seq seen; never regresses)
- Dependency injection for WebSocket ctor and timers enables deterministic tests

packages/chess/src/net/types.ts mirrors the server wire types without importing
from @paratype/chess-server (wrong dependency direction).

21 tests cover connect flow, message dispatch, send semantics, reconnect state
machine, seq tracking, and listener management.
2026-04-16 17:44:13 -06:00
2d0b8399f8
feat(server): add reconnection with 60s grace + snapshot resume (P4.7)
On disconnect, a player's slot is held for 60s via ReconnectManager.
During the grace window, game.delta frames destined for the absent
slot are buffered in order. If the client reconnects with its
original token (envelope-level), the grace timer is cancelled and
the server replays a fresh game.state snapshot plus every buffered
delta. Timer expiry triggers the original "player_left" game.end
cleanup that previously ran immediately on disconnect.

- new packages/server/src/reconnect.ts: ReconnectManager (no WS refs,
  no registry coupling, unref'd timers so tests don't block exit)
- broadcast.ts: unregisterConnection starts grace; handleRoomJoin
  routes reconnect path via envelope token + isPending check;
  handleGameMove buffers deltas for disconnected opponents
- reconnect.test.ts: 9 unit cases (grace/cancel/buffer/expire/reset)
- broadcast.test.ts: end-to-end reconnect scenario + negative case
2026-04-16 17:38:26 -06:00
e420ee18c5
feat(server): add move validation + fact-delta broadcast (P4.6)
Wire the WebSocket message handler to process game.move intents via
GameSession and broadcast game.delta to both room players. Add
broadcast.ts as the message router with handlers for room.create,
room.join (triggers game.state to both players when 2nd player
joins), room.leave, and game.move. Move-intent validation enforces
NOT_YOUR_TURN before applyMove so clients can distinguish turn
errors from illegal moves.

index.ts now extends ClientData with roomCode/token, registers
connections on open and unregisters (+ markDisconnected +
broadcast game.end) on close, and gates inbound frames through
the size cap and rate limiter before dispatch.

broadcast.test.ts drives handleMessage directly with mock
ServerWebSockets (vitest runs on Node, so we can't use Bun.serve)
covering: legal-move broadcast to both players, illegal-move
error-to-sender-only with silent opponent, NOT_YOUR_TURN, malformed
JSON fatal disconnect, VERSION_MISMATCH fatal disconnect,
ROOM_NOT_FOUND, and BAD_TOKEN for unauthenticated game.move.

bun run typecheck, bun run lint, bun run test all pass.
2026-04-16 17:27:00 -06:00
f37c0934aa
feat(server): add authoritative game session per room (P4.5)
Each room owns a ChessEngine wrapped in a GameSession; only the server
calls insert/retract/fireRules and all EntityIds are minted server-side.
GameSessionRegistry keys sessions by room code so two rooms cannot
observe or collide with each other's working-memory state.

GameSession.applyMove validates algebraic inputs, finds the matching
legal move via ChessEngine.findMove, applies it, and returns a fact-
level diff (inserted/retracted) plus the new turn and terminal state.
Terminal states are sticky: further moves after checkmate/draw return
GAME_OVER rather than silently mutating a dead session.

Exposes @paratype/chess's headless surface (ChessEngine, coord helpers,
schema types) via a new package entry point; the React app continues to
import concrete modules directly.
2026-04-16 17:17:42 -06:00
aafc18ef9e
feat(server): add rate-limit, origin allow-list, message-size cap (P4.4) 2026-04-16 17:11:40 -06:00
7d07bb78ba
feat(server): add room registry with codes + tokens (P4.3) 2026-04-16 17:09:43 -06:00
817b4d95f3
feat(server): add protocol schemas + validation (P4.2) 2026-04-16 17:07:21 -06:00
25695d69b2
feat(server): scaffold Bun HTTP+WS server with health + logging (P4.1) 2026-04-16 17:03:42 -06:00
13af8ddab3
test(chess): e2e full-flow scenario; tag Phase 3 (P3.15) 2026-04-16 16:55:28 -06:00
a75b6f041b
feat(chess): add localStorage auto-save and restore (P3.14) 2026-04-16 16:24:49 -06:00
3fdeb5822d
feat(chess): add JSON export/import with validation (P3.13) 2026-04-16 16:14:46 -06:00
bc753aadfd
feat(chess): add Save/Load panel + time-travel undo (P3.12) 2026-04-16 16:12:15 -06:00
116cbb42b5
feat(chess): add rule-toggle UI with compatibility warnings (P3.11) 2026-04-16 16:05:04 -06:00
d367f51171
feat(chess): add interactive Chessboard with drag-drop (P3.10) 2026-04-16 16:01:34 -06:00
0f891fa013
feat(chess): scaffold Vite + React app (P3.9) 2026-04-16 15:54:32 -06:00
8436df7986
feat(chess): add all 15 preset custom rules P3.4-P3.8 2026-04-16 15:32:18 -06:00
cf1a8a3aab
feat(chess): add preset rules 1-3 (P3.4)
Introduces PresetRegistry + three pawn-focused preset rules from
RULES.md (pawns-move-backward, double-pawn-sprint,
pawn-diagonal-no-capture). Presets register themselves via
side-effect imports and expose getExtraMoves/filterMoves hooks for
the ChessEngine to invoke during move generation (engine wiring is
P3.11). Registry enforces incompatibility and requires invariants.
2026-04-16 15:30:14 -06:00
35e270e3b5
feat(rete): add Immer snapshots at tick boundaries (P3.2) 2026-04-16 15:25:35 -06:00
c5c00153ab
feat(rete): add replay engine + state-hash determinism verifier (P3.3) 2026-04-16 15:25:13 -06:00
aa5105d1c7
feat(rete): add append-only event log with monotonic sequence (P3.1) 2026-04-16 15:24:11 -06:00
6baab9f3fd
test(chess): replay 5 classic FIDE games; Phase 2 acceptance gate (P2.23)
- packages/chess/src/engine.ts — ChessEngine integrates all rule modules
  (pawn, knight, sliding, king, castling, en-passant, promotion, check,
  checkmate, stalemate, draws) into a playable game without the Rete
  production network
- packages/chess/src/pgn.ts — minimal SAN/PGN parser with full
  disambiguation support (file/rank hints, full from-square)
- packages/chess/tests/fide-games/classic-games.test.ts — 5 game tests:
  Fool's Mate, Scholar's Mate, Ruy López, Sicilian Defence, Italian Game

All 5 tests green; typecheck clean.
2026-04-16 15:18:57 -06:00
f94ab386a8
feat(chess): add checkmate detection (P2.19) 2026-04-16 15:06:27 -06:00
3f8a38bb41
feat(chess): add insufficient material draw (P2.22) 2026-04-16 15:05:54 -06:00
29ea2136b8
feat(chess): add 50-move and threefold repetition rules (P2.21) 2026-04-16 15:05:33 -06:00
20a18e6c78
feat(chess): add stalemate detection (P2.20) 2026-04-16 15:04:38 -06:00
6b760c9535
feat(chess): add check detection + self-check filter (P2.18) 2026-04-16 15:00:39 -06:00
fbdf10ec51
feat(chess): add pawn promotion rule (P2.17) 2026-04-16 14:58:31 -06:00
6938a2aedb
feat(chess): add en passant rule (P2.16) 2026-04-16 14:57:58 -06:00
f1da22641f
feat(chess): add castling rules (P2.15) 2026-04-16 14:57:39 -06:00
d9eaeb2f06
feat(chess): add turn order + move integration (P2.13) 2026-04-16 14:54:30 -06:00
483e4ef686
feat(chess): add capture resolution (P2.14) 2026-04-16 14:53:37 -06:00
83d99778db
feat(chess): add king basic move rules (P2.12) 2026-04-16 14:52:51 -06:00
1013c40e2a
feat(chess): add bishop/rook/queen sliding rules (P2.11) 2026-04-16 14:52:39 -06:00
6994d5fe55
feat(chess): add knight move rules (P2.10) 2026-04-16 14:51:56 -06:00
9a87e57007
feat(chess): add pawn move/capture rules (P2.9) 2026-04-16 14:51:49 -06:00
eb58441752
feat(chess): add movement primitive rules (P2.8) 2026-04-16 14:49:03 -06:00
d4cee82b20
feat(chess): add coordinate + color helpers (P2.7) 2026-04-16 14:47:04 -06:00
cc584d4e33
feat(chess): add starting-position fact generator (P2.6) 2026-04-16 14:46:34 -06:00
ea4d6e9a69
feat(chess): add attribute schema and piece fact shape (P2.5) 2026-04-16 14:46:27 -06:00
aabd4a396a
feat(rete): add FilterNode (P1.9), ExistentialNode (P2.2), AggregationNode (P2.4) — commit missing files 2026-04-16 14:26:26 -06:00
08515012b1
feat(rete): add NCC nodes (P2.3) 2026-04-16 14:22:21 -06:00
1731c43eb2
feat(rete): add negation nodes (NOT) (P2.1) 2026-04-16 14:21:39 -06:00
0401295bbc
test(rete): port pararules golden tests; tag Phase 1 parity (P1.14)
10 integration tests in packages/rete/tests/golden/ manually wire
AlphaNode → BetaMemory → JoinNode → ProductionNode chains and drive
them via Session.insert/retract. Each test maps to a pararules Nim
reference test (documented in GOLDEN-MAP.md).

Coverage: packages/rete/src at 96.8% statements / 95.4% branch /
97.8% functions — all well above the 90% Phase 1 gate.

Tests: 227 total (166 pre-existing + 61 new golden), all green.
2026-04-16 14:16:19 -06:00
0259268e34
feat(rete): add deterministic conflict resolution (P1.13) 2026-04-16 14:01:32 -06:00
3104d33985
feat(rete): add derived facts with thenFinally + truth maintenance (P1.11) 2026-04-16 13:59:24 -06:00
04804545da
feat(rete): add cycle detection with recursionLimit (P1.12) 2026-04-16 13:58:12 -06:00