7.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, the login theme 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),
game-clock (5 minutes per player, ending games on time) and web-redesign (the web app's look: themes, board,
pages).
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 (with "types": ["styled-jsx", "react", "react-dom"]). 1 type error (react-dom/client) is known |
| 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. - Colours come from CSS variables of the theme class on
<html>(styles/app.css); the web app's themes (theme/themes.ts) are passed to core'sThemeManager, which the mobile app uses with its own default themes. - Styles that need container queries (
@container,cqw), such as the board's, are plain.cssfiles imported by the component; styled-jsx 5.0 does not handle them.BoardViewfills its container and stands upright below 600 px. - When a script drives the app in Chrome, wait for the header (
.hdr) and a few seconds after the first load before navigating; navigating during the guest login leaves an invalid token. Open the lobby with the "New Game" button, not by URL, or the matchmaking message goes out before the WebSocket is open. - 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. - The login theme (
apps/keycloak/themes/mancala) is only CSS on top ofkeycloak.v2. Keycloak instartmode caches themes and keeps gzipped resources indata/tmp/kc-gzip-cacheacross restarts; seeapps/keycloak/README.mdfor reloading it in a running container.
Mobile
apps/mobile/release_android.shcontains signing passwords (seeTODO.md); do not copy them anywhere.