Operator guide

Run, test & deploy BrandRadar AI

A plain-language guide to running the app locally, testing it, and the checklist between here and a live launch. Written for the operator and any developer picking it up.

1 · What BrandRadar is

BrandRadar AI checks whether ChatGPT, Perplexity, Gemini, and Google AI Overviews recommend a local business, scores that visibility 0–100, and generates a prioritized playbook of fixes — then re-scans to prove the fixes worked. The first scan is free, with no signup.

2 · The shape of the system

The app is multi-process, and that’s load-bearing: the queue worker runs as a separate long-running process, never inside serverless Next.js. Web and worker share the packages and one Redis.

apps/web        Next.js 15 — UI + route handlers (SSE / Stripe / embed)
apps/worker     BullMQ workers — scan engine, enrichment, playbook (ALWAYS-ON)
packages/core   pure logic: engines, scoring, site audit, action layer
packages/db     Drizzle + Postgres schema, tenancy, migrations
packages/queue  BullMQ queues + the SSE event bus
packages/cache  Redis (rate limits, scan progress, pub/sub)

3 · Run it locally (Docker)

The stack runs via Docker Compose — Postgres, Redis, web, worker, Adminer, plus a one-shot migrate container that runs migrations and seeds the admin, then exits.

# from the repo root
PG_PORT=5440 REDIS_PORT=6390 docker-compose up -d

On this machine use the standalone docker-compose (v2.x) — the docker compose plugin subcommand isn’t wired here.

# rebuild the worker after changing apps/worker or packages/core|db|queue
PG_PORT=5440 REDIS_PORT=6390 docker-compose up -d --build worker

# re-run migrations + seed only
PG_PORT=5440 REDIS_PORT=6390 docker-compose run --rm --build migrate

Dev gotcha: the first hit to any route compiles in dev (2–20s cold), then it’s fast — production (next build) prefetches and is instant. Don’t mistake cold-compile lag for a bug; it’s the #1 cause of “flaky” test timeouts.

4 · Testing — what the types mean

TypeChecksSpeedApp running?
Unitone function in isolation (e.g. the score math)fastno
E2Ea whole journey through the real app in a real browserslowyes
# typecheck (run before every commit)
pnpm --filter @brandradar/web typecheck

# unit tests
pnpm --filter @brandradar/web test

5 · E2E (end-to-end) tests — the full how-to

E2E = end-to-end. Playwright drives a real Chrome browser through whole flows — clicking, typing, navigating — exactly like a user. The core spec, magic-moment.spec.ts, walks the front door → reveal → score → the fix CTA → playbook.

Step 1 — bring the stack up (§3) and confirm the app loads.

Step 2 — enable stub mode so scans finish in seconds

Add this line to .env, then recreate the app containers:

BR_FORCE_STUB=1
PG_PORT=5440 REDIS_PORT=6390 docker-compose up -d --force-recreate web worker

Step 3 — install the browser once, then run the tests

cd apps/web
pnpm exec playwright install chromium
E2E_BASE_URL=http://localhost:3100 pnpm exec playwright test \
  e2e/magic-moment.spec.ts e2e/menu-nav.spec.ts --reporter=line

The Playwright config defaults to port 3000; this stack is on 3100, so the E2E_BASE_URL=http://localhost:3100 prefix is required.

Reading the result

Known flakiness (not a bug): against the dev server the first test of a cold run can time out while the route compiles — re-run and it passes. For a clean signal, run the guards in isolation or against a next build app.

⚠️ After testing, remove BR_FORCE_STUB=1 from .env — it fakes the AI engines and must never be on in production.

6 · Pre-deploy checklist

Topology: web (Vercel or Fly) + always-on worker (Fly — separate process, never serverless) + Neon Postgres + Upstash/Fly Redis + Cloudflare R2.

Blockers — must do

Confirm the intended-state switches

Should do before launch

Fuller detail (including the seeded admin credentials and the polish-session changelog) lives in docs/OPERATOR-GUIDE.md in the repository.