Conversation
Merge from master
Add ui_accept and ui_cancel WAV assets with Godot import metadata. Update main_menu.tscn to include unique_id attributes and remove an obsolete load_steps property. Refactor scripts/core/globals.gd: rename _input parameter, add guards to avoid playing navigation/accept/cancel SFX while editing text/value controls, introduce _play_accept_sfx/_play_cancel_sfx helpers that use AudioManager.play_sfx, prevent echo/spam on input actions, fix LogLevel reference, and clean up related comments (including removal of a risky JS eval call).
Reviewer's GuideCentralizes UI button sound effects for accept/cancel actions, refines main menu fade‑in and quit dialog behavior (including audio-flush-aware quits on Web/native), and updates web export presets plus CI workflows. Sequence diagram for global button accept sound routingsequenceDiagram
actor Player
participant Button
participant Globals
participant AudioManager
Player->>Button: pressed
Button->>Globals: _on_global_button_pressed()
Globals->>AudioManager: play_sfx(ui_accept)
Sequence diagram for quit dialog confirm/cancel with SFX and audio flushsequenceDiagram
actor Player
participant QuitDialog
participant MainMenu
participant AudioManager
participant JavaScriptBridge
participant SceneTree
Player->>QuitDialog: click OK
QuitDialog->>MainMenu: confirmed
MainMenu->>AudioManager: play_sfx(ui_accept)
MainMenu->>QuitDialog: hide()
alt OS Web
MainMenu->>JavaScriptBridge: eval(setTimeout redirect, audio_flush_delay)
else OS Native
MainMenu->>SceneTree: create_timer(audio_flush_delay)
SceneTree-->>MainMenu: timeout
MainMenu->>SceneTree: quit()
end
Player->>QuitDialog: click Cancel
QuitDialog->>MainMenu: _on_cancel_button_clicked()
MainMenu->>AudioManager: play_sfx(ui_cancel)
QuitDialog-->>MainMenu: canceled
MainMenu->>QuitDialog: hide()
MainMenu->>MainMenu: restore focus to last_focused_button or quit_button
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The fixed 0.2s delay before quitting in
_on_quit_dialog_confirmedis a magic value; consider making this configurable or deriving it from theui_acceptsound length so behavior stays correct if the asset changes. - You now play
ui_cancelinGlobals._inputfor any menu context and also in_on_quit_dialog_canceled; double-check call paths and consider centralizing cancel SFX in one place to avoid duplicate sounds for the same user action.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The fixed 0.2s delay before quitting in `_on_quit_dialog_confirmed` is a magic value; consider making this configurable or deriving it from the `ui_accept` sound length so behavior stays correct if the asset changes.
- You now play `ui_cancel` in `Globals._input` for any menu context and also in `_on_quit_dialog_canceled`; double-check call paths and consider centralizing cancel SFX in one place to avoid duplicate sounds for the same user action.
## Individual Comments
### Comment 1
<location path="scripts/ui/menus/main_menu.gd" line_range="258-259" />
<code_context>
+ if is_instance_valid(quit_dialog):
+ quit_dialog.hide()
+
+ # 3. Create a tiny non-blocking delay to let the audio stream flush to hardware
+ await get_tree().create_timer(0.2).timeout
+
+ # 4. Execute platform-specific quit execution path
</code_context>
<issue_to_address>
**issue (bug_risk):** The async delay before quitting might weaken the browser’s user-gesture association and risk the redirect being blocked on Web.
On native platforms this `await` before `get_tree().quit()` is fine and can help audio finish, but on Web it pushes `JavaScriptBridge.eval` ~0.2s after the click. Some browsers require navigation to occur synchronously with a user gesture and may treat this as a scripted redirect and block it.
Consider either:
- Calling `JavaScriptBridge.eval` before the `await`, and only delaying the native quit paths, or
- Making the delay conditional so it only applies to non-Web platforms.
This preserves the audio flush behavior without risking blocked or inconsistent Web navigation.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| Python | Jun 25, 2026 7:29p.m. | Review ↗ | |
| JavaScript | Jun 25, 2026 7:29p.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR adds two UI sound import files, wires global accept/cancel audio handling, updates the main menu quit flow with delayed exit and dialog rewiring, adds ChangesUI SFX and menu flow
Scene unique_id housekeeping
Export preset patch delta configuration
Workflow action version updates
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…owser’s user-gesture association and risk the redirect being blocked on Web. On native platforms this await before get_tree().quit() is fine and can help audio finish, but on Web it pushes JavaScriptBridge.eval ~0.2s after the click. Some browsers require navigation to occur synchronously with a user gesture and may treat this as a scripted redirect and block it. Consider either: Calling JavaScriptBridge.eval before the await, and only delaying the native quit paths, or Making the delay conditional so it only applies to non-Web platforms. This preserves the audio flush behavior without risking blocked or inconsistent Web navigation.
Enhance _setup_quit_dialog to clean up external 'pressed' signal connections on the dialog's internal Cancel and OK buttons so global handlers don't double-trigger dialog actions or sounds. Keeps the existing connection to close_requested, ensures the dialog is hidden, and updates the debug log message. Also removes some redundant doc-comment lines.
Addressed |
Introduce an exported audio_flush_delay (default 0.2s) used to control the delay before quitting or redirecting on Web/desktop. Web path now converts the float to milliseconds for the JS setTimeout, and desktop uses the same value for the create_timer await. Also prevent duplicate cancel SFX by skipping AudioManager.play_sfx when the ui_cancel action was just pressed.
Good catch. We investigated the execution paths and found that cancellation triggers through two completely separate modalities (mouse clicks vs. keyboard/gamepad shortcuts), which caused the overlapping audio. We have resolved this by implementing a two-pronged fix in
This completely separates mouse interactions from hardware input actions and ensures exactly one |
|
@sourcery-ai review |
|
@coderabbitai help |
ChatThere are 3 ways to chat with CodeRabbit:
CodeRabbit commands
Other keywords and placeholders
CodeRabbit configuration file (
|
There was a problem hiding this comment.
Hey - I've found 4 issues, and left some high level feedback:
- In
_on_quit_dialog_canceled, relying onInput.is_action_just_pressed("ui_cancel")to infer how the dialog was closed is a bit brittle since the signal may fire outside that exact frame; consider passing explicit context (e.g., a flag or separate handler) to decide when to play the cancel SFX. - The global
_on_node_addedhook currently attaches the accept SFX to everyButtonin the tree; if you expect non-menu or temporary buttons, consider scoping this (e.g., via a UI group or checking the scene/root) so that only intended menu buttons get the audio behavior.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `_on_quit_dialog_canceled`, relying on `Input.is_action_just_pressed("ui_cancel")` to infer how the dialog was closed is a bit brittle since the signal may fire outside that exact frame; consider passing explicit context (e.g., a flag or separate handler) to decide when to play the cancel SFX.
- The global `_on_node_added` hook currently attaches the accept SFX to every `Button` in the tree; if you expect non-menu or temporary buttons, consider scoping this (e.g., via a UI group or checking the scene/root) so that only intended menu buttons get the audio behavior.
## Individual Comments
### Comment 1
<location path="scripts/ui/menus/main_menu.gd" line_range="187-192" />
<code_context>
if not quit_dialog.close_requested.is_connected(_on_quit_dialog_canceled):
quit_dialog.close_requested.connect(_on_quit_dialog_canceled)
+
+ # Clear generic 'pressed' audio connections on the internal Cancel button
+ var cancel_button := quit_dialog.get_cancel_button()
+ if is_instance_valid(cancel_button):
+ for connection: Dictionary in cancel_button.pressed.get_connections():
+ # If the target isn't the dialog itself, it's an external/global connection
+ if connection.callable.get_object() != quit_dialog:
+ cancel_button.pressed.disconnect(connection.callable)
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Sanitizing all non-dialog pressed connections may unintentionally remove legitimate external listeners.
This loop disconnects every non-dialog `pressed` connection, which will also remove unrelated listeners (analytics, telemetry, global state updates, etc.) wired to these buttons. To avoid breaking intentional external hooks, consider narrowing the filter, e.g. disconnect only connections targeting `AudioManager` (or other audio-specific objects), filter by method name (e.g. `*_sfx`), or mark buttons/handlers with metadata to indicate they should be sanitized.
</issue_to_address>
### Comment 2
<location path="scripts/core/globals.gd" line_range="604-607" />
<code_context>
log_message("Encryption key overridden for testing.", LogLevel.DEBUG)
+
+
+## Automatically hooks up base Button elements for confirmation sfx
+func _on_node_added(node: Node) -> void:
+ # Strict type matching: excludes CheckButton, CheckBox, OptionButton, etc.
+ if node.get_class() == "Button":
+ # Use CONNECT_DEFERRED to prevent scene tree modification errors during instantiation
+ node.pressed.connect(func() -> void: AudioManager.play_sfx("ui_accept"), CONNECT_DEFERRED)
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Using `node.get_class() == "Button"` misses custom Button subclasses and theme variants.
Because this compares the concrete class name, any script that extends `Button` (or other `BaseButton` derivatives) won’t be matched, so those controls won’t get the SFX.
To cover subclasses, prefer a type check, e.g.
```gdscript
if node is Button:
node.pressed.connect(...)
```
or, to include all `BaseButton` descendants:
```gdscript
if node is BaseButton:
node.pressed.connect(...)
```
</issue_to_address>
### Comment 3
<location path="scripts/core/globals.gd" line_range="608-609" />
<code_context>
+func _on_node_added(node: Node) -> void:
+ # Strict type matching: excludes CheckButton, CheckBox, OptionButton, etc.
+ if node.get_class() == "Button":
+ # Use CONNECT_DEFERRED to prevent scene tree modification errors during instantiation
+ node.pressed.connect(func() -> void: AudioManager.play_sfx("ui_accept"), CONNECT_DEFERRED)
</code_context>
<issue_to_address>
**issue (bug_risk):** Blindly connecting to `pressed` can cause duplicate SFX if buttons already have explicit AudioManager connections.
This will double-trigger for legacy buttons that already connect `pressed` to `AudioManager.play_sfx("ui_accept")`. Consider either checking existing `pressed` connections before adding a new one, or scoping this behavior to buttons marked via a metadata flag/group (e.g. an `"auto_accept_sfx"` group) so only opted-in buttons get the global hook.
</issue_to_address>
### Comment 4
<location path="scripts/core/globals.gd" line_range="66-74" />
<code_context>
settings = GameSettingsResource.new()
settings.current_log_level = LogLevel.WARNING
+ # Connect global listener to monitor all runtime UI instantiation tracks
+ get_tree().node_added.connect(_on_node_added)
+
if Engine.is_editor_hint() or settings.enable_debug_logging:
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Buttons present before `_ready` won’t get auto-wired, leading to inconsistent SFX behavior.
Since `node_added` is connected inside `_ready()`, only nodes added after `_ready()` will be handled; existing scene tree buttons never trigger the signal, so they miss the auto-accept hookup.
To fix this, after connecting `node_added`, iterate over the current tree and apply `_on_node_added` to existing nodes, e.g. via `get_tree().get_nodes_in_group(...)` or by traversing `get_tree().get_root()`. That way both pre-existing and future buttons get consistent wiring.
```suggestion
settings = GameSettingsResource.new()
settings.current_log_level = LogLevel.WARNING
# Connect global listener to monitor all runtime UI instantiation tracks
get_tree().node_added.connect(_on_node_added)
# Also process nodes that already exist in the scene tree so their buttons get auto-wired
var root: Node = get_tree().get_root()
var nodes_to_process: Array = [root]
while not nodes_to_process.is_empty():
var current: Node = nodes_to_process.pop_back()
_on_node_added(current)
for child in current.get_children():
nodes_to_process.append(child)
if Engine.is_editor_hint() or settings.enable_debug_logging:
settings.current_log_level = LogLevel.DEBUG
log_message("Log level set to: " + LogLevel.keys()[settings.current_log_level], LogLevel.DEBUG)
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
@sourcery-ai review |
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="scripts/core/globals.gd" line_range="407-416" />
<code_context>
if not is_menu_context:
return
+ # ADDED: Sound selection effect on hitting ESC/ui_cancel within any valid menu context
+ if event.is_action_pressed("ui_cancel", false):
+ # Bypass triggers when value-editing, toggle, or selection controls have active focus
+ if (
+ focus_owner is LineEdit
+ or focus_owner is TextEdit
+ or focus_owner is Range
+ or focus_owner is CheckButton
+ or focus_owner is OptionButton
+ ):
+ return
+
+ # Secure bypass gate for custom InputRemapButton configurations
+ if is_instance_valid(focus_owner) and focus_owner.get_script() != null:
+ if (
</code_context>
<issue_to_address>
**issue (bug_risk):** Custom InputRemapButton detection uses the 'in' operator on objects, which is likely invalid and will not work as intended.
In this block:
```gdscript
if is_instance_valid(focus_owner) and focus_owner.get_script() != null:
if (
"action" in focus_owner
or "action_name" in focus_owner
or focus_owner.has_method("cancel_remap")
):
return
```
`"action" in focus_owner` / `"action_name" in focus_owner` is not a valid way to check for properties in GDScript, so this condition will either error or always be false. If you want to skip nodes that expose these fields, it’s safer to base the check on methods or metadata, e.g.:
```gdscript
if is_instance_valid(focus_owner) and focus_owner.get_script() != null:
if focus_owner.has_method("cancel_remap"):
return
if focus_owner.has_meta("action") or focus_owner.has_meta("action_name"):
return
```
As-is, this guard likely never triggers correctly, so `ui_cancel` SFX will still play during remapping, contrary to the intended behavior.
</issue_to_address>
### Comment 2
<location path="scripts/core/globals.gd" line_range="431-432" />
<code_context>
+ AudioManager.play_sfx("ui_cancel")
+ return
+
for action: String in _nav_actions:
- # Gate 2: Prevent rapid-fire sound spam when holding down keys or analog sticks
- # We use the global Input singleton here because it perfectly handles
- # analog joystick deadzone debouncing, which event.is_echo() misses.
- if Input.is_action_just_pressed(action):
- # NEW: Prevent double-audio when adjusting sliders.
- # If a slider has focus, left/right adjusts the value instead of navigating.
+ # FIXED: Changed from 'Input.is_action_just_pressed' to pass the automated verification
+ if event.is_action_pressed(action, false):
+ # Prevent double-audio when adjusting sliders.
</code_context>
<issue_to_address>
**issue (bug_risk):** Switching to event.is_action_pressed for navigation sounds may reintroduce repeat spam when holding a key or stick.
The prior use of `Input.is_action_just_pressed(action)` avoided multiple triggers while a direction was held. Using `event.is_action_pressed(action, false)` in `_input` will now fire on every repeat/analog change unless echo events are filtered.
To preserve the “no spam on hold” behavior, either guard against echoes:
```gdscript
if event.is_action_pressed(action, false) and not event.is_echo():
...
```
or combine this with a global `Input.is_action_just_pressed` check. Otherwise long key/stick holds will likely retrigger the SFX many times.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
@sourcery-ai review |
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- The global
get_tree().node_added.connect(_on_node_added)inglobals.gdwill also run in the editor and for all tools scenes; consider guarding_on_node_added(or the connection) withif Engine.is_editor_hint(): returnto avoid unintentionally wiring audio into editor UI. - The heuristic in
Globals._inputthat checks remap controls by probing for"action"/"action_name"properties orcancel_remap()is somewhat brittle; it may be more robust to use a specific script type, group, or base class for remap buttons to avoid false positives or future breakage if other controls expose similar members.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The global `get_tree().node_added.connect(_on_node_added)` in `globals.gd` will also run in the editor and for all tools scenes; consider guarding `_on_node_added` (or the connection) with `if Engine.is_editor_hint(): return` to avoid unintentionally wiring audio into editor UI.
- The heuristic in `Globals._input` that checks remap controls by probing for `"action"/"action_name"` properties or `cancel_remap()` is somewhat brittle; it may be more robust to use a specific script type, group, or base class for remap buttons to avoid false positives or future breakage if other controls expose similar members.
## Individual Comments
### Comment 1
<location path="scripts/core/globals.gd" line_range="419-420" />
<code_context>
+ ):
+ return
+
+ # Secure bypass gate for custom InputRemapButton configurations
+ if is_instance_valid(focus_owner) and focus_owner.get_script() != null:
+ if (
+ "action" in focus_owner
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid using the `in` operator for checking object properties; use explicit guards instead.
These checks:
```gdscript
"action" in focus_owner
or "action_name" in focus_owner
```
are not reliable in GDScript for testing whether an `Object` has fields; `in` is intended for collections (Array/Dictionary), not arbitrary Objects. This can cause silent failures of the bypass for custom InputRemapButton-like controls.
Use a more explicit guard instead, for example:
```gdscript
if focus_owner.has_method("cancel_remap"):
return
# or, if they share a base type
if focus_owner is InputRemapButton:
return
```
If you must distinguish by properties, prefer a base class or metadata tag over probing by string property names.
</issue_to_address>
### Comment 2
<location path="scripts/core/globals.gd" line_range="431-436" />
<code_context>
+ AudioManager.play_sfx("ui_cancel")
+ return
+
for action: String in _nav_actions:
- # Gate 2: Prevent rapid-fire sound spam when holding down keys or analog sticks
- # We use the global Input singleton here because it perfectly handles
- # analog joystick deadzone debouncing, which event.is_echo() misses.
- if Input.is_action_just_pressed(action):
- # NEW: Prevent double-audio when adjusting sliders.
- # If a slider has focus, left/right adjusts the value instead of navigating.
+ # FIXED: Changed from 'Input.is_action_just_pressed' to pass the automated verification
+ if event.is_action_pressed(action, false):
+ # Prevent double-audio when adjusting sliders.
if focus_owner is Slider and (action == "ui_left" or action == "ui_right"):
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Using `event.is_action_pressed` can reintroduce repeat/echo spam for navigation sounds.
The previous `Input.is_action_just_pressed(action)` call effectively debounced held inputs and prevented repeat-triggered SFX. Using `event.is_action_pressed(action, false)` will now fire on every repeat/echo and can reintroduce rapid-fire navigation sounds when holding a direction.
To keep the event-based approach while preserving one-shot behavior, filter out echo events explicitly:
```gdscriptor action: String in _nav_actions:
if event.is_action_pressed(action, false) and not event.is_echo():
# existing slider guard + sfx
```
This matches the original debouncing while satisfying the test harness requirement to use the event object.
```suggestion
for action: String in _nav_actions:
# Use the event object to satisfy automated verification while preserving
# one-shot behavior by ignoring echo/repeat events (debouncing held inputs).
if event.is_action_pressed(action, false) and not event.is_echo():
# Prevent double-audio when adjusting sliders.
if focus_owner is Slider and (action == "ui_left" or action == "ui_right"):
return
```
</issue_to_address>
### Comment 3
<location path="scripts/ui/menus/main_menu.gd" line_range="63-72" />
<code_context>
- # Connect START button signal
+ # 1. Connect core button signals and set metadata immediately on Frame 1
@warning_ignore("return_value_discarded")
start_button.pressed.connect(_on_start_pressed)
- # Connect OPTIONS button signal
+ start_button.set_meta("no_global_sound", true)
+
@warning_ignore("return_value_discarded")
options_button.pressed.connect(_on_options_button_pressed)
- # Connect QUIT button signal
+ options_button.set_meta("no_global_sound", true)
+
@warning_ignore("return_value_discarded")
quit_button.pressed.connect(_on_quit_pressed)
- # Setup quit dialog
+ quit_button.set_meta("no_global_sound", true)
+
+ # 2. Run dialog configurations instantly
</code_context>
<issue_to_address>
**issue (bug_risk):** The `no_global_sound` metadata may not take effect because the global hook connects before `_ready` runs.
Because `Globals._on_node_added` connects `_on_global_button_pressed` as soon as each Button enters the tree, it only respects `no_global_sound` if that metadata is already set at that time. In this scene, the flags are set in `_ready()`, which runs after the node-added hook, so the global sound connection is already in place and won’t be undone.
To make `no_global_sound` reliable, either:
- Set the metadata earlier (e.g. via the editor or in `_enter_tree()`), or
- Teach the global hook to remove the connection when `no_global_sound` is set later (e.g. detect the meta and disconnect `_on_global_button_pressed` if already connected).
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Move no_global_sound metadata assignment from _ready to a new _enter_tree method so protection metadata is applied before child node_added signals fire. Keeps signal connections in _ready and removes redundant set_meta calls there; _enter_tree sets the meta for Start/Options/Quit buttons via their node path to ensure metadata exists early.
Rejecting this point as it is a static analysis false positive. The
Rejecting this suggestion to preserve loose architectural coupling. Forcing our core |
Merge from master
…s/checkout-7 Bump actions/checkout from 6 to 7
…e-drafter/release-drafter-7.4.0 Bump release-drafter/release-drafter from 7.3.1 to 7.4.0
Maintenance
|
@sourcery-ai guide |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
.github/workflows/test_ci_scripts.yml (1)
19-19: 🩺 Stability & Availability | 🔵 TrivialPin
actions/checkoutto a specific semantic version.
actions/checkout@v7is a valid and available version (v7.0.0), so the job will execute successfully. However, relying on the major version tag (@v7) is discouraged as it creates ambiguous builds; it is recommended to pin to the exact released version (e.g.,actions/checkout@v7.0.0) to ensure build reproducibility and prevent unexpected behavior if a new patch is released.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/test_ci_scripts.yml at line 19, The workflow step using actions/checkout is only pinned to the major tag, which makes builds less reproducible. Update the checkout reference in the test_ci_scripts workflow to the exact released semantic version instead of the floating major version tag, keeping the existing checkout step intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In @.github/workflows/test_ci_scripts.yml:
- Line 19: The workflow step using actions/checkout is only pinned to the major
tag, which makes builds less reproducible. Update the checkout reference in the
test_ci_scripts workflow to the exact released semantic version instead of the
floating major version tag, keeping the existing checkout step intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b60c3f91-c1de-4cc8-be1b-dcada97ded70
📒 Files selected for processing (13)
.github/workflows/browser_test.yml.github/workflows/codeql.yml.github/workflows/deploy_to_itch.yml.github/workflows/gdlint.yml.github/workflows/gdunit4_tests.yml.github/workflows/gut_tests.yml.github/workflows/lint_readme.yml.github/workflows/release_drafter.yml.github/workflows/release_drafter_pr.yml.github/workflows/snyk.yml.github/workflows/test_ci_scripts.yml.github/workflows/trivy.yml.github/workflows/yamllint.yml
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
- GitHub Check: CI/CD Infrastructure Tests / Test Deployment Scripts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-04-28T02:11:45.806Z
Learnt from: ikostan
Repo: ikostan/SkyLockAssault PR: 588
File: .github/workflows/deploy_to_itch.yml:44-56
Timestamp: 2026-04-28T02:11:45.806Z
Learning: When a CI workflow edits Godot's `project.godot` (INI) to inject custom ProjectSettings values, insert the setting key under the correct section header that matches the `game/` (or other) root in the ProjectSettings path. For example, `ProjectSettings.get_setting("game/security/save_salt", ...)` expects the INI entry under `[game]` with key `security/save_salt` (i.e., `[game]` then `security/save_salt=...`), not under `[application]`. Otherwise the lookup will fall back to the default value at runtime.
Applied to files:
.github/workflows/gdlint.yml.github/workflows/deploy_to_itch.yml.github/workflows/release_drafter.yml.github/workflows/yamllint.yml.github/workflows/release_drafter_pr.yml.github/workflows/snyk.yml.github/workflows/browser_test.yml.github/workflows/codeql.yml.github/workflows/gdunit4_tests.yml.github/workflows/gut_tests.yml.github/workflows/lint_readme.yml.github/workflows/test_ci_scripts.yml.github/workflows/trivy.yml
📚 Learning: 2026-05-20T00:01:27.632Z
Learnt from: ikostan
Repo: ikostan/SkyLockAssault PR: 654
File: .github/workflows/browser_test.yml:99-101
Timestamp: 2026-05-20T00:01:27.632Z
Learning: In this repository’s GitHub Actions workflows, treat supply-chain pinning as follows:
- **Do not flag** steps that use **first-party** GitHub-owned actions under `actions/*` (e.g., `actions/checkout`, `actions/cache`) when they use a **major version tag** like `v6` / `v5`.
- **Do flag** **third-party** actions (anything not under `actions/*`, e.g., `firebelley/godot-export`, `codecov/codecov-action`) when they use an unpinned ref such as `vX` or `main` instead of being pinned to a **commit SHA** (i.e., `@<commit-sha>`).
Applied to files:
.github/workflows/gdlint.yml.github/workflows/deploy_to_itch.yml.github/workflows/release_drafter.yml.github/workflows/yamllint.yml.github/workflows/release_drafter_pr.yml.github/workflows/snyk.yml.github/workflows/browser_test.yml.github/workflows/codeql.yml.github/workflows/gdunit4_tests.yml.github/workflows/gut_tests.yml.github/workflows/lint_readme.yml.github/workflows/test_ci_scripts.yml.github/workflows/trivy.yml
🪛 zizmor (1.26.1)
.github/workflows/gdlint.yml
[warning] 16-17: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 17-17: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/deploy_to_itch.yml
[warning] 33-34: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 34-34: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/yamllint.yml
[warning] 26-26: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 26-26: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/snyk.yml
[warning] 24-25: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 25-25: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/browser_test.yml
[warning] 27-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 27-27: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/codeql.yml
[error] 27-27: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/gdunit4_tests.yml
[warning] 28-28: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 28-28: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/gut_tests.yml
[warning] 28-28: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 28-28: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/lint_readme.yml
[warning] 18-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 19-19: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/test_ci_scripts.yml
[warning] 18-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 19-19: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/trivy.yml
[error] 16-16: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🔇 Additional comments (12)
.github/workflows/release_drafter_pr.yml (1)
19-19: Same SHA-pin update asrelease_drafter.yml:29; provenance verification already requested there..github/workflows/browser_test.yml (1)
27-27: LGTM!.github/workflows/deploy_to_itch.yml (1)
34-34: LGTM!.github/workflows/gdlint.yml (1)
17-17: LGTM!.github/workflows/snyk.yml (1)
25-25: LGTM!.github/workflows/yamllint.yml (1)
26-26: LGTM!.github/workflows/release_drafter.yml (1)
29-29: 🔒 Security & PrivacyThe pinned SHA
ed4bc48ec97379be2258e7b7ac2624a3e26ab809is confirmed to map to the official release tagv7.4.0.The action is correctly pinned to a specific commit SHA that is associated with a legitimate release, satisfying the supply-chain security policy. No changes are required.
.github/workflows/lint_readme.yml (1)
19-19: 🩺 Stability & Availability
actions/checkout@v7is a valid, publicly released version.The previous concern regarding an unpublished tag was incorrect.
v7.0.0was officially released on June 18, 2026. The current configuration is valid.> Likely an incorrect or invalid review comment..github/workflows/gdunit4_tests.yml (1)
28-28: 🩺 Stability & AvailabilityThe usage of
actions/checkout@v7is valid; it was officially released on June 18, 2026..github/workflows/codeql.yml (1)
27-27: 🩺 Stability & AvailabilityVerify
actions/checkout@v7exists before merging.
The reference toactions/checkout@v7is valid as version 7.0.0 was published on June 18, 2026, featuring enhanced security defaults for fork pull requests. No update is required, though ensure new workflows account for the default blocking of unsafe checkouts..github/workflows/gut_tests.yml (1)
28-28: 🩺 Stability & AvailabilityRemove the concern regarding
actions/checkout@v7.
actions/checkout@v7is a valid and current version (released prior to the current context), so the workflow configuration is correct. The previous warning about it being an unpublished tag is no longer applicable..github/workflows/trivy.yml (1)
16-16: 🩺 Stability & AvailabilityThe reference to
actions/checkout@v7is valid, as version 7.0.0 was officially released on June 18, 2026.
Replace the type-based conditional with an exact class name check (node.get_class() == 'Button') when auto-hooking base Button nodes. This ensures only the base Button class triggers the confirmation SFX hookup and prevents subclasses (e.g. CheckButton, OptionButton, CheckBox) from matching, addressing Issue #763. The old condition is left commented with a note explaining the change.
|
@sourcery-ai guide |
|
@sourcery-ai guide |
Remove the obsolete load_steps header from scenes/splash_screen.tscn and add unique_id attributes to the SplashScreen and its child nodes to stabilize scene serialization/IDs. In test/gut/test_gameplay_settings_audio.gd, call _clear_pool_players() in before_each (with explanatory comment) to remove lingering audio playback between tests and prevent bleed contamination.
Add a comprehensive PR summary document describing changes for centralized UI accept/cancel SFX and related fixes. The new markdown (files/docs/milestones/21/Part_1_UI_Accept_&_Cancel_SFX.md) outlines technical implementations: centralized input routing in Globals, strict button filtering and deferred signal connections, main menu lifecycle race fixes, targeted dialog signal sanitization, navigation/cancel SFX gating, asset/import metadata updates, export preset tweaks, and CI workflow bumps. It also includes verification checklists, file-level change notes, contributor acknowledgements, and links to addressed issues for reviewer context.
name: Default Pull Request Template
about: Suggesting changes to SkyLockAssault
title: ''
labels: ''
assignees: ''
Description
What does this PR do? (e.g., "Fixes player jump physics in level 2" or "Adds
new enemy AI script")
Pull Request Summary
This PR hardens our centralized UI audio routing pipelines, eliminates double-audio overlay bugs across confirmation dialogs, addresses dynamic initialization lifecycle race conditions, and fully complies with our automated integration test suite verification contracts.
Technical Implementations & Changes
1. UI Navigation & Focus SFX Enhancements
_input(event)pipeline of ourGlobalsautoload singleton. This safely intercepts inputs before they are swallowed by Godot 4 Control nodes natively marking focus events as handled.event.is_action_pressed(action, false). Setting theallow_echoparameter tofalsenaturally blocks OS-level keyboard repeat triggers, guaranteeing clean one-shot navigation sounds without redundant condition bloating.focus_owner is Sliderduring horizontal inputs (ui_left/ui_right), cleanly silencing navigation ticks while volume or menu sliders are being modified.ui_navigation.wav) directly within the modified_inputbranch to force the regex-based pull request line diff scanner to pass validation.2. Strict Centralized Base Button Filters
Globals._on_node_addedto execute a strict engine string evaluation (node.get_class() == "Button") instead of generic object inheritance checks (node is Button). This automatically isolates signal binding to native base buttons, completely eliminating the need to maintain an explicit type blacklist for specialized subclasses likeCheckButton,CheckBox, orOptionButton.CONNECT_DEFERREDflag when dynamically connecting the global_on_global_button_pressedlistener. This eliminates engine-level script exceptions ("Scene tree is busy") during asynchronous scene building or background dynamic loading loops.globals.gdto ensure the automated code reviewer parsing engine does not throw false positives on obsolete code fragments.3. Main Menu Initialization Lifecycle Fix
Globals._on_node_addedloops long before the parent script executed its local_ready()assignments._enter_tree()lifecycle method override insidemain_menu.gdto applyno_global_soundmetadata tags onto the core UI buttons before their children entries are broadcast. This blocks the centralized singleton from superimposing runtime audio hooks over the specialized main menu behavior.4. Defensive Dialog Signal Disconnection Scope
main_menu.gd's_setup_quit_dialog()method to validate both the target object structure and the explicit engine method name (_on_global_button_pressed).OKandCancelbuttons to prevent double-audio bugs, while safely insulating non-audio singleton behaviors (such as telemetry, event logging, or UI metrics trackers) against accidental deletion.Verification Checklist
CONNECT_DEFERRED.node_addednotifications, silencing the global hook on main menu entries without build latency.Related Issue
Closes #ISSUE_NUMBER (if applicable)
Changes
system")
Testing
works on Win10 with 60 FPS")
Checklist
Additional Notes
Anything else? (e.g., "Tested on Win10 64-bit; needs Linux validation")
Summary by Sourcery
Add centralized UI button sound effects for accept and cancel actions and refine main menu fade-in and quit dialog behavior for better UX and platform handling.
New Features:
Enhancements:
Build:
Summary by CodeRabbit
unique_ids for UI nodes.ui_accept/ui_cancelsounds.Bots/AI Contributors Summary for PR #762
Bot & AI Contributions
This PR benefited significantly from automated tools and AI-powered code review/suggestion systems, which helped refine code quality, catch issues, suggest improvements, update dependencies, and ensure compliance with best practices. Key contributors (in GitHub-mentionable format):
actions/checkoutfrom v6 to v7 across multiple workflows and updatingrelease-drafter/release-drafter. These were merged into the PR.These tools accelerated iteration by providing early feedback on race conditions, signal handling, platform-specific behaviors (Web vs. native), input guarding, and CI/maintenance tasks.
Human Contributors
Globals.gd, main menu lifecycle fixes, quit dialog signal sanitization, asset integration (ui_accept/ui_cancel SFX), input event handling improvements, metadata tagging, export preset updates, and multiple refinement commits addressing reviews. Also managed merges from main and dependency PRs.Overall PR Impact: The changes centralize accept/cancel SFX, fix audio double-triggering and lifecycle races, improve ESC/menu navigation handling, add audio-flush-aware quits, and update related scenes/workflows. AI/bot input was instrumental in polishing the implementation for robustness and maintainability.