077330054b
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>
122 lines
10 KiB
Markdown
122 lines
10 KiB
Markdown
# 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 1–11 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.
|