- DECISIONS.md: new "Table-fidelity features" section + deferred items (smart-tracker rejected, highlight/phantom coupling deferred, abandoned-game localStorage cleanup deferred). - CLAUDE.md: current state, test count 78->87, key files, known gaps. - spec: record that the driver unit test covers the bot-suppression path in place of the considered-and-dropped ai-game-casual integration test (resolves a spec/implementation drift the final review flagged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.3 KiB
blind_chess
Web-based two-player chess where each player sees only their own pieces; the server acts as moderator.
Start Here
Read the latest handoff first: .claude/handoffs/ (most recent file).
Then check DECISIONS.md for settled choices, and the design specs:
docs/superpowers/specs/2026-04-28-blind-chess-design.md— original MVP spec (data model, protocol, FSM, testing).docs/superpowers/specs/2026-04-28-ai-player-design.md— AI/computer player spec (Casual + gemma4 recon bots, two-phase plan).docs/superpowers/specs/2026-05-18-table-fidelity-features-design.md— table-fidelity batch (announce-all, capture tally, phantom opponent-piece layer).
Project Identity
A digitization of a battleship-style chess variant. Two players, separated; a moderator who sees both boards and announces a fixed vocabulary of events ("Black has moved and captured", "Moving that piece will not help you", "White is in check"). The physical version requires three people; this version replaces the moderator with a server. Ships with both vanilla mode (full reveal — normal chess) and blind mode (the variation) on day one. Mode is a per-player view filter on a shared engine, not a different game.
The system's most distinctive property: highlighting in blind mode reveals zero opponent information. It's computed purely from (piece type, position, own-piece set) — a function whose signature literally cannot read opponent state. The moderator vocabulary is the only legitimate channel for opponent events.
Current State
- Phase: MVP deployed and live at https://chess.sethpc.xyz (2026-04-28). AI Phase 1 (Casual bot) deployed (2026-04-28) — "Play vs computer" → Casual bot. Blind Casual check-resolution fix shipped (2026-04-29). Table-fidelity feature batch code-complete on
main(2026-05-18) — moderator announces every move/attempt to both players, a capture tally, and a client-local phantom opponent-piece layer for blind mode. Pending deploy (both instances — see Operations). - Repo:
git.sethpc.xyz/Seth/blind_chess. - Stack: Node 22 + TypeScript, Fastify +
ws, Svelte 5 + Vite,chess.js,js-chess-engine(Casual vanilla AI). pnpm workspace withpackages/{server,client,shared}. - Deploy: LXC CT 690 on node-241 at 192.168.0.245, behind Caddy CT 600. Systemd unit
blind-chess.service, port 3000. In-memory state only. - Tests: 87 passing — 25 in shared (geometric + phantom-model helpers), 62 in server (FSM + view + candidates + casual brain + driver + captures + scripted-game + ai-game-casual integration). The client package has no test harness by design.
- Known gaps (deferred): drag-and-drop for real moves (still click-to-move; the phantom layer added pointer-drag for phantom pieces only), full integration coverage of every endgame path, mobile-specific polish, observability beyond
/api/healthand[bot resign](no metrics, no per-game tracing),localStoragecleanup for phantom layers of abandoned games. - AI Phase 2 (gemma4 recon, not built): Spec in
docs/superpowers/specs/2026-04-28-ai-player-design.md. Will reuse the Phase 1Brain/BotDriverinfrastructure. Plan to be written when Phase 1 has soaked. Bots play through the same view filter and FSM as humans — no oracle access.
Key files
IDEA.md— original project brief (Seth's words)DECISIONS.md— locked architectural and gameplay decisionsdocs/superpowers/specs/2026-04-28-blind-chess-design.md— full design spec (data model, protocol, FSM, testing)docs/superpowers/specs/2026-04-28-ai-player-design.md— AI/computer player spec (Casual + gemma4 recon, two-phase plan, endpoint priority list, acceptance bars)packages/shared/src/geometric.ts— the zero-leak helper. The signature is the proof.packages/server/src/view.ts—buildView, the security boundary.packages/server/src/commit.ts— touch-move FSM (the spec's hierarchy decision table).packages/server/src/translator.ts— chess.jsMove→ moderator-vocabulary enum.packages/server/src/captures.ts—captureTally, the per-viewer capture-count derivation (Feature 2).packages/server/src/game-end.ts— sharedendGame/finalizeIfEndedhelpers used by both ws and bot driver.packages/server/src/bot/— Brain interface, BotDriver, CasualBrain, candidates. Vanilla mode delegates tojs-chess-engineat level 2; blind mode uses a heuristic.packages/client/src/lib/stores/phantoms.svelte.ts— client-LOCAL phantom opponent-piece store (Feature 3). Never sent to the server;phantom-drag.svelte.tsis its pointer-drag controller.scripts/selfplay.ts— operator CLI for evaluating Casual vs Casual / Random self-play.pnpm selfplay --help.deploy/blind-chess.service— systemd unit (canonical at/etc/systemd/system/blind-chess.serviceon the CT).deploy/Caddyfile.snippet— block already added to/etc/caddy/Caddyfileon CT 600.
Operations
- Logs:
ssh root@192.168.0.245 'journalctl -u blind-chess -f' - Restart:
ssh root@192.168.0.245 'systemctl restart blind-chess' - Health:
curl https://chess.sethpc.xyz/api/health - Deploy update:
pnpm -r build→pnpm --filter @blind-chess/server deploy --prod --legacy .deploy-server→ rsync server bundle to/opt/blind-chess/server/and clientdist/to/opt/blind-chess/client/dist/→chown -R blindchess:blindchess /opt/blind-chess→systemctl restart blind-chess. Server restart drops in-memory games (acceptable for MVP).
Local instance — chess.local on VDJ-RIG
A second, LAN-only deploy on VDJ-RIG (192.168.0.143), fully independent of the CT 690 / chess.sethpc.xyz instance (separate in-memory state). Serves on port 80, reached at http://chess.local via an mDNS alias — no Caddy, no TLS.
- Artifacts (
deploy/):blind-chess-local.service(server unit; binds port 80 as the non-rootblindchessuser viaCAP_NET_BIND_SERVICE),chess-mdns-alias+chess-mdns-alias.service(publishes thechess.localmDNS name withavahi-publish -a -R—-Ravoids a reverse-PTR collision with the host's own.localname),install-local.sh(root-side installer). - On the rig: tree at
/opt/blind-chess/{server,client/dist}, unitsblind-chess.service+chess-mdns-alias.service, Node 22 via NodeSource. - Logs:
ssh vdj-rig 'journalctl -u blind-chess -f' - Restart:
ssh vdj-rig 'sudo systemctl restart blind-chess' - Health:
curl http://chess.local/api/health - Redeploy: on steel141
pnpm -r build+pnpm --filter @blind-chess/server deploy --prod --legacy .deploy-server; rsync.deploy-server/→ rig~/blind-chess-stage/server/,packages/client/dist/→~/blind-chess-stage/client-dist/, and thedeploy/files →~/blind-chess-stage/; thensudo bash ~/blind-chess-stage/install-local.sh.
Conventions
- Inherits global homelab conventions from
~/bin/CLAUDE.md(gitea CLI, conventional commits,.claude/handoffs/for session state). - pnpm workspace; do not use npm/yarn lockfiles.
- All inter-package types live in
packages/shared/. Never duplicate protocol types in client or server. - Server is the single authority on game state. Client
commitmessages are requests, not facts. - The view filter (
buildView) is the only egress for board state. Don't bypass it.