1
0

Remove launch.sh wrapper; document docker compose workflow in README

This commit is contained in:
James Ketr 2025-09-30 21:08:32 -07:00
parent 5159e0e5e3
commit ee965bd8ce
118 changed files with 422 additions and 47 deletions

1
.env
View File

@ -6,3 +6,4 @@ VITE_HMR_PORT=3001
# Compose settings added by assistant # Compose settings added by assistant
COMPOSE_PROJECT_NAME=peddlers-of-ketran COMPOSE_PROJECT_NAME=peddlers-of-ketran
COMPOSE_FILE=docker-compose.yml COMPOSE_FILE=docker-compose.yml
COMPOSE_PROFILES=dev

34
5-6.md Normal file
View File

@ -0,0 +1,34 @@
Out of the Box
11 terrain hex tiles:
2 forest
2 pasture
2 fields
2 hills
2 mountain
1 desert
4 frame pieces
2 sea
1 3:1 harbor frame piece
1 2:1 wool harbor frame piece
Green player pieces:
5 settlements
4 cities
15 roads
1 Building Costs card 😁
Brown player pieces:
5 settlements
4 cities
15 roads
1 Building Costs card
25 resource cards:
5 lumber
5 wool
5 grain
5 brick
5 ore
9 Development cards:
6 Knight cards
1 Road Building
1 Monopoly
1 Year of Plenty
28 die cut number tokens (replaces Settlers number tokens, with a darker brown number color)

View File

@ -16,38 +16,28 @@ The application can be launched in development or production mode by setting the
- Docker - Docker
- Docker Compose - Docker Compose
### Launching ### Launching (using docker compose)
This project runs directly with the Docker Compose CLI. We removed the previous helper wrapper so the canonical workflow is to use `docker compose` from the repository root. Examples:
```bash ```bash
# For development (hot-reload) # Development (hot-reload client/server)
PRODUCTION=0 ./launch.sh PRODUCTION=0 docker compose up -d --profile dev
# For production (static build) # Production (static build served by the server)
PRODUCTION=1 ./launch.sh PRODUCTION=1 docker compose up -d --profile prod
```
The repository includes a helper script `launch.sh` that wraps `docker compose` with sane defaults for this project. It sets a stable compose project name and the common compose files so you can run commands from the repo root without extra flags. # Tail logs for the client/service
docker compose logs -f peddlers-client
Common examples:
```bash
# Start the development stack (hot-reload client/server)
./launch.sh up
# Tail logs for the client
./launch.sh logs peddlers-client
# Show running services # Show running services
./launch.sh ps docker compose ps
# Stop everything # Stop everything and remove orphans
./launch.sh down docker compose down --remove-orphans
# Build images (no cache) # Build images
./launch.sh build docker compose build
# Start production mode (uses the 'prod' profile)
./launch.sh --production up
``` ```
#### Development Mode #### Development Mode

85
TODO.md Normal file
View File

@ -0,0 +1,85 @@
Project TODO and context
This TODO collects remaining tasks and diagnostic steps for the "peddlers-of-ketran" project,
especially focused on making headless harness runs deterministic and actionable.
High-level goals
- Fix UI bugs (House Rules read-only on new-game load).
- Make startup deterministic: server should send a consolidated attach-time "initial-game" snapshot and
the client should treat that as a full update so headless tests can start at a known state.
- Add an instrumented headless harness that captures lifecycle artifacts (DOMContentLoaded snapshot,
DOM when inputs first appear, screenshots, and intercepted WebSocket sends). Use this harness in CI.
Immediate tasks (priority order)
1) Add an assertion in the Puppeteer harness to fail early if the client doesn't send a player-name
message with the expected name (Automaton). This prevents flakey runs from masking regressions.
- Location: tools/puppeteer-test/test.js
- Implementation: after saving /workspace/tmp-ws-sends-<ts>.json, parse each intercepted send and
verify there exists an entry where JSON.parse(entry.data).type === 'player-name' and
extracted name === 'Automaton'. Fail the test if not present.
2) Confirm server-side handler accepts normalized payloads everywhere.
- Location: server/routes/games.js
- Implementation: Add a small normalizeIncoming(msg) helper used at the top of the ws.on('message')
handler that returns a consistent object shape: { type, data } where data may contain name, etc.
- Rationale: reduces repeated defensive parsing and prevents future undefined values.
3) Investigate duplicate player-name sends from the client.
- Symptom: harness observed two identical player-name sends per run.
- Approach: add client-side debounce or guard after the first send; or only send when the name actually changes.
- Files: client/src/PlayerName.tsx or client/src/App.tsx (where normalizedSend is invoked).
4) Verify persistence of name change in DB / game state.
- Query: after setPlayerName completes, check game's session object and the games DB to ensure changes persisted.
- Tools: sqlite3 queries against db/users.db or db/games.db, or add a short admin HTTP endpoint for inspection.
5) Harden the harness reporting and CI integration.
- Add explicit exit codes and artifact names so CI can surface failures.
- Add a GH Action that spins up the compose stack and runs the peddlers-test service inside the compose network.
New / follow-up actions (from recent session)
6) Re-run the instrumented harness while streaming server logs to capture exact server-side handling.
- Goal: get a single, reproducible run that writes `/workspace/tmp-ws-sends-<ts>.json` and produce
a server log snippet showing setPlayerName processing for the same timestamp.
- Acceptance criteria:
- Harness exits 0 (or the expected non-zero on assert failure) and artifacts are present.
- Server logs include a `setPlayerName` line mentioning the same name (Automaton) and a timestamp
that can be correlated to the harness-run (or the harness writes the server log lines into an artifact).
- Quick command (dev):
```bash
# run the harness and tail server logs in parallel (dev machine)
docker compose -f docker-compose.yml logs --no-color --follow server &
docker compose -f docker-compose.yml run --rm -e TEST_MAX_MS=20000 peddlers-test
```
- Notes: If you want, I can run this now and capture the correlated logs/artifacts.
7) Consolidate server-side message normalization helper.
- Goal: add a single `normalizeIncoming(raw)` helper in `server/routes/games.js` (or a small util) that
returns { type, data } and is used at the top of the websocket message handler so each case can assume
the canonical shape.
- Acceptance criteria:
- No functional changes to handlers beyond switching to the normalized shape.
- Unit/regression smoke: a quick local run validates `player-name` and a couple other common message types
still work (server logs unchanged behavior).
- Implementation sketch:
- add `function normalizeIncoming(msg) { try { const parsed = typeof msg === 'string' ? JSON.parse(msg) : msg; return { type: parsed.type, data: parsed.data || { ...parsed, ...(parsed.data||{}) } }; } catch(e){ return { type: null, data: null }; } }`
- call at the top of `ws.on('message', (m) => { const incoming = normalizeIncoming(m); switch(incoming.type) { ... }})`
8) Add a CI workflow that runs the harness inside the project's canonical container environment.
- Goal: run the harness as part of PR checks so regressions are caught early.
- Acceptance criteria:
- A GitHub Actions workflow `ci/harness.yml` is present that uses the project's Dockerfile/docker-compose
to run services and executes the `peddlers-test` service; artifacts (tmp-ws-sends-*.json, screenshots) are
uploaded as job artifacts on failure.
- Minimal approach:
- Spin up services via docker compose in the action runner (use the repo's Dockerfile images to avoid host npm installs),
run `docker compose run --rm peddlers-test`, collect artifacts, and fail the job on non-zero exit.
- Notes: Follow the repo-level guidance in `.github/copilot-instructions.md` — do not run `npm install` on the host; use the provided Docker workflows.
Lower priority / follow-ups
- Add server unit tests for message parsing and setPlayerName behavior.
- Consolidate log markers and timestamps to make correlation between client sends and server logs easier.
- Add a small README for the harness explaining how to run it locally with Docker Compose.
If you want, I can implement item #1 now (fail-fast assertion in the harness) and re-run the harness to demonstrate.

View File

@ -0,0 +1,24 @@
{
"files": {
"main.css": "/ketr.ketran/static/css/main.5c4eadc6.css",
"main.js": "/ketr.ketran/static/js/main.7f886035.js",
"static/js/453.724c1354.chunk.js": "/ketr.ketran/static/js/453.724c1354.chunk.js",
"static/media/tabletop.png": "/ketr.ketran/static/media/tabletop.c9d223b725a81ed5cc79.png",
"static/media/single-volcano.png": "/ketr.ketran/static/media/single-volcano.bc309b8b1af76dcff875.png",
"static/media/category-rolling.png": "/ketr.ketran/static/media/category-rolling.dfa26f433340fea8a04d.png",
"static/media/category-expansion.png": "/ketr.ketran/static/media/category-expansion.faffe7f9e48859fe6ea8.png",
"static/media/category-rules.png": "/ketr.ketran/static/media/category-rules.8cb23b3b0b76544e9f42.png",
"static/media/category-board.png": "/ketr.ketran/static/media/category-board.d3bd64a1f60ccfdee40d.png",
"static/media/raptor-robber.png": "/ketr.ketran/static/media/raptor-robber.57955327f2631db8d744.png",
"static/media/man-robber.png": "/ketr.ketran/static/media/man-robber.900eeff4bd222c68b72e.png",
"static/media/woman-robber.png": "/ketr.ketran/static/media/woman-robber.971a697b0e565db2c944.png",
"index.html": "/ketr.ketran/index.html",
"main.5c4eadc6.css.map": "/ketr.ketran/static/css/main.5c4eadc6.css.map",
"main.7f886035.js.map": "/ketr.ketran/static/js/main.7f886035.js.map",
"453.724c1354.chunk.js.map": "/ketr.ketran/static/js/453.724c1354.chunk.js.map"
},
"entrypoints": [
"static/css/main.5c4eadc6.css",
"static/js/main.7f886035.js"
]
}

View File

@ -0,0 +1,6 @@
# icon.svg => icon.png
```
convert -background none -geometry 256x256 icon.svg icon.png
```

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><path d="M0 0h512v512H0z" fill="#000000" fill-opacity="0.01"></path><g class="" transform="translate(0,0)" style=""><path d="M74.5 36A38.5 38.5 0 0 0 36 74.5v363A38.5 38.5 0 0 0 74.5 476h363a38.5 38.5 0 0 0 38.5-38.5v-363A38.5 38.5 0 0 0 437.5 36h-363zm48.97 36.03A50 50 0 0 1 172 122a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zm268 0A50 50 0 0 1 440 122a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zM256 206a50 50 0 0 1 0 100 50 50 0 0 1 0-100zM123.47 340.03A50 50 0 0 1 172 390a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zm268 0A50 50 0 0 1 440 390a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 734 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><path d="M0 0h512v512H0z" fill="#000000" fill-opacity="0.01"></path><g class="" transform="translate(0,0)" style=""><path d="M74.5 36A38.5 38.5 0 0 0 36 74.5v363A38.5 38.5 0 0 0 74.5 476h363a38.5 38.5 0 0 0 38.5-38.5v-363A38.5 38.5 0 0 0 437.5 36h-363zm48.97 36.03A50 50 0 0 1 172 122a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zm268 0A50 50 0 0 1 440 122a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zm-268 268A50 50 0 0 1 172 390a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zm268 0A50 50 0 0 1 440 390a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 684 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><path d="M0 0h512v512H0z" fill="#000000" fill-opacity="0.01"></path><g class="" transform="translate(0,0)" style=""><path d="M74.5 36A38.5 38.5 0 0 0 36 74.5v363A38.5 38.5 0 0 0 74.5 476h363a38.5 38.5 0 0 0 38.5-38.5v-363A38.5 38.5 0 0 0 437.5 36h-363zM256 206a50 50 0 0 1 0 100 50 50 0 0 1 0-100z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><path d="M0 0h512v512H0z" fill="#000000" fill-opacity="0.01"></path><g class="" transform="translate(0,0)" style=""><path d="M74.5 36A38.5 38.5 0 0 0 36 74.5v363A38.5 38.5 0 0 0 74.5 476h363a38.5 38.5 0 0 0 38.5-38.5v-363A38.5 38.5 0 0 0 437.5 36h-363zm48.97 36.03A50 50 0 0 1 172 122a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zm268 0A50 50 0 0 1 440 122a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zM122 206a50 50 0 0 1 0 100 50 50 0 0 1 0-100zm268 0a50 50 0 0 1 0 100 50 50 0 0 1 0-100zM123.47 340.03A50 50 0 0 1 172 390a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zm268 0A50 50 0 0 1 440 390a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 777 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><path d="M0 0h512v512H0z" fill="#000000" fill-opacity="0.01"></path><g class="" transform="translate(0,0)" style=""><path d="M74.5 36A38.5 38.5 0 0 0 36 74.5v363A38.5 38.5 0 0 0 74.5 476h363a38.5 38.5 0 0 0 38.5-38.5v-363A38.5 38.5 0 0 0 437.5 36h-363zm316.97 36.03A50 50 0 0 1 440 122a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zM256 206a50 50 0 0 1 0 100 50 50 0 0 1 0-100zM123.47 340.03A50 50 0 0 1 172 390a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 597 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><path d="M0 0h512v512H0z" fill="#000000" fill-opacity="0.01"></path><g class="" transform="translate(0,0)" style=""><path d="M74.5 36A38.5 38.5 0 0 0 36 74.5v363A38.5 38.5 0 0 0 74.5 476h363a38.5 38.5 0 0 0 38.5-38.5v-363A38.5 38.5 0 0 0 437.5 36h-363zm316.97 36.03A50 50 0 0 1 440 122a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97zm-268 268A50 50 0 0 1 172 390a50 50 0 0 1-100 0 50 50 0 0 1 51.47-49.97z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
client/build/assets/gfx/birds.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

BIN
client/build/assets/gfx/sheep.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
client/build/assets/robber.mp3 Executable file

Binary file not shown.

BIN
client/build/assets/sheep.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

BIN
client/build/assets/up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

BIN
client/build/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

1
client/build/index.html Normal file
View File

@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/ketr.ketran/favicon.ico"/><base href="/ketr.ketran"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Play Peddlers of Ketran!"/><link rel="apple-touch-icon" href="/ketr.ketran/logo192.png"/><link rel="manifest" href="/ketr.ketran/manifest.json"/><title>Peddlers of Ketran</title><script defer="defer" src="/ketr.ketran/static/js/main.7f886035.js"></script><link href="/ketr.ketran/static/css/main.5c4eadc6.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

BIN
client/build/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
client/build/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "Peddlers",
"name": "Peddlers of Ketran",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
client/build/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

Some files were not shown because too many files have changed in this diff Show More