Skip to content

Scribble lab: Spec Kit artifacts and full game implementation - #151

Open
manojm-ship-it wants to merge 40 commits into
everest-engineering:mainfrom
manojm-ship-it:main
Open

manojm-ship-it wants to merge 40 commits into
everest-engineering:mainfrom
manojm-ship-it:main

Conversation

@manojm-ship-it

Copy link
Copy Markdown

Summary

Brownfield enhancement of the Scribble starter using GitHub Spec Kit. All four business scenarios are specified, planned, implemented and validated.

Artifacts

  • docs/discovery.md: 10 gaps, 6 assumptions, relevant files.
  • .specify/memory/constitution.md: v1.0.0, six principles plus constraints and workflow.
  • specs/001-room-lobby, specs/002-game-start-drawer, specs/003-gameplay, specs/004-result-restart. Each has a spec with clarifications, plan, research, data model, API contract, quickstart, and tasks with validation results.
  • REFLECTION.md: what the starter had, what was added, AI usage, tradeoffs, deviations.
  • specs/NNN/analysis.md: /speckit-analyze cross-artifact reports for each feature (100% requirement-to-task coverage; findings recorded).

Implementation (by scenario)

  1. Room & lobby:
    • Fixed the default API URL; the creator is host.
    • Room codes are validated with clear messages, and late joins are refused.
    • The lobby polls every 2 s; only the host can start, with 2+ players.
    • Rooms idle for 30 min are removed.
  2. Start & drawer:
    • Names are trimmed, required, and at most 20 characters.
    • The host is the drawer, and the secret word is deterministic (room-code character sum + games started, mod 5).
    • The word is sent only to the drawer, and the candidate word list is no longer sent.
  3. Gameplay:
    • The drawer has a canvas with clear.
    • Guesses are trimmed and compared case-insensitively, empty ones are refused, and scoring is 100/0.
    • History and scores sync every 2 s, and correct answers stay hidden until the round ends.
    • The round ends when every guesser is correct.
  4. Result & restart:
    • Everyone sees the word, final scores and full history.
    • The host can "Play again", or end a round early.
    • Restart returns everyone to the lobby with players kept and round state cleared.
    • The bouncing "Exit Game" button is removed.

Validation

  • npm run build and npm test pass in both apps (backend 68 tests, frontend 7; the starter had 4 and 2).
  • Every rule was checked against a live backend with curl, and every scenario in 2–4 browser tabs. Results are recorded in each tasks.md.
  • Final end-to-end run from a fresh backend with two rooms at once: create → join → play → shared result → restart → play again.
  • The pre-evaluation check passes locally: 4/4 feature folders, constitution, reflection, no forbidden technology.

All scope exclusions were respected: polling only (no push protocol), in-memory only, no authentication, single round per game, no timers.

Contributor

🤖 Generated with Claude Code

admin and others added 30 commits October 12, 2026 14:59
Add Spec Kit scaffolding (.specify/ templates and scripts, .claude/skills
speckit-* commands) and ignore the local temp/ working-notes folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Constitution covers brownfield discipline, server-authoritative game
rules, scope boundaries, coding standards, verification gates and AI
usage rules. Discovery notes list 10 gaps, 6 assumptions and the
relevant starter files, mapped to the four feature groups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec covers Scenario 1 (host, code validation, isolation, 2 s polling,
host-only start with 2+ players, idle cleanup) with 3 clarifications.
Plan adds research R1-R8, data model, rooms API contract and quickstart;
tasks.md breaks the work into T001-T029 by user story.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The default API base URL ended in /bug, so every request 404'd unless
VITE_API_URL was set (discovery gap 1, FR-001). Adds a test pinning the
default URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…able errors

- Room gains hostId and lastActiveAt; status widens to lobby | playing;
  snapshots expose hostId (T003, plus T008's createRoom/snapshot change,
  which the new required field needs in the same commit to compile).
- errorHandler returns the Zod issue message (T004, FR-015).
- Room codes are trimmed, checked as 4 letters/digits and upper-cased in
  the params schema, replacing per-route toUpperCase (T005, FR-005/006).
- Schema tests for valid, blank and malformed codes (T006).
- Frontend ApiError carries the HTTP status; snapshot types updated (T007).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tests that the creator is host, snapshots expose hostId and 50 rooms
get distinct codes. The lobby labels the host and the viewer's own
entry "(you)" (FR-002, FR-003, FR-004).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
joinRoom throws 404 "Room not found. Check the code and try again." or
409 "This game has already started." and refreshes lastActiveAt; the
route no longer maps null itself. The join form refuses an empty code
before calling the API (FR-006, clarification: no late joins).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getRoom refreshes lastActiveAt, and a missing room now reports "This
room is no longer available." The lobby polls every 2000 ms while
mounted; a 404 clears the room via RoomStore.clearRoom and the start
page shows the notice; other errors keep the last snapshot (FR-007,
FR-008). Uses a dedicated notice field rather than error - recorded as
a deviation in plan.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
startGame checks room exists (404), requester is host (403), room still
in lobby (409) and at least 2 players (400) before moving it to
playing; exposed as POST /rooms/:code/start with a Zod-validated body.
The lobby shows Start only to the host (disabled under 2 players),
tells guests to wait, and navigates everyone to /game once the polled
snapshot is playing (FR-009 to FR-012).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
removeInactiveRooms(now) deletes rooms whose lastActiveAt is older than
ROOM_IDLE_LIMIT_MS (30 min); server.ts sweeps every 60 s on an unref'd
timer. Tests cover removal, retention within the limit, and that
joining/starting one room leaves another unchanged (FR-013, FR-014).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ipant id

Found during T027 API validation: a missing participantId returned Zod's
default "Required", which breaks FR-015. Adds a required_error and a
schema test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found during T028 two-browser validation (row 9): with the backend down
the lobby showed the browser's raw "Failed to fetch". request() now
turns network failures into ApiError(0, "Can't reach the game server.
Check your connection and try again.") (FR-008), with a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All quickstart API and two-browser rows pass after two fixes found
during validation. Records results in tasks.md, deviations in plan.md,
and renames the isolation test so it is findable by name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er flow

Spec covers Scenario 2 (trimmed, required names; host becomes drawer;
deterministic word visible only to the drawer) with 3 clarifications:
word rule (code char sum + games started) mod 5, hide the candidate word
list, 20-character name limit. Plan adds research R1-R8, data model,
contract changes and quickstart; tasks T001-T018.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Room gains drawerId, secretWord (both null in the lobby) and
gamesStarted (0 at creation); snapshots expose drawerId and an optional
secretWord. Frontend snapshot type updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A shared playerNameSchema (create and join) trims names and refuses
missing/blank names with "Enter your name." and names over 20
characters with "Names can be at most 20 characters.". The "Player"
fallback is removed. Feature 001's quickstart join checks now send a
name, since body validation runs before the room lookup (FR-001-003a).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
selectSecretWord(code, gamesStarted) picks
STARTER_WORDS[(sum of code char codes + gamesStarted) % 5]. startGame
records the host as drawer, sets the word with the current count and
increments it. Tests pin ABCD/0 -> pizza, ABCD/1 -> castle,
AAAA/0 -> rocket, repeatability, and lobby rooms having neither
(FR-004, FR-006, FR-008).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game page's Player Info card replaces the static "Playing" status
with "<name> is drawing" and "You are the drawer" / "You are
guessing" (FR-005).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
toRoomSnapshot now uses its viewer argument: secretWord is included
only when the viewer is the drawer. availableWords, roles, listWords and
the frontend ParticipantRole type are removed (no screen used them).
The game page shows "Your word: <word>" to the drawer and a hidden-word
message to guessers. Tests cover drawer, guesser, unknown, anonymous
and lobby views (FR-007, FR-008, FR-009).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All quickstart API and two-tab checks pass on the first run; no
deviations from the plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec covers Scenario 3 (drawer canvas with clear; trimmed,
case-insensitive guesses with empty ones refused; 100/0 scoring; history
and scores synced by 2 s polling) with 3 clarifications: the round ends
when every guesser is correct, the canvas is drawer-only, and correct
guess text is hidden while the round is active. Plan adds research
R1-R8, data model, contract and a three-tab quickstart; tasks T001-T023.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Participants gain a score (0 on create/join); rooms gain a guess list;
status can now be ended; snapshots carry guess views. Frontend types
mirror the change. Also corrects quickstart row 8: a guesser who already
guessed has a disabled form, so the repeat refusal is checked via the API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
submitGuessSchema trims guesses and refuses blank ones ("Enter a
guess."). submitGuess refuses guesses outside an active round, from
non-players, from the drawer and repeats after a correct guess; compares
case-insensitively; adds 100 for a correct guess; and ends the round
once every guesser is correct. startGame resets scores and guesses.
POST /rooms/:code/guesses exposes it (FR-004 to FR-009, FR-013, FR-014).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
api.submitGuess and RoomStore.submitGuess post to the new endpoint;
GuessForm takes an onSubmit prop, clears on success and shows refusal
messages. GamePage passes it (disabled for the drawer); the rest of the
game-screen wiring follows in T016.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
toRoomSnapshot sends text: null for correct guesses while the room is
playing, for every viewer, and reveals it once the round has ended.
Tests cover drawer, guessers and anonymous viewers, and order (FR-011).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves the lobby's 2 s polling, 404 handling and refresh-error state into
a hook so the game screen can share it. LobbyPage behaviour unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reen

GamePage polls every 2 s via useRoomPolling. Scoreboard lists every
player's score; the Activity panel shows the history in order, with
correct guesses as "<name> guessed the word!" while hidden. The guess
form is disabled for the drawer, after a correct guess and once the
round is over, which is announced on screen (FR-009 to FR-012, FR-014).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DrawingCanvas draws freehand lines with pointer events (mouse and touch,
coordinates scaled to the 800x500 surface) and clears with one button.
Only the drawer gets it; guessers see "<name> is drawing". Strokes stay
in the drawer's browser, as clarified (FR-001 to FR-003).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found during T022 three-tab validation: the notice used .status-line,
whose width: max-content stretched the right sidebar past the panel and
clipped its text. It is now a plain paragraph that wraps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All API and three-tab checks pass, plus feature 001 lobby regression
rows for the extracted polling hook; one layout fix found during
validation. Deviations recorded in plan.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
admin and others added 10 commits October 12, 2026 16:30
Spec covers Scenario 4 (word, final scores and full history shown to
everyone; host-only restart back to the lobby with players kept and
round state cleared) with 2 clarifications: the host may restart
mid-round, and the bouncing Exit Game button is removed. Plan adds
research R1-R6, data model, contract and quickstart; tasks T001-T014.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Once the round has ended, every viewer's snapshot includes the secret
word (still drawer-only while playing). A RoundResult card shows "The
word was <word>" and final scores highest first; the Activity panel
already shows the revealed history (FR-001 to FR-003).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
restartGame refuses rooms still in the lobby ("The game has not started
yet.") and non-hosts ("Only the host can restart the game."), then
returns the room to the lobby with players, host and games-started count
kept and drawer, word, guesses and scores cleared. Works from an active
or ended round; exposed as POST /rooms/:code/restart. Tests cover the
rules, the reset, duplicates, mid-round restart and the next word
(FR-004 to FR-006, FR-008).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
api.restartGame and RoomStore.restartGame call the new endpoint. The
result card shows "Play again" to the host and a waiting message to
others; during a round the host can "End game and return to lobby".
Every game screen moves to the lobby when the polled room is back in the
lobby. The bouncing "Exit Game" button is removed (FR-007, FR-009,
FR-010). T011 shares the GamePage edit, hence one commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All API and two-tab checks pass on the first run, including the full
create -> play -> result -> restart -> play again cycle without reloads.
Pre-evaluation now reports 4/4 feature folders complete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Covers what the starter had, what each feature added, how AI was used
(Spec Kit flow, 11 clarifications, guardrails) and where its output was
wrong and caught, tradeoffs, deviations, known limitations and how the
work was verified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fresh backend, two rooms at once: create, join, host-only start, draw
and clear, wrong and correct guesses, shared result, restart with
players kept, and the second room playing independently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tab count

Feature 002's FR-007, contract and data model said the word is sent only
to the drawer; feature 004 reveals it to everyone after the round ends.
Each now notes the amendment. The reflection's browser-check line now
says two to four tabs (the final run used four).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run after implementation (the constitution asks for before; noted in each
report). 100% FR-to-task coverage in every feature. Findings: one
CRITICAL constitution issue (inline style in RoundResult, Principle IV),
one HIGH spec conflict already resolved in f701762, and MEDIUM/LOW
documentation and evidence gaps. Nothing is fixed here; reports only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e implementation

Scribble lab: Spec Kit artifacts and full game implementation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant