The builder could only create games; nothing noticed when a live template stopped working, and there was no way to tell whether a builder change made it better or worse. - repair -id ID: boots an existing template as nodes run it; if it fails, the same investigation as for a draft runs and the model changes only what the diagnosis says. The worker handles "repair" jobs (gameId from the panel) and opens "fix(games): repair ..." on draft/<id>; a template that boots as it is reports "nothing to repair" and opens no PR. The boot and fix loop is now shared by builds and repairs. - validate.yml runs weekly and boot-tests every game; failed games are sent to the panel (X-GBR-Event: games_boot_failed) to queue repairs. - eval -ids all: builds games that already have a template from scratch, blind, and scores each draft against it (boots, same image and app, ports, ready line, settings found) into eval.md and eval.json. - research skips our own sites, so neither research nor eval reads our templates back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
95 lines
5.9 KiB
Markdown
95 lines
5.9 KiB
Markdown
# AI builder (gbrgames worker)
|
|
|
|
The panel's **AI builder** (Admin → Games → AI Config Generator) is run by a worker we host ourselves.
|
|
The panel only keeps a queue; every result is a pull request to this repo, which CI validates and
|
|
boot-tests like any other.
|
|
|
|
```
|
|
Panel Builder worker (gbrgames worker, Docker)
|
|
───── ─────────────────────────────────────────
|
|
admin starts a job ──► queued ◄─poll── research: web search + page reader + model
|
|
progress / log / variants ◄─post── build: first boot → draft → boot with the node-agent
|
|
"Review draft PR" → branch draft/<id>-<job> + PR as the bot ──► CI (validate + boot test)
|
|
```
|
|
|
|
1. **Research** (`gbrgames research -name N`): searches the web, reads the best pages, and has the model list
|
|
the server's variants (edition, image or SteamCMD app, startup, ports). Every image and Steam app is
|
|
checked for real (`docker manifest inspect`, anonymous `steamcmd +app_info_print`) before the admin
|
|
sees it.
|
|
2. The admin picks a variant in the panel.
|
|
3. **Build** (`gbrgames build -name N -variant @v.json`): first boot of the image untouched (`first-boot`), a
|
|
draft by the model (`draft`, rules in `AUTHORING.md`), then the draft is deployed with the node-agent's
|
|
`BootTest`, the same check CI runs. A failed boot is investigated before the model changes anything
|
|
(see below).
|
|
|
|
**Every** server the builder starts, first boots included, is deployed by the node-agent's own code, so
|
|
it runs exactly as on a node. A boot passes when the server reaches its ready line, stays up for
|
|
`holdSec` (60 s) after it, and listens on every port the template publishes. Files the server writes
|
|
anywhere but `/server` and `/data` (the image's own `VOLUME` folders included) are reported as lost:
|
|
nodes recreate the container on every start.
|
|
|
|
Steam games are downloaded once per build into a cache volume (`gbr-steamcache-<app>`, labelled
|
|
`gbr.cache`) and copied into each boot, so later boots only check the files. `STEAM_CACHE=off` turns
|
|
it off.
|
|
4. The worker pushes `games/<id>/` to a branch and opens a pull request as the bot account. A draft that
|
|
validates but never booted still gets a PR, titled `WIP:`, with what's left listed. Nothing reaches
|
|
players until a person merges it and enables the game.
|
|
|
|
The builder is **game-agnostic**: everything about a game comes from that game's research, first boot and
|
|
boot tests. No game's name, id or code may appear in the builder's source, its rules for the model
|
|
(`AUTHORING.md`), the schemas or this file, and the model is never shown another game's template (its format
|
|
example is an invented server). `TestBuilderIsGameAgnostic` fails the build if one does.
|
|
|
|
The worker only connects out (to the panel, Gitea, the model and the search services).
|
|
|
|
## Keeping games working
|
|
|
|
- **Repair** (`gbrgames repair -id ID`): boots an existing template as nodes run it. If it boots, there is
|
|
nothing to do. If not, the same investigation as for a draft runs, the model changes only what the
|
|
diagnosis says, and the worker opens `fix(games): repair …` on `draft/<id>` (replacing any open draft of
|
|
that game). The panel queues repairs from a game's admin page, or by itself (next point).
|
|
- **Weekly check**: `validate.yml` boot-tests every game each Monday. Each game that no longer boots is sent
|
|
to the panel (`X-GBR-Event: games_boot_failed`), which queues a repair job for it.
|
|
- **Eval** (`gbrgames eval -ids all`): builds games that already have a template from scratch, blind (the
|
|
builder never reads `games/`, and research skips our own sites), and scores each draft against the merged
|
|
template: boots, same image and Steam app, same ports, ready line, settings found. Run it before and after
|
|
changing the builder or the model. It takes as long as the builds; on the builder VM:
|
|
|
|
```bash
|
|
docker exec gbr-builder sh -c 'cd /work && rm -rf eval && git clone -q https://www.gitea.gbreed.com/gbreed-public/gbr-games.git eval && cd eval && gbrgames eval -ids all -out eval.md && cat eval.md'
|
|
```
|
|
|
|
## Requirements
|
|
|
|
A Docker host with the isolated `gbr-boottest` network (`RUNNER.md`, step 1), so servers booted by the
|
|
builder can't reach private networks. Enough memory for the largest server it builds (the biggest need ~16 GB) and
|
|
disk for SteamCMD downloads. The image `gbr-builder:latest` is published by
|
|
`.gitea/workflows/builder-image.yml` whenever `cmd/` changes on main.
|
|
|
|
## Configuration
|
|
|
|
All through environment variables; nothing about where it runs lives in this repo.
|
|
|
|
| Variable | What |
|
|
| --- | --- |
|
|
| `PANEL_URL` | The panel the worker takes jobs from |
|
|
| `BUILDER_TOKEN` | Shared secret the panel checks on `/api/v1/builder/**` (same value in the panel's env) |
|
|
| `GITEA_TOKEN` | The bot account's token (repository read/write; the account needs Write on this repo): pushes draft branches, opens PRs as that account. Checked when the worker starts |
|
|
| `OLLAMA_URL` | Ollama for the model (or `MODEL_PROVIDER=gemini` with `GEMINI_API_KEY`) |
|
|
| `SEARXNG_URL`, `READER_URL` | A SearXNG instance (JSON API) and a page-to-text reader (`READER_URL/<page url>`) |
|
|
| `MODEL`, `POLL_INTERVAL`, `WORKER_NAME`, `WORK_DIR` | Optional: model name, poll interval (20s), name shown in the panel, work folder |
|
|
| `BOT_NAME` | Optional: commit author when the token can't read its own account (no `read:user` scope); default `gbr-bot` |
|
|
|
|
## Running a build by hand
|
|
|
|
On any machine with Docker and the `gbr-boottest` network, with `OLLAMA_URL`, `SEARXNG_URL` and
|
|
`READER_URL` set:
|
|
|
|
```bash
|
|
go run ./cmd/gbrgames research -name "<game>" -out research.json
|
|
# pick a variant from research.json into v.json, then:
|
|
go run ./cmd/gbrgames build -name "<game>" -variant @v.json -out build.json
|
|
```
|
|
|
|
The draft lands in `games/<id>/`, first-boot reports in `firstboot-out/`.
|