{"id":51899320,"url":"https://github.com/googlecloudplatform/race-condition","last_synced_at":"2026-07-26T14:00:59.106Z","repository":{"id":353860185,"uuid":"1193970689","full_name":"GoogleCloudPlatform/race-condition","owner":"GoogleCloudPlatform","description":"The open source multi-agent simulation from the Developer Keynote at Google Cloud Next '26. A deployable reference architecture for autonomous AI agents using Gemini and ADK.","archived":false,"fork":false,"pushed_at":"2026-07-26T02:04:26.000Z","size":23458,"stargazers_count":207,"open_issues_count":54,"forks_count":68,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-07-26T02:06:18.007Z","etag":null,"topics":["agent-development-kit","ai-agents","angular","cloud-run","gemini","golang","google-cloud","multi-agent-systems","python","reference-architecture","threejs"],"latest_commit_sha":null,"homepage":"https://developers.google.com/solutions/learn/race-condition","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GoogleCloudPlatform.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-27T19:23:25.000Z","updated_at":"2026-07-26T02:04:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/GoogleCloudPlatform/race-condition","commit_stats":null,"previous_names":["googlecloudplatform/race-condition"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/GoogleCloudPlatform/race-condition","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleCloudPlatform%2Frace-condition","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleCloudPlatform%2Frace-condition/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleCloudPlatform%2Frace-condition/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleCloudPlatform%2Frace-condition/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GoogleCloudPlatform","download_url":"https://codeload.github.com/GoogleCloudPlatform/race-condition/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleCloudPlatform%2Frace-condition/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35916664,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-26T02:00:06.503Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-development-kit","ai-agents","angular","cloud-run","gemini","golang","google-cloud","multi-agent-systems","python","reference-architecture","threejs"],"created_at":"2026-07-26T14:00:57.769Z","updated_at":"2026-07-26T14:00:59.098Z","avatar_url":"https://github.com/GoogleCloudPlatform.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Race Condition\n\n[![CI](https://github.com/GoogleCloudPlatform/race-condition/actions/workflows/ci.yml/badge.svg)](https://github.com/GoogleCloudPlatform/race-condition/actions/workflows/ci.yml)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/GoogleCloudPlatform/race-condition/blob/main/LICENSE)\n![Go](https://img.shields.io/badge/Go-1.25+-00ADD8?logo=go\u0026logoColor=white)\n![Python](https://img.shields.io/badge/Python-3.13+-3776AB?logo=python\u0026logoColor=white)\n![Node.js](https://img.shields.io/badge/Node.js-24+-339933?logo=nodedotjs\u0026logoColor=white)\n![Angular](https://img.shields.io/badge/Angular-21-DD0031?logo=angular\u0026logoColor=white)\n![Google ADK](https://img.shields.io/badge/Google_ADK-1.31-4285F4?logo=google\u0026logoColor=white)\n![Google Cloud](https://img.shields.io/badge/Google_Cloud-4285F4?logo=googlecloud\u0026logoColor=white)\n\n[![Open in Cloud Shell](assets/cloud_shell_button.svg)](https://shell.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/GoogleCloudPlatform/race-condition.git\u0026cloudshell_tutorial=tutorial.md)\n\u003c!-- TODO(public-release): the cloudshell_git_repo URL above assumes the\n     public GitHub mirror at GoogleCloudPlatform/race-condition. Verify the\n     org/repo name when the public mirror lands and swap if different. --\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/images/banner.png\" alt=\"Race Condition\" /\u003e\n\u003c/p\u003e\n\nA multi-agent marathon simulation built with [Google ADK](https://google.github.io/adk-docs/) and Gemini. AI agents plan a marathon route through Las Vegas, simulate the environment around it (weather, traffic, crowds), and run the race autonomously. Everything talks over the [A2A protocol](https://github.com/google/a2a).\n\nOriginally demoed at the Google Cloud Next '26 Developer Keynote.\n\n## What this repo is good for\n\nThis is the open-source release of the multi-agent simulation we ran at the Google Cloud Next '26 Developer Keynote. It is also a working reference architecture for a few patterns that are hard to study in isolation:\n\n- **Cached vs live replay.** The frontend can replay NDJSON streams recorded from real agent runs, indistinguishable from a live session. We use it for keynote reliability; you can use it to demo, test UI changes, or teach without paying for LLM calls.\n- **A deterministic runner variant.** `runner_autopilot` makes the same shape of decisions a real LLM-powered runner would, but with zero API calls. It is the right baseline when you want to measure the simulator under load without your bill becoming the experiment.\n- **A planner ladder.** Three variants of the planner (`planner`, `planner_with_eval`, `planner_with_memory`) show what each layer actually adds — eval gating, persistent memory in AlloyDB — as separate agents instead of feature flags.\n- **The Hub session pattern.** A Go gateway routes WebSocket traffic to and from Python ADK agents over A2A, with batching to keep the system from thundering-herding itself when hundreds of runners broadcast on the same tick.\n- **Backend-driven UI via A2UI.** Agents emit UI primitives (cards, route lists, action buttons) over the wire instead of the frontend hard-coding layouts per response shape.\n\nThe README and the in-repo agent skills are organized so you can pull any one of these threads without running the whole thing.\n\n## What's different from the keynote build\n\nA few things you saw on stage are not in this repo, and that is intentional:\n\n- Some demos used products that are still in private preview. We cut those paths rather than ship code only the preview list could run.\n- Some resources (always-on Memorystore at production sizing, GKE autoscaling) cost more than is reasonable for a demo. The default deploy scales compute to zero between runs.\n- Some choices in the code reflect a keynote schedule — deadlines measured in days for things you would normally give weeks. Those edges are real. Other choices look strange and are deliberate; the cached-replay mode is the obvious one. If you are not sure which is which, the agent skills (`exploring-the-codebase`) explain the design decisions.\n\nWe will fold preview-gated features back in as those products move to public preview.\n\n## How the agents split the work\n\nA multi-agent system on Google Cloud, dressed up as a marathon. The agents split the work like this:\n\n- A Planner designs the race course using Google Maps data, GIS tools, and a bit of financial modeling.\n- A Simulator runs the environment tick by tick: weather, traffic, crowds, race progression.\n- Runner agents (the NPCs) each decide their own pacing, hydration, and strategy as the race unfolds.\n\nThey coordinate over the Agent-to-Agent (A2A) protocol. A Go gateway sits in the middle and routes WebSocket traffic between a 3D Angular/Three.js frontend and the Python agents.\n\nLocally, it runs on Docker Compose (Redis, Pub/Sub emulator, PostgreSQL). In the cloud, it deploys to Cloud Run and Vertex AI Agent Engine.\n\n## Get started with your AI coding assistant\n\nThe fastest way to get Race Condition running is to ask your AI coding assistant to do it. The repo ships an `AGENTS.md` at the root that any modern AI dev tool will read on entry, plus four detailed skill files under `.claude/skills/`.\n\n### If you use Claude Code\n\nSkills auto-discover. Open the repo and ask:\n\n```bash\ncd race-condition\nclaude\n\u003e Set up this project and get the simulation running\n```\n\nClaude will load the `getting-started` skill on its own.\n\n### If you use Gemini CLI, OpenCode, Cursor, or GitHub Copilot\n\nThese tools read `AGENTS.md` automatically but do not auto-discover the skill files. Point your assistant at the relevant skill explicitly:\n\n```bash\ncd race-condition\ngemini   # or opencode, or open the folder in Cursor / VS Code\n\u003e Read .claude/skills/getting-started/SKILL.md and walk me through it\n```\n\nSame pattern for any other task. Swap in the skill that fits:\n\n| Task | Skill to point at |\n| --- | --- |\n| First-time local setup | `.claude/skills/getting-started/SKILL.md` |\n| Understanding the architecture and design decisions | `.claude/skills/exploring-the-codebase/SKILL.md` |\n| Deploying to your own GCP project | `.claude/skills/deploying/SKILL.md` |\n| Preparing a contribution (hooks, tests, PR) | `.claude/skills/contributing/SKILL.md` |\n\nThe skill files are plain Markdown with YAML frontmatter. The assistant will walk you through GCP authentication, API enablement, dependency installation, and starting the simulation. Some steps (like `gcloud auth login`) need you to act in a browser; the assistant will tell you when.\n\nYou can also set up manually. See the [Quickstart](#quickstart) section below.\n\n## Architecture\n\n```mermaid\ngraph TD\n    Frontend[\"Frontend\u003cbr/\u003eAngular + Three.js\"]\n    Gateway[\"Gateway\u003cbr/\u003eGo · WebSocket · Gin\"]\n    Planner[\"Planner Agent\u003cbr/\u003ePython · ADK\"]\n    Simulator[\"Simulator Agent\u003cbr/\u003ePython · ADK\"]\n    Runners[\"Runner Agents\u003cbr/\u003ePython · ADK\"]\n    Redis[\"Redis\"]\n    PubSub[\"Pub/Sub\"]\n    Postgres[\"PostgreSQL\u003cbr/\u003epgvector\"]\n\n    Frontend --\u003e|WebSocket| Gateway\n    Gateway --\u003e|A2A| Planner\n    Gateway --\u003e|A2A| Simulator\n    Gateway --\u003e|A2A| Runners\n    Planner ---|A2A| Simulator\n    Simulator ---|A2A| Runners\n    Gateway --\u003e Redis\n    Gateway --\u003e PubSub\n    Planner --\u003e Postgres\n```\n\n| Component | What it does |\n| --- | --- |\n| Gateway | Central WebSocket hub (Go/Gin). Routes requests, manages sessions, bridges frontends with agents via A2A. |\n| Planner | Designs marathon routes using GIS data, Google Maps MCP tools, and financial modeling. Three variants: base, with eval (LLM-as-Judge), and with memory (AlloyDB persistence). |\n| Simulator | Runs the race as a pipeline: pre-race setup, a tick-based loop engine (up to 200 ticks), and post-race analysis. Spawns and coordinates runner agents. |\n| Runners | Individual NPC agents. The LLM-powered variant uses Gemini to make strategic decisions each tick. The autopilot variant is deterministic (no LLM calls). |\n| Frontend | Angular 21 + Three.js app rendering a 3D Las Vegas environment with real-time runner positions, weather, and crowd reactions. |\n| Infrastructure | Redis (state/pub-sub fanout), Pub/Sub emulator (telemetry streaming), PostgreSQL with pgvector (route memory and embeddings). |\n\nFor the deep version of any of this, see [`docs/`](docs/README.md) — the\narchitecture subtree explains the topology, the protocols, and the design\ndecisions in detail.\n\n## Prerequisites\n\nInstall these before you start. `make check-prereqs` will verify them for you.\n\n| Tool | Version | What it's for | Install |\n| --- | --- | --- | --- |\n| Go | 1.25+ | Gateway, admin, and frontend BFF servers | [go.dev/dl](https://go.dev/dl/) |\n| Python | 3.13+ | AI agents (installed and managed by uv) | [python.org](https://www.python.org/downloads/) |\n| uv | latest | Python package manager, virtual env, and task runner | [docs.astral.sh/uv](https://docs.astral.sh/uv/) |\n| Node.js | 24+ | Frontend (Angular), admin dashboard, tester UI | [nodejs.org](https://nodejs.org/) |\n| Docker + Compose | latest | Local infrastructure: Redis, Pub/Sub emulator, PostgreSQL | [docs.docker.com](https://docs.docker.com/get-docker/) |\n| Google Cloud SDK | latest | `gcloud` CLI for auth and API enablement | [cloud.google.com/sdk](https://cloud.google.com/sdk/docs/install) |\n\n### Cost note\n\nCompute scales to zero by default (`min_instances=0`, `max_instances=1`), so you pay for it only when something is running. The unavoidable fixed cost is around $91/month for Memorystore Redis, Cloud SQL, and Cloud NAT. Each simulation run is roughly $3-4 in Gemini API calls. If you want to develop without burning API credits, use the `runner_autopilot` variant — it's deterministic and makes zero LLM calls.\n\nThe deploy entry point (`scripts/deploy.sh` and `make deploy`) prints the same breakdown and waits for confirmation before it provisions anything. Once you confirm, it pre-flights the project (enables the APIs Cloud Build needs and grants the Cloud Build default SA the IAM roles to run Terraform; safe to re-run), then submits the build.\n\nTear down with `cd infra \u0026\u0026 terraform destroy`. If you want to keep services warm and skip cold starts, bump `min_instances` for the services you care about in `infra/terraform.tfvars` via the `service_sizing` map and re-apply.\n\n## Quickstart\n\n### 1. Set up your GCP project\n\nYou need a GCP project with billing enabled where you are an Owner (or have `roles/aiplatform.user` at minimum). If you just created the project, you're already Owner.\n\n```bash\n# Log in and write Application Default Credentials in one step\ngcloud auth login --update-adc\n\n# Set your project (replace MY_PROJECT_ID everywhere below)\ngcloud config set project MY_PROJECT_ID\n\n# Enable required APIs\ngcloud services enable aiplatform.googleapis.com             # Vertex AI (agent LLM calls)\ngcloud services enable generativelanguage.googleapis.com     # Gemini API (GIS traffic tool)\ngcloud services enable cloudresourcemanager.googleapis.com   # Required by Pub/Sub client\ngcloud services enable pubsub.googleapis.com                 # Telemetry (emulated locally, but client validates the API)\ngcloud services enable iam.googleapis.com                    # ADC token exchange\n\n# Set the quota project so API calls are billed correctly\ngcloud auth application-default set-quota-project MY_PROJECT_ID\n```\n\n\u003e **Note:** API enablement can take a minute or two to propagate. If you see 403 errors on first start, wait a minute and run `make restart`.\n\n### 2. Clone and initialize\n\n```bash\ngit clone https://github.com/GoogleCloudPlatform/race-condition.git\ncd race-condition\n\n# Install everything and build (checks prereqs, creates .env, installs deps)\nmake init\n\n# Set your GCP project in .env (this is what the agents actually read)\nsed -i '' 's/your-gcp-project-id/MY_PROJECT_ID/g' .env  # macOS\n# sed -i 's/your-gcp-project-id/MY_PROJECT_ID/g' .env   # Linux\n```\n\nThe `sed` command sets `GOOGLE_CLOUD_PROJECT` and `PROJECT_ID` in `.env`. The agents read their project from this file, not from `gcloud config`.\n\n### 3. Start the simulation\n\n```bash\nmake start\n```\n\nThe frontend opens at http://localhost:9119. The admin dashboard at http://localhost:9100 shows service health.\n\n### What `make init` does\n\n1. Checks that Go, Python, uv, Node.js, and Docker are installed.\n2. Copies `.env.example` to `.env` (if `.env` doesn't exist).\n3. Installs Python dependencies with `uv sync`.\n4. Installs and builds the frontend and web UIs.\n5. Starts Docker infrastructure (Redis, Pub/Sub emulator, PostgreSQL).\n6. Builds Go services.\n\n### What `make start` does\n\n1. Verifies `.env` exists and no services are already running.\n2. Starts Docker infrastructure.\n3. Checks that all required ports are free.\n4. Launches all services via [Honcho](https://github.com/nickstenning/honcho) (13 processes).\n5. Logs output to `logs/simulation.log`.\n\nUse `make stop` to shut everything down, or `make restart` to cycle.\n\n## Using the frontend\n\nThe frontend boots in **Cached** mode. That is on purpose. When you are presenting to thousands of people on a stage with one network drop, you do not want a live LLM call to be the difference between a clean demo and a long, awkward pause. Cached mode replays NDJSON streams that were recorded from real runs, so the timing is real and the agent output is real, but nothing depends on the network.\n\n**Live** mode talks to the agents over WebSockets and runs them for real.\n\nToggle between the two with `Ctrl+L`, or use the segmented control in the chat panel's Settings dropdown. A small indicator flashes the new mode in the corner.\n\n### Demos\n\nThe simulation ships with nine demos. Each one configures the active agent, the cached recording, and a few UI defaults. Switch between them with `Ctrl+\u003ckey\u003e`:\n\n| Hotkey | Demo | What it shows |\n| --- | --- | --- |\n| `Ctrl+0` | Sandbox | Held intro shot and a pre-loaded marathon plan. Press `Ctrl+I` to release the camera. |\n| `Ctrl+1` | Build agents with Agent Platform | The base planner working on its own. |\n| `Ctrl+2` | Creating multi-agent systems | Planner with eval (LLM-as-Judge). |\n| `Ctrl+3` | Enhancing agents with memory | Planner backed by AlloyDB route memory. |\n| `Ctrl+4` | Debugging at scale | Simulator with deliberate fault injection. |\n| `Ctrl+5` / `Ctrl+Shift+5` | Intent to infrastructure with Cloud Assist | Base then upgraded variant. |\n| `Ctrl+7` / `Ctrl+Shift+7` | Securing agents | Insecure then secure variant. |\n\n`Ctrl+R` resets the current demo. Pressing the same demo's hotkey twice does the same thing.\n\n### Camera\n\n`Ctrl+A`, `Ctrl+S`, and `Ctrl+D` switch between Camera A, B, and C. Heads up: `Ctrl+D` also toggles the alternative side panels because of a hotkey collision we never split apart. Patches welcome.\n\n`Ctrl+I` plays the held camera intro (Sandbox uses this). `Ctrl+Shift+I` skips it.\n\n### Sandbox\n\nSandbox is the playground. It loads `planner_with_memory` and shows the Organizer's \"top 3 routes\" panel as soon as the page is ready. In cached mode you get three pre-recorded routes you can preview by clicking **Show Route**. Switch to Live mode and the same panel sends `list the top 3 best routes` to a fresh `planner_with_memory` agent — whatever that agent decides to return is what you see.\n\nIf you want to poke at the memory agent ad-hoc without spinning up a full simulation, this is the place to do it.\n\n## Google Maps API key (optional)\n\nThe Planner agent can use Google Maps MCP tools (`search_places`, `compute_routes`, `lookup_weather`) to design geographically accurate marathon routes. This requires a Google Maps API key. Without one, the planner still works but plans routes without live map data.\n\n### Step 1: Enable the Maps APIs\n\nEnable these APIs before creating the key -- the key restriction dropdown in the console only shows APIs that are already enabled.\n\n```bash\ngcloud services enable apikeys.googleapis.com        # API key management\ngcloud services enable agentregistry.googleapis.com  # ADK discovers Maps MCP server here\ngcloud services enable mapstools.googleapis.com      # Maps MCP server\ngcloud services enable places.googleapis.com         # search_places tool\ngcloud services enable weather.googleapis.com        # lookup_weather tool\n```\n\n\u003e **Note:** API enablement can take a minute or two to propagate. Wait a couple minutes before creating the key.\n\n### Step 2: Create an API key\n\n1. Open the [Credentials page](https://console.cloud.google.com/apis/credentials) in the Google Cloud Console.\n2. Click **Create credentials** \u003e **API key**.\n3. Copy the key value (you'll need it for Step 3).\n4. Click **Edit API key** (or click the key name in the list).\n5. Under **API restrictions**, select **Restrict key**.\n6. From the dropdown, select these APIs (use the filter box to find them):\n   - Cloud API Registry API\n   - Maps Grounding Lite API\n   - Places API (New)\n   - Weather API\n7. Click **Save**.\n\n### Step 3: Add the key to your .env\n\n```bash\nGOOGLE_MAPS_API_KEY=AIza...your-key-here\n```\n\nThen restart the simulation (`make restart`).\n\nThe planner resolves the key in this order:\n1. `GOOGLE_MAPS_API_KEY` environment variable (if set and non-empty).\n2. Google Cloud Secret Manager: `gcloud secrets versions access latest --secret=maps-api-key --project=$GOOGLE_CLOUD_PROJECT`.\n3. If neither is available, Maps tools are disabled and the planner logs a warning.\n\n## Project structure\n\n```\nrace-condition/\n├── agents/                     # Python AI agents (Google ADK)\n│   ├── planner/                #   Route planning with GIS + Maps MCP\n│   ├── planner_with_eval/      #   + LLM-as-Judge plan evaluation\n│   ├── planner_with_memory/    #   + AlloyDB route persistence\n│   ├── simulator/              #   Race engine (pipeline: setup → ticks → results)\n│   ├── simulator_with_failure/ #   Fault-injection test variant\n│   ├── runner/              #   LLM-powered marathon runner (Gemini/Ollama/vLLM)\n│   └── runner_autopilot/   #   Deterministic runner (no LLM calls)\n├── cmd/                        # Go service entry points\n│   ├── gateway/                #   WebSocket hub + A2A routing\n│   ├── admin/                  #   Admin dashboard server\n│   ├── tester/                 #   Tester UI server\n│   └── frontend/               #   Frontend BFF (serves Angular app)\n├── internal/                   # Go internal packages\n│   ├── hub/                    #   Session routing + WebSocket management\n│   ├── ecs/                    #   Entity-Component-System for simulation state\n│   ├── sim/                    #   Simulation lifecycle management\n│   ├── session/                #   Session store (Redis, in-memory)\n│   └── agent/                  #   A2A client + agent discovery\n├── web/                        # Web frontends\n│   ├── frontend/               #   Angular 21 + Three.js (3D visualization)\n│   ├── admin-dash/             #   Service health dashboard (Vite)\n│   ├── tester/                 #   Developer testing console (Vite + Tailwind)\n│   └── agent-dash/             #   Real-time agent debug console (Chart.js)\n├── gen_proto/                  # Generated protobuf code (Go + Python, committed)\n├── docker-compose.yml          # Redis, Pub/Sub emulator, PostgreSQL (pgvector)\n├── Dockerfile                  # Multi-stage build for all services\n├── Makefile                    # Build, test, lint, run targets\n├── Procfile                    # Service definitions for Honcho\n└── pyproject.toml              # Python dependencies (managed by uv)\n```\n\n## Key concepts\n\n### Agent-to-Agent (A2A) protocol\n\nAgents discover each other through agent cards served at `/.well-known/agent-card.json`. The gateway fetches these cards at startup and routes messages to the right agent based on declared skills.\n\n### Simulation pipeline\n\nThe simulator runs a `SequentialAgent` pipeline:\n\n```mermaid\ngraph LR\n    PreRace[\"Pre-race\u003cbr/\u003eParse plan, spawn runners\"] --\u003e RaceEngine[\"Race engine\u003cbr/\u003eLoopAgent, up to 200 ticks\"]\n    RaceEngine --\u003e PostRace[\"Post-race\u003cbr/\u003eCompile results\"]\n```\n\nEach tick, the simulator advances the race clock, updates conditions (weather, traffic, crowd density), and broadcasts state to all runner agents. Runners respond with their decisions (accelerate, brake, hydrate).\n\n### Runner agent variants\n\n| Variant | Model | Cost | Use case |\n| --- | --- | --- | --- |\n| `runner` | Gemini 3.1 Flash Lite (default) | Low | LLM-driven strategic decisions per tick |\n| `runner` (Ollama) | Gemma 4 (local) | Free | Local development without API costs |\n| `runner` (vLLM/GKE) | Gemma 4 on GKE | Self-hosted | Production-scale on Kubernetes |\n| `runner_autopilot` | None (deterministic) | Free | Baseline testing, no LLM calls |\n\nConfigure the runner model in `.env`:\n\n```bash\n# Gemini (default, requires Vertex AI)\nRUNNER_MODEL=gemini-3.1-flash-lite-preview\n\n# Ollama (local, free)\nRUNNER_MODEL=ollama_chat/gemma4:e2b\n\n# vLLM on GKE\nRUNNER_MODEL=openai/gemma-4-E4B-it\nVLLM_API_URL=http://localhost:8080/v1\n```\n\n## Services and ports\n\nAll ports are configured in `.env`. Defaults:\n\n| Service | Port | URL |\n| --- | --- | --- |\n| Frontend (3D) | 9119 | http://localhost:9119 |\n| Admin dashboard | 9100 | http://localhost:9100 |\n| Gateway API | 9101 | http://localhost:9101 |\n| Tester UI | 9112 | http://localhost:9112 |\n| Agent debug console | 9111 | http://localhost:9111 |\n| Planner | 9105 | |\n| Planner (with eval) | 9106 | |\n| Planner (with memory) | 9109 | |\n| Simulator | 9104 | |\n| Runner | 9108 | |\n| Runner (autopilot) | 9110 | |\n| Redis | 9102 | |\n| Pub/Sub emulator | 9103 | |\n| PostgreSQL | 9113 | |\n\n## Common make targets\n\n| Target | What it does |\n| --- | --- |\n| `make init` | One-time setup: installs deps, creates `.env`, starts infra, builds |\n| `make start` | Start all services |\n| `make stop` | Stop all services |\n| `make restart` | Stop then start |\n| `make test` | Run Go + Python + web tests |\n| `make build` | Build Go services |\n| `make lint` | Run Go + Python linters |\n| `make fmt` | Format all code |\n| `make coverage` | Generate coverage reports |\n| `make eval` | Run agent evaluations (requires Gemini API) |\n| `make check-prereqs` | Verify all tools are installed |\n\n## Deployment\n\nIn production, Race Condition runs on Cloud Run (gateway, frontend BFF), Vertex AI Agent Engine (Python agents), AlloyDB (route memory, embeddings), Memorystore Redis (sessions), and Pub/Sub (telemetry).\n\nThe `Dockerfile` is multi-stage and covers every service. The local `docker-compose.yml` mirrors the same topology, which is what makes \"works on my laptop\" actually mean something here.\n\n### Recovering from a failed bootstrap\n\nIf a Cloud Build run fails partway through `tf-apply-services`, the next run may fail at `tf-apply-base` with:\n\n```\nError: cannot destroy service without setting deletion_protection=false\n       and running `terraform apply`\n```\n\nThis happens when Terraform state holds Cloud Run service resources from the partial run, but those resources were created before the deletion-protection fix was applied to the local code. The recovery is one-shot: delete the orphan Cloud Run services via `gcloud`, then re-run the same Cloud Build:\n\n```bash\nPROJECT=$(gcloud config get-value project)\nREGION=us-central1\n\nfor SVC in admin dash frontend gateway runner-autopilot runner-cloudrun tester; do\n  gcloud run services delete \"$SVC\" \\\n    --project=\"$PROJECT\" --region=\"$REGION\" --quiet || true\ndone\n\n# Then re-run the bootstrap (Make target or scripts/deploy.sh).\n```\n\nGCP-side `deletionProtection` is `unset` on these services (the block is purely a Terraform-provider-level guard), so the deletes succeed without needing `gcloud run services update --no-deletion-protection` first. After the deletes, Terraform's next `apply` will refresh state, see the resources are gone, and re-create them cleanly with the new declarations.\n\n## Testing\n\n```bash\n# Run everything\nmake test\n\n# Just Go\nmake test-go\n\n# Just Python (skips slow/eval tests)\nmake test-py\n\n# Just web UIs\nmake test-web\n\n# Agent evaluations (calls Gemini, costs money)\nmake eval\n```\n\nPython tests run without real GCP credentials. A root `conftest.py` patches `google.auth.default` with mock credentials so agent modules can import and run tests offline.\n\n## Where to take this\n\nWe treat this as scaffolding more than a finished product. Plenty of ideas got cut on our way to the keynote stage that we would love to see someone pick up:\n\n- **Public safety agents.** Medics, traffic management, emergency response. What happens to the runner field when an agent has to reroute the marathon around an incident?\n- **Local economy agents.** Coffee shops, rideshare, vendors reacting to crowd density and weather. Pull the simulation out of pure logistics and into something more like a city.\n- **Less-cooperative runners.** What if some runner agents cheat — hide a shortcut, draft illegally, lie about their hydration? Can the simulator catch them?\n- **Spectator interaction.** Wire a phone, a Twitch chat, or a microphone into the gateway so the crowd can actually cheer for runners. The runner agents already model crowd density as an environmental input; turn that input into something a real human can move, and you have a live multi-agent system the audience is part of.\n\nThose are the ones we still talk about. If you build something interesting, open a PR or just tell us about it on the issue tracker.\n\n## Contributing\n\nContributions welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for the CLA process, code style, and PR guidelines.\n\n## Contributors\n\nThe humans who built this, grouped by what they led. Inspired by the [all-contributors](https://allcontributors.org/) spec, with role labels that match how the team actually worked.\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\n### 🧭 Technical Leadership\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/cwest\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/cwest\" width=\"100px;\" alt=\"Casey West\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eCasey West\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/njw275\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/njw275\" width=\"100px;\" alt=\"Nicholas White\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eNicholas White\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n### ⚙️ Backend Development\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/davenportjw\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/davenportjw\" width=\"100px;\" alt=\"Jason Davenport\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eJason Davenport\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/weifonghsia\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/weifonghsia\" width=\"100px;\" alt=\"Wei Hsia\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eWei Hsia\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/lsubatin\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/lsubatin\" width=\"100px;\" alt=\"Lucia Subatin\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eLucia Subatin\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/jackwotherspoon\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/jackwotherspoon\" width=\"100px;\" alt=\"Jack Wotherspoon\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eJack Wotherspoon\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/moficodes\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/moficodes\" width=\"100px;\" alt=\"Mofi Rahman\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eMofi Rahman\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n### 🖥️ Frontend Development\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/yogsther\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/yogsther\" width=\"100px;\" alt=\"Olle Kaiser\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eOlle Kaiser\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/oosmoxiecode\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/oosmoxiecode\" width=\"100px;\" alt=\"Klas Kroon\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eKlas Kroon\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n### 🎨 Design\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://www.linkedin.com/in/lisa-granlund/\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/u/0?v=4\" width=\"100px;\" alt=\"Lisa Granlund\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eLisa Granlund\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n### 📋 Project Management\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://github.com/tcmg\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/tcmg\" width=\"100px;\" alt=\"Tom Greenaway\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eTom Greenaway\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003cspan\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/u/0?v=4\" width=\"100px;\" alt=\"Jonatan Vallin\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eJonatan Vallin\u003c/b\u003e\u003c/sub\u003e\n        \u003c/span\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003ca href=\"https://www.linkedin.com/in/alexandrahsie/\"\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/u/0?v=4\" width=\"100px;\" alt=\"Alex Hsie\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eAlex Hsie\u003c/b\u003e\u003c/sub\u003e\n        \u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\n        \u003cspan\u003e\n          \u003cimg src=\"https://avatars.githubusercontent.com/u/0?v=4\" width=\"100px;\" alt=\"Jon Callard\"/\u003e\u003cbr /\u003e\n          \u003csub\u003e\u003cb\u003eJon Callard\u003c/b\u003e\u003c/sub\u003e\n        \u003c/span\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\n## License\n\nApache 2.0. See [LICENSE](LICENSE).\n\n## Disclaimer\n\nThis is not an officially supported Google product.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgooglecloudplatform%2Frace-condition","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgooglecloudplatform%2Frace-condition","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgooglecloudplatform%2Frace-condition/lists"}