| name | gbl-new-game-app |
| description | Scaffold a new GBL platform game app inside this monorepo by copying apps/demo-game - file-by-file copy/rename checklist, prisma setup, local dev environment, and first-run verification. Use when starting a brand-new game. |
GBL New Game App
App unreachable or environment broken? Run the gbl-environment-doctor skill before scaffolding.
There is no generator; the complete supported scaffold starts as a copy of the reference game inside a clone/fork of this monorepo. @gbl-uzh/ui also has a verified public-package contract for external games after its one-time npm bootstrap, but package installation does not replace the app, database, auth, and platform scaffolding described here. Background: docs/platform-overview.md, docs/developing-a-game.md.
Design the game FIRST (gbl-game-design skill) — the scaffold asks for your facts shapes immediately.
Scaffold checklist
cp -R apps/demo-game apps/<game> (skip node_modules, .next, dist).
apps/<game>/package.json: set "name": "@gbl-uzh/<game>"; keep @gbl-uzh/platform and @gbl-uzh/ui at workspace:*; keep the prisma:copy / prisma:generate script chain untouched.
pnpm install from the repo root (workspace glob apps/* picks the package up).
- Prisma:
prisma/schema/platform.prisma is generated by prisma/copy.ts from the platform package — NEVER edit it by hand.
prisma/schema/specific.prisma is yours for game-specific tables (keep the stub if you have none).
- Point
DATABASE_URL at a fresh database (one DB per game app).
- Replace game content, in this order:
src/types/ — your facts types + yup schemas (Game/Period/PeriodSegment/Player facts).
src/services/ — the six computation modules (gbl-backend-computations skill).
prisma/seed.ts — your level ladder, story/learning elements, achievements.
src/pages/ + src/components/ — your cockpit/admin UI (gbl-frontend-game-ui skill).
- Branding:
public/ assets, app name in layout/nav.
- Auth env: copy
.env.local.template; local dev works without a real OIDC tenant via the devcontainer's mock OIDC server (see .devcontainer/README.md); production needs real Auth0/OIDC credentials + NEXTAUTH_SECRET.
Starter-mode (Docker-only) specifics
If you run the starter stack (.devcontainer/starter/, app on http://localhost:3000), four things the checklist above doesn't cover:
- Add a
node_modules volume for the new app in .devcontainer/starter/docker-compose.yml (copy the node_modules_demo_game pattern, then docker compose -p <name> up -d to recreate). Without it the new app's node_modules land on the host bind mount — slow on Windows/macOS and wrong-OS binaries.
DATABASE_URL from starter.env is container env and beats your app's .env. "Point DATABASE_URL at a fresh database" only works if you export DATABASE_URL=... explicitly for every prisma command AND the dev-server start. Create the DB first: docker compose -p <name> exec postgres psql -U prisma -d prisma -c 'CREATE DATABASE <game>;'
- Port 3000 is single-tenant.
post-start.sh starts the demo-game dev server on every container start — kill it (pkill -f next-server inside the container) before starting your game's dev server on 3000. Only one game app runs at a time.
- A fresh database has no admin. The dev admin
User+Account rows are created on first OIDC login — sign in once via /admin/login in a browser before any admin API/seed work that references the owner.
Decontaminate the copy
cp -R apps/demo-game apps/<game> copies the demo game's domain logic too. Step 5 above tells you what to replace; this section is the hit-list of demo-game residue that is easy to miss because it does not fail the build. Run through it after step 5, before first run. Every item here is a real defect found in the first dogfood game build.
.env.production + .env.production.prd: still point NEXT_PUBLIC_* and NEXTAUTH_URL at demo-game.stg.env.bf-app.ch. Rewrite them to your game's domain, or empty them and set env vars at deploy time. See gbl-deploy-staging / docs/deploying-a-game.md.
src/pages/index.tsx: the app's / route. In the demo game this is a trading/portfolio showcase (StorageOverview, TradingForm, ProbabilityChart, "hello world"). Replace it with a game landing page or a redirect to /play/welcome.
src/lib/analysis.ts: demo-game portfolio analytics (assetsWithReturns, accBankBenchmarkReturn, totalAssetsReturn, sharpeRatio). Unused in any other game. Delete it.
src/lib/constants.ts: demo-game time constants MONTHS, NUM_MONTHS, NUM_MONTHS_PER_SEGMENT (12-month portfolio cycle). Delete them if nothing else uses them; keep LOCATIONS, COLORS, AVATARS.
GameFactsService.ts + src/types/Game.ts: the demo stubs increment a meaningless myInt counter. Replace GameFacts with your game's facts (or an empty schema) and stub update accordingly (gbl-backend-computations skill).
- Grep the whole
src/ tree for demo-game vocabulary: assetsWithReturns, spotPrice, futuresPrice, cashBalance, storageAmount, bank / bonds / stocks (as allocation fields). Any hit is residue to remove or replace.
[!TIP] > Also clean unused imports. A copied file often imports types it no longer uses (e.g. PlayerResult in a result service that switched to any). pnpm --filter @gbl-uzh/<game> run check (lint + check:ts) surfaces these - do not skip it.
Local dev + first run
Preferred: the devcontainer/devrouter flow (.devcontainer/README.md) — brings up Postgres + mock OIDC and seeds automatically; admin login is one click as a fixed dev admin. Manual alternative: own Postgres + real OIDC creds, then prisma migrate dev/db push + seed, then the package's dev script.
First-run verification (do this before writing any new feature):
- Sign in at
/admin/login, create a game with 2 players.
- Add one period + one segment; open a player join link from the game detail page in a second browser context.
- Drive one full loop: Start Period → Next Segment → submit a player decision → Segment Results → Consolidate → Period Results (docs/game-lifecycle.md).
- Confirm the player screen updates on each admin transition without manual reload (realtime works).
Definition of done
Before calling the scaffold complete, verify all of:
pnpm --filter @gbl-uzh/<game> run check is green (lint + check:ts; no unused imports, no demo-game types).
- No residue grep hits from the decontamination list above (
rg "assetsWithReturns|spotPrice|futuresPrice|cashBalance|storageAmount" apps/<game>/src returns nothing; bank/bonds/stocks only appear if they are your game's actual concepts).
- The
/ route renders your game, not demo-game content.
- If you keep a progress tracker (e.g.
task.md), update it. A stale checklist that says "results view not done" when it is done is a real review hazard.
[!WARNING]
Throwaway game (dogfood/experiment you will NOT commit to the target branch)? Clean up when you remove it. apps/<game>/.gitignore (which ignores .next/, next-env.d.ts, tsconfig.tsbuildinfo) only exists next to committed source. Once you delete the source but leave the built app on disk, those artifacts are no longer ignored, so a later git add -A would stage the whole .next/ (webpack caches included) plus any stray root tracker like task.md. Run rm -rf apps/<game> and delete the tracker before staging, and confirm with git status that nothing under the removed app remains.
Pitfalls
- Do not edit
platform.prisma; schema changes for your game go in specific.prisma, platform-level changes go in packages/platform/public/schema.prisma (affects every game).
- Do not add a second React or pin different versions of shared deps — check
pnpm-workspace.yaml overrides before touching dependency versions.
- Keep the demo game intact as the working reference; never repurpose it in place.
- The copied
src/graphql/ (or src/server/trpc/ after the migration — see docs/api-layer.md) is wiring, not game logic: adjust the injected services/schemas, don't rewrite the transport.