Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Mechanical verification of the built game: headless build, unit tests, lint, static checks. Explicit invocation only — use /gm-verify.
.claude/skills/randallliuxin-gm-verify/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 97% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 432% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 113% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 269% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 106% | 0% |
$ARGUMENTS
You are performing mechanical verification of a built Godot game project. This is a non-creative, checklist-driven process.
FIRST ACTION — before anything else: Write verify to .godotmaker/current_role.
Permission: Read-only with three exceptions — you may write .godotmaker/current_role, append to .godotmaker/stage.jsonl, and write .godotmaker/verify_report.json. Verify never modifies game code or planning docs.
Read .godotmaker/stage.jsonl (treat as empty if missing) — each line is {"role": X, "ts": Y}.
role == "build" AND no event with role == "fixgap" exists anywhere in the file → STOP. Tell user to run /gm-build first.role == "verify" → STOP. Tell the user:> "Verify already ran at {timestamp} with no state-changing event since. Recommended next: /gm-evaluate. > If you need to redo this step or have other plans, just tell me."
From the project root:
bashpython tools/run_verify.py
run_verify.py wraps the four mechanical checks (build / unit tests / lint / static check) and prints a JSON document matching Output Format Section B to stdout. Capture stdout.
What the script does:
godot_path from .claude/godotmaker.yaml; falls back toplain godot from PATH. A missing or broken binary surfaces as a tooling_notes[].suggested_fallback = "escalate" entry.
<godot_path> --headless --quit and writes blocking Godot diagnosticsto checks.build.errors[].
--ignoreHeadlessMode --add res://test/ --report-directory <temp> and parses the generated JUnit XML into checks.unit_tests.{passed, failed, failures]}. Stdout is diagnostic fallback only.
checks.lint as pass with format_drift: null. Do NOTre-enable here.
checks.static_check topython tools/check_project.py <project_dir> --build --ecs --tests --plan --mcp.
Before writing the report, validate. Block on any of these:
result / ts / checks /tooling_notes is missing
checks.{build,unit_tests,lint,static_check}entries is absent
result == "pass" but tooling_notes is non-empty — re-run or escalatechecks.unit_tests.passed + .failed == 0 — spot-check by running the gdUnit4 command directly with --report-directory <temp>tooling_notes entry whose crashed_on looks unrelated to thefailing check
If any block fires, diagnose by running the implicated command yourself, then either re-run run_verify.py or surface the issue verbatim to the user. Do NOT silently rewrite the script's output.
You produce two outputs:
Build this from the JSON the script returned — do not re-run any command for the chat side.
## Verification Report
### Build
Result: PASS | FAIL
{If FAIL, one line per checks.build.errors[] entry: `- {file}:{line}: {message}` (file/line may be empty)}
### Unit Tests
Result: PASS | FAIL
{N passed, M failed}
{If FAIL, one line per checks.unit_tests.failures[]: `- {test}: {message}`}
### Lint
Status: SKIP (gdtoolkit disabled — ROADMAP R-112)
### Static Check
Result: PASS | FAIL
{If FAIL, one line per checks.static_check.issues[]: `- {check}: {detail}`}
### Overall: PASS | FAIL
{If tooling_notes is non-empty, append:
## Tooling Notes
- {tool}: {error} (suggested_fallback: {suggested_fallback})
…}.godotmaker/verify_report.json)Write this file every run (PASS or FAIL). /gm-build and /gm-fixgap read it on their next invocation to translate failures into pending tasks.
Schema:
json{ "result": "pass | fail", "ts": "<UTC ISO 8601 timestamp, e.g. 2026-05-07T14:23:00Z>", "checks": { "build": { "result": "pass | fail | error", "errors": [ {"file": "src/foo.gd", "line": 42, "message": "Identifier 'bar' not declared"} ] }, "unit_tests": { "result": "pass | warn | fail | error", "passed": 624, "failed": 0, "failures": [ {"test": "test_player_input::test_jump", "message": "expected 10, got 0"} ], "warnings": [ "Found 4 possible orphan nodes." ] }, "lint": { "result": "pass | warn | fail | error", "issues": [ {"file": "src/foo.gd", "rule": "max-line-length", "message": "line too long"} ], "format_drift": { "file_count": 92, "command": "gdformat src/ test/ scenes/" } }, "static_check": { "result": "pass | fail | error", "issues": [ {"check": "missing_unit_test", "detail": "s_level_up_overlay has no test"} ] } }, "tooling_notes": [ { "tool": "gdlint", "crashed_on": "src/foo.gd", "error": "NotImplementedError at gdtoolkit/linter/class_checks.py:144", "suggested_fallback": "exclude_file", "narrowed_command": null, "rule_name": null, "check_name": null } ] }
Field rules:
result — "pass" iff every checks.*.result ∈ {pass, warn}. Any fail / error makes overall fail. tooling_notes alone never makes overall fail — the error it pairs with does.ts — UTC ISO 8601 at the moment you write the file. Consumers compare it against their own last-event timestamp for freshness.[]). Do not omit them.result — pass / fail are project-content. warn is non-blocking diagnostic noise (lint style drift or gdUnit warnings such as orphan nodes when every assertion passed). error means the tool itself crashed and the project's actual state is unknown for this check; pair error with exactly one tooling_notes entry. Consumers fix error via config, NOT project code.format_drift — object when gdformat --check reports drift; null otherwise.suggested_fallback + matching operand — the producer fills the operand so the consumer can act deterministically:| suggested_fallback | Required operand | |---|---| | exclude_file | crashed_on (already required on every note) | | scope_narrow | narrowed_command (replacement command, e.g. "gdlint src/") | | add_gdlintrc_rule | rule_name (e.g. "class-name") | | skip_check | check_name (e.g. "missing_unit_test") | | escalate | — (none) |
Producer rule: if you cannot fill the required operand for a non-escalate fallback, emit escalate instead.
Consumer rule (open-enum forward-compat): a missing required operand or an unknown suggested_fallback value MUST be treated as escalate (surface to user, do NOT auto-fix). Never crash.
When the script's JSON has result: "fail":
.godotmaker/verify_report.json./gm-build if the last state-changing event was build, /gm-fixgap if it was fixgap.verify event to stage.jsonl — only PASS records a stage event.When the script's JSON has result: "pass":
.godotmaker/verify_report.json. (Field rules apply: tooling_notes == [], all checks.*.result ∈ {pass, warn} — the script enforces these but spot-check them once more before writing.)python tools/append_stage_event.py verify to append a {"role": "verify", "ts": "<server-generated UTC>"} line to .godotmaker/stage.jsonl. Do NOT hand-write the JSON or the timestamp — the helper exists so the timestamp comes from the system clock, not your own output.git add -A && git commit -m "chore(verify): <Tag>"Verify complete. Recommended next: /gm-evaluate| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | fail→fail | 14,755 | 7,697 | -48% | 1 | 1 | 0% | 2,521 | 3,945 | +56% | 0 | 0 | — |
case-03 | fail→fail | 4,893 | 4,363 | -11% | 1 | 1 | 0% | 315 | 2,700 | +757% | 0 | 0 | — |
case-01 | fail→fail | 5,014 | 3,969 | -21% | 1 | 1 | 0% | 278 | 2,723 | +879% | 0 | 0 | — |
case-02 | fail→fail | 3,469 | 4,667 | +35% | 1 | 1 | 0% | 194 | 2,680 | +1281% | 0 | 0 | — |
case-04 | fail→fail | 7,833 | 6,764 | -14% | 1 | 1 | 0% | 1,461 | 3,048 | +109% | 0 | 0 | — |
case-05 | pass→pass | 9,362 | 4,361 | -53% | 1 | 1 | 0% | 1,540 | 3,256 | +111% | 0 | 0 | — |
case-06 | fail→fail | 6,135 | 7,371 | +20% | 1 | 1 | 0% | 982 | 3,085 | +214% | 0 | 0 | — |
case-07 | fail→pass | 11,191 | 10,723 | -4% | 1 | 1 | 0% | 1,707 | 3,361 | +97% | 0 | 0 | — |
case-08 | fail→pass | 3,291 | 4,646 | +41% | 1 | 1 | 0% | 628 | 3,339 | +432% | 0 | 0 | — |
case-10 | fail→pass | 6,867 | 2,284 | -67% | 1 | 1 | 0% | 1,342 | 2,852 | +113% | 0 | 0 | — |
case-11 | pass→pass | 11,834 | 6,196 | -48% | 1 | 1 | 0% | 1,783 | 2,809 | +58% | 0 | 0 | — |
case-12 | fail→pass | 5,534 | 3,444 | -38% | 1 | 1 | 0% | 836 | 3,087 | +269% | 0 | 0 | — |
case-13 | fail→pass | 8,807 | 2,855 | -68% | 1 | 1 | 0% | 1,430 | 2,949 | +106% | 0 | 0 | — |
case-19 | pass→pass | 11,502 | 3,867 | -66% | 1 | 1 | 0% | 1,878 | 3,165 | +69% | 0 | 0 | — |
case-14 | fail→pass | 11,732 | 2,853 | -76% | 1 | 1 | 0% | 1,821 | 2,892 | +59% | 0 | 0 | — |
case-15 | pass→fail | 8,091 | 7,274 | -10% | 1 | 1 | 0% | 1,303 | 2,690 | +106% | 0 | 0 | — |
case-16 | fail→pass | 8,128 | 1,758 | -78% | 1 | 1 | 0% | 1,346 | 2,704 | +101% | 0 | 0 | — |
case-17 | pass→pass | 2,469 | 6,618 | +168% | 1 | 1 | 0% | 366 | 2,930 | +701% | 0 | 0 | — |
case-18 | fail→pass | 10,929 | 3,733 | -66% | 1 | 1 | 0% | 1,715 | 3,126 | +82% | 0 | 0 | — |
case-20 | pass→fail | 13,977 | 7,545 | -46% | 1 | 1 | 0% | 2,811 | 3,061 | +9% | 0 | 0 | — |
case-21 | fail→fail | 8,588 | 6,723 | -22% | 1 | 1 | 0% | 1,526 | 2,886 | +89% | 0 | 0 | — |
case-22 | pass→fail | 18,837 | 6,660 | -65% | 1 | 1 | 0% | 2,771 | 2,814 | +2% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 13 counted toward the lift figure. The other 9 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +23 percentage points is the difference between those two pass rates over the 13 comparable cases. 4 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.