Backend README (GAME_CLOCK_MS), AGENTS.md notes, TODO items for the open points and the mobile release order; game-history R3.3 and R3.4 marked as replaced. Checked end to end in Chrome: moves, a backend restart, a timeout and the replay.
6.8 KiB
AGENTS.md
Guidance for coding agents working in this repository. Keep it current: when you learn something that would have saved time, add it here.
Project
Online Mancala (the Turkish Mangala rules) with a web app, an Android/iOS app and a Node backend.
| Path | What |
|---|---|
packages/mancala.js |
Game engine; rules are plug-ins in src/common/game_rules |
packages/core |
Code shared by the apps: models, WebSocket client (RTMTWS), channel names, history helpers, themes, PitAnimator, translations (en, tr) |
apps/backend |
Express + ws: matchmaking, games, auth, saved games, HTTP API |
apps/frontend |
React web app, built with Parcel |
apps/mobile |
React Native app |
apps/keycloak |
Keycloak image with the realm and a Java extension for guest login |
apps/postgres |
Init script creating the keycloak and mancala databases |
apps/compose.yaml |
Postgres, Valkey, Keycloak, backend, frontend |
config/nginx.conf |
The production server's nginx config (Certbot managed) |
docs/specs/<feature> |
Specs: requirements.md, design.md, tasks.md |
TODO.md |
Open work, known bugs and deployment steps; read it before starting |
Specs that exist: keycloak-auth (login, guests, tokens), game-history (saved games, live games, replay) and
game-clock (5 minutes per player, ending games on time).
Running locally
yarn build:packages # repo root; builds mancala.js and core
cd apps && docker compose up -d --build postgres valkey keycloak # needs apps/.env, see README.md
cd apps/backend && yarn dev # http://localhost:5005
cd apps/frontend && yarn dev # http://localhost:1234 (must be 1234, Keycloak only allows that)
Postgres is on localhost:5433 (5432 is often taken), Valkey on 6379, Keycloak on 8080
(admin console /admin). The backend's and frontend's defaults point at these. The mobile app points at production
unless useLocalServer is set in apps/mobile/src/const/config.ts.
Checks
| What | Command | Notes |
|---|---|---|
| Engine | cd packages/mancala.js && npx jest |
|
| Core | cd packages/core && npx jest |
|
| Backend | cd apps/backend && npx tsc --noEmit -p . && npx jest |
Needs Docker: Postgres and Valkey run in testcontainers |
| Keycloak extension | cd apps/keycloak/extension && mvn test |
Needs Docker; starts Keycloak with the real realm file |
| Web app | cd apps/frontend && yarn build |
There is no tsconfig.json, so nothing type-checks it; use a temporary one when needed. 3 type errors are known and older than recent work |
| Mobile | cd apps/mobile && npx tsc --noEmit -p . and npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output /tmp/x.bundle |
11 type errors are known and older than recent work |
There is no automated end-to-end suite. Changes to the game flow were checked by driving Chrome with
playwright-core (installed outside the repo, executablePath: /usr/bin/google-chrome) against the local stack.
How we work
- The user writes in Turkish; code, comments, commit messages and specs are in English.
- Larger features start with a spec in
docs/specs/<feature>/(requirements with acceptance criteria, design with decisions and reasons, tasks as a checklist). Implement the tasks in order, one commit each, and tick them. Small fixes need no spec. When resuming a feature, read its spec andtasks.mdfirst. - Work on feature branches; commit messages follow
type(scope): summarywith a short body. - Never add AI attribution (Co-Authored-By lines, "Generated with …") to commits or PRs.
- Do not commit files the user changed but did not ask to commit; stage only your own changes (hunks if needed).
- New dependencies get exact versions.
Things that are easy to get wrong
Packages and builds
- The apps use
packages/coreandpackages/mancala.jsthroughlink:dependencies, i.e. their builtdist. After changing a package, runyarn buildin it (oryarn devto rebuild on change), or the apps use stale code. Running dev servers do not notice a rebuilt package: restart the backend, and restart Parcel after deletingapps/frontend/.parcel-cache. - Backend and frontend Docker images are built from the repo root:
docker build -f apps/backend/Dockerfile .. - The mobile app uses
RTMTWSfrom core (its ownsrc/rtmtcopy is unused), so changing the core WebSocket client changes mobile too. Metro needswatchFoldersandnodeModulesPaths(already inmetro.config.js).
Backend
- User ids: Keycloak users are
kc:<sub>; legacy mobile users are theiruserKey, which is a secret. Anything saved or listed publicly goes throughpublicUserId(); clients gettoClientGame(), never spectator ids. - Live games: memory is the source of truth, Valkey is a write-through copy restored on startup; the backend assumes a single instance. Finished games are written to Postgres once, with retry when it is down.
- Postgres or Valkey being down must not stop games; reads fall back, writes are retried.
- The game clock is stored as each player's time at the start of the turn plus
turnStartedAt; compute the time left withgetClockTimes, never store a ticking value.GAME_CLOCK_MS=40000makes games short for manual tests. RTMT.listenOnClientConnectionChangeadds a listener (several are allowed).- Jest 27 lacks Node's web globals that testcontainers needs;
tests/support/NodeEnvironment.jscopies them in. - A Valkey client pointed at a dead port keeps Jest alive for
disconnectTimeout; tests pass a short one.
Web app
- styled-jsx only scopes JSX written directly in a component's render; elements built in helper functions or
mapcallbacks need.parent :global(.child)rules. - Pages are full-height flex columns: a block with
overflow: hiddenneedsflex-shrink: 0or it gets squeezed. - Board components size themselves from the
--board-unitCSS variable;BoardViewtakes an optionalwidth. - The WebSocket client emits
openafter authentication; anything the server must know about a connection (spectating a game, live game previews) is sent onopen, because the server forgets it when a user goes offline.
Keycloak
- The realm file is imported only when the realm does not exist; changes to it must also be made in a running Keycloak (or recreate the database).
- Listing some
requiredActionsin the realm file replaces Keycloak's whole default list, so the file contains all of them. - The guest authenticator uses an internal Keycloak SPI: keep the version in
extension/pom.xmlandDockerfilein sync and runmvn testwhen upgrading. - Keycloak sets
Securecookies; browsers send them tohttp://localhost, simple HTTP clients do not.
Mobile
apps/mobile/release_android.shcontains signing passwords (seeTODO.md); do not copy them anywhere.