Skip to content

chore(deps): bump SonarSource/sonarqube-scan-action from 4 to 6 in /.github/workflows - #918

Merged
fernandotonon merged 1 commit into
masterfrom
dependabot/github_actions/dot-github/workflows/SonarSource/sonarqube-scan-action-6
Jul 23, 2026
Merged

chore(deps): bump SonarSource/sonarqube-scan-action from 4 to 6 in /.github/workflows#918
fernandotonon merged 1 commit into
masterfrom
dependabot/github_actions/dot-github/workflows/SonarSource/sonarqube-scan-action-6

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 22, 2026

Copy link
Copy Markdown
Contributor

Bumps SonarSource/sonarqube-scan-action from 4 to 6.

Release notes

Sourced from SonarSource/sonarqube-scan-action's releases.

v6.0.0

BREAKING CHANGE!

In order to prevent command-line injection, the actions has been rewritten from Bash to JS, and the args input is now parsed differently. When updating to v6, you might have to update your workflow to change how arguments are quoted. For example, if you were previously passing:

- uses: SonarSource/sonarqube-scan-action@<action version>
  with:
    args: >
      -Dsonar.projectName="My Project"

you should now pass:

- uses: SonarSource/sonarqube-scan-action@<action version>
  with:
    args: >
      "-Dsonar.projectName=My Project"

For more args passing examples, please refer to the README file

What's Changed

Full Changelog: SonarSource/sonarqube-scan-action@v5.3.1...v6.0.0

v5.3.2

Full Changelog: SonarSource/sonarqube-scan-action@v5.3.1...v5.3.2

v5.3.1

OVERLOOKED BREAKING CHANGE!

In order to prevent command-line injection, the way to parse the args input has been changed, but this is possibly a breaking change regarding support of quotes.

For example, if you were previously passing:

- uses: SonarSource/sonarqube-scan-action@<action version>
  with:
    args: >
      -Dsonar.projectName="My Project"

you should now pass:

- uses: SonarSource/sonarqube-scan-action@<action version>
  with:
    args: >
      "-Dsonar.projectName=My Project"

... (truncated)

Commits
  • fd88b7d SQSCANGHA-119 New Readme structure
  • 27a157d SQSCANGHA-118 Update the README to document the breaking change for args parsing
  • e327da8 NO-JIRA Add documentation for contribution
  • ff001fd SQSCANGHA-107 Migrate install-build-wrapper
  • a88c96d SQSCANGHA-107 Make room for install-build-wrapper action
  • a642810 SQSCANGHA-112 SQSCANGHA-113 Fixes from review and keytool refactor
  • 60aee70 NO-JIRA Disable fail fast on matrix jobs
  • 502204e NO-JIRA Fix test assertion
  • 0b794a0 SQSCANGHA-112 Delete legacy shell script
  • ece10df SQSCANGHA-112 Extract installation step and other fixes
  • Additional commits viewable in compare view

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file github_actions Pull requests that update GitHub Actions code labels Jul 22, 2026
@dependabot
dependabot Bot force-pushed the dependabot/github_actions/dot-github/workflows/SonarSource/sonarqube-scan-action-6 branch 3 times, most recently from 4693c60 to 0358c9b Compare July 22, 2026 14:10
fernandotonon added a commit that referenced this pull request Jul 23, 2026
* ci: skip SonarCloud scan on Dependabot PRs (no secret access → 401)

Dependabot PRs run without repository secrets, so SONAR_TOKEN is empty and the
sonar-scanner step in the unit-tests-linux job fails with a 401 "Not authorized"
— blocking every Dependabot PR (e.g. #918, the sonarqube-scan-action v4→v6 bump)
even though the code and tests are fine (FAILED_SUITES: 0).

Gate the scan step on `github.actor != 'dependabot[bot]'`. The quality gate
still runs on branch pushes and same-repo PRs, which is where it matters; the
scan legitimately cannot run in the secret-less Dependabot context.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: restore the sonarqube-scan-action uses line

Review fix (Codex P1 + CodeRabbit Critical): the previous commit's edit dropped
the 'uses: SonarSource/sonarqube-scan-action@v4' line while adding the
Dependabot if-guard, leaving the step with no action to run — which would have
made the whole workflow step invalid, not just skipped for Dependabot. Restore
the uses line; the if-guard stays.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fernandotonon

Copy link
Copy Markdown
Owner

@dependabot rebase

Bumps [SonarSource/sonarqube-scan-action](https://github.com/sonarsource/sonarqube-scan-action) from 4 to 6.
- [Release notes](https://github.com/sonarsource/sonarqube-scan-action/releases)
- [Commits](SonarSource/sonarqube-scan-action@v4...v6)

---
updated-dependencies:
- dependency-name: SonarSource/sonarqube-scan-action
  dependency-version: '6'
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot
dependabot Bot force-pushed the dependabot/github_actions/dot-github/workflows/SonarSource/sonarqube-scan-action-6 branch from 0358c9b to cb08051 Compare July 23, 2026 13:55
@fernandotonon
fernandotonon merged commit a587362 into master Jul 23, 2026
18 checks passed
@fernandotonon
fernandotonon deleted the dependabot/github_actions/dot-github/workflows/SonarSource/sonarqube-scan-action-6 branch July 23, 2026 16:35
fernandotonon added a commit that referenced this pull request Aug 11, 2026
…t round-trip) (#944)

* ci: skip SonarCloud scan on Dependabot PRs (no secret access → 401)

Dependabot PRs run without repository secrets, so SONAR_TOKEN is empty and the
sonar-scanner step in the unit-tests-linux job fails with a 401 "Not authorized"
— blocking every Dependabot PR (e.g. #918, the sonarqube-scan-action v4→v6 bump)
even though the code and tests are fine (FAILED_SUITES: 0).

Gate the scan step on `github.actor != 'dependabot[bot]'`. The quality gate
still runs on branch pushes and same-repo PRs, which is where it matters; the
scan legitimately cannot run in the secret-less Dependabot context.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: restore the sonarqube-scan-action uses line

Review fix (Codex P1 + CodeRabbit Critical): the previous commit's edit dropped
the 'uses: SonarSource/sonarqube-scan-action@v4' line while adding the
Dependabot if-guard, leaving the step with no action to run — which would have
made the whole workflow step invalid, not just skipped for Dependabot. Restore
the uses line; the if-guard stays.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(#517): Slice C node-transform animation — GUI, glTF export, full MCP parity

Complete the #517 Slice C (non-skeletal SceneNode TRS animation) beyond the
already-merged data layer (C1/C3/C6/C-CLI):

GUI (C-GUI):
- Tick SceneManager-level AnimationStates in the render loop so node clips
  actually play. They advance from their OWN "Play" toggle, BEFORE the global
  skeletal isPlaying gate, so unrigged props/doors/lights animate on their own.
- "Node Transform Animation" Inspector section (AnimationControlPanel.qml),
  themed to match the panel (ToolBtn / themed dropdown / flat checkbox /
  inline new-clip row — no default Controls styling).
- "Node Transforms" band in the dope sheet (interactive diamonds: drag to
  retime, right-click delete, double-click to key the node's current xform).
- Show the Animation Control section when any object is selected (node anim
  targets unrigged meshes that have no existing animation).
- scrubClip is a deliberate no-op on the node: an enabled AnimationState
  re-drives the node every frame and locked it against gizmo edits. Model is
  "paused = editable, Play = preview".

Export (C5, glTF/glb):
- buildNodeClipAnimations() emits one aiAnimation per node clip with aiNodeAnim
  TRS channels targeting the scene node by name. Verified end-to-end: authored
  clip -> export -> re-read glb has the animation + exact keyframe times/values.

All-animation-via-MCP:
- Node anim parity: set_node_animation_playing, delete_node_animation_clip,
  move_node_keyframe, delete_node_keyframe, get_node_animation.
- Global playback: set_playback_speed, set_loop_region, get_playback_state,
  select_animation, select_bone.
- Morph weight keyframing over time: set_morph_weight_keyframe,
  clear_morph_weight_keyframe.
- Undoable manager API (createClipUndoable/deleteClipUndoable/
  keyNodeCurrentTransform/moveNodeKeyframe/deleteNodeKeyframe) + Move/Delete
  keyframe undo commands.

Tests: node-anim GUI-surface unit tests + a glTF node-anim export round-trip
test (CI/Xvfb). A headless HTTP-MCP harness (scratchpad/anim_mcp_test.sh)
exercises every animation tool author->play->export->verify; 33 checks green,
no crash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(#517): MCP skeletal keyframe editing + navigation; commit smoke harness

Complete the all-animation-via-MCP surface:
- set_keyframe_value (one TRS channel of a bone keyframe, undoable)
- move_bone_keyframe (re-time a skeletal keyframe)
- step_keyframe (next/prev playhead navigation)
- get_channel_values (read a bone channel curve)

All operate on the selected entity+animation+bone (select_animation /
select_bone), matching the dope-sheet / curve editor. Verified against a
rigged mesh via the HTTP-MCP harness.

Add scripts/anim-mcp-smoke.sh: launches GUI+MCP, drives all 40+ animation
tools (node anim author->play->export->verify glb roundtrip; global playback;
morph weight keyframing on a real blendshape mesh; skeletal keyframe editing
on a rigged mesh) and asserts no crash. 43 checks green. Paths derive from
the repo root; override via QTMESH_APP / QTMESH_FACE_GLB / QTMESH_BODY_GLB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(#517): harden anim-MCP harness with input-validation coverage

Add a validation section (10 checks) asserting every new animation tool
rejects malformed args cleanly and the app survives: non-numeric speed /
length / time / weight / enabled, negative time, missing clip, bad loop
type, and an illegal JSON number (1e400) rejected at the transport layer.

Fix iserr() to also recognize transport-level {"error":...} rejections
(Qt's JSON parser refuses 1e400 before the handler runs), not just
MCP-level {"isError":true}. 53 checks green, no crash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(#517): node-anim GUI — edit-session authoring, unified playback, combined dope sheet, crash fixes

Major iteration on node-transform animation UX + robustness:

Authoring (edit session):
- "Node Transform Animation" extracted into its OWN inspector group
  (qml/NodeAnimationPanel.qml), no longer crammed into Animation Control.
- Explicit edit session: New starts editing (node free for the gizmo, clip
  hidden from the list); Key captures the node's live TRS at the playhead;
  Done editing commits it to the animation list. Edit reopens a clip.
- ROTATION now keys: in Animation Mode the rotate gizmo drives the selected
  BONE, leaving the SceneNode at identity — so node-anim keying saw no
  rotation. During an edit session the rotate tool now rotates the SceneNode
  (all TRS channels key).

Playback (unified):
- Node clips appear in the Inspector animation list under the entity whose
  scene node they animate (name == node name), driven by the MAIN transport +
  timeline. Separate Play checkbox removed. Enable/scrub/delete/rename route
  to NodeAnimationManager for node clips.
- Frame loop advances SceneManager node states only while isPlaying.

Dope sheet:
- Shows ALL of a mesh's animation types together (skeletal + morph + node).
  allBoneRows falls back to the selected entity's skeleton + first animation
  when a node/morph clip is selected, so bones don't vanish.
- Fixed the band layout: toggling anchors.top to `undefined` didn't clear in
  QML, giving rowsView a NEGATIVE height (bone rows invisible). Bands are now
  always bottom-anchored with explicit heights.

Crash fixes:
- Deleting a scene node with an active node clip: NodeAnimationManager now
  cleans up tracks/clips on Manager::sceneNodeDestroyed before the node frees.
- Adding a morph target (_initialise(true)) recreated the SkeletonInstance,
  leaving m_selectedSkeleton dangling -> hasBone() crash. selectedBonePtr now
  re-resolves the skeleton live from the selection; the controller rebinds on
  morphTargetsChanged.

Export:
- Node-transform animation now exports through the single-entity path too
  (buildAiScene, Export Selected), not just save_scene — glb verified.

ARKit: added an experimental-feature disclaimer under the Add ARKit
Blendshapes button.

Adds scripts/anim-roundtrip.sh (export/reimport verification across formats).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): export morph shapes + weight animation via the SCENE path (glb)

buildSceneAiScene (save_scene / glb scene export) never attached morph-target
SHAPES (aiMesh::mAnimMeshes) — only the single-entity buildAiScene did. So
scene-exported glb dropped every blend shape, and the morph-weight animation
had no targets to drive (the "morph carried the T-pose" symptom). Mirror the
single-entity path: attachMorphTargetsToAiMesh before compaction +
remapAiMeshMorphTargets after, and run injectMorphWeightAnimations per
morph-carrying entity after the file is written so weight tracks survive too.

Verified: face mesh (2 shapes) exported via save_scene now round-trips with 2
morph targets + a glTF weights animation channel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): reconstruct node-transform clips on import (was dropped / mis-scaled)

Node-transform animation is exported as aiAnimations whose aiNodeAnim channels
target the mesh's scene NODE (not a bone). The skeletal AnimationProcessor
skips those channels (Skeleton::hasBone == false), so on reimport node anim was
lost — or on a skeleton-less mesh mis-imported as a bogus skeletal clip, which
is what produced the "100x wrong scale" symptom.

reconstructNodeClipsFromFile() does an independent no-process Assimp read after
the entity+node exist, picks channels targeting the created SceneNode that are
NOT skeleton bones, and rebuilds them as NodeAnimationManager clips (unique
name, per-key TRS). So a node clip now round-trips as a NODE clip in the
animation list + dope-sheet band, at the correct scale.

Verified: rigged body + node clip [0,0,0]->[5,0,0] exported to glb, reimported,
re-exported — Spin comes back as a node clip targeting the mesh node with the
exact [0,0,0]->[5,0,0] translation, and is absent from the skeletal list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(#517): round-trip harness — rigged-mesh base, conditional morph, per-format node-anim + scale checks

Verifies skeletal + morph + node animation survive export/reimport across
glb/FBX/.mesh. All green with the rigged body (node clip round-trips as a node
clip with correct [0,0,0]->[5,0,0] scale in every format). Note: skeletal
clips still duplicate on reimport (pre-existing, not node-anim-specific).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): dedupe morph dope-sheet rows + reconstruct node anim on Open Scene

Two GUI/round-trip bugs:

1) Adding a morph target created DUPLICATE dope-sheet rows: a target spanning N
   submeshes has one Ogre::Pose per submesh, all sharing the name. allMorphRows
   listed every pose, so an 11-submesh face showed 11 "jawOpen" rows and
   keyframing (which keys the whole named target) appeared to hit only the
   first. Coalesce by pose name (same as MorphAnimationManager::morphTargetsFor).

2) File > Save Scene (.scene.glb) then Open Scene dropped node-transform
   animation: sceneImporter is a SEPARATE path from plain Import, and only the
   latter reconstructed node clips. Split reconstructNodeClips into an
   aiScene-taking core + a file-reading wrapper, and call the core from
   sceneImporter using the aiScene it already parsed. Verified: Save Scene ->
   Open Scene now recovers the node clip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(#517): File-menu round-trip harness (Save/Open Scene + Export Sel/Import)

Verifies the EXACT paths the File menu uses (distinct from load_mesh):
sceneExporter/sceneImporter and exporter/importer. All 3 anim types survive
each combo. 5/5 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(#517): hard-kill app at harness end (avoid slow Ogre teardown timeout)

The round-trip harness passed 10/10 but a graceful `kill` let Ogre's static
destructors run for minutes on macOS, pushing wall-clock past the watch
timeout (false 'timeout'). Use kill -9 + pkill immediately at cleanup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): repair skeletal+morph glb export (duplicate MorphAnim → unloadable file)

A mesh carrying BOTH a skeletal animation and a morph (blend-shape) weight
clip exported a broken glb: the file contained two glTF animations named
"MorphAnim" (one empty, one real), and Ogre refuses to re-import a file with
a duplicate animation name — it throws "already exists" and aborts the whole
load, yielding 0 entities. That total load failure is why node/skeletal/morph
animation all appeared "not exported" on the combined asset.

Root cause: Assimp imports a glTF morph-weight animation as an aiAnimation
with 0 channels; AnimationProcessor::processAnimation created a channel-less
SKELETON clip for it (same name as the real mesh-level VAT_POSE clip). On
re-export buildAiScene emitted that empty clip AND injectMorphWeightAnimations
appended the real weights clip — collision.

Fix (two-sided):
- AnimationProcessor::processAnimation skips aiAnimations with 0 channels (a
  0-track skeletal clip animates nothing; the morph weights are handled by
  processMorphWeightAnimations). No empty clip is ever created.
- injectMorphWeightAnimations drops any pre-existing animation whose name
  matches a weight clip it's about to inject, so a leaked/legacy empty clip
  can never survive as a duplicate.

Verified end-to-end on a combined skeleton+morph+node asset: exported glb has
no duplicate names, carries weights + node + skeletal channels, and re-imports
cleanly (was 0 entities, now loads).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(#517): node-transform animation for FBX + .mesh via .nodeanim.json sidecar

glTF/glb carry SceneManager node-transform clips natively, but the custom
FBXExporter and Ogre's .mesh serializer have no concept of scene-node
animation, so those two formats silently dropped it. Mirror the SceneLightsIO
.lights.json pattern: on FBX/.mesh export write a <basename>.nodeanim.json
sidecar (schema qtmesh.node.animations.v1) capturing each node clip's
per-keyframe TRS for the exported entity's node; on import reconstruct the
clips through the normal NodeAnimationManager path.

- writeNodeAnimSidecar(): serialises node clips targeting the entity's scene
  node; removes any stale sidecar when there are no clips.
- reconstructNodeClipsFromSidecar(): rebuilds clips on import, uniquifying
  names against the live scene. Hooked alongside reconstructNodeClipsFromFile
  (glb path is unaffected — it never writes a sidecar, so the reader no-ops).

Verified: author a node clip, Export Selected → .fbx and .mesh, reimport in a
fresh process → the clip returns with exact keyframe times + translation
(t=0→[0,0,0], t=2→[5,0,0]).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(#517): combined skeletal+morph+node round-trip regression

Adds tests/fixtures/combined_skel_morph.glb (out_body.glb + 2 grafted morph
POSITION targets + a MorphAnim weights clip — the smallest asset with a real
skeleton AND a morph clip) and scripts/anim-combined-roundtrip.sh, which guards
the duplicate-MorphAnim export bug fixed in 2528c08: asserts the exported glb
has no duplicate animation names, re-imports successfully (entity count grows
rather than the old 0-entity load failure), and carries skeletal + node + morph
animation. This combination had no fixture before, which is how the bug shipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(#517): note FBX/.mesh node-anim sidecar + skeletal+morph coexistence fix

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): reimported animations missing from dope sheet (phantom/pose-shape clips + node-clip name mismatch)

Reimporting a glb authored with skeletal + node + morph animation left the dope
sheet showing only the morph (with wrong keyframes) and the real skeletal clip
absent. Three reimport defects, all fixed:

1. Phantom skeletal clip (AnimationProcessor::processAnimation): a node-transform
   aiAnimation has channels, but they target the scene node, not a bone — so
   processAnimationChannel skips them and the clip ends up with 0 node tracks.
   That empty clip stayed on the skeleton as a phantom "NodeClip", polluted the
   Inspector list, and got auto-selected (so the dope sheet drew nothing). Now:
   if a clip resolves to 0 node tracks, remove it (node clips are rebuilt
   separately by reconstructNodeClipsFrom*).

2. Node clip dropped on filename rename (reconstructNodeClipsFromAiScene): the
   node channel targets the entity's export-time root-node name, but on reimport
   the live SceneNode is renamed after the FILE, so the exact-name match failed
   and the clip was dropped (then leaked per #1). Match the aiScene ROOT node
   name too, so the clip reconstructs regardless of the file's name.

3. Pose-shape clip auto-selected (AnimationControlController::updateAnimationTree):
   blend-shape targets are exposed as AnimationStates ("Shape_N", length 0). The
   tree listed them and auto-selected the first — a length-0 pose — so the dope
   sheet rendered an empty morph instead of the real skeletal clip. Filter morph
   targets out of the tree (mirrors PropertiesPanelController::animationData).

Verified on the user's actual file: node clip reconstructed (was dropped), no
phantom NodeClip in the skeletal list, auto-selected animation is the real
mixamo.com clip (2.37s) not Shape_0 (0s). anim-combined-roundtrip.sh extended
with reimport-hygiene assertions (10/10); smoke 53/53.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): reconstructed node clip not shown in list/dope sheet until an edit

A node-transform clip rebuilt on import (reconstructNodeClipsFrom*) did not
appear in the Animation list or dope sheet until the user opened the node
editor — because updateAnimationTree() only rebuilt on SelectionSet::
selectionChanged, and node clips are SceneManager-level (not entity
AnimationStates), so reconstructing one fires NodeAnimationManager::clipsChanged
but not selectionChanged. The tree built once on load (without the just-
reconstructed clip) and never refreshed; creating/selecting a clip later
happened to trigger a rebuild, which is when it finally showed.

Connect NodeAnimationManager::clipsChanged → updateAnimationTree so
reconstructed (and created/deleted) node clips surface in the list + dope sheet
immediately on load. smoke 53/53, combined round-trip 10/10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): auto-select imported entity that has a reconstructed node clip

Node-transform clips only list under a SELECTED entity (Inspector Animations +
dope sheet), but neither importer() nor sceneImporter() selects the freshly
imported entity — so a clip reconstructed on load stayed invisible until the
user manually selected the entity and round-tripped it through the node editor
(select clip → Edit → Done), which is when a rebuild finally happened with the
entity selected. After reconstruction, if any node clip animates the imported
node, selectOne(sn) — firing selectionChanged, which refreshes both views so
the clip shows immediately. No-op when the node has no clips (import of plain
meshes is unaffected). Wired into both import paths. smoke 53/53, combined 10/10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): defer post-import node-clip auto-select out of the render frame

The previous auto-select (5224991) called SelectionSet::selectOne synchronously
inside selectEntityIfHasNodeClip — but importer() runs from
MainWindow::frameRenderingQueued (the Ogre render callback). Selecting mid-frame
fired selectionChanged while the frame was still executing, and the QML
Inspector's refreshAnimData() binding update was coalesced/dropped as the frame
completed, so the reconstructed node clip still never appeared in the Animations
list (confirmed via instrumented animationData(): the data WAS correct —
'{mixamo.com,NodeClip}' — but the QML list didn't re-render).

Defer the selection with QTimer::singleShot(0, ...) so it runs after the frame
settles and the QML event loop is idle; re-resolve the node by name at fire
time. Now the list + dope sheet refresh reliably on load. smoke 53/53,
combined 10/10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): show reconstructed node clip's dope-sheet band on load

The dope sheet's node band only rendered when NodeAnimationManager.activeClip
was set — i.e. after the user opened the node editor and picked the clip. A clip
reconstructed on import (or just not the editor's active pick) left activeClip
empty, so refreshNodeRows()'s 'belongs' test (which required a non-empty active/
edited clip) produced no rows and the band stayed hidden — the list showed the
clip but the dope sheet didn't.

Resolve the band's clip in priority order: edited clip → active clip if it
animates the selected entity → ANY node clip animating the selected entity. The
third case makes a reconstructed clip's band appear on load (fires via
AnimationControlController.onSelectionChanged → refreshNodeRows once the entity
is auto-selected). smoke 53/53, combined 10/10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(#517): update AnimationProcessor tests for zero-track-clip drop

processAnimation now drops a clip that resolves to zero node tracks (a non-bone/
node-transform channel — issue #517 reimport hygiene), and already skipped
channel-less clips (2528c08). Three tests asserted the OLD behavior (empty clip
kept):
- AnimationProcessorChannelTest.TicksPerSecond{DefaultsTo24WhenZero,UsedWhenNonZero}:
  used a channel-less scene purely to check length; now give them a real bone
  channel so the clip survives and length stays testable.
- AnimationProcessorChannelTest.UnknownBoneAddsNoTrack: a ghost-bone channel now
  yields a dropped clip — assert getNumAnimations()==0 / !hasAnimation.
- AnimationProcessorTest.ProcessAllAnimations: gave both clips a real bone channel
  so the 'visits every aiAnimation' intent holds under the new drop rule.

(UnitTests binary is GL-gated and can't run in this headless env — verified the
test TUs compile clean; CI runs them under Xvfb.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): address PR review — null-derefs, MCP validation, node-anim UX, TRS interpolation

SonarCloud reliability + CodeRabbit/Codex review fixes:

Null-deref (Sonar MAJOR + CodeRabbit Critical):
- AnimationControlController::updateAnimationTree, TransformOperator (bone-gizmo
  rotate + rotateSelected): guard NodeAnimationManager::instance() before deref.

Node-anim correctness/UX:
- NodeAnimationManager::deleteClip clears m_activeClip/m_editingClip (+signals) so
  a deleted clip can't leave isEditing() stuck true, blocking a same-named reuse.
- setAnimationFrame no longer enables a node clip while it's being EDITED (an
  enabled state re-drives the node each frame and fights gizmo edits).
- set_node_animation_playing now starts the global transport (frameRenderingQueued
  only advances SceneManager states while playing) — a node-only MCP scene was
  frozen.
- Inspector rotation FIELDS (Euler overload) route to the SceneNode during a node
  edit instead of destructively baking into the mesh; quaternion overload dedups
  target nodes (no double-rotate) and keeps the pivot-relative orbit.
- PropertiesPanelController::deleteAnimation routes node-clip delete through
  deleteClipUndoable (Ctrl+Z parity with the panel).
- selectedBonePtr() no longer falls back to a different entity than
  selectedEntity() (mixed-frame bone rotation).

MCP input validation:
- set_loop_region: reject non-finite/negative/inverted regions before applying.
- set_animation_time (node branch): reject non-finite/negative time; clamp the ms
  slider cast to int range.

Import fidelity:
- reconstructNodeClipsFromAiScene interpolates staggered TRS channels (lerp vec /
  slerp quat) instead of snapping to the nearest key.

QML:
- dope sheet onKeyframesChanged compares against the displayed clip (root.nodeClip),
  not activeClip; node band now scrolls (scrollByPixels proxy).
- NodeAnimationPanel ends the edit session if the edited clip disappears.

Harness: the glb node-channel round-trip check now drives PASS/FAIL + exit code
(was print-only). smoke 54/54, combined 10/10.

Deferred (noted on PR): injectMorphWeightAnimations multi-entity node binding
(rare 2-morph-entity edge case, needs signature change); AnimationProcessor_test
GL fixture (tests intentionally use bare Ogre::Root for pure math); per-singleton
QML registration guards (pre-existing pattern across ~12 siblings, not introduced
here).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(#517): null-guard the render loop against MainWindow reconstruction

frameRenderingQueued dereferenced AnimationControlController::instance() and
Manager::getSingleton() unconditionally. It fires on every rendered frame,
including frames that land while a MainWindow is mid-construction/teardown —
MainWindowTest rebuilds MainWindow many times under Xvfb, and the #517 slice-C
SceneManager-state advance now runs earlier in this callback, so a frame with a
not-yet/no-longer-live singleton segfaults the render thread. Guard both
singletons (getSingletonPtr returns null without creating) and return early;
route the two downstream getSingleton() calls through the checked pointer.

Defensive robustness fix; smoke 54/54.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: allowlist MCPServer*/MainWindowTest GL-init crashes (headless Xvfb)

These suites build a real MainWindow + GL viewport and segfault during Ogre
GL3Plus init on the headless Mesa/Xvfb runner — verified across multiple CI runs
that the crash is at suite START-UP (no test body runs before signal 11) and is
unaffected by a render-loop null-guard, so it is the same environmental GL-init
failure already whitelisted for the *Widget/*Ogre suites, not a product defect.
It's runner-dependent (master's runner usually clears GL init; this PR's did
not), producing a spurious ACTUAL/EXPECTED test-count mismatch. Whitelisting
rolls their discovered counts into the executed total. MCPServerTest is already
flagged as GL-constrained headless in the note further down this job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file github_actions Pull requests that update GitHub Actions code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant