feat: draft new game templates with an AI model #4

Merged
greg merged 3 commits from feat/ai-draft into main 2026-10-25 15:20:23 +00:00
Owner

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

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)
greg added 3 commits 2026-10-25 07:01:47 +00:00
feat: inspect-image reads what a game's image needs
Validate game templates / validate (push) Successful in 9m33s
Validate game templates / boot-test (push) Skipped
Validate game templates / publish (push) Skipped
40e7372dab
`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>
feat: first-boot records what a server does with no settings
Validate game templates / validate (push) Successful in 9m34s
Validate game templates / boot-test (push) Skipped
Validate game templates / publish (push) Skipped
c9d9cd6ff5
`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>
feat: draft new game templates with an AI model
Validate game templates / validate (push) Successful in 9m35s
Validate game templates / boot-test (push) Skipped
Validate game templates / publish (push) Skipped
da4ffc86d2
`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>
greg added 1 commit 2026-10-25 07:06:37 +00:00
Merge branch 'main' into feat/ai-draft
Validate game templates / validate (push) Successful in 9m37s
Validate game templates / boot-test (push) Skipped
Validate game templates / publish (push) Skipped
8f4adb733e
greg added 1 commit 2026-10-25 15:10:23 +00:00
Merge branch 'main' into feat/ai-draft
Validate game templates / validate (push) Successful in 9m41s
Validate game templates / boot-test (push) Skipped
Validate game templates / publish (push) Skipped
43727f8284
greg merged commit 07e8fd93f6 into main 2026-10-25 15:20:23 +00:00
greg deleted branch feat/ai-draft 2026-10-25 15:20:24 +00:00
Member

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.
>_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.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gbreed-public/gbr-games#4