name: godot-refactor version: 3.0.0 displayName: "Godot Refactoring Orchestrator" description: > Use when Godot project has code-created objects, tight coupling via direct references, monolithic scripts over 150 lines, or needs scene-first architecture with signals and composition. Orchestrates all 5 code quality mini-skills: extract-to-scenes, split-scripts, add-signals, extract-resources, and clean-conflicts. Each operation runs independently with git commits and validation. author: "Asreonn" license: MIT category: game-development type: agent difficulty: intermediate audience: [developers, teams] keywords:
- godot
- refactoring
- code-quality
- scene-architecture
- signals
- composition
- gdscript
- clean-code
- godot4
platforms: [macos, linux, windows]
repository: https://github.com/asreonn/godot-superpowers
homepage: https://github.com/asreonn/godot-superpowers#readme
filesystem:
read:
- "${PROJECT_ROOT}/**/*.gd"
- "${PROJECT_ROOT}/**/*.tscn"
- "${PROJECT_ROOT}/project.godot"
- "${PROJECT_ROOT}/**/*.tres" write:
- "${PROJECT_ROOT}/**/*.gd"
- "${PROJECT_ROOT}/**/*.tscn"
- "${PROJECT_ROOT}/components/**"
- "${PROJECT_ROOT}/resources/**" deny:
- "**/.env*"
- "**/secrets*"
- "**/*.key"
- "**/*.pem" behavior: timeout: 600 retry: 3 cache: true interactive: true use_cases:
- "Building new features and code uses .new() instead of scenes"
- "Scripts are over 150 lines and do too many things"
- "Code has get_node() chains creating tight coupling"
- "Game data is hardcoded in const arrays"
- "Need clean architecture for team collaboration"
- "Preparing code for review or testing"
- "Want full project refactoring with all operations" outputs: "Component library, split scripts, signal architecture, resource files, git commits per operation" requirements: "Git repository, Godot 4.x, bash tools (grep, find, awk)" execution: "Fully automatic with user approval after Phase 1 analysis" auto_rollback: "Yes - auto-reverts on validation test failure" integration: "Orchestrates: godot-extract-to-scenes, godot-split-scripts, godot-add-signals, godot-extract-resources, godot-clean-conflicts"
Godot Refactor Orchestrator
This orchestrator runs 5 code quality mini-skills in sequence. For individual operations, invoke mini-skills directly.
Core Principle: Scene-First, Signal-Based, Component Composition
Iron Law: NO functional or visual changes during refactoring. Period.
This skill automatically refactors Godot projects to cleaner, more modular architecture while preserving exact behavior. Every operation creates a git commit. Rollback is always available.
UPON INVOCATION - START HERE
When this skill is invoked, IMMEDIATELY execute the following sequence:
1. Verify Godot Project (5 seconds)
# Check if this is a Godot project
ls project.godot 2>/dev/null && echo "✓ Godot project detected" || echo "✗ Not a Godot project"
If NOT a Godot project:
- Inform user this skill only works on Godot projects
- Ask if they want to navigate to the correct directory
- STOP here
If IS a Godot project:
- Proceed to step 2
2. Begin Phase 1: Analysis & Baseline (AUTOMATIC)
Do NOT ask "What would you like me to do?" - Start analyzing immediately.
Execute these commands in parallel:
# Detect code-created objects
grep -rn "\.new()" --include="*.gd" . | grep -E "(Node|Timer|Area|Sprite|Control|Collision)" | wc -l
# Detect monolithic scripts
find . -name "*.gd" -exec wc -l {} + | awk '$1 > 150' | wc -l
# Detect tight coupling
grep -rn "get_node\|has_method" --include="*.gd" . | wc -l
# Detect inline data
grep -rn "^[[:space:]]*const.*\[" --include="*.gd" . | wc -l
3. Present Findings (30 seconds)
Show the user:
=== Godot Refactoring Analysis ===
Project: [project name from project.godot]
Anti-patterns detected:
- Code-created objects: X
- Monolithic scripts: X
- Tight coupling: X
- Inline data: X
- Conflicting operations: (will be detected after refactoring)
Total: X anti-patterns found
Refactoring includes:
✓ Automatic testing after each operation
✓ Auto-rollback on test failure
✓ Git commit per operation
✓ Push to remote on demand
Would you like me to:
1. Proceed with automatic refactoring (recommended)
2. Show detailed breakdown first
3. Cancel
4. Wait for User Choice
- If 1 (Proceed): Start Phase 2 immediately
- If 2 (Details): Show detailed file-by-file breakdown, then offer to proceed
- If 3 (Cancel): Exit skill
CRITICAL: Do NOT stop after loading the skill. Do NOT ask "What would you like me to do?" Start with step 1 immediately.
When to Use This Skill
Use this skill when you detect ANY of these symptoms:
Code-Created Objects:
.new()calls in_ready(),_process(), or other lifecycle methodsadd_child()with programmatically created nodes- Timers, Areas, Sprites created in code
Monolithic Scripts:
- Scripts >150 lines
- Multiple unrelated responsibilities in one file
- God objects that do everything
Tight Coupling:
get_node()chains to access other nodeshas_method()checks before calling- Direct property access across nodes
- Parent-child dependencies for behavior
Inline Data:
constarrays with game data- Hardcoded configuration in scripts
- Data that should be resources
Deep Inheritance:
- More than 2 levels of script inheritance
- Inheritance used for code reuse instead of composition
Decision Flowchart
User mentions Godot project
↓
Does it have ANY of the above symptoms?
↓ YES ↓ NO
Use this skill Regular development
↓
Run Phase 1: Analysis
↓
Anti-patterns found?
↓ YES ↓ NO
Run Phases 2-4 Document clean state
The Four Phases
Phase 1: Analysis & Baseline (Automatic)
Purpose: Detect all anti-patterns and create safety baseline.
Steps:
- Scan Project Files
# Find all Godot script files
find . -name "*.gd" -type f
# Find all scene files
find . -name "*.tscn" -type f
- Detect Anti-Patterns
Use detection patterns from anti-patterns-detection.md:
# Code-created objects
grep -rn "\.new()" --include="*.gd" .
# Monolithic scripts (>150 lines)
find . -name "*.gd" -exec wc -l {} + | awk '$1 > 150 {print $2 " (" $1 " lines)"}'
# Tight coupling
grep -rn "get_node\|get_parent\|has_method" --include="*.gd" .
# Inline data
grep -rn "^[[:space:]]*const.*\[" --include="*.gd" .
- Create Git Baseline
# Save current branch
current_branch=$(git branch --show-current)
# Create baseline tag
git tag baseline-$(date +%Y%m%d-%H%M%S)
# Initial commit if there are uncommitted changes
git add .
git commit -m "Baseline: Pre-refactoring state (on $current_branch)"
- Generate Refactoring Manifest
Create a priority-ordered list:
| Priority | Anti-Pattern | File | Lines | Operation |
|---|---|---|---|---|
| 1 | Inline data | enemy_data.gd | 45-78 | Extract to .tres |
| 2 | Code-created Timer | laser_beam.gd | 38-45 | Extract to .tscn |
| 3 | Tight coupling | base_station.gd | 92 | Signal decoupling |
| 4 | Monolithic script | player_movement.gd | 287 lines | Split script |
Phase 1 Output:
- ✓ Baseline git commit/tag created
- ✓ Anti-patterns detected and prioritized
- ✓ Manifest table generated
- ✓ Ready for automatic refactoring
Phase 2: Automatic Refactoring (Five Operations)
Process anti-patterns in priority order: Data → Scenes → Signals → Scripts → Conflicts
CRITICAL - After Each Operation:
- Git commit the changes
- Run automatic validation test
- If test fails: Auto-revert and report error
- If test passes: Continue automatically to next operation
- No user prompt unless test fails or user confirmation needed
Automatic Testing Procedure:
# Quick validation test (runs after each git commit)
echo "Running validation test..."
godot --headless --quit-after 5 project.godot 2>&1 | tee test_output.log
# Check for errors
if grep -q "ERROR\|SCRIPT ERROR\|Parse Error" test_output.log; then
echo "⚠️ Tests failed - reverting operation"
git reset --hard HEAD~1
echo "❌ Operation reverted. Error details:"
grep "ERROR\|SCRIPT ERROR\|Parse Error" test_output.log
# STOP and report to user
else
echo "✓ Tests passed - continuing"
rm test_output.log
# CONTINUE to next operation automatically
fi
Note: If test fails, STOP workflow and report to user. Otherwise, continue automatically through all operations.
Operation A: Extract Code-Created Objects to Modular Components
Target: Any .new() calls that create nodes
Enhanced Process:
-
Detect and Analyze
- Find
.new()calls for node types - Extract 30 lines of context (properties, methods, signals)
- Analyze variable names, properties, methods
- Consult
node-selection-guide.mddecision trees - Calculate confidence score for node type
- Select optimal node from
godot-node-reference.md
- Find
-
Check Component Library
- Does
components/{category}/exist? (e.g.,components/timers/) - If YES: Reuse base component, skip to Step 4
- If NO: Generate full component structure (Steps 2-3)
- Does
-
Generate Component (First Time Only)
- Create
{type}_config.gd(Resource class with @export properties) - Create
configurable_{type}.gd(Script that extends node, applies config) - Create
configurable_{type}.tscn(Base scene with script) - Creates modular, reusable foundation
- Create
-
Generate Preset Resource
- Extract properties from analyzed code
- Create
presets/{preset_name}.tres(specific configuration) - Each detected instance gets unique preset
- Presets reference the base component
-
Update Parent Scene
- Add ext_resource entries for base scene and preset
- Instance base component with preset resource
- Same scene reused, different presets = ZERO duplication
-
Update Parent Script
- Add @onready reference to component instance
- Keep signal connections
- Remove .new(), add_child(), static property assignments
- Configuration now comes from preset resource
-
Git Commit & Validate
- Commit all component files and parent changes
- Run validation test
- Ensure behavior unchanged
Example Transformation:
Before (Code-Created):
func _ready():
_damage_timer = Timer.new()
_damage_timer.wait_time = 0.5
_damage_timer.one_shot = false
add_child(_damage_timer)
_damage_timer.timeout.connect(_on_damage)
_cooldown_timer = Timer.new()
_cooldown_timer.wait_time = 2.0
_cooldown_timer.one_shot = true
add_child(_cooldown_timer)
_cooldown_timer.timeout.connect(_on_cooldown)
After (First Timer - Full Component Created):
Files created:
✓ components/timers/timer_config.gd (resource class)
✓ components/timers/configurable_timer.gd (reusable script)
✓ components/timers/configurable_timer.tscn (reusable base scene)
✓ components/timers/presets/damage_timer.tres (configuration preset)
parent.gd:
@onready var _damage_timer: ConfigurableTimer = $DamageTimer
func _ready():
_damage_timer.timeout.connect(_on_damage)
After (Second Timer - Reuses Base):
Files created:
✓ components/timers/presets/cooldown_timer.tres (NEW preset only!)
parent.gd:
@onready var _damage_timer: ConfigurableTimer = $DamageTimer
@onready var _cooldown_timer: ConfigurableTimer = $CooldownTimer
func _ready():
_damage_timer.timeout.connect(_on_damage)
_cooldown_timer.timeout.connect(_on_cooldown)
Key Benefits:
- ✅ Intelligent node selection (no arbitrary choices)
- ✅ Modular components (reusable across project)
- ✅ Zero duplication (same base, different presets)
- ✅ Inspector-configurable (edit presets in editor)
- ✅ Automatic library building (grows organically)
- ✅ Signal connections preserved
- ✅ Behavior unchanged (Iron Law maintained)
Success Criteria:
- Component structure created (if first of type)
- Preset resource created with extracted values
- Parent scene instances component with preset
- Parent script has @onready reference
- Signal connections work
- Old code removed
- Behavior unchanged
Operation B: Split Monolithic Scripts
Target: Scripts >150 lines with multiple responsibilities
Detection:
find . -name "*.gd" -exec wc -l {} + | awk '$1 > 150'
For each detected script:
-
Analyze script structure
- Identify logical sections (look for comment headers, function groups)
- Find responsibilities (input, physics, abilities, UI, etc.)
- Determine dependencies between sections
-
Plan split
- Main script keeps core responsibility
- Extract secondary responsibilities to components
- Define signal interface between components
-
Create component scripts
Example: Split
player_movement.gd(287 lines)Extract to
player_abilities.gd:extends Node class_name PlayerAbilities signal ability_used(ability_name: String) signal cooldown_started(duration: float) # Ability-related functions moved here -
Update main script
@onready var abilities: PlayerAbilities = $Abilities func _ready(): abilities.ability_used.connect(_on_ability_used) -
Implement signal communication
- Replace direct function calls with signals
- Connect signals in _ready()
- Remove tight coupling
-
Git commit
git add player_movement.gd player_abilities.gd git commit -m "Refactor: Split abilities from player_movement.gd" -
Run automatic test (see Phase 2 header for test procedure)
Success criteria:
- Main script <150 lines
- Component scripts focused (80-120 lines optimal)
- Signal-based communication
- No direct dependencies
- Behavior unchanged
Operation C: Implement Signal-Based Decoupling
Target: Direct coupling via get_node(), has_method(), direct calls
Detection:
grep -rn "get_node\|has_method" --include="*.gd" .
For each detected coupling:
-
Identify the relationship
- What is being accessed?
- What information is being exchanged?
- Is this event-based or state query?
-
Create/use Events autoload
If not exists, create
events.gd:extends Node # Global event bus signal player_entered_safe_zone(zone: Node2D) signal enemy_spawned(enemy: Node2D) signal score_changed(new_score: int)Add to project settings: Project → Project Settings → Autoload
-
Replace direct coupling with signals
Before:
func _on_body_entered(body): if body.has_method("set_beam_enabled"): body.set_beam_enabled(false)After (emitter):
func _on_body_entered(body): if body.is_in_group("player"): Events.player_entered_safe_zone.emit(self)After (receiver):
func _ready(): Events.player_entered_safe_zone.connect(_on_safe_zone) func _on_safe_zone(zone): set_beam_enabled(false) -
Remove coupling code
- Delete get_node() calls
- Delete has_method() checks
- Delete direct property access
-
Git commit
git add events.gd base_station.gd player_movement.gd git commit -m "Refactor: Decouple base_station from player via signals" -
Run automatic test (see Phase 2 header for test procedure)
Success criteria:
- No get_node() for behavior access
- No has_method() checks
- Events.gd exists and is used
- Signal connections established
- Behavior unchanged
Operation D: Extract Data to .tres Resources
Target: const declarations with game data
Detection:
grep -rn "^[[:space:]]*const.*\[" --include="*.gd" .
For each detected data constant:
-
Analyze data structure
const ENEMY_TYPES = [ {"type": "basic", "health": 100, "speed": 200}, {"type": "fast", "health": 50, "speed": 400} ] -
Create Resource class
Create
enemy_type_data.gd:extends Resource class_name EnemyTypeData @export var type_name: String @export var health: int @export var speed: float -
Generate .tres files
Create
enemy_types/basic.tres:[gd_resource type="EnemyTypeData" load_steps=2 format=3] [ext_resource type="Script" path="res://enemy_type_data.gd" id="1"] [resource] script = ExtResource("1") type_name = "basic" health = 100 speed = 200.0 -
Update script to use resources
@export var enemy_types: Array[EnemyTypeData] # In scene, assign resources via inspector -
Remove const declaration
-
Git commit
git add enemy_type_data.gd enemy_types/*.tres enemy_spawner.gd git commit -m "Refactor: Extract enemy data to .tres resources" -
Run automatic test (see Phase 2 header for test procedure)
Success criteria:
- Resource class created
- .tres files generated with correct data
- Script updated to use resources
- Const declaration removed
- Behavior unchanged
Operation E: Clean Conflicting/Ineffective Operations
Target: Code that runs without errors but has no effect or conflicts with other code
IMPORTANT: This operation runs after Operations A-D are complete, as refactoring may introduce or remove conflicts.
Detection:
# Run comprehensive conflict detection
bash << 'EOF'
echo "=== Detecting Conflicting Operations ==="
# Self-assignments
echo "Self-assignments:"
grep -rn "position\s*=\s*position\|scale\s*=\s*scale\|rotation\s*=\s*rotation" --include="*.gd" .
# Redundant defaults
echo "Redundant defaults:"
grep -rn "modulate\s*=\s*Color\.WHITE\|scale\s*=\s*Vector2\.ONE" --include="*.gd" .
# Duplicate property assignments (common properties)
echo "Checking for duplicate assignments..."
for prop in "scale" "position" "rotation" "modulate" "visible"; do
grep -rn "\.$prop\s*=" --include="*.gd" . | \
awk -F: -v prop="$prop" '{
file=$1; line=$2;
if (file==prev_file && line-prev_line<20) {
print file":"prev_line" and "line" - Duplicate "prop
}
prev_file=file; prev_line=line
}'
done
# Conflicting tweens
echo "Conflicting tweens:"
grep -n "tween_property" --include="*.gd" -r . | \
awk -F: '{
file=$1; line=$2;
if (match($0, /tween_property.*"([^"]+)"/, arr)) {
prop=arr[1];
key=file":"prop;
if (key in seen && line-seen[key]<15) {
print file":"seen[key]" and "line" - Conflicting tweens on "prop
}
seen[key]=line
}
}'
# Code after queue_free
echo "Code after queue_free:"
grep -A2 "queue_free()" --include="*.gd" . | grep -v "^--$"
EOF
For each detected conflict:
-
Auto-fix (no user input):
- Self-assignments: Remove entirely
- Code after
queue_free(): Move code before queue_free - Obvious redundant defaults: Remove
-
User confirmation (ask user):
- Duplicate assignments: Which value to keep?
- Conflicting tweens: Which to keep or chain?
- Multiple process calls: Final intended state?
-
Example fixes:
Self-assignment:
# Before position = position # After (remove line)Duplicate assignment:
# Before sprite.scale = Vector2(2, 2) # Line 45 sprite.scale = Vector2(1, 1) # Line 52 # After (keep last) sprite.scale = Vector2(1, 1) # Line 52Conflicting tweens (user chose "chain"):
# Before var tween1 = create_tween() tween1.tween_property(self, "scale", Vector2(2,2), 1.0) var tween2 = create_tween() tween2.tween_property(self, "scale", Vector2(0.5,0.5), 1.0) # After var tween = create_tween() tween.tween_property(self, "scale", Vector2(2,2), 1.0) tween.tween_property(self, "scale", Vector2(0.5,0.5), 1.0) -
Git commit
git add <modified files> git commit -m "Refactor: Clean conflicting/ineffective operations - Removed self-assignments - Fixed duplicate property assignments - Resolved conflicting tweens - Cleaned up redundant operations No functional changes - code cleanup only" -
Run automatic test (see Phase 2 header for test procedure)
Success criteria:
- No self-assignments remain
- No obvious duplicate assignments
- Conflicting tweens resolved
- Code after queue_free() moved
- Behavior unchanged
Note: See conflicting-operations-detection.md for comprehensive detection patterns and refactoring-operations.md Operation E for detailed procedures.
Phase 3: Git Commits (Automatic)
After each operation, commit with clear message:
# Template
git add <files>
git commit -m "Refactor: <Operation> in <file> - <specific change>"
# Examples
git commit -m "Refactor: Extract Timer to scene in laser_beam.gd"
git commit -m "Refactor: Split abilities from player_movement.gd (287→142 lines)"
git commit -m "Refactor: Decouple base_station from player via signals"
git commit -m "Refactor: Extract enemy data to .tres resources"
git commit -m "Refactor: Clean conflicting/ineffective operations"
After all operations:
git tag refactor-complete-$(date +%Y%m%d)
Commit history should show:
- Baseline commit
- One commit per refactoring operation (A, B, C, D, E)
- Clear, descriptive messages
- Easy to review
- Easy to rollback individual changes
Push on Demand:
After all operations complete, ask user:
=== Refactoring Complete ===
All operations completed successfully!
5 operations performed, X commits created on current branch.
Would you like to push to remote?
1. Yes, push now
2. No, I'll push manually later
3. Show me the commit log first
Choice [1-3]:
If user chooses 1 (push now):
# Push to current branch's remote
git push origin $(git branch --show-current)
If user chooses 3 (show log first):
git log --oneline --graph baseline-*..HEAD
# Then ask again: Push now? [y/n]
If user says "push" or "push et" at ANY time during the workflow:
# Immediately push current branch
git push origin $(git branch --show-current)
echo "✓ Pushed to remote"
Phase 4: Final Verification (Minimal, Automatic)
Purpose: Ensure no functional or visual regressions.
Steps:
-
Open project in Godot
godot --editor path/to/project.godot -
Check for errors
- No red errors in console
- No yellow warnings about missing nodes
- All scenes load successfully
-
Visual verification
- Run main scene
- Quick visual check (30 seconds)
- Compare against expected behavior
-
Play test checklist (from
verification-checklist.md):- Player movement works
- Abilities trigger correctly
- Enemies spawn and behave normally
- UI updates properly
- No crashes or errors
-
Performance baseline (optional):
# Compare FPS before/after # Should be identical ±2 FPS
If verification fails:
# Rollback procedure
git reset --hard baseline-YYYYMMDD-HHMMSS
git tag refactor-failed-$(date +%Y%m%d-%H%M%S)
# Report what broke
# Debug with systematic-debugging skill
If verification succeeds:
Report summary:
✓ Refactoring complete
✓ 8 operations performed
✓ 287 lines reduced to 142 across 3 files
✓ 4 .tscn scenes created
✓ 2 .tres resources created
✓ Signal-based architecture established
✓ No functional changes
✓ All tests passing
Supporting Files
This skill uses modular reference files:
- anti-patterns-detection.md: All grep/find patterns for detection
- refactoring-operations.md: Detailed step-by-step procedures
- godot-best-practices.md: Clean patterns reference
- tscn-generation-guide.md: .tscn file format templates
- verification-checklist.md: Testing procedures
Read these files for detailed implementation guidance.
Red Flags - STOP
These thoughts mean you're rationalizing away discipline:
| Rationalization | Reality | Fix |
|---|---|---|
| "While I'm here, let me add this feature" | Feature creep breaks Iron Law | Refactor only, no features |
| "This behavior is wrong, I'll fix it now" | Bug fixes are separate work | Note bug, refactor only |
| "I don't need to test this change" | Untested = broken | Always verify |
| "I'll commit after a few more changes" | Loses granularity | Commit per operation |
| "This .tscn format looks close enough" | Invalid scenes crash | Use exact format |
| "The user won't notice this visual change" | Iron Law has no exceptions | Revert and fix |
| "Signals are overkill for this" | Coupling creeps back | Use signals anyway |
| "I'll manually test later" | Later never comes | Test now |
| "This script is fine at 160 lines" | Arbitrary exceptions grow | Split at 150 |
| "I understand the pattern, no need to read" | Assumptions create bugs | Read the guide |
Quick Reference: Anti-Pattern → Clean Pattern
| Anti-Pattern | Clean Pattern | Tool |
|---|---|---|
Timer.new() in code | .tscn scene with Timer | Operation A |
get_node("../Player") | Signal via Events | Operation C |
if has_method("take_damage"): | Signal listener | Operation C |
| 287-line script | Split to 3 focused scripts | Operation B |
const WEAPONS = [...] | .tres resources | Operation D |
add_child(sprite) | Scene composition | Operation A |
| Direct method calls | Signal emit/connect | Operation C |
| Deep inheritance | Component composition | Operations A+B |
File Operation Flow
1. Data Extraction
.gd with const → Resource class .gd + .tres files
2. Scene Extraction
.gd with .new() → .gd with @onready + new .tscn
3. Signal Decoupling
.gd with get_node() → events.gd + updated .gd files
4. Script Splitting
Large .gd → Multiple focused .gd files with signals
Godot Naming Conventions
Scenes (.tscn):
- PascalCase for node names:
DamageTimer,AbilitySystem - snake_case for file names:
damage_timer.tscn,ability_system.tscn
Scripts (.gd):
- snake_case for files:
player_movement.gd,enemy_spawner.gd - PascalCase for class_name:
class_name PlayerMovement
Resources (.tres):
- snake_case for files:
enemy_types/basic.tres - PascalCase for Resource class:
class_name EnemyTypeData
Signals:
- snake_case:
signal ability_used,signal player_entered_safe_zone - Past tense for events:
signal enemy_spawned(notenemy_spawn)
@onready variables:
- snake_case with type hint:
@onready var _damage_timer: Timer = $DamageTimer - Private with underscore:
_damage_timer,_ability_system
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Invalid .tscn format | Scene won't load in Godot | Use exact template format |
| Missing node type in .tscn | Godot can't parse scene | Always specify type="Timer" |
| Forgetting @onready | Null reference at runtime | Add @onready for all node refs |
| Wrong signal signature | Connection fails silently | Match parameter types exactly |
| Removing necessary .new() | Some objects SHOULD be created | Only refactor scene nodes |
| Not testing after each op | Compounding errors | Test after every commit |
| Coupling through globals | Different coupling, same problem | Use Events for decoupling only |
| Over-splitting scripts | Too many tiny files | 80-120 lines is optimal |
| Async signals without checks | Race conditions | Verify node existence |
| Changing behavior "slightly" | Iron Law violation | Revert immediately |
Signal Architecture Patterns
Event Bus (Events.gd):
# Use for global events
signal player_died
signal level_completed(level_num: int)
signal score_changed(new_score: int)
Direct Node Signals:
# Use for parent-child communication
signal health_depleted
signal ability_activated(ability_name: String)
When to use which:
- Events.gd: Cross-tree communication, global state changes
- Node signals: Parent-child, local behavior
Real-World Impact
Expected improvements after refactoring:
Code Quality:
- 30-50% reduction in total lines of code
- Scripts average 80-120 lines (down from 150-300)
- Zero direct node dependencies
- Signal-based architecture throughout
Maintainability:
- Changes isolated to single files
- Components reusable across scenes
- Testing individual components possible
- New developers onboard faster
Performance:
- Identical to baseline (±2 FPS)
- Scene loading slightly faster
- Memory usage unchanged
Execution Strategy
This skill runs fully automatically:
- User invokes skill on Godot project
- Phase 1: Scan and report findings
- User approves refactoring
- Phases 2-3: Execute all operations with commits on current branch
- Phase 4: Verify and report results
User input required:
- Initial invocation
- Approval after Phase 1 analysis
- Final verification check (30 seconds)
Everything else is automatic:
- Anti-pattern detection
- .tscn file generation
- Script modifications
- Git commits on current branch
- Testing
Success Criteria
Refactoring is complete when:
- ✓ Zero
.new()calls for scene nodes - ✓ All scripts <150 lines
- ✓ Signal-based architecture established
- ✓ No
get_node()for behavior access - ✓ Data in .tres resources
- ✓ Clean git history with descriptive commits on current branch
- ✓ All scenes load without errors
- ✓ Behavior identical to baseline
- ✓ Visual appearance unchanged
- ✓ Performance within ±2 FPS
Post-Refactoring
After successful refactoring:
-
Continue development
- Changes are already on current branch
- Push to remote if not already pushed
- Continue normal development workflow
-
Document architecture
- Create architecture diagram
- Document signal flows
- List component responsibilities
-
Establish standards
- Use this clean architecture as template
- Apply patterns to new code
- Prevent anti-pattern regression
-
Consider further improvements (separate tasks):
- Add unit tests (use TDD skill)
- Optimize performance (profile first)
- Add new features (use brainstorming skill)
Remember: Refactoring changes HOW code works internally, not WHAT it does externally. The Iron Law is absolute.