Files
blind_chess/.claude/handoffs/2026-05-18-205736-table-fidelity-features.md
T
claude (blind_chess) 077330054b docs: table-fidelity batch deployed to both instances
Deployed 2026-05-18 to CT 690 (chess.sethpc.xyz) and chess.local
(VDJ-RIG); both verified serving the new client build. CLAUDE.md and
the handoff updated to deployed state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:09:02 -04:00

10 KiB
Raw Blame History

Handoff: Table-fidelity feature batch — code-complete, pending deploy

Session Metadata

  • Created: 2026-05-18 20:57:36
  • Project: /home/claude/bin/blind_chess
  • Branch: main (all work committed and pushed to git.sethpc.xyz/Seth/blind_chess)
  • Session duration: ~one full session (brainstorm → spec → plan → 12-task execution)

Handoff Chain

Recent Commits (for context)

  • 2e80800 docs: record table-fidelity feature batch as code-complete
  • 59717b3 docs: amend plan to reflect code-review fixes
  • 82a69d8 fix(client): key phantom-load effect on gameId, gate the drag ghost
  • 313837e feat(client): wire the phantom opponent-model layer into the game view
  • 816f89b feat(client): phantom-piece palette component
  • Full implementation range: be8ecd9..2e80800 (20 commits).

Current State Summary

The "table-fidelity feature batch" (three features Andrew Freiberg — Seth's dad, a physical-blind-chess player — requested by email) is fully implemented, reviewed, committed to main, and deployed (2026-05-18) to both live instances — chess.sethpc.xyz (CT 690) and chess.local (VDJ-RIG), both verified serving the new build (index-DNriqpNJ.js). All 12 plan tasks were executed via subagent-driven development with two-stage review per task plus a final whole-batch review. Build, typecheck, and the 87-test suite all pass. The one thing not done: a manual browser pass — the client UI was never click-tested (an agent can't drive a browser).

Architecture Overview

Three features, two increments, all shipped:

  1. Announce-all (F1): every moderator Announcement is now audience: 'both'. Previously move events went only to the opponent and attempted-move errors only to the actor. The audience field is retained (uniformly 'both') as the egress-control hook in ws.ts/ModeratorPanel. The Casual bot's intermediate retry-rejection announcements are popped in BotDriver.dispatch so its blind-mode search does not broadcast as churn — only its final move is announced.
  2. Capture tally (F2): a server-derived per-viewer captures: CaptureTally field on the joined/update messages, rendered by a new CaptureTally.svelte panel. Must be server-side — in blind mode the capturing client cannot see what it took.
  3. Phantom layer (F3): a client-LOCAL overlay of guessed opponent pieces, blind mode only. Seeded once with the opponent's standard army, then fully manual: pointer-drag a phantom anywhere, off-board to remove, re-add from an unlimited palette. Persisted to localStorage (bc:phantoms:<gameId>). Never sent to the server — it lives in its own store so the zero-leak property is auditable.

The zero-leak core (buildView, geometric.ts) was deliberately untouched.

Critical Files

File Purpose Relevance
docs/superpowers/specs/2026-05-18-table-fidelity-features-design.md The spec Design rationale, info-leak analysis
docs/superpowers/plans/2026-05-18-table-fidelity-features.md The 12-task plan Amended to match shipped code
packages/server/src/translator.ts, commit.ts F1 audience change All announcements 'both'
packages/server/src/bot/driver.ts F1 bot-churn suppression dispatch pops intermediate rejections
packages/server/src/captures.ts F2 captureTally Pure per-viewer derivation
packages/shared/src/phantoms.ts F3 pure model opponentStartPosition, deserializePhantoms (tested)
packages/client/src/lib/stores/phantoms.svelte.ts F3 local store Never read in any send path
packages/client/src/lib/stores/phantom-drag.svelte.ts F3 drag controller Pointer events, tap-vs-drag, pointercancel-safe
packages/client/src/lib/Board.svelte, Game.svelte, PhantomPalette.svelte F3 UI Phantom rendering + wiring

Key Patterns Discovered

  • Build ordering: server/client resolve @blind-chess/shared from its built dist/. After editing shared, run pnpm --filter @blind-chess/shared build before downstream typecheck/build. pnpm -r build handles order automatically.
  • Client has no test harness (by design). Pure logic worth testing goes to packages/shared (vitest); Svelte components are covered by svelte-check + manual.
  • ply-parity actor derivation: the four attempted-move enums carry no colour; the client derives White/Black from ply % 2 (an attempt only happens on the actor's turn).

Work Completed

  • Tasks 111 of the plan (all three features), each with implementer + spec-compliance review + code-quality review and fix loops.
  • Final whole-batch code review — verdict: ready to ship, no Critical/Important issues.
  • Checkpoint A and B verifications: pnpm -r build && pnpm -r typecheck && pnpm -r test all clean; 87 tests pass (25 shared + 62 server).
  • DECISIONS.md, CLAUDE.md, and the spec updated to reflect the shipped state.

Files Modified

See git diff --stat be8ecd9..2e80800. New files: packages/server/src/captures.ts, packages/server/test/unit/captures.test.ts, packages/shared/src/phantoms.ts, packages/shared/test/phantoms.test.ts, packages/client/src/lib/{CaptureTally,PhantomPalette}.svelte, packages/client/src/lib/stores/{phantoms,phantom-drag}.svelte.ts. Modified: translator.ts, commit.ts, bot/driver.ts, ws.ts, shared/{types,protocol,index}.ts, client/lib/{Board,Game,ModeratorPanel}.svelte, client/lib/stores/game.svelte.ts.

Decisions Made

All recorded in DECISIONS.md under "Table-fidelity features (2026-05-18)" and "Deferred / Rejected". Key ones: announcements widened to 'both' (deliberate, authorised); manual phantom model (smart-tracker rejected); phantom layer client-local only; drag-and-drop for phantoms only (real moves stay click-to-move).

Immediate Next Steps

  1. Manual browser/phone verification of the now-live client UI — not done this session (an agent can't drive a browser). Open https://chess.sethpc.xyz, create a blind game vs computer, and check: the moderator panel shows White/Black-labelled attempt lines; the Captures panel updates on a capture; the phantom layer renders 16 seeded pieces; dragging a phantom moves it / off-board removes it; the palette places phantoms; a tap still makes real moves; phantoms persist across reload; vanilla mode shows no phantom UI; phantoms hide on game end. The phantom drag is the main mobile-risk surface — test on a phone.
  2. (Optional) Fix the install-local.sh redeploy gap — see Potential Gotchas.

Blockers / Open Questions

  • Client UI not manually verified in a browser. The implementation passed build, svelte-check, the 87-test automated suite, and multi-stage code review — but an AI agent cannot run an interactive browser. The phantom drag-and-drop interaction in particular (pointer events, tap-vs-drag threshold, hit-testing) is verified only by review, not by clicking. Recommend Seth (or a browser-capable session) does the manual pass before/after deploy.

Deferred Items

  • Phantom-layer localStorage cleanup for games abandoned mid-play (no finished transition) — tiny leak, add a stale-key sweep only if it matters.
  • Highlighting interacting with phantoms (rays stopping at phantom pieces) — safe but out of v1 scope.
  • An ai-game-casual WebSocket integration test for F1 — the driver.test.ts unit test was chosen instead (covers the same commit-path; spec updated to record this).

Important Context

  • Everything is on main and pushed. Seth explicitly chose to work directly on main (no feature branch). There is nothing to merge.
  • The work is code-complete but undeployed. chess.sethpc.xyz is still running the pre-batch code until someone deploys. Deployment is an outward-facing action — confirm with Seth before doing it.
  • Two deploy instances now exist (CLAUDE.md was updated mid-project): CT 690 / chess.sethpc.xyz, and chess.local on VDJ-RIG. Both need the update.
  • The final review confirmed the security invariant holds: no phantom token anywhere under packages/server/src/, buildView/geometric.ts byte-for-byte unchanged.

Assumptions Made

  • The client a11y trade-off (phantom spans and palette pieces are pointer-only, with a documented svelte-ignore a11y_no_static_element_interactions) is acceptable — adding tabindex without a keyboard drag would be worse a11y. Real gameplay stays fully keyboard-operable via the square buttons.
  • 87 is the expected test count (25 shared + 62 server); the client contributes 0 (no harness).

Potential Gotchas

  • packages/server/tsconfig.tsbuildinfo shows persistent M in git status — pre-existing drift (tracked before *.tsbuildinfo was gitignored), not this session's work.
  • The pre-commit hook is detect-secrets-hook --baseline .secrets.baseline.
  • Server restart on deploy drops all in-memory games. (CT 690 had 3 "active" games at deploy time — almost certainly stale abandoned games, since active games never auto-expire and uptime was 19 days; dropped per the pre-accepted MVP policy.)
  • deploy/install-local.sh (the chess.local installer) ends with systemctl enable --now blind-chess.service, which does NOT restart an already-running service — a redeploy via the script alone leaves the old code running. This deploy worked around it with an explicit sudo systemctl restart blind-chess after the script. Proper fix: change the script's enable --now to enable then restart.

Environment State

  • Tools/Services: pnpm workspace; gitea CLI for push. Subagent-driven development for execution.
  • Active Processes: none. No dev servers left running.
  • Environment Variables: none added or changed.

Security Reminder: No credentials or secrets are included in this handoff.