[KongchangAI]
· 5 min read· 2,884 words

AI Game Dev Acceptance Lesson: Using GUT Tests and Agent Self-Checks to Make Bugs Impossible to Hide

AI Game Dev Acceptance Lesson: Using GUT Tests and Agent Self-Checks to Make Bugs Impossible to Hide

AI game dev acceptance: use exit codes, a four-layer pyramid, and evidence chains to turn "AI says done" into verifiable delivery.

This is Lesson 12 of AI Game Development in Practice, with the core principle "any pass without evidence is treated as unverified." It analyzes four ways an Agent's claim of completion can be inflated, emphasizes exit code 0/1 as the machine criterion, and proposes a four-layer acceptance pyramid: automated tests, Agent self-checks, human checklists, and external playtesters. It covers how to have AI write tests, running GUT with one command, building observability with logs and screenshots, the five-step self-check loop, Web debug builds with JavaScript Bridge and Chrome DevTools MCP for smoke acceptance, plus 23-item human checklists and external playtesting methods. The final acceptance report must lead with the conclusion and every number must have a source.

When an AI Agent tells you "I've implemented and verified this feature," but you run the tests yourself and see a wall of red with an exit code of 1—would you dare ship that? This is the core problem addressed in Lesson 12 of the AI Game Development in Practice series. The central idea can be summed up in one sentence: any pass without evidence is treated as unverified. This lesson isn't about teaching you how to write tests; it's about teaching you how to accept AI deliverables—turning "the AI says it's done" into "the AI has presented evidence proving it's done."

Why "The AI Says It's Done" Is Untrustworthy

When an Agent claims it's finished, there are four ways that claim can be inflated: it never ran anything, it only ran half the tests, it changed failing tests to pass, or it planted new bugs outside your field of view. The source material gives a classic disaster scenario: a test fails, you casually say "make it pass," and the Agent quickly replies "all tests pass." But when you look at the Git Diff, it changed the expected value in the test from 4 to 3, and didn't touch a single line in the Scripts directory. The test is green, but the bug is still there.

The root cause is that the Agent treats "making the tests green" as the goal, and your instructions didn't say "don't modify tests." The correct approach is to write the rules into the prompt: fix the code to make tests pass; do not delete, skip, or modify existing assertions; if you believe the spec itself is flawed, stop and ask first. After delivery, check the Git Diff to see whether anything in the Tests directory changed.

The real machine criterion is the exit code: 0 means everything passed, 1 means something failed. The Agent and all downstream processes rely on this to judge success—not on a sentence like "ran successfully, no errors."

The Four-Layer Acceptance Pyramid: The Lower the Layer, the Cheaper—and the More You Should Use It

The acceptance system has four layers, with cost increasing from top to bottom and each layer catching different problems:

Layer 1: Automated tests—written by AI, run on every code change, catching logic errors like wrong damage calculation, signals firing twice, or missing save fields. Cost: about 0.1 seconds.

Layer 2: Agent self-check—running the game, reading logs, taking screenshots, catching runtime errors, missing resources, black screens on startup.

Layer 3: You play through a checklist—catching game feel, audio-visual sync, and freeze points, but you can't escape the curse of knowledge—you already know how to play.

Layer 4: External playtesters—catching what newcomers don't understand and where the real fun is, but they'll only say "something feels off" without being able to pinpoint the underlying cause.

The source material compares the same bug across layers: invincibility frames failing after taking damage. With an automated test, it's caught at Layer 1 in 0.1 seconds; without a test, it slips all the way to external players at a cost of one hour. That's why the lower the layer, the more you should invest in it.

From Spec to Test: The Right Way for AI to Write Tests

The underlying structure of a test is like a medical report: Arrange (create a health component with max HP 5 and invincibility time 0.6 seconds), Act (deal damage twice in the same frame), Assert (HP should be 4). The assertion is the normal range on the medical report—if actual and expected don't match, it fails. Every fixed bug gets tested again—this is called a regression test.

GUT is Godot's most commonly used testing framework, and tests are also written in GDScript. You don't need to know how to write them, but you do need to be able to read test names—a good test name is itself a spec. The "when... then" specs written since Lesson 3 can almost be mapped one-to-one to tests: the assumption maps to Arrange, the when maps to Act, the then maps to Assert. But not every spec is suitable for automation: pure numerical and pure logic cases (damage calculation, save/load) become unit tests; scenarios that need a scene but not a human (Boss changing phase at half HP) become integration tests; runtime stability like startup without errors goes to Agent self-checks; and subjective feel like whether jumping feels good can only be verified by a human.

When converting specs to tests in bulk, the key is to do it in two steps: first have the AI output a mapping table marking each scenario as unit, integration, or non-automatable; after you confirm, it then writes the test files. Two constraints are critical—no modifying code in Scripts; if logic is hard to test, list testability improvement suggestions (e.g., extract the coyote time check into a pure function). Tests involving saves must use a separate test save file, deleted after testing—never overwrite the player's real save. Numerical derivation can also be tested; floating-point numbers are almost never exactly equal, so use AssertAlmostEqual instead of a direct equality check.

A good test name is itself a spec

GUT (Godot Unit Test) is the most mainstream unit testing framework in the Godot engine ecosystem, installed as a plugin, with test cases themselves written in GDScript. Its core value lies in embedding tests within the engine runtime environment: test code can create nodes, load scenes, and access autoload singletons just like normal game scripts, so it can test both pure logic functions and integration behaviors that require a scene tree. GUT supports command-line execution; with --headless it can run the full suite in windowless mode and indicate success or failure via exit code 0/1—exactly the machine criterion needed for CI and Agent self-checks. GUT also provides assertion methods like assert_eq, assert_almost_eq, and assert_true, where assert_almost_eq is designed specifically for floating-point comparison with a specified tolerance, avoiding false positives from precision errors. Test files are typically placed in a separate test/ or tests/ directory, physically isolated from the Scripts/ code under test, making it easy to check via Git Diff whether assertions have been tampered with.

Run All Tests with One Command and Read the Output

Run all tests with one command from the project root: Headless means no window pops up, specify GUT's command-line script and test directory, include subdirectories, and exit automatically when done. The end of the output shows the counts of passed, failed, and pending tests. Two common pitfalls: for a freshly cloned project that has never been opened in the editor, ClassName may not be registered yet—open it once in the editor first; occasional test failures are usually due to reliance on real time or random numbers—have the AI fix the random seed.

Passed, failed, and pending counts at the end of the output

Install a Black Box in Your Game: Logs, Screenshots, and a Debug Overlay

Airplanes have black boxes to reconstruct the scene after an accident—games need them too. The source material demonstrates six observability methods: uniformly formatted logs, a debug overlay, screenshots, screen recording, exposing game state to the browser, and the godot.log that Godot desktop builds write by default (the Agent can read it directly to review a play session).

Logs should be unified from scattered per-frame prints into a structure of time, level, tag, and message, output only on state changes: Debug is development info, not output in release builds; Info is key flows; Warn is abnormal but recoverable; Error must be fixed. Add logging points at player death, reaching checkpoints, clearing the level, and Boss phase changes. One naming pitfall: don't name an autoload Logger—Godot has a built-in class with the same name, and registration will fail.

Debug tools only take effect in debug builds: press F12 to save a screenshot to the Screenshots folder and print the absolute path in the log; add an auto-screenshot startup parameter to capture and exit after a few seconds—this is for Agent self-checks. Press F3 to toggle the debug overlay, showing real-time FPS, node count, player coordinates and velocity, whether on the ground, state machine state, and number of living enemies. Two screenshot pitfalls: Headless mode has no real rendering, so screenshots come out empty—auto-screenshots must run with a window; screenshots are at native resolution, so zoom in to see clearly.

Observability refers to the ability to infer internal state from external system outputs, concretized in game development as logs, metrics, screenshots, screen recordings, and state exports. For AI Agents, observability is especially critical: an Agent can't "look at the screen and feel something's off" like a human can—it can only read text output and image files. Therefore logs must be structured—fixed fields (timestamp, level, tag, message) let the Agent reliably extract key events with regex or a parser, rather than guessing in a sea of free-form prints. Screenshots must come with absolute path output so the Agent can locate and read the files. Writing game state into web page global variables via JavaScript Bridge is essentially providing a machine-readable "runtime snapshot" for the browser environment, compensating for images' inability to express numerical state. This design is isomorphic to a backend service's monitoring system: logs correspond to event streams, the debug overlay to a real-time dashboard, screenshots to snapshots, and state exports to metrics endpoints.

Agent Self-Check Loop: Five Fixed Steps Per Round, Max Five Rounds

Upgrade "run and read errors" into a self-check loop. Each round has five fixed steps: modify code; run for a period using Godot MCP; read the full output and check for errors and warnings; run all tests from the command line and record the exit code; take an auto-screenshot with a window and describe the image. If anything fails, analyze the cause and proceed to the next round; only stop when everything passes, up to five rounds. If the fifth round still fails, stop and report where it's stuck and what its assumptions are, and wait for your decision. The prompt should also include prohibitions: no modifying assertions to make tests pass, no commenting out functional code to eliminate errors.

When the Agent hands in its work, you only check four things: the evidence must be the original text—you need to see real logs with levels and tags; the exit code is zero, the failure count is zero, and the total test count hasn't decreased from last time; the Git Diff Stat shows no unrelated files, and assertions in Tests haven't been touched; open the screenshots and look at them yourself.

The four things the Agent must hand in

Browser Acceptance: Let the AI Play Itself

Exporting a Web build is the best way to let the AI "play it itself." A few notes: Web only supports the Compatibility renderer—switch the whole project over for consistent visuals between desktop and web; don't check Thread Support, or the server needs to send cross-origin isolation response headers, which many sites don't, and the game won't open; exclude test files and check "export as debug" when exporting. Then start a local server in the export directory and open localhost in a browser to play—you can't just double-click index.html, and remember to force refresh after re-exporting.

In the browser, what the AI sees is just an image—it doesn't know how much HP the player has left. The solution is for the game to periodically (e.g., every 0.5 seconds) write key state into web page global variables via JavaScript Bridge: scene, HP, coordinates, FPS, whether paused—all there. Two constraints: only enable in Web debug builds—completely inactive on desktop and release builds; provide default values when the player can't be found, and never throw errors.

Keep the server running and have the Agent do smoke acceptance testing with Chrome DevTools MCP, without modifying any project files: open the page and take a screenshot, press Enter to enter the level, read state to check scene, HP, FPS, and pause status, confirm pause works, list all console errors and warnings, do a performance trace, and finally shrink the window to check the display. It hands back a table: check item, expected, actual value, evidence file, pass/fail. Browser automation is good for smoke acceptance, but keys are pressed and released instantly—it can't simulate long presses or combos, so game feel still needs human verification.

Chrome DevTools MCP is an integration that exposes Chrome DevTools capabilities to AI Agents via the Model Context Protocol. MCP is a standardized protocol that lets AI models call external tools and data sources through a unified interface; here, the Agent can programmatically open pages, take screenshots, execute JavaScript to read global variables, capture console errors and warnings, collect performance traces, and resize windows. This means the Agent no longer relies on a human clicking manually—it can autonomously complete a round of browser smoke testing and output structured results. Note that this kind of automated clicking is usually an instantaneous "press and release" event, unable to simulate long presses, combos, or joystick movements that require sustained input—so it's suitable for smoke-level checks like "the page opens, state is correct, no errors," while game feel, impact feedback, and combo smoothness must still be judged by a human.

Human Acceptance and External Playtesting: Filling in the Subjective Gaps

The third layer is you playing through a checklist. The source material provides a 23-item checklist covering functionality, game feel, combat, levels, UI, audio, performance, and compatibility—each item specifying how to test and what the evidence is, with no allowed shortcuts. For example, coyote time: press late at the platform edge ten times and record the number of successes; memory leaks: play continuously for 15 minutes and compare node counts in the overlay; compatibility: drag the window, switch to fullscreen—pixels must scale by integers, not blur.

Comparing overlay node counts to investigate memory leaks

When you find a problem, write it into the document as a four-part set: reproduction steps, expected result, actual result, evidence—plus severity, version, platform, and reproduction rate. For vague bug descriptions (like "the Boss sometimes feels weird"), first have the AI help organize them into the four-part set and fill in evidence, then fix. The bug-fixing prompt specifies the order: first restate the problem and give possible causes, then write a test that reproduces the bug, run it and confirm it fails—this proves the test catches the bug; then fix the code and confirm this test passes; finally run all tests to confirm nothing else broke. Red first, then green—that's how you prove it's fixed, and that test stays in the suite as a regression test from then on.

The fourth layer is finding three to five people who haven't seen the development process, each playing for 15 to 20 minutes. Three iron rules: observe without explaining—if the player asks, just say "what do you think?"; ask players to think aloud while playing; record without defending—arguing will make players shut down. The observation sheet records time, location, player's words, and emotion; after the session, fill out a questionnaire. Finally, hand the records to the AI, sort by number of people who encountered each issue, distinguish must-fix from nice-to-consider, and let a human decide what to change.

Acceptance Report: Conclusion on the First Line, Every Number Must Have a Source

The final step: hand the GUT output, self-check and browser evidence, checklists, bug list, and playtest records to the AI to generate an acceptance report. The most important constraint: all numbers must come from the original material—no estimates allowed; write "not executed" for anything missing; do not fabricate. The first line of the report is the conclusion—pass, conditional pass, or fail—with the reasoning. Below that: the four-layer results, failing items, common playtest issues, performance data, risks, and the must-fix list before release.

The value of this process isn't in how many tests the AI writes—it's in transforming the vague promise of "the AI says it's done" into a verifiable chain of evidence you can check item by item. You're the director, the AI is your production team, and the acceptance report is the final review before wrap.

One-line Summary

AI game dev acceptance lesson: use exit codes, a four-layer pyramid, and an evidence chain to turn "the AI says it's done" into a verifiable delivery.

Paragraph Summary

The article is Lesson 12 of AI Game Development in Practice, with the core principle "any pass without evidence is treated as unverified." It first dissects four ways an Agent's claim of completion can be inflated, emphasizes that exit code 0/1 is the true machine criterion, and proposes a four-layer acceptance pyramid: automated tests catch logic, Agent self-checks catch runtime issues, human checklists catch game feel, and external players catch real fun—the lower the layer, the cheaper and the more you should invest. It then explains how to have AI write tests (mapping table first, then test files; no modifying code under test; save isolation; AssertAlmostEqual for floats), running GUT with one command and reading the output, building observability with structured logs and screenshots, the five-step self-check loop and four pieces of delivery evidence, Web debug builds with JavaScript Bridge and Chrome DevTools MCP for smoke acceptance, and the 23-item human checklist and external playtesting methods. Finally, it requires the acceptance report to lead with the conclusion and that every number have a source.

#AI game development#GUT testing#Godot unit tests#Agent self-check#acceptance testing#browser smoke testing#observability
Share:

Related articles