feat(builder): repair existing templates, a weekly check that queues repairs, and eval
Validate game templates / validate (push) Successful in 36s
Validate game templates / boot-test (push) Successful in 1m11s
Validate game templates / publish (push) Skipped

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>
This commit is contained in:
gregandClaude Opus 5.5 committed 2026-10-26 00:42:34 -04:00
1 parent f11c5eaf2f
commit f1271341ef
12 files changed
+535 -20

No files matched your search

+4 -2
View File
@@ -84,6 +84,8 @@ type draftJob struct {
// Text of the pages research based the variant on; when set, every launch option in the startup
// command must appear in it (or in the image's scripts)
Docs string
// Repair: games/<id> is an existing template to fix, not a new game
Repair bool
}
// drafter keeps what the model needs about one game across rounds. Each request is rebuilt from it
@@ -109,8 +111,8 @@ func newDrafter(j draftJob) (*drafter, error) {
id = gameID(j.Name)
}
d := &drafter{job: j, id: id, dir: filepath.Join(j.GamesDir, id)}
if _, err := os.Stat(d.dir); err == nil {
return nil, fmt.Errorf("%s already exists; drafts only create new games", d.dir)
if _, err := os.Stat(d.dir); err == nil && !j.Repair {
return nil, fmt.Errorf("%s already exists; drafts only create new games (repair fixes existing ones)", d.dir)
}
var err error
if d.m, err = newModel(j.Provider, j.Model); err != nil {