# Scriptward > Audit a Godot 4 script before it ships - will Godot 4 load it, does it crash on first use, what does it pay every frame, are its signals wired to handlers that exist - then fix it. The free check runs in the browser and uploads nothing: it lexes pasted GDScript (one or more .gd files, plus a .tscn scene for the node tree and project.godot for the version and autoloads), rebuilds its blocks from indentation, inventories every func, member variable, node lookup, signal, connection, emission and physics call, and runs 19 deterministic checks drawn from Godot 4 practice. The paid audit rates syntax, lifecycle, signals and resources, confirms or dismisses every flag and gives exact before/after edits per finding; the paid fix lane writes each whole fixed script and a changes list tied to the flags. The page checks every quote and line against the paste, applies every edit and script back into the paste and re-runs its checks, and calls out a reply that repeats a credential from the paste. App: https://scriptward.skillsafe.ai/ API tutorial: https://scriptward.skillsafe.ai/api.html ## Input - The code pasted into one box, each file introduced by a `# file: ` line; dropped files or a dropped scene folder are joined that way automatically. A paste with no `# file:` line is read as one script (script.gd). - A pasted `.tscn` gives the real node tree: `$Path`, `get_node("Path")` and `%Unique` lookups in the script attached to a node are checked against the nodes that exist (paths through an instanced sub-scene are skipped). `project.godot` gives the Godot version (`config/features`) and the autoload names. - Nothing is run: every flag is a reading of the text, with the file and line it points at. Strings (including `&"StringName"` and `^"NodePath"`) and comments are lexed out first, so a string that mentions `yield(` is never a finding. Tabs and spaces are tracked per file, since GDScript refuses a file that mixes them. - Parent scripts, scenes and autoloads that are not pasted are outside the check (GD-20 says so); running the scene with the Debugger open and the GDScript warnings on stays the ground truth. ## Flags the free check raises - GD-01 (high): Godot 3 syntax or API Godot 4 refuses - onready/export/tool keywords, yield(), setget, remote/master/puppet func, connect(signal, self, "method"), .instance(), KinematicBody2D, Spatial, Position2D, rand_range(), stepify(), deg2rad(), Pool*Array, change_scene(), OS.get_ticks_msec(), move_and_slide(velocity), File.new(), to_json(), .empty(), BUTTON_LEFT and more; one flag per file with every line. - GD-02 (high): does not parse - an @export var with no type and no default, or a file that indents with both tabs and spaces. - GD-03 (high): a member variable set from the scene tree ($, %, get_node) without @onready - it runs before the children exist. - GD-04 (high): a $Path, get_node path or %Unique name that is not in the pasted scene the script sits on (medium for get_node_or_null). - GD-05 (medium): node lookups and tree searches (get_node, $, find_child, get_nodes_in_group, get_first_node_in_group) in _process, _physics_process or _integrate_forces; one flag per callback. - GD-06 (medium): move_and_slide, move_and_collide or forces and impulses applied in _process. - GD-07 (medium): position/rotation changed with += or translate/rotate in a per-frame callback without delta (or a local derived from it). - GD-08 (medium): a RigidBody2D/3D whose position or transform is set outside _integrate_forces. - GD-09 (high/medium): a signal connected to a handler that is not a func of the script or its pasted parents (high for an identifier - a parse error; medium for a string name). - GD-10 (medium): connect() inside a per-frame callback with no is_connected guard. - GD-11 (low): a lambda connected to an autoload's, get_tree()'s or another long-lived object's signal and never disconnected. - GD-12 (low): signals or methods wired by a string name - emit_signal("x"), connect("x", ...), Callable(self, "m"), call_deferred("m"); one flag per file. - GD-13 (medium): free() on this node or on a node reference, where queue_free() belongs. - GD-14 (medium/low): load() on the per-frame path (medium); instantiate() on the per-frame path without a pool (low). - GD-15 (low): an absolute /root/ path, noted when it names an autoload. - GD-16 (medium): a credential literal in a script - it ships in the exported .pck. - GD-17 (low): untyped member variables, parameters and return types; one flag per file with the count. - GD-18 (low): an empty _process or _physics_process. - GD-19 (low): a signal declared and never emitted anywhere in the paste. - GD-20 (info): context - counts, the Godot version, what is outside the check. ## Free extras - Every node lookup with the func it runs in and whether it runs every frame or in a member initialiser; every connect() with its source, signal, handler and form; every signal and whether it is emitted; every script with what it extends, its engine callbacks and the scene node it sits on; every file with its indentation. - Put any audit finding (or all of them, or the fixed scripts) back into the paste and re-run the check; undo puts the original back. - Exports: the check as Markdown, the flags as CSV, the edited or fixed code, the findings or changes as CSV, the whole result as JSON. ## Paid lanes (the `task` field) - `audit`: `{task, verdict: blocked|needs_work|ready, headline, scene, areas: [{area: syntax|lifecycle|signals|resources, status: sound|fragile|broken, note}], findings: [{id, flags, file, line, severity, area, title, evidence, why, edits: [{before, after}]}], flag_answers: [{flag, status: confirmed|dismissed, reason}], plan, questions, summary}`. `before` is an exact slice of the file with its own indentation (tabs as `\t`), applied in order. - `fix`: `{task, verdict: fixed|partial, headline, files: [{path, content}], changes: [{file, function, flags, change, why}], verify, open: [{item, flags, reason}], questions, summary}`. `content` is the whole script; every func keeps its name and parameters, every signal, @export and class_name stays. - Run input: `{task, code, facts, note?, audit_handoff?, retry_note?}` - all strings; `facts` is the page's analysis as a JSON string, bounded on a very large paste (lists keep their first entries, flags most severe first; `notes` says what was left out). The audit hands its findings to the fix lane with every edit applied to `code`. - Model: gpt-terra, 10% publisher markup. A run needs a signed-in account; the reservation shown before a run is a hold, and only what the run uses is charged. ## Sources - godot-gdscript-patterns by Seth Hobson, wshobson/agents (github.com/wshobson/agents, plugins/game-development/skills/godot-gdscript-patterns), MIT - https://skillsafe.ai/skill/@wshobson/godot-gdscript-patterns - The checks and the reconciliation are original to this app. Godot is a trademark of the Godot Foundation; this app is not affiliated with or endorsed by it.