Expert architectural standards for scalable Godot Apps, Tools, EditorPlugins, and Control-heavy UIs using Composition (Has-A Orchestrator + components). Use when building dashboards, tool windows, forms, settings panels, or EditorPlugin UIs. Do NOT use for gameplay entities (Player/Enemy/Weapon/Hitbox) — route those to godot-composition. Trigger keywords: Control, EditorPlugin, tool UI, Orchestrator, VLS, rock test, AuthComponent, ThemeManager, Saveable component, dependency injection.
Instrucciones de origen · Vista previa de solo lectura
name
godot-composition-apps
description
Expert architectural standards for scalable Godot Apps, Tools, EditorPlugins, and Control-heavy UIs using Composition (Has-A Orchestrator + components). Use when building dashboards, tool windows, forms, settings panels, or EditorPlugin UIs. Do NOT use for gameplay entities (Player/Enemy/Weapon/Hitbox) — route those to godot-composition. Trigger keywords: Control, EditorPlugin, tool UI, Orchestrator, VLS, rock test, AuthComponent, ThemeManager, Saveable component, dependency injection.
Godot Composition & Architecture (Apps & UI)
Decision Gate — App vs Gameplay Entity
Root node / task
Route
Control, EditorPlugin, tool window, settings dock, form UI
App-only gate: If the node is a gameplay actor (Player/Enemy/Weapon/Hitbox), use godot-composition. This skill owns Control / EditorPlugin / tool composition.
The Core Philosophy
The Litmus Test (Rock Test)
Before writing a script, ask: "If I attached this script to a literal rock, would it still function?"
Pass: An AuthComponent on a rock allows the rock to log in. (Context Agnostic)
Fail: A LoginForm script on a rock tries to grab text fields the rock doesn't have. (Coupled)
Separate theme component — never mutated inside form logic
Focus ownership
Orchestrator grants/releases Control focus; components never steal siblings' focus
Implementation Standards
Type Safety — class_name on components; no untyped core architecture.
Dependency Injection — @export var auth: AuthComponent (Inspector / %UniqueNames). NEVERget_node("Path/To/Child") for components.
Stateless workers — Orchestrator passes data into functions; components do not scrape sibling Controls.
NEVER Do (Expert Architectural Rules)
Hierarchy & Dependencies
NEVER use get_parent() to fetch data — Inject via @export or function args.
NEVER talk sideways — Signal up; Orchestrator calls down.
NEVER use brittle Node Paths — Prefer @export / %.
Logic & State
NEVER put business logic in the Orchestrator — Only _on_signal delegators.
NEVER store global state in individual components — Shared Context Resource or Autoload.
NEVER assume a component's parent is a specific type — Rock Test failure.
Polish & Orchestration
NEVER skip signal cleanup — Disconnect on exit / use CONNECT_ONE_SHOT where appropriate.
NEVER let Logic know about Visuals — Emit; VLS / Orchestrator plays animations and applies Theme.
Fragile App Workflow: Saveable + Theme Ownership
Do not put save I/O or Theme mutation inside form Controls. Route through components:
MANDATORYcomp_persistence_component.gd on the Orchestrator (or a dedicated Saveable child) — add_to_group("Saveable") + get_save_data().
Theme / StyleBox changes belong in a theme component (theme_manager.gd) called down by the Orchestrator after logic signals success/failure.
Focus: Orchestrator owns grab_focus() after validation failures so logic stays Control-agnostic.
# settings_dock_orchestrator.gd (pattern — wire via @export, not $)
extends Control
@export var persistence: CompPersistenceComponent
@export var theme_mgr: Node # theme_manager.gd API
@export var form_logic: Node
func _ready() -> void:
form_logic.settings_valid.connect(_on_settings_valid)
form_logic.settings_invalid.connect(_on_settings_invalid)
func _on_settings_valid(payload: Dictionary) -> void:
theme_mgr.apply_user_theme(payload.get("theme_id"))
# Save systems collect via Saveable group — persistence component stays dumb
func _on_settings_invalid(field: StringName) -> void:
# Orchestrator owns focus; logic never touches sibling LineEdits
var target := get_node_or_null("%" + String(field))
if target is Control:
target.grab_focus()
Expert Composition Patterns (Apps)
1. App-Level Service Locator
Prefer Engine.register_singleton() for lightweight non-Node services (Auth, Config) instead of dozens of Autoload Nodes [6].
Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
Official Documentation
Scene organization — Canonical signal-up / call-down ownership so Orchestrators wire components without sibling coupling.