Chapter 6 ยท operations

Ops & Testing

One dev server runs the whole system locally; one GitHub push deploys it to the world. In between: a live ops console, a REST API foundation, an AI layer, and a test pyramid from pure engine units to full headless-browser walks.

Hub console (/hub)

live

The ops + corporate console: live match telemetry, bot health, and an activity feed โ€” everything the demo runner needs on one screen.

Hub console with stats, matches table and activity feed
ops-hub.png ยท /hub live โ€” stats, matches table (expandable player rows), activity feed

How it works. Polls /api/state every 3 seconds. That endpoint reads the bot match store, recomputes each player's raw/adjusted/progress/verified % server-side (same multipliers as the engine), merges bot heartbeats (fresh if seen < 45s) and the Qalarc Hub health, and returns one JSON. The feed is derived client-side by diffing successive polls โ€” no server push needed. Degrades gracefully when anything is missing; the corporate tab (org leagues, employer-funded pots, kโ‰ฅ5 aggregate-only wellbeing dashboards) renders from the same state.

The match visible in the screenshot was played through the real CommandBus (the /debug simulator) minutes before capture.

lives: apps/hub/{index.html,hub.js,hub.css} ยท serve.ts apiState() ยท verified end-to-end by apps/figma-app/bots-sim.mjs (plays a match โ†’ asserts /api/state โ†’ asserts the hub renders it)

Debug console (/debug)

live
Debug bot simulator console mid-match
ops-debug.png ยท the bot simulator mid-match โ€” see Bots for the transcript

What. Chat-shaped access to the real CommandBus plus the element gallery. The fastest way to demo the game end-to-end without a phone or a WhatsApp account โ€” documented in depth in Bots.

lives: apps/debug/ ยท /api/sim in serve.ts (isolated scratch store)

Deploy pipeline

live
git push (main)
   โ””โ”€โ–ถ GitHub Actions: .github/workflows/deploy.yml
         โ”œโ”€ bun setup
         โ”œโ”€ bash scripts/build-deploy.sh        โ† assembles deploy/public from the monorepo
         โ”‚     site โ†’ / ยท design โ†’ /design ยท web app โ†’ /app ยท hub โ†’ /hub ยท debug โ†’ /debug
         โ”‚     slack โ†’ /slack ยท connect โ†’ /connect ยท demo โ†’ /demo ยท system โ†’ /system
         โ”‚     figma-app โ†’ /figma-app ยท wiki โ†’ /wiki ยท functions/ โ†’ /api/{ai,state}
         โ”œโ”€ wrangler pages deploy โ†’ Cloudflare Pages (project "rwf")
         โ””โ”€ bun test packages/game-core packages/bot-core apps/api   โ† 122 tests gate every push
~20 seconds later: live at rwf.qalarc.com

How it works. The deploy bundle is assembled by scripts/build-deploy.sh โ€” pure copies, no per-app build except the web app bundle (Bun build, minified). Pages Functions (deploy/functions/api/) provide the AI proxy and state endpoint in production, mirroring the local serve.ts routes. The local dev story is one command โ€” bun serve.ts on :4173 serves everything including this wiki at /wiki โ€” so local and production surface the same paths.

Always-on hosting: the GMKtec minirig kit is prepared (scripts/hosting/ โ€” install script, systemd units for the Slack bot) for bots that run without a dev machine; see blocker T3 in Status.

lives: scripts/build-deploy.sh ยท .github/workflows/deploy.yml ยท deploy/functions/api/ ยท scripts/hosting/ ยท docs/16_HOSTING.md

The REST API (apps/api)

built ยท unwired

What. A unified backend for crews, players, matches and seasons โ€” the foundation that replaces the app's localStorage and the bots' file state with one source of truth.

How it works. Bun server on :4174 with CORS for the dev origin and production domain. Persistence is a single JSON file (.data/api-db.json) written atomically (tmp + rename) so a crash never corrupts the store. Crew records carry the 5-char join codes (no 0/O/1/I) that match the CREW-XXXX grammar the app and bots already display. The wiring step (app โ†’ API โ† bots) is mechanical and tracked as blocker T5.

lives: apps/api/src/{main,routes,db}.ts
proven by: apps/api/test/api.test.ts โ€” 24 tests, run in CI

AI layer

live ยท metered

What. GLM-5.3 behind a server-side proxy โ€” powers the site's AI guide, the app narrator and the bots' taunts/digest one-liners.

How it works. Clients post to /api/ai; the key lives server-side only (local .env / Pages Function secret) and is never shipped to a client. The proxy injects a system prompt describing the whole product, keeps the last 12 messages, caps content length, rate-limits 60/min, preserves upstream error classes (429 โ‰  502), and every consumer has a canned fallback โ€” if the AI is asleep the product still talks. Locally the same endpoint is served by serve.ts; in production by the Pages Function.

lives: serve.ts aiChat() ยท deploy/functions/api/ai.js ยท packages/bot-core/src/ai.ts (2s timeout + fallback)

The test suites

~700 checks green

Unit โ†’ integration โ†’ headless-browser. Every push runs the packaged suites; the E2E walks run on demand and produce the screenshots this wiki is built from.

SuiteTestsWhat it provesRun
game-core โ€” ENGINE V4110the daily model: createDay/logSet/closeDay, first-to-target wins, bank/fail outcomes, streaks, seasons 1:1, stakes lifecycle, SOT power-up canon (pure-gain steal, shield saves, bomb fuse)bun test packages/game-core
bot-core โ€” bus + gfamily + SOT additions89command grammar, aliases, cards, store behaviour, error cards, rematch/nemesis/digest, the daily-model additionsbun test packages/bot-core
api โ€” api.test.ts24REST routes, crew codes, atomic persistencebun test apps/api
sot (v4) โ€” e2e.mjs120 checksthe full SOT product walk on the real app: onboard โ†’ wizard โ†’ season โ†’ steal โ†’ logs โ†’ Daily Wins โ†’ bombs โ†’ charity resolution โ†’ join flow; language + console gates; zero console errorsbun apps/sot/e2e.mjs
sot (v4) โ€” e2e-states.mjs59 checksthe critical states: offline banner + queued logs + badge, reconnect replay, sync conflict + duplicate warning sheets (confirm/undo), rest-day home, winner-known + battle-complete transitions; zero console errorsbun apps/sot/e2e-states.mjs
figma-app (v1) โ€” engine.test.js23the 300-era engine port matches the spec (incl. power-ups)bun test apps/figma-app/engine.test.js
figma-app (v1) โ€” e2e / e2e-daily / e2e-squads122 + 74 + 90the v1 product walks: full journey, the temporal DZ loop with time travel, squads + wagersbun apps/figma-app/e2e.mjs โ€ฆ
board (v2) โ€” e2e / geom56 + 27the board presentation + lane geometry (centred, 1.2โ€“1.4ms frames)bun apps/board/{e2e,geom}.mjs
v3 โ€” e2e / geom69 + 35the 3D course + SFX wiringbun apps/v3/{e2e,geom}.mjs
figma-app (v1) โ€” bots-sim.mjsโ€”plays a real match through the CommandBus then asserts /api/state + the hub render itbun apps/figma-app/bots-sim.mjs
wiki โ€” walk.mjs25 checksthis wiki: 10 pages load with zero console errors, every referenced asset resolves, every PNG decodesbun apps/wiki/test/walk.mjs

CI runs the packaged suites on every deploy push โ€” a red suite blocks nothing at Cloudflare but fails the workflow loudly. The E2E walks are the design contract: if a screen breaks, its screenshot can't be taken. v4's two walks were the wiki's own source material โ€” 120 + 59 checks re-run for this page's screenshots.

lives: the test dirs above ยท CI gate in .github/workflows/deploy.yml ยท v4 walks measured 4 Sep 2026: 120 + 59 pass ยท zero console errors
โ† backAvatars