Launch options must be documented. A draft passed an option its server doesn't have; servers ignore unknown options, so it booted and passed every boot test while its setting did nothing. build now reads the pages research based the variant on, in full, and the drafter rejects any launch option in the startup command that none of them, nor the image's own scripts, mention. Shell tools' options (mkdir -p, [ -n ...]) are skipped; the check is off without sources. The builder is game-agnostic: - AUTHORING.md (the model's rules), the research prompt and the schema describe the rules without naming a game. - The draft prompt no longer includes real templates from the repo. Its format example is one invented server (no real command, options, ports or app id), checked by TestExampleTemplateFollowsTheRules. - Comments, help texts, BUILDER.md and test fixtures use invented names and values. - TestBuilderIsGameAgnostic reads every game's name and id from games/ and fails if any appears in the builder's code, tests, rules, schemas or docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.0 KiB
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)
- 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, anonymoussteamcmd +app_info_print) before the admin sees it. - The admin picks a variant in the panel.
- Build (
gbrgames build -name N -variant @v.json): first boot of the image untouched (first-boot), a draft by the model (draft, rules inAUTHORING.md), then the draft is deployed with the node-agent'sBootTest, the same check CI runs. Failures (crash, error lines, saves written outside/server) go back to the model, three boots at most. - 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, titledWIP:, 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:
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/.