Files
gbr-games/BUILDER.md
T
gregandClaude Opus 5.5 04967a8a16 feat(builder): every boot through the node-agent, every lost file counted, Steam cache
What a builder boot sees is now what a node does:
- First boots (untouched, or -as-deployed with exactly a new server's
  settings) are deployed by the node-agent's BootTest in keep mode; the
  builder only watches the console and inspects what's left. The Go copy of
  the deploy (its own docker run, mounts and startup prefix) is gone. It had
  drifted: it gave the image's VOLUME folders their own volumes, which nodes
  don't, so saves there looked safe while every start loses them.
- Files written outside /server and /data are counted whatever their type
  (it only counted config-like extensions, so .sav or .db saves went
  unseen), including the image's VOLUME folders; logs, pid and lock files
  and system folders are left out.
- Boot tests ask the server to stay up holdSec (60 s, test.yaml can set it)
  after its ready line and to listen on every published container port
  (needs the node-agent's matching BootTest).
- Steam apps are downloaded once per build into a gbr.cache volume and copied
  into each boot, so later boots only check the files (STEAM_CACHE=off).
- RUNNER.md no longer suggests docker system prune, which deletes the
  gbr-boottest network when nothing is using it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 00:00:50 -04:00

78 lines
4.7 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).
## 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/`.