The AI step of the game builder: a model writes a new game's template from facts the tools read off the real server, and the same checks as any template keep it honest.
Builds on #2 (inspect-image and first-boot); merge that first. Until then, this PR's diff also shows #2's commits.
Use
OLLAMA_URL=http://192.168.4.30:11434 go run ./cmd/gbrgames draft \
-name Valheim -image cm2network/steamcmd -steam-app 896660 \
-first-boot firstboot-out/valheim/report.json
-provider gemini (with GEMINI_API_KEY) switches to Gemini. It writes games/<id>/game.yaml and test.yaml; if they fail the checks, the problems go back to the model, up to 3 rounds.
What the model gets
AUTHORING.md (new): house rules for templates, for people and the AI alike. It covers how nodes really run servers (volume at /server and /data, uid 1000, how settings become env vars, e.g. RCONPort → RCONPORT) and the lessons from this week's bugs: x-generate passwords, no published RCON/REST ports, containerUser, heap ~1 GB below the limit, retry flaky installs, ready lines from a real boot.
The inspect-image facts, the first-boot report, and the two smallest templates as examples.
With Ollama, the answer is forced into the template schema, tightened for drafts so ports, install config, settings and their descriptions can't be left out or null. Without that, the model answered null and the writer dropped the fields, so no fix round could ever succeed.
Extra checks for drafts (beyond validation and the image checks)
Complete: player ports, ≥3 settings each with title/description/type/category, real memory, a readyPattern or settleSec.
Setting names the image reads: e.g. ServerPassword reaches the container as SERVER_PASSWORD, but the image reads SERVER_PASS.
Files first boot saw outside /server: lost on every start.
Root-only images: if first boot shows the image dying within seconds on permission errors, the draft stops before any model call and suggests another image or SteamCMD.
A missing readyPattern is filled from first boot's verified suggestion.
Valheim, a game we have no template for
lloesche/valheim-server: rejected. First boot as uid 1000 died in 2 s (/etc/localtime: Permission denied); it needs root.
Via SteamCMD (app 896660): first boot installed and ran it as uid 1000, found the ready line, and flagged that Valheim writes its admin lists and worlds to ~/.config/unity3d/....
qwen3.8:27b on a local 3090 Ti: round 1 was caught by the save-location check, round 2 passed (~1 minute total).
Honest result: about 70% of a good template. It got the Steam install, correct variable names, 5 well-described settings, a blank join password, the server-name placeholder and the verified ready line. But:
it "fixed" the saves with --save-dir /server, which Valheim doesn't have (it's -savedir), and the save check is a text check, so it passed;
it missed -crossplay (Xbox) and 2457/udp.
Next: have draft boot its own draft (first-boot on the drafted template, requiring no writes outside /server and a clean start) instead of trusting text checks. No Valheim template is included here.
Tests
New unit tests for each draft check, using the real Valheim and ARK first-boot findings. All existing tests pass.
The AI step of the game builder: a model writes a new game's template from facts the tools read off the real server, and the same checks as any template keep it honest.
**Builds on #2** (inspect-image and first-boot); merge that first. Until then, this PR's diff also shows #2's commits.
## Use
```
OLLAMA_URL=http://192.168.4.30:11434 go run ./cmd/gbrgames draft \
-name Valheim -image cm2network/steamcmd -steam-app 896660 \
-first-boot firstboot-out/valheim/report.json
```
`-provider gemini` (with `GEMINI_API_KEY`) switches to Gemini. It writes `games/<id>/game.yaml` and `test.yaml`; if they fail the checks, the problems go back to the model, up to 3 rounds.
## What the model gets
- **`AUTHORING.md`** (new): house rules for templates, for people and the AI alike. It covers how nodes really run servers (volume at `/server` and `/data`, uid 1000, how settings become env vars, e.g. `RCONPort` → `RCONPORT`) and the lessons from this week's bugs: `x-generate` passwords, no published RCON/REST ports, `containerUser`, heap ~1 GB below the limit, retry flaky installs, ready lines from a real boot.
- The `inspect-image` facts, the `first-boot` report, and the two smallest templates as examples.
- With Ollama, the answer is **forced into the template schema**, tightened for drafts so ports, install config, settings and their descriptions can't be left out or `null`. Without that, the model answered `null` and the writer dropped the fields, so no fix round could ever succeed.
## Extra checks for drafts (beyond validation and the image checks)
- **Complete:** player ports, ≥3 settings each with title/description/type/category, real memory, a `readyPattern` or `settleSec`.
- **Setting names the image reads:** e.g. `ServerPassword` reaches the container as `SERVER_PASSWORD`, but the image reads `SERVER_PASS`.
- **Files first boot saw outside `/server`:** lost on every start.
- **Root-only images:** if first boot shows the image dying within seconds on permission errors, the draft stops before any model call and suggests another image or SteamCMD.
- A missing `readyPattern` is filled from first boot's verified suggestion.
## Valheim, a game we have no template for
1. `lloesche/valheim-server`: **rejected**. First boot as uid 1000 died in 2 s (`/etc/localtime: Permission denied`); it needs root.
2. Via SteamCMD (app 896660): first boot installed and ran it as uid 1000, found the ready line, and flagged that Valheim writes its admin lists and worlds to `~/.config/unity3d/...`.
3. `qwen3.8:27b` on a local 3090 Ti: round 1 was caught by the save-location check, round 2 passed (~1 minute total).
**Honest result: about 70% of a good template.** It got the Steam install, correct variable names, 5 well-described settings, a blank join password, the server-name placeholder and the verified ready line. But:
- it "fixed" the saves with `--save-dir /server`, which Valheim doesn't have (it's `-savedir`), and the save check is a text check, so it passed;
- it missed `-crossplay` (Xbox) and `2457/udp`.
**Next:** have `draft` boot its own draft (first-boot on the drafted template, requiring no writes outside `/server` and a clean start) instead of trusting text checks. No Valheim template is included here.
## Tests
New unit tests for each draft check, using the real Valheim and ARK first-boot findings. All existing tests pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
`gbrgames inspect-image -game <id>` pulls the image and reads, without
starting the game (probe runs with --network none and bypasses the
entrypoint): its user, accounts, home folder owners and modes, tools
(java, steamcmd, proton...), startup scripts and the variables they read.
It then checks the template against those facts:
- an image built around its own user (a locked home owned by another uid,
or a declared USER) needs a matching containerUser
- the startup command must read settings under the names the node gives
them (the agent's toUpperSnakeCase), and must not read unset variables
that have no default
On the template from the first commit it reports both ARK bugs found by
boot tests today (uid 25000; ${RCON_PORT} vs RCONPORT); the current five
templates pass. Boot tests now run it first, so such templates fail in
seconds instead of after a boot. -json prints the facts for tooling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`gbrgames first-boot -game <id>` (or -image for a game with no template)
starts the server once like the node-agent does (same volumes, user,
SteamCMD install and startup prefix) but with none of the template's
settings and no restart policy, in the isolated gbr-boottest network.
It watches the console until the server exits, goes quiet or hits -max,
stops it gracefully (many servers write their settings on shutdown) and
records:
- the console with timings, plus ready and error lines; the last strong
ready line before the stop becomes a readyPattern / readyExample pair
- every file created; config files are copied out and parsed into
settings with their defaults (INI, properties, JSON, YAML, Unreal
OptionSettings=(...) lists); log files are scanned for ready lines too
- files written outside the volumes, which a recreated container loses
Minecraft: ready line `Done \([0-9.]+s\)! For help, type "help"` and 76
settings (69 from server.properties) found with no template input. ARK:
showed its image retries nothing when SteamCMD fails with "Missing
configuration" and starts a server that was never installed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`gbrgames draft -name <game> -image <image> [-steam-app ID]
[-first-boot report.json]` has a model write games/<id>/game.yaml and
test.yaml, then checks them like any template and sends problems back
for up to three fix rounds. Ollama (default qwen3.8:27b, OLLAMA_URL) or
Gemini (GEMINI_API_KEY).
The model gets AUTHORING.md (new: the house rules, from how nodes really
run servers and today's bugs: x-generate passwords, no published RCON
ports, containerUser, heap below the limit, env var naming, ready lines),
the inspect-image facts, the first-boot report and the two smallest
templates as examples. With Ollama the answer is forced into the
template schema, tightened for drafts so ports, install config, settings
and their descriptions can't be left out or null.
On top of validation and the image checks, drafts must:
- be complete: player ports, >=3 settings each described, real memory,
a readyPattern or settleSec
- name settings so they reach the container as variables the image reads
(ServerPassword -> SERVER_PASSWORD when the image reads SERVER_PASS)
- not leave files first boot saw outside /server (lost on every start)
A first boot showing the image dies within seconds on permission errors
(it needs root) stops the draft before any model call. A missing
readyPattern is filled from first boot's verified suggestion.
Valheim, which we have no template for: lloesche/valheim-server was
rejected as root-only; via SteamCMD (app 896660) qwen3.8:27b on a local
3090 Ti wrote a passing template in two rounds (~1 min). It is about
70% there: it chose "--save-dir /server", which Valheim doesn't have
(-savedir), and missed -crossplay and 2457/udp. The save check is a text
check; booting the draft itself is the next step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Gemini API key is passed as a URL query parameter (?key=) rather than via an x-goog-api-key or Authorization header, risking leakage into proxy/server logs.
draftCompleteness type-asserts requiredMemoryMb to json.Number, but Go YAML decoders (yaml.v2/v3) typically produce int/float64, so the assertion will fail and report a false "requiredMemoryMb is missing" problem after a YAML round-trip.
postJSON never checks resp.StatusCode before unmarshalling, so 401/403/500 responses from Ollama or Gemini surface as the misleading "unreadable answer" error instead of a clear HTTP-status message.
>_Automated AI Review_
Here's some quick feedback on your PR:
- The Gemini API key is passed as a URL query parameter (`?key=`) rather than via an `x-goog-api-key` or `Authorization` header, risking leakage into proxy/server logs.
- `draftCompleteness` type-asserts `requiredMemoryMb` to `json.Number`, but Go YAML decoders (yaml.v2/v3) typically produce `int`/`float64`, so the assertion will fail and report a false "requiredMemoryMb is missing" problem after a YAML round-trip.
- `postJSON` never checks `resp.StatusCode` before unmarshalling, so 401/403/500 responses from Ollama or Gemini surface as the misleading "unreadable answer" error instead of a clear HTTP-status message.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The AI step of the game builder: a model writes a new game's template from facts the tools read off the real server, and the same checks as any template keep it honest.
Builds on #2 (inspect-image and first-boot); merge that first. Until then, this PR's diff also shows #2's commits.
Use
-provider gemini(withGEMINI_API_KEY) switches to Gemini. It writesgames/<id>/game.yamlandtest.yaml; if they fail the checks, the problems go back to the model, up to 3 rounds.What the model gets
AUTHORING.md(new): house rules for templates, for people and the AI alike. It covers how nodes really run servers (volume at/serverand/data, uid 1000, how settings become env vars, e.g.RCONPort→RCONPORT) and the lessons from this week's bugs:x-generatepasswords, no published RCON/REST ports,containerUser, heap ~1 GB below the limit, retry flaky installs, ready lines from a real boot.inspect-imagefacts, thefirst-bootreport, and the two smallest templates as examples.null. Without that, the model answerednulland the writer dropped the fields, so no fix round could ever succeed.Extra checks for drafts (beyond validation and the image checks)
readyPatternorsettleSec.ServerPasswordreaches the container asSERVER_PASSWORD, but the image readsSERVER_PASS./server: lost on every start.readyPatternis filled from first boot's verified suggestion.Valheim, a game we have no template for
lloesche/valheim-server: rejected. First boot as uid 1000 died in 2 s (/etc/localtime: Permission denied); it needs root.~/.config/unity3d/....qwen3.8:27bon a local 3090 Ti: round 1 was caught by the save-location check, round 2 passed (~1 minute total).Honest result: about 70% of a good template. It got the Steam install, correct variable names, 5 well-described settings, a blank join password, the server-name placeholder and the verified ready line. But:
--save-dir /server, which Valheim doesn't have (it's-savedir), and the save check is a text check, so it passed;-crossplay(Xbox) and2457/udp.Next: have
draftboot its own draft (first-boot on the drafted template, requiring no writes outside/serverand a clean start) instead of trusting text checks. No Valheim template is included here.Tests
New unit tests for each draft check, using the real Valheim and ARK first-boot findings. All existing tests pass.
🤖 Generated with Claude Code
`gbrgames inspect-image -game <id>` pulls the image and reads, without starting the game (probe runs with --network none and bypasses the entrypoint): its user, accounts, home folder owners and modes, tools (java, steamcmd, proton...), startup scripts and the variables they read. It then checks the template against those facts: - an image built around its own user (a locked home owned by another uid, or a declared USER) needs a matching containerUser - the startup command must read settings under the names the node gives them (the agent's toUpperSnakeCase), and must not read unset variables that have no default On the template from the first commit it reports both ARK bugs found by boot tests today (uid 25000; ${RCON_PORT} vs RCONPORT); the current five templates pass. Boot tests now run it first, so such templates fail in seconds instead of after a boot. -json prints the facts for tooling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Here's some quick feedback on your PR:
?key=) rather than via anx-goog-api-keyorAuthorizationheader, risking leakage into proxy/server logs.draftCompletenesstype-assertsrequiredMemoryMbtojson.Number, but Go YAML decoders (yaml.v2/v3) typically produceint/float64, so the assertion will fail and report a false "requiredMemoryMb is missing" problem after a YAML round-trip.postJSONnever checksresp.StatusCodebefore unmarshalling, so 401/403/500 responses from Ollama or Gemini surface as the misleading "unreadable answer" error instead of a clear HTTP-status message.