/gm-verify
Mechanical verification of the built game: headless build, unit tests, lint, static checks. Explicit invocation only — use /gm-verify.
$ npx -y skills add RandallLiuXin/GodotMaker --skill gm-verify --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/gm-verify
Context preview
The summary Claude sees to decide when to auto-load this skill.
Mechanical verification of the built game: headless build, unit tests, lint, static checks. Explicit invocation only — use /gm-verify.
SKILL.md
gm-verify.SKILL.mdname: gm-verify
description: |
Mechanical verification of the built game: headless build, unit tests, lint, static checks.
Explicit invocation only — use /gm-verify.
disable-model-invocation: true
GodotMaker Verify
$ARGUMENTS
You are performing mechanical verification of a built Godot game project. This is a non-creative, checklist-driven process.
Session Setup
**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.
Resume Check
Read `.godotmaker/stage.jsonl` (treat as empty if missing) — each line is `{"role": X, "ts": Y}`.
- If **no event with `role == "build"` AND no event with `role == "fixgap"`** exists anywhere in the file → STOP. Tell user to run `/gm-build` first.
- If the **last event** has `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."
- Otherwise → proceed (verify is naturally re-invoked after each build/fixgap cycle).
Run the checks
From the project root:
python 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:
- Reads `godot_path` from `.claude/godotmaker.yaml`; falls back to
plain `godot` from PATH. A missing or broken binary surfaces as a `tooling_notes[].suggested_fallback = "escalate"` entry.
- Runs `<godot_path> --headless --quit` and writes blocking Godot diagnostics
to `checks.build.errors[]`.
- Runs `<godot_path> --headless ... res://addons/gdUnit4/bin/GdUnitCmdTool.gd
--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.
- Stubs `checks.lint` as `pass` with `format_drift: null`. Do NOT
re-enable here.
- Delegates `checks.static_check` to
`python tools/check_project.py <project_dir> --build --ecs --tests --plan --mcp`.
- Exit code 0 = ran to completion (per-check pass/fail is in the JSON).
Sanity-check the script output
Before writing the report, validate. Block on any of these:
- JSON does not parse, or any of `result` / `ts` / `checks` /
`tooling_notes` is missing
- Any of the four `checks.{build,unit_tests,lint,static_check}`
entries is absent
- `result == "pass"` but `tooling_notes` is non-empty — re-run or escalate
- `checks.unit_tests.passed + .failed == 0` — spot-check by running the gdUnit4 command directly with `--report-directory <temp>`
- Any `tooling_notes` entry whose `crashed_on` looks unrelated to the
failing 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.
Output Format
You produce **two outputs**:
A. Human-readable report (chat)
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})
…}B. Machine-readable report (`.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:
{
"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:
- **Top-level `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.
- **All array fields are required** (possibly empty `[]
Read more
name: gm-verify description: | Mechanical verification of the built game: headless build, unit tests, lint, static checks. Explicit invocation only — use /gm-verify. disable-model-invocation: true
GodotMaker Verify
$ARGUMENTS
You are performing mechanical verification of a built Godot game project. This is a non-creative, checklist-driven process.
Session Setup
**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.
Resume Check
Read `.godotmaker/stage.jsonl` (treat as empty if missing) — each line is `{"role": X, "ts": Y}`.
- If **no event with `role == "build"` AND no event with `role == "fixgap"`** exists anywhere in the file → STOP. Tell user to run `/gm-build` first.
- If the **last event** has `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."
- Otherwise → proceed (verify is naturally re-invoked after each build/fixgap cycle).
Run the checks
From the project root:
python 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:
- Reads `godot_path` from `.claude/godotmaker.yaml`; falls back to
plain `godot` from PATH. A missing or broken binary surfaces as a `tooling_notes[].suggested_fallback = "escalate"` entry.
- Runs `<godot_path> --headless --quit` and writes blocking Godot diagnostics
to `checks.build.errors[]`.
- Runs `<godot_path> --headless ... res://addons/gdUnit4/bin/GdUnitCmdTool.gd
--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.
- Stubs `checks.lint` as `pass` with `format_drift: null`. Do NOT
re-enable here.
- Delegates `checks.static_check` to
`python tools/check_project.py <project_dir> --build --ecs --tests --plan --mcp`.
- Exit code 0 = ran to completion (per-check pass/fail is in the JSON).
Sanity-check the script output
Before writing the report, validate. Block on any of these:
- JSON does not parse, or any of `result` / `ts` / `checks` /
`tooling_notes` is missing
- Any of the four `checks.{build,unit_tests,lint,static_check}`
entries is absent
- `result == "pass"` but `tooling_notes` is non-empty — re-run or escalate
- `checks.unit_tests.passed + .failed == 0` — spot-check by running the gdUnit4 command directly with `--report-directory <temp>`
- Any `tooling_notes` entry whose `crashed_on` looks unrelated to the
failing 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.
Output Format
You produce **two outputs**:
A. Human-readable report (chat)
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})
…}B. Machine-readable report (`.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:
{
"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:
- **Top-level `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.
- **All array fields are required** (possibly empty `[]
Autonomous text-to-game pipeline for Godot, powered by Claude Code,Codex,Opencode
Repo: RandallLiuXin/GodotMaker
Other skills on godotmaker.
- /background-map
Generate and validate a fixed-viewport background, map base, or parallax plate as a ready-to-load Texture2D.
Open skill - /card-kit
Produce reusable card art sources and native Godot card UI resources.
Open skill - /character-bundle
Produce one illustrated character SpriteFrames resource from high-level body-action intent, optional character and style references, and a resolved animation plan.
Open skill - /compact-prop-pack
Produce a reusable compact-prop atlas from one provider source sheet, with independently loadable AtlasTexture resources for every declared prop.
Open skill - /fx-bundle
Produce a standalone static Texture2D effect or one explicitly timed animated SpriteFrames effect.
Open skill - /platform-strip
Generate non-pixel-art, horizontally repeatable platform strips from real image sources as fixed Texture2D cells or AtlasTexture regions.
Open skill

