Godot .NET Threading
Use this skill when a Godot 4.6 + .NET/C# task involves threading, async work, background loading, main-thread marshaling, or lifetime hazards after await.
This skill is not a generic C# threading tutorial, not a .NET Threading skill in disguise, and not a permission slip to throw Godot APIs into Task.Run. Its job is to review or design a safe execution flow for Godot/.NET projects only when Godot engine state, scene lifecycle, resource loading, or main-thread affinity is part of the risk: decide what may leave the Godot main thread, what must return before touching engine-owned state, and how cancellation, stale results, and scene exit are validated.
Prefer this skill when the main risk is code that looks asynchronous but may crash, freeze, update freed nodes, mutate the SceneTree from a worker, or hide a lifetime bug behind a cheerful little await.
If the task can be answered entirely with ordinary .NET concepts such as locks, ConcurrentDictionary, TPL scheduling, channels, cancellation-token basics, or thread-safe collections without mentioning Godot-owned objects, SceneTree, Node, Resource, Control, lifecycle callbacks, or main-thread apply, do not use this skill.
Purpose
This skill is used to:
- classify work as CPU-bound, IO-bound, resource-loading, scene mutation, UI update, or mixed
- identify Godot
Node, SceneTree, Control, Resource, and Object operations that must stay on or return to the main thread
- choose the safest execution model: frame-slicing,
Task, Thread, WorkerThreadPool, Godot threaded resource loading, or no threading
- design a
Snapshot -> Work -> Result -> Main-thread apply flow
- define explicit continuation, marshaling, cancellation, and scene-lifetime rules
- return a structured threading review or execution plan another agent can implement safely
Hard boundary: Godot first, .NET second
This skill must stay anchored to Godot/.NET overlay concerns.
Use it only when at least one of these is true:
- background work may touch
Node, SceneTree, Control, Resource, PackedScene, signals, autoloads, or live scene state
- an async continuation may update UI, presentation, scene nodes, resources, or gameplay-facing engine state
- a task may outlive
_ExitTree, scene transition, loading-screen ownership, or node disposal
- the decision is between Godot-specific models such as main-thread apply, frame-slicing,
WorkerThreadPool, or Godot threaded resource loading
- a hitch, crash, or stale result is tied to Godot lifecycle or engine API access
Do not broaden the answer into a general .NET concurrency design unless the .NET detail is necessary to protect the Godot boundary. Keep generic .NET mechanics as supporting notes, not the center of the artifact.
Use this skill when
Invoke this skill for requests such as:
- reviewing
Task.Run, Thread, WorkerThreadPool, or async/await in Godot C#
- fixing scene freezes, loading-screen hitches, pathfinding stalls, procedural generation spikes, or heavy parsing that blocks frames
- deciding whether background work should be a thread, frame-sliced loop, Godot threaded resource load, or pure main-thread sequence
- updating UI, nodes, tile maps, spawned scenes, resources, or presentation state after an async operation
- handling
ObjectDisposedException, invalid instance access, node-freed-after-await, or stale task results after scene changes
- adding cancellation and lifetime safety around long-running node-owned work
Trigger examples
- "I use
Task.Run for pathfinding but Godot sometimes crashes"
- "Loading a big scene causes frame hitches; should I use threaded loading?"
- "
WorkerThreadPool finishes procedural generation and then updates a TileMap"
- "After an HTTP await, my UI sometimes updates after the scene is gone"
- "Can this Godot C# code safely run off the main thread?"
Do not use this skill when
Do not use this skill when:
- the task is ordinary C# concurrency with no Godot engine, scene, resource, or lifecycle boundary
- the request is mainly about
lock, Monitor, SemaphoreSlim, ConcurrentQueue, ConcurrentDictionary, Channel<T>, TPL scheduling, memory barriers, or thread-safe collection choice without a Godot main-thread or lifecycle hazard
- the code is a pure .NET service/library used by a Godot project but the question does not affect
Node, SceneTree, Resource, Control, signals, loading, or main-thread result application
- the main problem is cooldowns, turn order, or gameplay time semantics rather than threading safety
- the issue is a general build, SDK, editor, or package failure with no threading signal; use
runtime-triage
- the task is primarily scene ownership or UI/UX review without async/thread risk
- the user wants a full custom async framework or global dispatcher generated by default
- the safest answer is one local synchronous operation and no meaningful threading decision is needed
Pattern
- Primary pattern: Reviewer
- Secondary pattern: Tool Wrapper, Generator
Why this fit is better than the alternatives:
- the main job is to evaluate threading and lifetime risk against Godot/.NET-specific constraints
- the skill packages reusable Godot main-thread, async, worker, and loading heuristics
- the output should be a repeatable review or safe execution plan, not loose advice
- a full Pipeline would add ceremony without enough value, because the ordered safety checks can live directly in this workflow
Core safety model
Default to this model unless the task has a stronger reason not to:
Main thread:
capture immutable snapshot
Background work:
compute / parse / load data without live Node or SceneTree mutation
Result boundary:
return plain DTOs, immutable data, IDs, or value objects
Main thread:
verify owner still alive and request still current
apply result to Node / SceneTree / Control / Resource / UI
Treat live Godot objects as main-thread-affine unless the project and Godot API documentation prove a narrower exception.
For reusable details, see references/threading-model.md and references/main-thread-marshaling-patterns.md.
Inputs
Collect or infer these inputs when available:
- task summary and expected user-visible behavior
- current async/threading code or proposed execution model
- whether work touches
Node, SceneTree, Control, Resource, signals, autoloads, PackedScene, TileMap, UI, or live scene state
- work type: CPU-bound calculation, IO, background parsing, resource loading, scene instantiation, UI update, or mixed
- owner lifetime: which node, scene, service, or autoload starts and owns the work
- cancellation expectations and what should happen on
_ExitTree, scene change, retry, or game quit
- validation evidence: freeze reproduction, crash logs, stack traces, profiler notes, or slow-task scenario
- current Godot version, .NET target, and platform/export constraints if relevant
If inputs are incomplete, do not block on perfect detail. Infer the smallest safe execution model and mark the assumptions that would change the recommendation.
If the available inputs show only generic .NET concurrency with no Godot engine boundary, route away instead of forcing this template.
Workflow
Follow this sequence every time.
1. Classify the work and execution pressure
State what kind of work is being moved or considered for asynchronous execution.
First state the Godot boundary trigger. If there is no Godot boundary trigger, stop and say this is a generic .NET threading question rather than a godot-dotnet-thread task.
Classify the primary work type as one of:
- CPU-bound pure calculation
- IO-bound read/write or remote call
- resource loading
- scene or node mutation
- UI or presentation update
- mixed work that must be split
- frame hitch that may be better solved by chunking or frame-slicing
Do not start by choosing Task.Run or WorkerThreadPool. Start from the work shape and the engine boundary.
2. Identify Godot main-thread touch points
List every known or likely touch of engine-owned state.
Check especially for:
GetNode, GetTree, AddChild, RemoveChild, QueueFree, Free, scene instantiation, node property mutation, UI Control updates, signal emission used by presentation, live resource mutation, autoload state with scene-facing side effects
Task.Run, Thread, or WorkerThreadPool callbacks that capture Node, Resource, PackedScene, SceneTree, or UI references
- code that reads a live node in a worker instead of reading an immutable snapshot captured on the main thread
If a background path touches these surfaces, classify it as a risk and move the engine interaction back to a main-thread apply step unless there is explicit documented safety.
3. Choose the execution model deliberately
Pick the smallest safe execution model:
- Stay on main thread when work is cheap or engine mutation dominates
- Frame-slice / chunk when work must interact with scene state over time or can yield across frames
Task / Task.Run when work is CPU-bound or IO/post-processing and can operate on plain managed data
WorkerThreadPool when many independent Godot-side jobs can run without touching live scene state and need bounded completion tracking
- Godot threaded resource loading when the need is loading large resources or scenes rather than custom CPU work
- Dedicated
Thread only for a long-lived, clearly owned service with explicit shutdown and no scene-tree mutation
Explain why rejected options were riskier or unnecessarily complex.
4. Design the safe data flow
Use Snapshot -> Work -> Result -> Apply for mixed work.
Define:
- snapshot captured on the main thread
- background inputs that are plain data, IDs, immutable records, or DTOs
- result shape returned from the worker
- main-thread apply method and what it is allowed to mutate
- stale-result rule if a newer request supersedes the old one
Do not let a worker hold a live Node reference just because it only "reads" from it. If a read can be snapshotted, snapshot it.
5. Define continuation and main-thread marshaling policy
State explicitly:
- which section is guaranteed to run off the main thread
- which section must run on the Godot main thread
- how the result returns:
CallDeferred, a known main-thread signal or frame boundary, a project-owned dispatcher, or a direct continuation only when the runtime context is verified
- what happens if the owner has exited the tree before apply
Do not assume every await continuation is safe for Godot engine calls unless the project has evidence for that execution context.
See references/main-thread-marshaling-patterns.md for safe return patterns and dispatcher cautions.
6. Define cancellation, lifetime, and stale-result handling
For node-owned work, define:
- cancellation token owner
- cancellation on
_ExitTree or equivalent owner disposal
- request/version ID for ignoring stale results
- validity checks before apply, such as owner still current and still inside the tree
- cleanup or wait policy that avoids blocking the main thread
- exception handling for background failures
Use references/cancellation-lifetime-checklist.md for the full review checklist.
7. Identify anti-patterns and minimal refactor seams
Call out anti-patterns only when they are relevant to the task.
High-signal examples:
- Godot API calls inside
Task.Run
async void lifecycle methods without containment
.Wait() or .Result on the main thread
- background worker mutating nodes, UI, or live resources
- await-then-apply with no cancellation or owner validity check
- scene instantiation or
AddChild from a worker
- resource loading done with generic threading when Godot threaded loading is the better fit
Recommend the smallest safe refactor seam instead of rewriting the whole subsystem.
8. Define validation and regression checks
Include checks that prove both safety and user-visible behavior.
At minimum, consider:
- reproduce the original hitch/crash path
- slow-task simulation
- scene exit while work is running
- repeated enter/exit or retry
- cancellation path
- stale-result path
- no main-thread blocking through
.Wait() or .Result
- export/platform smoke check if threading behavior may differ by target
Output contract
Return the result using assets/threading-review.md in this section order:
Task classification
Thread-safety risk map
Recommended execution model
Safe flow
Main-thread marshaling policy
Cancellation and lifetime plan
Anti-patterns found
Validation plan
Assumptions and open checks
Output rules:
- classify before recommending APIs
- distinguish background data work from main-thread engine mutation
- prefer evidence and concrete risk mapping over generic async warnings
- make cancellation and scene-lifetime handling explicit whenever work outlives a frame
- do not generate a global dispatcher, task framework, or scanner unless the user explicitly asks and the project architecture justifies it
- call out when official API naming or thread-safety details still need version-specific verification
- explicitly state when the request is out of scope because it lacks a Godot engine/lifecycle/main-thread boundary
Godot/.NET threading heuristics
- The Godot boundary is mandatory; generic .NET concurrency is supporting context only.
Task.Run is a high-risk signal, not automatically a bug.
WorkerThreadPool is useful for bounded data jobs, not a free pass to mutate the scene tree.
- Godot threaded resource loading is usually a better starting point for large resource loads than custom
Task.Run(() => ResourceLoader.Load(...)) code.
- If the work must touch live scene state repeatedly, chunking across frames may be safer than threads.
- Main-thread blocking through
.Wait() or .Result is a freeze/deadlock risk until proven otherwise.
async void is especially risky in lifecycle methods because exceptions, cancellation, and owner lifetime are hard to contain.
- Node-owned async work must assume the node may exit the tree before the result returns.
Related skills and routing notes
- Use
runtime-triage first when the symptom is a broad crash, build failure, or editor failure and threading is only one possible cause.
- Use
godot-csharp when the main task is idiomatic C# implementation and threading is only a minor API detail.
- Use
scene-architecture-review when the main risk is scene ownership or coupling rather than thread safety.
- Use
test-strategy-review after the threading plan is chosen and the main question becomes coverage selection.
- Use cross-engine
game-development-time-source-and-tick-policy for cooldowns, ticks, cadence, and pause semantics that are not primarily about .NET threading or Godot main-thread affinity.
- Answer generic .NET threading questions directly or with a general C# route when no Godot engine boundary exists; do not route them here just because the repository name contains
.NET.
Companion files
assets/threading-review.md — reusable output template for the final threading review or execution plan
references/threading-model.md — Godot/.NET main-thread, worker, Task, WorkerThreadPool, and resource-loading decision notes
references/main-thread-marshaling-patterns.md — safe return-to-main-thread patterns, snapshot/result/apply guidance, and dispatcher cautions
references/cancellation-lifetime-checklist.md — cancellation, scene-exit, stale-result, exception, and validation checklist
references/routing-and-acceptance-cases.md — trigger, non-trigger, acceptance, and regression cases for future eval or routing checks
Validation
A good result should satisfy all of the following:
- work type is classified before execution APIs are recommended
- Godot main-thread touch points are named explicitly
- background work is separated from main-thread apply work
- the execution model is the smallest safe model, not the fanciest async surface
- continuation and marshaling policy is explicit
- cancellation, owner lifetime, and stale-result handling are addressed
- anti-patterns are tied to observed code or stated assumptions
- validation includes scene-exit, slow-task, and repeat-run checks when relevant
- the result stays Godot/.NET-specific instead of drifting into generic C# threading advice
- generic .NET concurrency mechanics are included only when they support a Godot boundary decision
Common pitfalls
- wrapping
GetNode, AddChild, UI updates, or live resource mutation inside Task.Run
- assuming
await automatically returns to a safe Godot main-thread context
- using
.Wait() or .Result on the main thread to wait for workers
- capturing live nodes or resources in worker closures instead of using snapshots
- omitting cancellation because the first test only covers the happy path
- applying a result after scene change, retry, or node free
- treating
WorkerThreadPool as a universal performance upgrade
- creating a global dispatcher before the project has a clear ownership reason for one
- turning the skill into a
.NET threading reference that could apply unchanged to a console app or server process
Completion rule
This skill is complete when the agent has:
- classified the async/threading work and engine touch points
- chosen the safest execution model
- designed a safe snapshot/work/result/apply flow where needed
- defined continuation, marshaling, cancellation, lifetime, and stale-result rules
- called out relevant anti-patterns
- returned a concrete validation plan for hitch/crash prevention and scene-lifetime safety
1---2name: godot-dotnet-thread3description: Use when a Godot/.NET task needs a Layer 4 overlay review or plan for threading, async/await, Task.Run, WorkerThreadPool, threaded resource loading, main-thread marshaling, scene-freeze mitigation, or node lifetime safety specifically because Godot engine state, SceneTree access, Node/Resource lifetime, or main-thread affinity is at risk. Do not use for generic .NET threading without a Godot engine boundary.4---56# Godot .NET Threading78Use this skill when a Godot 4.6 + .NET/C# task involves **threading, async work, background loading, main-thread marshaling, or lifetime hazards after `await`**.910This skill is not a generic C# threading tutorial, not a `.NET Threading` skill in disguise, and not a permission slip to throw Godot APIs into `Task.Run`. Its job is to review or design a safe execution flow for Godot/.NET projects only when Godot engine state, scene lifecycle, resource loading, or main-thread affinity is part of the risk: decide what may leave the Godot main thread, what must return before touching engine-owned state, and how cancellation, stale results, and scene exit are validated.1112Prefer this skill when the main risk is code that looks asynchronous but may crash, freeze, update freed nodes, mutate the `SceneTree` from a worker, or hide a lifetime bug behind a cheerful little `await`.1314If the task can be answered entirely with ordinary .NET concepts such as locks, `ConcurrentDictionary`, TPL scheduling, channels, cancellation-token basics, or thread-safe collections without mentioning Godot-owned objects, `SceneTree`, `Node`, `Resource`, `Control`, lifecycle callbacks, or main-thread apply, do not use this skill.1516## Purpose1718This skill is used to:1920- classify work as CPU-bound, IO-bound, resource-loading, scene mutation, UI update, or mixed21- identify Godot `Node`, `SceneTree`, `Control`, `Resource`, and `Object` operations that must stay on or return to the main thread22- choose the safest execution model: frame-slicing, `Task`, `Thread`, `WorkerThreadPool`, Godot threaded resource loading, or no threading23- design a `Snapshot -> Work -> Result -> Main-thread apply` flow24- define explicit continuation, marshaling, cancellation, and scene-lifetime rules25- return a structured threading review or execution plan another agent can implement safely2627## Hard boundary: Godot first, .NET second2829This skill must stay anchored to Godot/.NET overlay concerns.3031Use it only when at least one of these is true:3233- background work may touch `Node`, `SceneTree`, `Control`, `Resource`, `PackedScene`, signals, autoloads, or live scene state34- an async continuation may update UI, presentation, scene nodes, resources, or gameplay-facing engine state35- a task may outlive `_ExitTree`, scene transition, loading-screen ownership, or node disposal36- the decision is between Godot-specific models such as main-thread apply, frame-slicing, `WorkerThreadPool`, or Godot threaded resource loading37- a hitch, crash, or stale result is tied to Godot lifecycle or engine API access3839Do not broaden the answer into a general .NET concurrency design unless the .NET detail is necessary to protect the Godot boundary. Keep generic .NET mechanics as supporting notes, not the center of the artifact.4041## Use this skill when4243Invoke this skill for requests such as:4445- reviewing `Task.Run`, `Thread`, `WorkerThreadPool`, or `async/await` in Godot C#46- fixing scene freezes, loading-screen hitches, pathfinding stalls, procedural generation spikes, or heavy parsing that blocks frames47- deciding whether background work should be a thread, frame-sliced loop, Godot threaded resource load, or pure main-thread sequence48- updating UI, nodes, tile maps, spawned scenes, resources, or presentation state after an async operation49- handling `ObjectDisposedException`, invalid instance access, node-freed-after-await, or stale task results after scene changes50- adding cancellation and lifetime safety around long-running node-owned work5152### Trigger examples5354- "I use `Task.Run` for pathfinding but Godot sometimes crashes"55- "Loading a big scene causes frame hitches; should I use threaded loading?"56- "`WorkerThreadPool` finishes procedural generation and then updates a `TileMap`"57- "After an HTTP await, my UI sometimes updates after the scene is gone"58- "Can this Godot C# code safely run off the main thread?"5960## Do not use this skill when6162Do not use this skill when:6364- the task is ordinary C# concurrency with no Godot engine, scene, resource, or lifecycle boundary65- the request is mainly about `lock`, `Monitor`, `SemaphoreSlim`, `ConcurrentQueue`, `ConcurrentDictionary`, `Channel<T>`, TPL scheduling, memory barriers, or thread-safe collection choice without a Godot main-thread or lifecycle hazard66- the code is a pure .NET service/library used by a Godot project but the question does not affect `Node`, `SceneTree`, `Resource`, `Control`, signals, loading, or main-thread result application67- the main problem is cooldowns, turn order, or gameplay time semantics rather than threading safety68- the issue is a general build, SDK, editor, or package failure with no threading signal; use `runtime-triage`69- the task is primarily scene ownership or UI/UX review without async/thread risk70- the user wants a full custom async framework or global dispatcher generated by default71- the safest answer is one local synchronous operation and no meaningful threading decision is needed7273## Pattern7475- Primary pattern: **Reviewer**76- Secondary pattern: **Tool Wrapper**, **Generator**7778Why this fit is better than the alternatives:7980- the main job is to evaluate threading and lifetime risk against Godot/.NET-specific constraints81- the skill packages reusable Godot main-thread, async, worker, and loading heuristics82- the output should be a repeatable review or safe execution plan, not loose advice83- a full Pipeline would add ceremony without enough value, because the ordered safety checks can live directly in this workflow8485## Core safety model8687Default to this model unless the task has a stronger reason not to:8889```text90Main thread:91 capture immutable snapshot9293Background work:94 compute / parse / load data without live Node or SceneTree mutation9596Result boundary:97 return plain DTOs, immutable data, IDs, or value objects9899Main thread:100 verify owner still alive and request still current101 apply result to Node / SceneTree / Control / Resource / UI102```103104Treat live Godot objects as main-thread-affine unless the project and Godot API documentation prove a narrower exception.105106For reusable details, see `references/threading-model.md` and `references/main-thread-marshaling-patterns.md`.107108## Inputs109110Collect or infer these inputs when available:111112- task summary and expected user-visible behavior113- current async/threading code or proposed execution model114- whether work touches `Node`, `SceneTree`, `Control`, `Resource`, signals, autoloads, `PackedScene`, `TileMap`, UI, or live scene state115- work type: CPU-bound calculation, IO, background parsing, resource loading, scene instantiation, UI update, or mixed116- owner lifetime: which node, scene, service, or autoload starts and owns the work117- cancellation expectations and what should happen on `_ExitTree`, scene change, retry, or game quit118- validation evidence: freeze reproduction, crash logs, stack traces, profiler notes, or slow-task scenario119- current Godot version, .NET target, and platform/export constraints if relevant120121If inputs are incomplete, do not block on perfect detail. Infer the smallest safe execution model and mark the assumptions that would change the recommendation.122123If the available inputs show only generic .NET concurrency with no Godot engine boundary, route away instead of forcing this template.124125## Workflow126127Follow this sequence every time.128129### 1. Classify the work and execution pressure130131State what kind of work is being moved or considered for asynchronous execution.132133First state the **Godot boundary trigger**. If there is no Godot boundary trigger, stop and say this is a generic .NET threading question rather than a `godot-dotnet-thread` task.134135Classify the primary work type as one of:136137- CPU-bound pure calculation138- IO-bound read/write or remote call139- resource loading140- scene or node mutation141- UI or presentation update142- mixed work that must be split143- frame hitch that may be better solved by chunking or frame-slicing144145Do not start by choosing `Task.Run` or `WorkerThreadPool`. Start from the work shape and the engine boundary.146147### 2. Identify Godot main-thread touch points148149List every known or likely touch of engine-owned state.150151Check especially for:152153- `GetNode`, `GetTree`, `AddChild`, `RemoveChild`, `QueueFree`, `Free`, scene instantiation, node property mutation, UI `Control` updates, signal emission used by presentation, live resource mutation, autoload state with scene-facing side effects154- `Task.Run`, `Thread`, or `WorkerThreadPool` callbacks that capture `Node`, `Resource`, `PackedScene`, `SceneTree`, or UI references155- code that reads a live node in a worker instead of reading an immutable snapshot captured on the main thread156157If a background path touches these surfaces, classify it as a risk and move the engine interaction back to a main-thread apply step unless there is explicit documented safety.158159### 3. Choose the execution model deliberately160161Pick the smallest safe execution model:162163- **Stay on main thread** when work is cheap or engine mutation dominates164- **Frame-slice / chunk** when work must interact with scene state over time or can yield across frames165- **`Task` / `Task.Run`** when work is CPU-bound or IO/post-processing and can operate on plain managed data166- **`WorkerThreadPool`** when many independent Godot-side jobs can run without touching live scene state and need bounded completion tracking167- **Godot threaded resource loading** when the need is loading large resources or scenes rather than custom CPU work168- **Dedicated `Thread`** only for a long-lived, clearly owned service with explicit shutdown and no scene-tree mutation169170Explain why rejected options were riskier or unnecessarily complex.171172### 4. Design the safe data flow173174Use `Snapshot -> Work -> Result -> Apply` for mixed work.175176Define:177178- snapshot captured on the main thread179- background inputs that are plain data, IDs, immutable records, or DTOs180- result shape returned from the worker181- main-thread apply method and what it is allowed to mutate182- stale-result rule if a newer request supersedes the old one183184Do not let a worker hold a live `Node` reference just because it only "reads" from it. If a read can be snapshotted, snapshot it.185186### 5. Define continuation and main-thread marshaling policy187188State explicitly:189190- which section is guaranteed to run off the main thread191- which section must run on the Godot main thread192- how the result returns: `CallDeferred`, a known main-thread signal or frame boundary, a project-owned dispatcher, or a direct continuation only when the runtime context is verified193- what happens if the owner has exited the tree before apply194195Do not assume every `await` continuation is safe for Godot engine calls unless the project has evidence for that execution context.196197See `references/main-thread-marshaling-patterns.md` for safe return patterns and dispatcher cautions.198199### 6. Define cancellation, lifetime, and stale-result handling200201For node-owned work, define:202203- cancellation token owner204- cancellation on `_ExitTree` or equivalent owner disposal205- request/version ID for ignoring stale results206- validity checks before apply, such as owner still current and still inside the tree207- cleanup or wait policy that avoids blocking the main thread208- exception handling for background failures209210Use `references/cancellation-lifetime-checklist.md` for the full review checklist.211212### 7. Identify anti-patterns and minimal refactor seams213214Call out anti-patterns only when they are relevant to the task.215216High-signal examples:217218- Godot API calls inside `Task.Run`219- `async void` lifecycle methods without containment220- `.Wait()` or `.Result` on the main thread221- background worker mutating nodes, UI, or live resources222- await-then-apply with no cancellation or owner validity check223- scene instantiation or `AddChild` from a worker224- resource loading done with generic threading when Godot threaded loading is the better fit225226Recommend the smallest safe refactor seam instead of rewriting the whole subsystem.227228### 8. Define validation and regression checks229230Include checks that prove both safety and user-visible behavior.231232At minimum, consider:233234- reproduce the original hitch/crash path235- slow-task simulation236- scene exit while work is running237- repeated enter/exit or retry238- cancellation path239- stale-result path240- no main-thread blocking through `.Wait()` or `.Result`241- export/platform smoke check if threading behavior may differ by target242243## Output contract244245Return the result using `assets/threading-review.md` in this section order:246247- `Task classification`248- `Thread-safety risk map`249- `Recommended execution model`250- `Safe flow`251- `Main-thread marshaling policy`252- `Cancellation and lifetime plan`253- `Anti-patterns found`254- `Validation plan`255- `Assumptions and open checks`256257Output rules:258259- classify before recommending APIs260- distinguish background data work from main-thread engine mutation261- prefer evidence and concrete risk mapping over generic async warnings262- make cancellation and scene-lifetime handling explicit whenever work outlives a frame263- do not generate a global dispatcher, task framework, or scanner unless the user explicitly asks and the project architecture justifies it264- call out when official API naming or thread-safety details still need version-specific verification265- explicitly state when the request is out of scope because it lacks a Godot engine/lifecycle/main-thread boundary266267## Godot/.NET threading heuristics268269- The Godot boundary is mandatory; generic .NET concurrency is supporting context only.270- `Task.Run` is a high-risk signal, not automatically a bug.271- `WorkerThreadPool` is useful for bounded data jobs, not a free pass to mutate the scene tree.272- Godot threaded resource loading is usually a better starting point for large resource loads than custom `Task.Run(() => ResourceLoader.Load(...))` code.273- If the work must touch live scene state repeatedly, chunking across frames may be safer than threads.274- Main-thread blocking through `.Wait()` or `.Result` is a freeze/deadlock risk until proven otherwise.275- `async void` is especially risky in lifecycle methods because exceptions, cancellation, and owner lifetime are hard to contain.276- Node-owned async work must assume the node may exit the tree before the result returns.277278## Related skills and routing notes279280- Use `runtime-triage` first when the symptom is a broad crash, build failure, or editor failure and threading is only one possible cause.281- Use `godot-csharp` when the main task is idiomatic C# implementation and threading is only a minor API detail.282- Use `scene-architecture-review` when the main risk is scene ownership or coupling rather than thread safety.283- Use `test-strategy-review` after the threading plan is chosen and the main question becomes coverage selection.284- Use cross-engine `game-development-time-source-and-tick-policy` for cooldowns, ticks, cadence, and pause semantics that are not primarily about .NET threading or Godot main-thread affinity.285- Answer generic .NET threading questions directly or with a general C# route when no Godot engine boundary exists; do not route them here just because the repository name contains `.NET`.286287## Companion files288289- `assets/threading-review.md` — reusable output template for the final threading review or execution plan290- `references/threading-model.md` — Godot/.NET main-thread, worker, Task, WorkerThreadPool, and resource-loading decision notes291- `references/main-thread-marshaling-patterns.md` — safe return-to-main-thread patterns, snapshot/result/apply guidance, and dispatcher cautions292- `references/cancellation-lifetime-checklist.md` — cancellation, scene-exit, stale-result, exception, and validation checklist293- `references/routing-and-acceptance-cases.md` — trigger, non-trigger, acceptance, and regression cases for future eval or routing checks294295## Validation296297A good result should satisfy all of the following:298299- work type is classified before execution APIs are recommended300- Godot main-thread touch points are named explicitly301- background work is separated from main-thread apply work302- the execution model is the smallest safe model, not the fanciest async surface303- continuation and marshaling policy is explicit304- cancellation, owner lifetime, and stale-result handling are addressed305- anti-patterns are tied to observed code or stated assumptions306- validation includes scene-exit, slow-task, and repeat-run checks when relevant307- the result stays Godot/.NET-specific instead of drifting into generic C# threading advice308- generic .NET concurrency mechanics are included only when they support a Godot boundary decision309310## Common pitfalls311312- wrapping `GetNode`, `AddChild`, UI updates, or live resource mutation inside `Task.Run`313- assuming `await` automatically returns to a safe Godot main-thread context314- using `.Wait()` or `.Result` on the main thread to wait for workers315- capturing live nodes or resources in worker closures instead of using snapshots316- omitting cancellation because the first test only covers the happy path317- applying a result after scene change, retry, or node free318- treating `WorkerThreadPool` as a universal performance upgrade319- creating a global dispatcher before the project has a clear ownership reason for one320- turning the skill into a `.NET threading` reference that could apply unchanged to a console app or server process321322## Completion rule323324This skill is complete when the agent has:325326- classified the async/threading work and engine touch points327- chosen the safest execution model328- designed a safe snapshot/work/result/apply flow where needed329- defined continuation, marshaling, cancellation, lifetime, and stale-result rules330- called out relevant anti-patterns331- returned a concrete validation plan for hitch/crash prevention and scene-lifetime safety