VibeBots is an online autonomous robot battler with a mining-driven economy. Players dig for resources, buy or discover modular robot parts, build bots in a connector-based workshop, then watch those bots fight CPU bots or other player-built bots without direct control.
- Next.js 16 (App Router, TypeScript strict, Turbopack) on Vercel, pnpm
- three.js via
three/webgpu(WebGPURenderer, automatic WebGL2 fallback) + react-three-fiber 9 - Rapier 3D physics:
@dimforge/rapier3d-deterministic-compat, exact-pinned, identical WASM build in the browser and in Vercel Node functions - VibeKit (
@randroids-dojo/vibekit, tag-pinned) for seeded rng, math, storage, and server helpers - Biome 2 (lint + format), Vitest 4 (unit), Playwright (e2e smoke)
The core architectural bet is hybrid match authority: the browser previews matches with a local deterministic sim, and the server reruns the exact same sim to produce the official result. GET /api/sim/verify?seed=42&steps=600 returns the world snapshot hash; it must match what any other machine computes for the same inputs.
pnpm install
pnpm dev # dev server at http://localhost:3000
pnpm test # unit tests (incl. sim determinism)
pnpm typecheck # next typegen + tsc --noEmit
pnpm lint # biome ci
pnpm build # production build
pnpm test:e2e:critical # critical Playwright smoke for every CI push
pnpm test:e2e # playwright smoke, local defaults to 2 file-level workers (run pnpm build first)
pnpm ci:inventory # validate and list stable Playwright case metadata
pnpm check:dashes # AGENTS.md Rule 1 (no em/en-dashes)
pnpm check:purity # src/sim determinism contract
pnpm ops:check-account-env # non-secret Clerk readiness preflightSet on the Vercel project (the dashboard or CLI), never committed:
DATABASE_URLand friends: auto-provisioned by the dedicated Neon Postgres marketplace integration (Rule 11: one backing store per project, never shared). Created as sensitive values, so they are injected at deploy time and do notvercel env pulllocally; local dev without them degrades to 503 "storage not configured" on persistence routes.AUTH_SECRET: HMAC secret for the signed guest cookie (Production + Preview).NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYandCLERK_SECRET_KEY: Clerk project keys for the approved Google sign-in integration. Local development keys can be pulled by the Clerk CLI into ignored.env.local; production keys should come from the Vercel project integration or dashboard. Guest-first play and local saves still work without them because the Clerk provider and proxy are guarded. Ready setup requires@clerk/nextjs, a publishable key that starts withpk_test_orpk_live_, and a secret key that starts withsk_test_orsk_live_.NEXT_PUBLIC_CLERK_SIGN_IN_URL: public Clerk sign-in entrypoint used by the mine Account dialog. Set to/sign-inwhen Clerk keys are present so the Account dialog can create a one-time handoff before opening Clerk.NEXT_PUBLIC_CLERK_SIGN_UP_URL: leave unset for the first VibeBots account-sync release./sign-inis the single sign-in-or-up page, keeping account creation on the same handoff path as sign-in.NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL: Clerk fallback for direct sign-in visits without a one-time handoff. Set to/mine?account=1only after the Clerk provider is wired, so those visits return to the Account dialog.NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL: Clerk fallback for account creation reached from the sign-in component without a one-time handoff. Set to/mine?account=1with the sign-in fallback so completed account creation returns to the Account dialog.NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL,NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL,CLERK_SIGN_IN_FORCE_REDIRECT_URL, andCLERK_SIGN_UP_FORCE_REDIRECT_URL: leave unset for VibeBots account sync. Force redirects bypass the one-time account handoff token and the preflight rejects them when present.NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL,NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL,CLERK_AFTER_SIGN_IN_URL, andCLERK_AFTER_SIGN_UP_URL: leave unset. Clerk marks these redirect variables deprecated for new apps, and VibeBots uses the fallback redirect variables instead.VAPID_PUBLIC_KEYandVAPID_PRIVATE_KEY: Web Push keys for native browser release notifications. Generate and store them withpnpm ops:setup-push-env -- --production-only; keep the private key secret.WEB_PUSH_CONTACT_EMAIL: optional contact email for the Web Push VAPID subject. Defaults tosupport@randroid.dev.NOTIFICATION_ADMIN_TOKEN: bearer token required byPOST /api/notifications/release, the manual fallback for dispatching the current release summary to enabled subscriptions. Normal release dispatch is triggered idempotently by the no-store/api/versioncheck when storage and Web Push keys are configured.
Production Vercel URLs can be protected by Vercel authentication or bot checks before the request reaches VibeBots. A fresh headless Playwright context, curl, or plain fetch may see a Vercel login or security checkpoint instead of the app.
For normal closeout, verify production readiness with GitHub deployment status and Vercel CLI or API metadata tied to the pushed commit sha:
gh api -X GET repos/Randroids-Dojo/VibeBots/deployments -f sha=<sha>
gh api repos/Randroids-Dojo/VibeBots/deployments/<deployment-id>/statuses
vercel inspect <deployment-url>
vercel ls vibe-botsUse local next start, preview CI, or GitHub Actions for Playwright app smoke coverage. Only automate production UI when the slice explicitly needs production playtest evidence, and use an authenticated real browser context for that run.
Fresh worktrees need the ignored Vercel project link before using Vercel env commands:
ln -sfn /Users/randroid/Documents/Dev/VibeBots/.vercel .vercel
# or, when that source is unavailable:
vercel link --yes --project vibe-botsDo not copy .env files between worktrees. They may contain secrets and are not needed for production env setup.
To generate and store a fresh production VAPID pair plus the admin fallback token without printing secret values:
pnpm ops:setup-push-env -- --production-only
vercel redeploy <latest-production-deployment-url> --target productionThe helper passes values through stdin and suppresses Vercel CLI diagnostics so generated secrets do not appear in argv or command output. After redeploy, verify without printing keys:
node -e "fetch('https://vibe-bots.vercel.app/api/notifications/config').then(r=>r.json()).then(j=>console.log({configured:j.configured, hasVapidPublicKey: Boolean(j.vapidPublicKey), releaseNoticeId:j.releaseNoticeId}))"Expected result: configured: true, hasVapidPublicKey: true.
Mine cash-out monitoring uses structured JSON logs from
src/server/monitoring.ts. Warning and error events include source
("vibebots"), component ("mine.cash_out"), alert (true), a stable
event name, safe request context, and a hashed player identifier when an
existing player cookie is available. Configure Vercel log drains or log-based
alerts on these events:
mine.cash_out.invalid_json_bodymine.cash_out.request_validation_failedmine.cash_out.storage_not_configuredmine.cash_out.consumables_not_ownedmine.cash_out.gear_not_ownedmine.cash_out.mine_version_mismatchmine.cash_out.no_mine_on_filemine.cash_out.player_not_foundmine.cash_out.trip_already_cashed_outmine.cash_out.wrong_mine_seedmine.cash_out.legacy_support_reconciledmine.cash_out.cash_out_failed
Successful sells emit mine.cash_out.cash_out_succeeded with alert=false.
Use it to correlate a player hash, seed, trip index, credited value, charged
consumables, and remaining stock without paging on normal traffic.
Mine performance samples are stored in player_performance_samples. The
client records compact frame percentiles from real browser play, plus renderer
mode, draw calls, viewport size, device pixel ratio, hardware hints, app
version, mine version, user agent, and active player id. Use this table to
triage low-frame or glitch reports from old laptops and compare them by
renderer, DPR, viewport, and p95 frame time.
Balance tuning events are stored in player_balance_events. Each row belongs
to one player and records app version, mine version, event name, and compact
JSON properties. Use this append-only stream to tune progression by aggregating
events such as mine.cash_out, gear.upgrade, consumable.purchase,
base_part.purchase, elevator.upgrade, and bunker.raid_reward.
For a known affected long-running player whose client support snapshot drifted, repair stored support stock explicitly instead of adding runtime replay exceptions:
pnpm ops:repair-support -- --player-hash <log-player-hash> --ladder <count> --plank <count>
pnpm ops:repair-support -- --player-hash <log-player-hash> --ladder <count> --plank <count> --applyThe command dry-runs by default and only raises ladder_count and
plank_count. It never changes vibes, resources, gear, paid consumables, mine
worlds, or trip counters.
For a known affected player whose local in-flight trip checkpoint is stale after a mine-version bump, repair the stored replay guard explicitly instead of changing payout rules:
Run it with DATABASE_URL set for the target database. For production, prefer
vercel env run -e production -- pnpm ops:repair-stale-trip -- ... so the
connection string is loaded from Vercel and not copied into the shell history.
pnpm ops:repair-stale-trip -- --player-hash <log-player-hash> --expected-seed <seed> --expected-trip-index <count>
pnpm ops:repair-stale-trip -- --player-hash <log-player-hash> --expected-seed <seed> --expected-trip-index <count> --applyThe command dry-runs by default and only advances mine_worlds.trip_count from
the exact expected value to expected + 1 when the seed also matches. It never
changes the durable mine diff, gear, wallet, inventory, achievements, parts,
designs, or saved bots.
After the approved Clerk SDK slice, run the non-secret preflight with the target environment loaded before treating account sync as ready:
pnpm ops:check-account-env -- --require-ready --google-scopes "openid email profile"For local development, clerk doctor verifies the Clerk CLI login, linked app,
and ignored .env.local key presence without printing key values. Production is
not ready until the production Clerk instance and Vercel project env are
configured.
For test coverage of the preflight itself, use fake key-shaped values rather than real secrets:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_fake CLERK_SECRET_KEY=sk_test_fake NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL='/mine?account=1' NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL='/mine?account=1' pnpm ops:check-account-env -- --require-ready --google-scopes "openid email profile"The command prints booleans, issue codes, and the required identity scope list
only. It never prints Clerk key values. Ready setup requires
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in and
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/mine?account=1 and
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/mine?account=1, with
NEXT_PUBLIC_CLERK_SIGN_UP_URL, public and server Clerk force redirect env
vars, and public and server deprecated after redirect env vars unset.
AGENTS.md: rules for all agentic tools working in this repodocs/gdd/: the Game Design Document (source of truth for what VibeBots is)docs/research/account-sync-google-signin.html,docs/research/account-sync-clerk-vercel-setup.html,docs/research/account-link-vibekit-extraction.html: optional Google sign-in design, Clerk setup checklist, and VibeKit extraction contractdocs/IMPLEMENTATION_PLAN.html,docs/WORKING_AGREEMENT.html: the loop and process contractsdocs/CI_WORKFLOW.html: parallel CI, critical smoke, full smoke, and superseded-run policydocs/ci-rewrite-prompt.md,docs/CI_REWRITE_OWNER_QUESTIONS.html: staged CI migration design and owner decisions that would otherwise pause itsrc/lib/app-release-notes.ts: player-facing release notes shown in game (docs/RELEASE_NOTES.htmlis a frozen pre-0.1.259 archive, not maintained)docs/PROGRESS_LOG.html,docs/GDD_COVERAGE.json,docs/OPEN_QUESTIONS.html,docs/FOLLOWUPS.html,docs/DEPENDENCY_LEDGER.html: continuity ledgers.dots/: task backlog (HTML Dots,dot-htmlCLI)