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

128 lines
12 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 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
- **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)
- `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.
## 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 (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.