Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7b48abf
docs: add discovery notes
radha-soni Jun 9, 2026
f683298
docs: add speckit constitution
radha-soni Jun 9, 2026
7cbab53
docs: specify scenario 1 lobby
radha-soni Jun 9, 2026
669d2e3
docs: plan scenario 1 implementation
radha-soni Jun 9, 2026
a79f704
docs: tasks for scenario 1
radha-soni Jun 9, 2026
f144eb0
feat(rooms): track host and expose in lobby
radha-soni Jun 9, 2026
2430291
feat(join): validate room code and show errors
radha-soni Jun 9, 2026
5da6f61
feat(lobby): poll room snapshot
radha-soni Jun 9, 2026
6b71006
feat(game): host-only start with minimum players
radha-soni Jun 9, 2026
47be796
fix(lobby): clarify start prerequisites
radha-soni Jun 9, 2026
5c5d1c5
docs: scenario 2 artifacts
radha-soni Jun 9, 2026
9dadad1
feat(players): validate and trim player names
radha-soni Jun 9, 2026
3ecb735
feat(game): assign drawer and reveal secret word to drawer only
radha-soni Jun 9, 2026
1b2d93c
docs: specify scenario 3 gameplay
radha-soni Jun 9, 2026
a0b5da1
docs: plan scenario 3
radha-soni Jun 9, 2026
902a5e4
docs: tasks scenario 3
radha-soni Jun 9, 2026
7097763
feat(canvas): implement draw and clear
radha-soni Jun 9, 2026
c131f5c
feat(guesses): submit and validate guesses
radha-soni Jun 9, 2026
c920186
feat(sync): poll and display guess history
radha-soni Jun 9, 2026
da1a7f3
feat(scoring): award points for correct guesses
radha-soni Jun 9, 2026
61d3d45
docs: specify scenario 4 results and restart
radha-soni Jun 9, 2026
151d68c
docs: plan scenario 4
radha-soni Jun 9, 2026
b7c7f94
docs: tasks scenario 4
radha-soni Jun 9, 2026
235d11f
feat(results): show round results to all players
radha-soni Jun 9, 2026
88cd209
feat(restart): host restart clears round state and returns to lobby
radha-soni Jun 9, 2026
a2140d9
docs: add reflection report
radha-soni Jun 9, 2026
aeecc60
docs: add Spec Kit artifact layout for pre-evaluation checks
radha-soni Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Spec Kit Constitution — Scribble Lab

## Purpose

This constitution constrains how we specify, plan, implement, and validate changes in this repo so the delivered behavior matches the README business scenarios and remains easy to review.

## Non-negotiable boundaries (must match README)

- **No WebSockets / real-time push**: all synchronization is HTTP + polling only.
- **No database / persistence**: backend state is in-memory only; restarts wipe rooms.
- **No authentication/accounts/sessions**.
- **No scope creep**: do not add multi-rounds, timers, bonuses, spectator mode, moderation, passwords/invites, custom word packs, new routing/state libraries, or unrelated refactors.
- **No “rewrite the starter”**: enhance incrementally; prefer minimal, local changes.

## Engineering principles

- **TypeScript-first**: avoid `any`; use `unknown` and narrow when needed.
- **Deterministic game rules**: outcomes must be repeatable across runs and tabs (no random word selection or random scoring behavior).
- **Backwards-compatible increments**: each commit should keep the app runnable.
- **Keep state minimal**: room state should store only what is required for the scenarios; remove/clear round state on restart.
- **Fail fast with clear errors**:
- Backend returns consistent JSON errors with useful messages.
- Frontend shows clear, user-facing validation feedback (not stack traces).

## AI usage rules

- **AI is allowed for**: exploration help, drafting specs/plans, proposing code changes, writing tests, and suggesting refactors that are directly justified by scenarios.
- **AI is not allowed to decide** ambiguous product rules silently:
- If a rule is unclear, record an assumption explicitly in `/speckit.specify` (and/or discovery notes) before implementing.
- **No blind copy/paste**: every AI-proposed change must be read, understood, and matched against acceptance criteria before committing.

## Spec → Plan → Tasks → Code traceability

- Every scenario must have:
- **Specification**: acceptance criteria + edge cases.
- **Plan**: state model + endpoints + file-level change list.
- **Tasks**: ordered, testable slices with dependencies.
- Implementation must remain consistent with artifacts; if implementation deviates, update the spec/plan/tasks in the same feature group.
- Complete **at least 4 specify iterations** (one per scenario group minimum).

## Validation expectations (manual)

All scenarios must be validated with **two browser tabs** (or two browsers) to simulate multiplayer.

- **Polling**: observable refresh roughly every ~2 seconds where required (Lobby and Game sync).
- **Input validation**:
- Names and guesses are **trimmed**; empty/whitespace-only is rejected with a clear message.
- Guess matching is **case-insensitive**.
- Room code invalid/empty is rejected with clear feedback.
- **Isolation**: actions in one room must not affect another room.
- **Determinism**: word selection and scoring behave predictably and repeatably.

## Commit discipline

- Keep commits **granular and meaningful**:
- Prefer one behavioral slice per commit (backend + frontend together if needed for a slice).
- Include artifact updates in their own commits (docs-only) when possible.
- Before committing:
- Verify the slice manually against its acceptance criteria.
- Ensure no unrelated files (like lockfiles) are included unless intentionally changed.

## Build verification (required before final handoff)

- `cd backend && npm run build`
- `cd frontend && npm run build`



## Discovery summary

## What the starter already provides

- Frontend routes/screens for Start, Create Room, Join Room, Lobby, and Game.
- Minimal REST backend with in-memory room store:
- `GET /health`
- `POST /rooms`
- `POST /rooms/:code/join`
- `GET /rooms/:code`
- Room snapshot includes participant list plus starter seed lists:
- words: `rocket`, `pizza`, `castle`, `guitar`, `sunflower`
- roles: `drawer`, `guesser`

## Key incomplete behaviors observed (gaps vs README scenarios)

- **No host concept / permissions**
- Room creation does not record a host participant id.
- Lobby “Start Game” is just a navigation button; there is no backend “start game” action, no host-only gating, and no 2-player minimum enforcement.

- **No automatic polling**
- Lobby state updates only via a manual “Refresh Room” button; no ~2s polling loop.
- There is no game-state polling at all (guesses, results, etc.).

- **Gameplay state model is missing**
- `RoomStatus` is only `"lobby"`; there is no `"playing"` or `"results"` state.
- No drawer assignment, no secret word selection/storage, and no “drawer-only visibility”.
- No guess submission endpoint/state, no guess history, no scoring, no result state, no restart flow.

- **Validation is permissive / not aligned to scenarios**
- `playerName` is optional on create/join; backend coerces missing to `"Player"` and does not trim or reject whitespace-only names.
- `roomCode` param schema is `z.string()` (no length/format validation), so “invalid/empty codes rejected with clear feedback” is not implemented.

- **Viewer-specific snapshot logic is not implemented**
- Backend `toRoomSnapshot(room, viewerParticipantId)` currently ignores `viewerParticipantId`.
- This will matter for “secret word visible only to drawer”.

- **Potential API base URL bug in frontend**
- `frontend/src/services/api.ts` defaults `VITE_API_URL` to `http://localhost:3001/bug` (appending `/bug`).
- Without a proxy, this would call `/bug/rooms` which the backend does not serve. The README “Quick Verification” implies the starter works, so this may rely on an environment override or be an intentional scaffold flaw.

## Assumptions (to resolve ambiguity while staying deterministic)

- **A1 — Host definition**
- The creator of the room is the host.
- Store `hostParticipantId` on the room and expose it in snapshots so the frontend can gate host-only actions.

- **A2 — Polling strategy**
- Implement client polling with `setInterval` (about every 2000ms) in screens that must stay fresh (Lobby and Game).
- Polling will call `GET /rooms/:code?participantId=...` and replace the room snapshot in the store.

- **A3 — Deterministic word selection**
- Select the secret word deterministically from the starter word list using a stable input (e.g., room code) so it is repeatable and testable without randomness.

- **A4 — Drawer assignment**
- Drawer for the single-round game is the host (or, if host is missing for any reason, the first participant in the room).

## Relevant files inspected (likely to be touched later)

### Backend
- Routes and validation:
- `backend/src/api/rooms.ts`
- `backend/src/api/schemas.ts`
- `backend/src/api/router.ts`
- In-memory room store + snapshot:
- `backend/src/services/roomStore.ts`
- Types + state model:
- `backend/src/models/game.ts`
- Seed data:
- `backend/src/seed/starterData.ts`

### Frontend
- Room client + types:
- `frontend/src/services/api.ts`
- `frontend/src/state/roomStore.ts`
- Screens:
- `frontend/src/pages/CreateRoomPage.tsx`
- `frontend/src/pages/JoinRoomPage.tsx`
- `frontend/src/pages/LobbyPage.tsx`
- `frontend/src/pages/GamePage.tsx`

## Notes / risks to track

- Ensure all “sync” remains HTTP polling (explicitly no websockets).
- Backend is in-memory only; restarting backend wipes all rooms (expected).
- If the API base URL default really is wrong, we will need to fix it early (otherwise later work can’t be verified in two tabs).
79 changes: 79 additions & 0 deletions REFLECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Reflection Report — Scribble Lab

## What the starter app already had

The starter was a runnable scaffold with:

- React routes for Start, Create Room, Join Room, Lobby, and Game
- A minimal Express API: `POST /rooms`, `POST /rooms/:code/join`, `GET /rooms/:code`, `GET /health`
- In-memory room storage with participant lists
- Placeholder game UI (canvas, guess form, scoreboard, results) without real gameplay logic
- Manual lobby refresh only (no polling)
- No host permissions, drawer assignment, secret word rules, scoring, or restart flow

## What I added

Work followed the Spec Kit loop: discovery → constitution → specify/plan/tasks per scenario → incremental implementation → validation.

### Artifacts

- `speckit.discovery.md` — gaps, assumptions, relevant files
- `speckit.constitution` — scope boundaries, deterministic rules, AI/review discipline
- `speckit.specify`, `speckit.plan`, `speckit.tasks` — four iterations aligned to Scenarios 1–4

### Scenario 1 — Room setup & lobby

- Host tracking on room creation
- Join validation with clear 400/404 errors
- Lobby polling (~2s)
- Host-only start with 2-player minimum
- Commits: `feat(rooms)`, `feat(join)`, `feat(lobby)`, `feat(game)`

### Scenario 2 — Game start & drawer flow

- Player name trim + non-empty validation
- Deterministic drawer assignment (host) and secret word selection (from room code)
- Drawer-only word visibility during `playing`
- Commits: `feat(players)`, `feat(game)`

### Scenario 3 — Gameplay interaction

- Drawer-only canvas draw/clear with synced strokes via polling
- Guess submission with trim, case-insensitive match, shared history, and scoring (+100 first correct, +0 incorrect)
- Commits: `feat(canvas)`, `feat(guesses)`, `feat(sync)`, `feat(scoring)`

### Scenario 4 — Result & restart

- Transition to `results` on first correct guess; word/scores/history visible to all
- Host-only restart clears round state and returns everyone to lobby via polling
- Commits: `feat(results)`, `feat(restart)`

## Decisions and tradeoffs

- **Polling over push**: kept HTTP polling (~2s) everywhere to match lab constraints; simple and sufficient for two-tab validation, but not real-time.
- **Deterministic word pick**: used room-code checksum modulo starter word list for repeatable behavior and easier manual testing.
- **First correct guess ends round**: single-round flow; avoids timers/multi-round complexity called out as out of scope.
- **Score once per player**: first correct guess awards 100; later correct guesses do not stack, keeping scoring predictable.
- **Minimal canvas model**: stored stroke paths rather than pixel buffers; easier to sync over REST but not optimized for high-frequency drawing.
- **Incremental commits**: each scenario split into small backend/frontend slices so changes stay traceable to spec acceptance criteria.

## AI usage

AI assisted with:

- Drafting and refining Spec Kit artifacts from README scenarios
- Exploring the starter codebase and proposing file-level plans
- Implementing slices incrementally with typed backend/frontend changes
- Smoke-testing API flows and build checks between commits

I reviewed AI output against `speckit.specify` acceptance criteria before committing, especially for permission rules (host/drawer/guesser), validation messages, and snapshot visibility (drawer-only word during play, all players in results).

## Traceability

Each implementation commit maps to a scenario slice in `speckit.tasks` and acceptance criteria in `speckit.specify`. Out-of-scope items from the README (WebSockets, databases, auth, multi-round rotation) were intentionally not implemented.

## Manual validation performed

- Two-tab flows for lobby sync, game start, drawing, guessing, results, and restart
- Backend and frontend `npm run build` after major slices
- API smoke checks for validation codes, role gating, and scoring/restart behavior
Loading
Loading