mancala/AGENTS.md

6.4 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) and game-history (saved games, live games, replay).

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 and tasks.md first.
  • Work on feature branches; commit messages follow type(scope): summary with 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/core and packages/mancala.js through link: dependencies, i.e. their built dist. After changing a package, run yarn build in it (or yarn dev to rebuild on change), or the apps use stale code.
  • Backend and frontend Docker images are built from the repo root: docker build -f apps/backend/Dockerfile ..
  • The mobile app uses RTMTWS from core (its own src/rtmt copy is unused), so changing the core WebSocket client changes mobile too. Metro needs watchFolders and nodeModulesPaths (already in metro.config.js).

Backend

  • User ids: Keycloak users are kc:<sub>; legacy mobile users are their userKey, which is a secret. Anything saved or listed publicly goes through publicUserId(); clients get toClientGame(), 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.
  • RTMT.listenOnClientConnectionChange adds a listener (several are allowed).
  • Jest 27 lacks Node's web globals that testcontainers needs; tests/support/NodeEnvironment.js copies 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 map callbacks need .parent :global(.child) rules.
  • Pages are full-height flex columns: a block with overflow: hidden needs flex-shrink: 0 or it gets squeezed.
  • Board components size themselves from the --board-unit CSS variable; BoardView takes an optional width.
  • The WebSocket client emits open after authentication; anything the server must know about a connection (spectating a game, live game previews) is sent on open, 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 requiredActions in 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.xml and Dockerfile in sync and run mvn test when upgrading.
  • Keycloak sets Secure cookies; browsers send them to http://localhost, simple HTTP clients do not.

Mobile

  • apps/mobile/release_android.sh contains signing passwords (see TODO.md); do not copy them anywhere.