- Add Better Auth config with Google + GitHub social providers
- Mount auth handler on /api/auth/* in Express
- Generate and migrate auth tables (user, session, account, verification)
- Deduplicate term_glosses data for tighter unique constraint
- Drop legacy users table
- Unit tests for createGameSession and evaluateAnswer (14 tests)
- Endpoint tests for POST /game/start and /game/answer via supertest (8 tests)
- Mock @glossa/db — no real database dependency
- Add Vite proxy for /api → localhost:3000 (no CORS needed in dev)
- Create /play route with hardcoded game settings (en→it, nouns, easy)
- Three-phase state machine: loading → playing → finished
- Show prompt, optional gloss, and 4 answer buttons per question
- Submit answers to /api/v1/game/answer, show correct/wrong feedback
- Manual Next button to advance after answering
- Score screen on completion
- Add selectedOptionId to AnswerResult schema (discovered during
frontend work that the result needs to be self-contained for
rendering feedback without separate client state)
Intentionally unstyled — component extraction and polish come next.
Complete the game answer flow:
- Add evaluateAnswer service function: looks up the session in the
GameSessionStore, compares the submitted optionId against the stored
correct answer, returns an AnswerResult.
- Add submitAnswer controller with safeParse validation and error
handling (session/question not found → 404).
- Add POST /api/v1/game/answer route.
- Fix createGameSession: was missing the answerKey tracking and the
gameSessionStore.create() call, so sessions were never persisted.
The full singleplayer game loop now works end-to-end:
POST /game/start → GameSession, POST /game/answer → AnswerResult.
The service now tracks the correct optionId for each question and
stores the answer key in the GameSessionStore after building the
session. The client response is unchanged — the store is invisible
to the outside.
- Build answerKey (questionId → correctOptionId) during question
assembly by finding the correct answer's position after shuffle
- Store the answer key via gameSessionStore.create() before returning
- Add excludeText parameter to getDistractors to prevent a distractor
from having identical text to the correct answer (different term,
same translation). Solved at the query level, not with post-filtering.
- Module-level InMemoryGameSessionStore singleton in the service
Add the session storage infrastructure for tracking correct answers
during a game. Designed for easy swap to Valkey in Phase 4.
- GameSessionStore interface with create/get/delete methods, all async
to match the eventual Valkey implementation
- InMemoryGameSessionStore backed by a Map
- GameSessionData holds only the answer key (questionId → correctOptionId)
- Also fix root build script to build packages in dependency order
Extend the game flow from raw term rows to a complete GameSession
matching the shared schema:
- Add getDistractors model query: fetches N same-POS, same-difficulty,
same-target-language terms excluding a given termId. Returns bare
strings since distractors only need their display text.
- Fix getGameTerms select clause to use the neutral field names
(sourceText, targetText, sourceGloss) that the type already declared.
- Rename gameService entry point to createGameSession; signature now
takes a GameRequest and returns a GameSession.
- Per question: fetch 3 distractors, combine with the correct answer,
shuffle (Fisher-Yates), assign optionIds 0-3 by post-shuffle index,
and assemble into a GameQuestion with a fresh UUID.
- Wrap the questions in a GameSession with its own UUID.
- Run per-question distractor fetches in parallel via Promise.all.
Known gap: the correct option is not yet remembered server-side, so
/game/answer cannot evaluate submissions. SessionStore lands next.
- Add GameRequestSchema and derived types to packages/shared
- Add SupportedLanguageCode, SupportedPos, DifficultyLevel type exports
- Add getGameTerms() model to packages/db with pos/language/difficulty/limit filters
- Add prepareGameQuestions() service skeleton in apps/api
- Add createGame controller with Zod safeParse validation
- Wire POST /api/v1/game/start route
- Add scripts/gametest/test-game.ts for manual end-to-end testing
- Configure PostgreSQL 18 and Valkey 9.1 services
- Create multi-stage Dockerfiles for API and Web apps
- Set up pnpm workspace support in container builds
- Configure hot reload via volume mounts for both services
- Add healthchecks for service orchestration
- Support dev/production stage targets (tsx watch vs compiled)