Files
blind_chess/.claude/handoffs/2026-05-18-205736-table-fidelity-features.md
T
claude (blind_chess) 5d995eb428 docs: update handoff — deployed, test pass underway, contrast fix pending deploy
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:33:46 -04:00

12 KiB
Raw Blame History

Handoff: Table-fidelity batch — deployed; manual test pass underway

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)

  • d10e581 fix(client): light outline on dark phantom glyphs for panel contrast — NOT yet deployed
  • 0773300 docs: table-fidelity batch deployed to both instances
  • 0c0e739 docs: session handoff
  • 2e80800 docs: record table-fidelity feature batch as code-complete
  • 59717b3 docs: amend plan to reflect code-review fixes
  • Feature 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). 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.

A manual browser test pass is now underway (Seth). It has surfaced one bug so far: opponent (black) phantom pieces had too little contrast on the dark --panel background. Fixed in d10e581 — black piece glyphs in the palette, the Captures panel, and the drag-ghost now get a light text-shadow outline. That fix is committed to main but NOT yet deployed — the two live instances are at 0773300, one commit behind main.

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. Decide and do the redeploy of d10e581 (the contrast fix). It is on main but not live. Seth was asked whether to deploy it now or batch it with further test-pass findings — undecided (he requested this handoff instead). Each redeploy drops in-memory games. Deploy steps: CLAUDE.md → Operations (both instances; for chess.local remember the explicit systemctl restart — see Potential Gotchas).
  2. Continue the manual browser/phone test pass. 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.
  3. Check the board phantom glyph contrast. .phantom-b (dark phantoms on the board) render at opacity: 0.4 on the board squares; they have a dashed frame so the square reads, but the piece type may be hard to tell on dark squares. Flagged to Seth to eyeball during the test pass — if too faint, give .phantom-b a subtle light outline without killing the intentional translucency. (Same root cause as the d10e581 fix, left alone for now because board phantoms are deliberately ghostly.)
  4. (Optional) Fix the install-local.sh redeploy gap — see Potential Gotchas.

Blockers / Open Questions

  • Deploy timing for d10e581 (contrast fix) — undecided. Seth was asked "deploy now, or batch with other test-pass findings?" and requested a handoff instead. Resolve next session. The live site lacks the contrast fix until then.
  • Board phantom glyph contrast — open question. See Immediate Next Steps #3 — needs a human eye on a real board before deciding whether .phantom-b needs an outline.
  • Manual browser test pass is in progress, not complete. Only the palette-contrast issue has been found and fixed so far; the rest of the checklist in Next Step #2 is unverified. The phantom drag-and-drop in particular (pointer events, tap-vs-drag, hit-testing) is verified only by code review, not by clicking.

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 feature batch is deployed and live on both instances (chess.sethpc.xyz and chess.local) at commit 0773300. The only un-deployed change is d10e581 (the contrast fix). Deployment is outward-facing and drops in-memory games — confirm timing with Seth.
  • 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.
  • Spec: docs/superpowers/specs/2026-05-18-table-fidelity-features-design.md
  • Plan: docs/superpowers/plans/2026-05-18-table-fidelity-features.md
  • DECISIONS.md → "Table-fidelity features (2026-05-18)"
  • Live URL: https://chess.sethpc.xyz (deployed at 0773300) · Repo: https://git.sethpc.xyz/Seth/blind_chess (main at d10e581)

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