Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10 KiB
Handoff: Duplicate Chess v1 — built, tested, merged to main
Session Metadata
- Created: 2026-05-19 06:01:41
- Project: /home/claude/bin/duplicate_chess
- Branch: main (all work merged and pushed to
git.sethpc.xyz/Seth/duplicate_chess) - Session duration: one long session — brainstorm → spec → plan → full 14-task implementation → merge.
Recent Commits (for context)
5047ad7docs: update CLAUDE.md — v1 implemented and mergedfae9f8ddocs: correct Task 4 test move data in the plan5db0410fix: real project README and save-file version validationead4839feat(ui): assemble the duplicate chess sandbox appbedb5a0feat(ui): promotion dialog- Full implementation range:
9611c0a(scaffold + spec) →5047ad7. 18 commits.
Handoff Chain
- Continues from: None (first handoff — this is a brand-new project).
- Supersedes: None.
Current State Summary
duplicate_chess is a brand-new project created this session — a local browser
sandbox for "duplicate chess", a four-player chess variant invented by Andrew
Freiberg (Seth's father; also the inventor behind the sibling blind_chess
project). The session ran the full superpowers pipeline: brainstorming → design
spec → implementation plan → subagent-driven execution of all 14 plan tasks → final
review → merge. v1 is code-complete, all 27 engine tests pass, the build and
typecheck are clean, and it is merged to main and pushed. The one thing not
done: a human interactive browser test (clicking through a real game). The app
mounts and renders correctly (verified via a headless smoke render).
Codebase Understanding
Architecture Overview
Single Vite + Svelte 5 + TypeScript app, no server (duplicate chess is
perfect-information, so everything runs client-side — this is the key difference
from blind_chess, which needs a server as its trusted view boundary).
- Engine (
src/engine/, pure TypeScript, DOM-free, vitest-tested): fourchess.jsgames (NW/NE/SW/SE). A player's legal moves = the intersection of the moves legal on their two boards, keyed by(from,to,promotion). Ghost immobility, the synchronized-checkmate definition, and en-passant/castling divergence all fall out of the intersection — no special-case code. - UI (
src/lib/): a reactive store wraps the engine; the compass renders the four boards as a 45°-rotated pinwheel; the triple-highlight (green = playable on both boards, grey = legal on one only) is the teaching feature.
Critical Files
| File | Purpose | Relevance |
|---|---|---|
docs/superpowers/specs/2026-05-19-duplicate-chess-design.md |
The full design spec | Read first — variant rules, engine model, provisional rules |
docs/superpowers/plans/2026-05-19-duplicate-chess-sandbox.md |
The 14-task implementation plan | What was built, task by task |
src/engine/legality.ts |
legalSyncedMoves + selectionHighlight |
The intersection — the heart of the variant |
src/engine/game.ts |
DuplicateGame — 4 chess.js, history, undo, draw clocks |
The single source of truth for game state |
src/engine/ghosts.ts |
Ghost derivation by cross-board comparison | |
src/engine/endgame.ts |
checkmate/stalemate/threefold/fifty-move; PROVISIONAL rules | Andrew can revise the provisional rulings — grep PROVISIONAL |
src/lib/stores/game.svelte.ts |
Reactive store wrapping the engine | #game is plain (non-reactive); view is the $state snapshot |
src/lib/Compass.svelte |
The four-board pinwheel + click-to-move wiring |
Key Patterns Discovered
- The engine is DOM-free and the single source of truth. The UI never computes legality; it calls the engine and renders the result.
- Store reactivity:
chess.jsobjects must NOT be wrapped in a Svelte$stateproxy. The store keepsDuplicateGamein a plain private#gamefield and exposes a plain-dataviewsnapshot in$state, rebuilt after every change. - The pinwheel rotations (NW 225°, NE 135°, SW 315°, SE 45°) put each player's army on the board edge facing their seat. Confirmed against Andrew's sketch.
- Tests reach real positions via
playSymmetric(test-helpers.ts): when all four players move symmetrically the four boards stay identical, so each board is an ordinary chess game — that is how the checkmate/stalemate/threefold tests reach real terminal positions.
Work Completed
Tasks Finished
- All 14 tasks of the implementation plan, executed via subagent-driven development (fresh implementer subagent per task + a combined spec/quality review per task + a final whole-implementation review by an opus reviewer).
- Engine:
types, boards, game, legality, ghosts, endgame, notation+ an integration test. 27 vitest tests, all passing. - UI: the reactive store +
Board,Compass,Panel,PromotionDialog,Appcomponents. - Two post-review fixes: a real project README, and save-file
versionvalidation indeserialize. - Merged
build-sandbox→main, pushed, feature branch deleted.
Files Modified
The whole project was created this session. See git log on main. New trees:
src/engine/ (7 modules + 6 test files), src/lib/ (store + 4 components),
src/App.svelte, src/app.css, plus the Vite scaffold and project docs.
Decisions Made
All recorded in DECISIONS.md. Key ones: local sandbox first (not networked);
single Vite app, no server; engine = 4× chess.js + intersection; compass UI as a
pinwheel of diamonds; coordinate notation; provisional endgame rules picked by
Claude and marked PROVISIONAL. One decision surfaced during the build and is NOT
yet in DECISIONS.md — see "Blockers/Open Questions".
Pending Work
Immediate Next Steps
- Manual interactive browser test. Run
pnpm install && pnpm dev, open the URL, and play a real game: click a piece on a glowing board → confirm the green/grey triple-highlight → click a green square → move applies to both that player's boards → turn advances. Verify ghosts appear after a one-sided capture, promotion dialog fires, undo / Prev / Next / Live work, Save downloads JSON and Load restores it. The engine is well-tested; the UI interaction is verified only bysvelte-check+ a headless smoke render so far. - Decide the scrubbing semantics (see Open Questions) and reconcile spec §4.3.
- (Optional) The remaining minor follow-ups below, if they matter.
Blockers/Open Questions
- Scrub semantics — spec vs shipped code disagree. Spec §4.3 says "making a new move while scrubbed truncates history." The shipped code instead makes scrubbing view-only (you must click "● Live" before moving). The final review flagged this; the view-only behaviour is arguably cleaner. Seth to confirm which to keep; then update spec §4.3 (or the code) to match.
- The provisional endgame rules (spec §6) are Claude's defaults, not Andrew's rulings — double-board-mate = two winners, any stalemate ends the game all-draw, threefold/50-move tracked on the whole system. Andrew should confirm.
Deferred Items
deserializetrusts theplayerfield in a save file rather than recomputing it from turn order — a corrupt/hand-edited save could desync. App-written saves are always consistent, so this is robustness-only. Fix: haveDuplicateGame's constructor ignoreentry.playerand use the turn-order default.- Move log has no round-number column (within spec, but a nicety).
- Spec §4.3 names a
replayTo(n)primitive; the code usesnew DuplicateGame(history.slice(0,n))instead — functionally equivalent, cosmetic naming mismatch only. - Networking / AI / position editor — explicitly out of v1 scope (spec §7).
Context for Resuming Agent
Important Context
- The project is DONE for v1 and merged to
main. There is nothing half-built. A resuming agent's job is the manual browser test (#1 above) and then deciding whether to ship/deploy or extend. blind_chessis the sibling project (~/bin/blind_chess) — same inventor, same homelab conventions, similar shared-engine + view-filter shape. The original inventor conversation that defines duplicate chess is~/bin/blind_chess/USERFILES/4-person-chess.txt, and Andrew's compass sketch is~/bin/blind_chess/USERFILES/4personchess.png.- Provisional rules are isolated in
src/engine/endgame.tsand commentedPROVISIONAL (spec §6)— grep for it to find every spot a future ruling lands. - v1 is local only — no deploy. Hosting the static
pnpm buildoutput behind Caddy is a trivial later option (it is just static files), not done.
Assumptions Made
- The interactive browser test passing is assumed but unverified — the headless smoke render confirmed the app mounts and renders all four boards with no console errors, but no clicks were exercised.
- 27 is the expected test count (all in
src/engine/; the UI has no test harness by design —svelte-check+ manual, same asblind_chess).
Potential Gotchas
pnpm testuses--passWithNoTests(vitest 4.x exits 1 on no test files) — a deliberate scaffold choice; harmless now that tests exist.- A
.secrets.baselinefile exists for the globaldetect-secretspre-commit hook (it flagspnpm-lock.yamlSHA-512 integrity hashes as false positives). svelte-checkreports 5 warnings — all pre-existing Vite-templatetsconfigwarnings (deprecatedmoduleResolution, missingcomposite). 0 errors. The warnings are not defects; ignore them or fix the template tsconfig if desired.- The brainstorming visual-companion mockups for the compass live under
~/bin/blind_chess/.superpowers/brainstorm/.../content/(layout-v6.htmlis the approved layout) — they are in theblind_chessrepo, not this one.
Environment State
Tools/Services Used
- pnpm workspace tooling (Node 22, pnpm 10).
giteaCLI for push. - Subagent-driven development for the build (sonnet implementers/reviewers, an opus final reviewer).
Active Processes
- None. No dev server left running.
Environment Variables
- None added or required.
Related Resources
- Spec:
docs/superpowers/specs/2026-05-19-duplicate-chess-design.md - Plan:
docs/superpowers/plans/2026-05-19-duplicate-chess-sandbox.md DECISIONS.md,IDEA.md- Repo: https://git.sethpc.xyz/Seth/duplicate_chess (
mainat5047ad7) - Inventor conversation + sketch:
~/bin/blind_chess/USERFILES/4-person-chess.txt,~/bin/blind_chess/USERFILES/4personchess.png
Security Reminder: No credentials or secrets are included in this handoff.