feat(builder): documented launch options only, and no game-specific code
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>
This commit is contained in:
1 parent
dd92b6be4c
commit
8623ba2d7b
16 files changed
+395
-120
No files matched your search
@@ -76,7 +76,7 @@ var researchSchema = map[string]any{
|
||||
|
||||
func runResearch(args []string) error {
|
||||
fs := flag.NewFlagSet("research", flag.ExitOnError)
|
||||
gameName := fs.String("name", "", "the game's name, e.g. \"Valheim\"")
|
||||
gameName := fs.String("name", "", "the game's name, as players know it")
|
||||
notes := fs.String("notes", "", "what the admin asked for (crossplay, a modded edition...)")
|
||||
provider := fs.String("provider", "ollama", "ollama or gemini")
|
||||
modelName := fs.String("model", "", "model name (default as for draft)")
|
||||
@@ -171,8 +171,8 @@ func research(m model, gameName, notes string, pages int) (*researchResult, erro
|
||||
|
||||
const researchPrompt = "You research how to host a game's dedicated server for GBR Hosting, a game server host. Servers run " +
|
||||
"in Docker on Linux, as a non-root user, with a volume at /server. From the search results and pages given, list " +
|
||||
"the ways this game's dedicated server can be run (variants): different editions (e.g. Java vs Bedrock, Evolved vs " +
|
||||
"Ascended), crossplay builds, or notably different server images. For each variant give:\n" +
|
||||
"the ways this game's dedicated server can be run (variants): separate editions of the game with their own server, " +
|
||||
"crossplay builds, or notably different server images. For each variant give:\n" +
|
||||
"- images: Docker Hub images that run this server, best first (prefer images that run as a non-root user, and the " +
|
||||
"plain SteamCMD route cm2network/steamcmd when the game ships a Linux dedicated server on Steam). Only images that " +
|
||||
"appear in the pages.\n" +
|
||||
@@ -250,13 +250,18 @@ func dedupeHits(hits []searchHit) []searchHit {
|
||||
}
|
||||
|
||||
func readPage(reader, pageURL string) (string, error) {
|
||||
return readPageLimit(reader, pageURL, 9000)
|
||||
}
|
||||
|
||||
// readPageLimit reads a page as text through the reader, cut to limit bytes.
|
||||
func readPageLimit(reader, pageURL string, limit int) (string, error) {
|
||||
raw, err := httpGet(strings.TrimRight(reader, "/")+"/"+pageURL, 60*time.Second)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
text := string(raw)
|
||||
if len(text) > 9000 {
|
||||
text = text[:9000] + "\n...(truncated)"
|
||||
if len(text) > limit {
|
||||
text = text[:limit] + "\n...(truncated)"
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user