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

122 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
- **Continues from**: [2026-04-29-060121-blind-casual-check-fix.md](./2026-04-29-060121-blind-casual-check-fix.md) — the prior session fixed blind Casual's premature resignation.
- **Supersedes**: None.
## 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.
## Related Resources
- 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 · Repo: https://git.sethpc.xyz/Seth/blind_chess (`main` at `2e80800`)
---
**Security Reminder**: No credentials or secrets are included in this handoff.