About anthfarm

anthfarm is a small anthology game. You start in a field office and go out into a handful of short scenes, and each one is a different kind of game: in some you walk around, in one you type commands at a prompt. One story and one save run through all of them, and the scenes only talk to each other through that shared story state.

We built it by directing AI coding agents. Games, and the Godot engine it runs on, are not what we normally work on, and that was the point: it is a field where we could not have become productive quickly on our own. The agents wrote most of the code. We decided what the game should be, drew the lines between its parts, and reviewed what came back.

That only works if you can trust code you have not read line by line. What let us trust it was a set of checks run after every change. Automated tests cover each piece. A headless copy of the game plays itself from the first scene to the last and fails if any scene breaks the rules for handing off to the next. A browser test does the same walk in a real browser. When those pass, we believe the game works, and when they fail, they usually say where.

Each scene lives in its own folder and states what it reads and writes, so different people can build different scenes at the same time without stepping on each other. That structure, with this story taken out, is what the event starts from.

How it fits together

The main parts of the game, each one line. Select one to read more.

CellsEach scene is one folder that follows one small contract.

A cell is a folder under cells/ holding its scene and a manifest. The game hands the cell a window onto the shared state and calls enter(); the cell plays and calls finish() exactly once, with an outcome and a short summary of what happened. The manifest lists every key the cell reads and writes, for example m.reads = ["constants.entropy"] and m.writes = ["ledger", "cells"].

Every cell also has a demo() that plays it through to a real ending with nobody at the keyboard. That is what lets the whole game run without a player.

WorldStateOne shared record of the run, split into namespaces and saved as JSON.

All state that outlives a scene lives in one place, divided into namespaces: shared for things like the route position and inventory, one private namespace per cell, the ledger, clocks, hooks and a few more. A separate meta file survives starting a new game and holds things like runs completed and settings.

Saves are JSON files with a version number and migrations, and the pause menu has save slots. Fields are only ever added, never renamed or removed, so an old save keeps loading; a test holds the list of shipped fields.

How consequences travelScenes leave marks for later scenes without knowing who will read them.

The ledger is an append-only list of entries tagged with words like mercy or deception, and later scenes read it by tag, never by who wrote it. A hook is a named value one scene offers and another wants; every want names a fallback, such as {"name": "iron_key", "fallback": null}, so a scene still works when the one that offers it was skipped or never built.

Clocks are named counters with thresholds that any scene can advance. Items the player carries cross from scene to scene, and in this game each one can be lost on the way.

Self-registration and route orderA scene puts itself on the route. No shared file changes.

The manifest sets the cell's place on the route, and the game builds the route at start by scanning every cell's manifest. Adding a scene means adding a folder, so teams working at the same time do not collide on one shared list.

In this game the route is a board of work orders released in tiers. The template drops the tiers and simply sorts by the order each cell declares.

The shellLoads each scene, checks what it did, and moves the player on.

The shell walks the route. For each scene it checks the manifest, mounts the scene, waits for it to finish, and compares the state before and after. A changed key the manifest did not declare is a violation, and so is a declared write that never happened. Then it frees the scene and checks nothing was left behind.

Between scenes it plays the crossing: a transition over the last frame of the scene you left, with one line of narration.

The hubThe room you return to between scenes.

In this game the hub is the field office, where you read the report on the scene you just closed, take the next work order off the board, and declare what you carried at the door. It is loaded through the same contract as any other scene but never sits on the route, and which scene fills the slot is a setting.

The template ships a minimal default hub instead: the list of scenes with their status, a way to continue, and a way to pick any open scene.

The DirectorA scripted narrator that reacts to what happened. No LLM.

The Director's lines live in JSON tables. Each line has a stage of the run, a priority, conditions on what happened, and text with slots, such as "That concludes {cell_title}.". At each boundary it picks the most specific line not yet used, so nothing repeats in a run.

Given the same save and seed it always says the same thing, which means the narration can be previewed for a whole route without playing it. There is no network call anywhere in the game.

Content as dataThe story's words, numbers and colors are files, not code.

The strings the player reads, the world constants, the ledger's tags, the palettes and moods, the Director's stages and its line tables all live under content/. A constant, for instance, is one line: "entropy": {"default": 0.1, "min": 0.0, "max": 1.0}.

Each file is shape-checked when it loads. A malformed entry is skipped with a warning and a fallback stands in, so editing the words cannot crash the game.

The presentation kitShared widgets every scene draws with.

A dialogue box with narration and choices, a terminal with a verb parser for typed commands, a small low-poly 3D stage for visual novel scenes, a status panel and pop-up notes, the pause menu with text size, typewriter speed and saves, the field guide, and the transitions between scenes.

Every widget can be driven by a script as well as a player, and every pause in it resolves at once when the game is playing itself. That is what makes the unattended runs possible.

The art kitPictures are generated by code, or imported and locked to a palette.

There are no asset packs. Generators build pixel creatures, pixel people and low-poly 3D monsters from a seed and a few parameters, and palettes keep them in the colors of the current mood. An outside image comes in through an import step that locks it to a palette and records where it came from; an image without that record fails the checks.

The companionThe Volunteer, who follows you between scenes. Specific to this game.

The Volunteer is named for a plant that comes up where nobody planted it. It travels with you, loses a little of itself at each crossing, picks up what you drop on the way, and scenes can draw it in 2D or 3D. It belongs to this story, so the template leaves it out.

The debugger verbsWatch, poke, snapshot and restore, used on the game's own state.

The player's tools treat the world as a program being debugged. Watch marks a value so its change is shown, poke nudges a world constant within its limits, snapshot saves the moment, and restore goes back to it. Pokes and snapshots are limited, so a scene has to work even when they are spent.

In the template these verbs are an optional module, off until the content turns them on.

The browser hookThe running game reports its state so a script in the browser can check it.

In a web build the game publishes a small object, window.anthfarm, with the current state, the route, the last report and the Director's last lines. The engine updates it at every scene boundary and on every change that matters. Automated browser checks read it instead of guessing from pixels, and nothing in the game depends on it.

VerificationHow we know a change did not break the game.

Each piece has its own unit tests, and every scene is run through its demo to check that it keeps to its manifest and ends the same way for the same seed. The whole game is then played headless from the first scene to the last with the real scenes, and the same walk is repeated in a real browser against a web build.

One more rule sits under all of it: any run whose log contains an engine error fails, even if every test passed. That also catches a test file that failed to load and would otherwise have been skipped without a sound.