Chapter 6 · operations
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.
The ops + corporate console: live match telemetry, bot health, and an activity feed — everything the demo runner needs on one screen.

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.
apiState() · verified end-to-end by apps/figma-app/bots-sim.mjs (plays a match → asserts /api/state → asserts the hub renders it)
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.
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.
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.
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.
aiChat() · deploy/functions/api/ai.js · packages/bot-core/src/ai.ts (2s timeout + fallback)Unit → integration → headless-browser. Every push runs the suites; the E2E walks run on demand and produce the screenshots this wiki is built from.
| Suite | Tests | What it proves | Run |
|---|---|---|---|
| game-core — core.test.ts | 29 | 300-format math: scoring, handicaps, closure, comeback, pot, standings | bun test packages/game-core |
| game-core — season.test.ts | 15 | 3/2/1 + MVP, ladder ordering, forgiveness, relegation | |
| game-core — gfamily.test.ts | 15 | second wave: rematch, photo finish, nemesis, digest inputs | |
| bot-core — bus.test.ts | 27 | command grammar, aliases, cards, store behaviour, error cards | bun test packages/bot-core |
| bot-core — gfamily.test.ts | 12 | rematch/nemesis/digest commands over the bus | |
| api — api.test.ts | 24 | REST routes, crew codes, atomic persistence | bun test apps/api |
| figma-app — engine.test.js | 23 | the engine port matches the spec (incl. power-ups) | bun test apps/figma-app/engine.test.js |
| figma-app — e2e.mjs | ~60 steps | full product walk in headless Chromium; asserts DOM + state at every step, fails on any console error; writes the walk screenshots | bun apps/figma-app/e2e.mjs |
| figma-app — e2e-daily.mjs | ~40 steps | the temporal loop with time travel: DZ1→2→3, day close, recap, day 2 | bun apps/figma-app/e2e-daily.mjs |
| figma-app — bots-sim.mjs | — | plays a real match through the CommandBus then asserts /api/state + the hub render it | bun apps/figma-app/bots-sim.mjs |
| wiki — walk.mjs | — | this wiki: every page loads with zero console errors, every referenced asset resolves | bun apps/wiki/test/walk.mjs |
CI runs the three packaged suites (122 tests) 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.