Files
blind_chess/.claude/handoffs/2026-05-18-205736-table-fidelity-features.md
T
claude (blind_chess) 0c0e739bd3 docs: session handoff — table-fidelity batch code-complete
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:00:20 -04:00

122 lines
9.5 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, and committed to `main`**, but **not yet deployed**. 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 remaining steps are a manual browser pass and deployment to the two live instances.
## 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 verification** of the client UI (see Blockers — not done this session). Run `pnpm dev:server` + `pnpm dev:client`, 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.
2. **Deploy** both instances per `CLAUDE.md` → Operations: CT 690 (`chess.sethpc.xyz`) and `chess.local` on VDJ-RIG. Server restart drops in-memory games.
3. After deploy, smoke-test on a phone (the phantom drag is the main mobile-risk surface).
## 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.
## 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.