From 2e808008b19fa3159dbcb8abdfddff2118a90c8a Mon Sep 17 00:00:00 2001 From: "claude (blind_chess)" Date: Mon, 18 May 2026 20:57:02 -0400 Subject: [PATCH] docs: record table-fidelity feature batch as code-complete - 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) --- CLAUDE.md | 9 ++++++--- DECISIONS.md | 15 +++++++++++++++ .../2026-05-18-table-fidelity-features-design.md | 8 ++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2783720..057334b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,6 +9,7 @@ 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 @@ -18,12 +19,12 @@ The system's most distinctive property: highlighting in blind mode reveals **zer ## 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) — heuristic now reads the `_in_check` announcement and prefers king moves; retry cap raised 5→25; bot resigns now logged. 100-game blind self-play: resigns 100%→17%, avgPly 26→90. +- **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 with `packages/{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:** 78 passing — 21 in shared (geometric helper), 57 in server (FSM + view + candidates + casual brain + driver + scripted-game + ai-game-casual integration). -- **Known gaps (deferred):** drag-and-drop input (click-to-move only), full integration coverage of every endgame path, mobile-specific polish, observability beyond `/api/health` and `[bot resign]` (no metrics, no per-game tracing). +- **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/health` and `[bot resign]` (no metrics, no per-game tracing), `localStorage` cleanup 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 1 `Brain`/`BotDriver` infrastructure. 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 @@ -36,8 +37,10 @@ The system's most distinctive property: highlighting in blind mode reveals **zer - `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.js `Move` → moderator-vocabulary enum. +- `packages/server/src/captures.ts` — `captureTally`, the per-viewer capture-count derivation (Feature 2). - `packages/server/src/game-end.ts` — shared `endGame` / `finalizeIfEnded` helpers used by both ws and bot driver. - `packages/server/src/bot/` — Brain interface, BotDriver, CasualBrain, candidates. Vanilla mode delegates to `js-chess-engine` at 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.ts` is 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.service` on the CT). - `deploy/Caddyfile.snippet` — block already added to `/etc/caddy/Caddyfile` on CT 600. diff --git a/DECISIONS.md b/DECISIONS.md index a1c89d2..3aceb52 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -75,6 +75,18 @@ Spec: `docs/superpowers/specs/2026-04-28-ai-player-design.md`. **Phase 1 (Casual - 2026-04-28: **Bot-slot synthetic token is randomized, not a fixed placeholder.** Using a hard-coded placeholder ("botxxxxxxxxxxxxxxxxxxxxx") would let any client knowing it claim the bot's color via `hello`. Random tokens (same shape as human tokens) close that hole. Caught in code review of Task 7. - 2026-04-28: **`endGame` and `finalizeIfEnded` extracted from `ws.ts` to `packages/server/src/game-end.ts`.** Both `ws.ts` and `bot/driver.ts` need to set the game-finished state — duplication risk. Hoist resolves it. +## Table-fidelity features (2026-05-18) + +Spec: `docs/superpowers/specs/2026-05-18-table-fidelity-features-design.md`. Plan: `docs/superpowers/plans/2026-05-18-table-fidelity-features.md`. Three features requested by Andrew Freiberg (a physical-game player); shipped to `main` 2026-05-18, 12 tasks via subagent-driven development. 87 tests passing (25 shared + 62 server). + +- 2026-05-18: **All moderator announcements are `audience: 'both'`** — every move event and every attempted-move error reaches both players, faithful to the physical game where the moderator speaks aloud. A deliberate, authorised widening of the moderator channel (it makes blind mode slightly less blind — e.g. you hear "won't help you" on the opponent's turn). The `audience` field is retained (now uniformly `'both'`) as the egress-control hook in `ws.ts` / `ModeratorPanel`. +- 2026-05-18: **Bot intermediate retry-rejection announcements are popped in `BotDriver.dispatch`** — the blind Casual bot's retry search would otherwise broadcast up to 25 churn announcements per turn. Only the bot's final committed move is announced. Human probes (1–3 pieces, human-paced) still broadcast — that is the feature. +- 2026-05-18: **Capture tally is a server-derived per-viewer `captures` field on `joined`/`update`**, not a `ModeratorText` enum entry — the announcement vocabulary stays a pure event enum; the tally is structured data (`CaptureTally = { byYou, byOpponent }`). Must be server-side: in blind mode the capturing client can't see what it took. +- 2026-05-18: **Phantom opponent-piece layer is 100% client-local** — never sent to the server, persisted only to `localStorage` (`bc:phantoms:`), in its own store (`phantoms.svelte.ts`) separate from the protocol store so the zero-leak property is auditable. Blind mode only. `buildView` / `geometric.ts` untouched. +- 2026-05-18: **Manual phantom model** — seeded once with the opponent's standard starting army, then fully manual: drag anywhere, drag off-board to remove, re-add from an unlimited palette, no automation. Rejected: a "smart tracker" that auto-removes on capture and tracks promotions (Seth chose the manual model). +- 2026-05-18: **Phantom manipulation is pointer-event drag-and-drop** with a tap-vs-drag threshold so a tap still makes a real move. Real chess moves stay click-to-move — the deferred drag-and-drop decision for *real* moves still stands; F3's drag is phantom-only. +- 2026-05-18: **Client has no unit-test harness** (deliberate) — Feature 3's testable pure logic (`opponentStartPosition`, `deserializePhantoms`) lives in `packages/shared` and is unit-tested there; Svelte components/stores are covered by `svelte-check` typechecking plus manual verification. + ## Deferred / Rejected @@ -99,3 +111,6 @@ Spec: `docs/superpowers/specs/2026-04-28-ai-player-design.md`. **Phase 1 (Casual - 2026-04-28: **Per-turn context compaction** — deferred. Spec uses `num_ctx: 32768` which covers ~128 turns; longer games would overflow but are rare in casual play. Add running-summary compaction if seen in practice. - 2026-04-28: **Bot rating / Elo / personalities** — out of scope. Two named buttons, no scoreboard. - 2026-04-28: **In-game chat (player ↔ player and human ↔ Gemma)** — deferred indefinitely. Two failure modes drove the deferral: (1) blind-mode chat is a side channel that bypasses the moderator-vocabulary security boundary ("knight on c3, take it" defeats the entire view-filter architecture); (2) chatting with Gemma during play leaks the bot's belief state and undermines the post-game reasoning reveal. Resolvable but expensive (two-history split for Gemma, blind-mode mute or social-variant warnings, mobile UI real estate). Revisit only if users explicitly ask. The post-game reasoning reveal already covers most of the "see what Gemma was thinking" appeal without the leak surface. +- 2026-05-18: **Smart-tracker phantom model** (auto-remove a phantom on capture, track promotions, constrain the phantom set to the opponent's surviving army) — rejected in favour of the fully-manual model. More code and more edge cases; Seth wanted the manual ritual. +- 2026-05-18: **Highlighting interacting with phantoms** (bishop/rook rays stopping at phantom pieces) — deferred. Safe to do (phantoms carry zero real opponent info) but out of scope for v1; phantoms are a pure annotation layer that highlighting ignores. +- 2026-05-18: **Phantom-layer `localStorage` cleanup for abandoned games** — deferred. `clearForGame` only fires when the game reaches `finished` while `` is mounted; a tab closed mid-game leaves a stale `bc:phantoms:` key. Each entry is a tiny JSON object; add a stale-key sweep on app start only if it ever matters. diff --git a/docs/superpowers/specs/2026-05-18-table-fidelity-features-design.md b/docs/superpowers/specs/2026-05-18-table-fidelity-features-design.md index bb71eae..6b8c72d 100644 --- a/docs/superpowers/specs/2026-05-18-table-fidelity-features-design.md +++ b/docs/superpowers/specs/2026-05-18-table-fidelity-features-design.md @@ -156,8 +156,12 @@ core) are untouched. - Unit (`driver`): after a decision cycle that incurs ≥1 retry, only the final move's announcement(s) remain in `game.announcements`; intermediate rejections are absent. -- Integration (`ai-game-casual`): the human side receives the bot's final move - announcement and no retry-rejection announcements. +- The `driver` unit test is the chosen coverage for the bot-suppression path: + it drives the real `BotDriver` → `handleCommit` → `announceWith` pipeline, so + it verifies suppression at the commit-path level. A separate `ai-game-casual` + WebSocket integration test was considered and dropped — it would only + additionally exercise the trivial `broadcastSinceLast` pass-through filter, + for materially more harness complexity. ---