Skip to content

feat(vat): slice 4 — Inspector subgroup + MCP bake_vat tool - #568

Merged
fernandotonon merged 3 commits into
masterfrom
feat/vat-slice-4-inspector-mcp
May 17, 2026
Merged

feat(vat): slice 4 — Inspector subgroup + MCP bake_vat tool#568
fernandotonon merged 3 commits into
masterfrom
feat/vat-slice-4-inspector-mcp

Conversation

@fernandotonon

@fernandotonon fernandotonon commented May 17, 2026

Copy link
Copy Markdown
Owner

Closes the final piece of #371 (parent epic: #517 / Slice E). Surfaces the existing VATBaker via the two user-facing channels QtMeshEditor expects: the QML Inspector and the MCP server.

What ships

VATBakerController (QML_SINGLETON)

QObject wrapper around VATBaker::bake():

  • `availableAnimations` — list of clip names on the currently-selected entity. Refreshes on `selectionChanged` and on QML mount.
  • `isBaking` — true between `bake()` call and `bakeFinished`. QML uses it to disable the Bake button.
  • `bake(animationName, fps, encoding, target, bakeNormals, outputDir, basename)` — validates args, builds `VATBaker::Options`, calls `VATBaker::bake`, emits `bakeFinished(bool ok, QString posTexture, QString error)`. Synchronous for slice 4 (off-thread sampling would race with Manager's per-frame Ogre animation state updates).

Inspector UI

New "Bake VAT" subgroup inside the Animations section in `qml/PropertiesPanel.qml`. Anim picker, FPS, encoding/target dropdowns, normals checkbox, output dir field, Bake button + status line.

MCP `bake_vat` tool

`MCPServer::toolBakeVat(args)` mirrors the CLI subcommand: `file`, `anim`, `fps`, `encoding`, `target`, `normals`, `output_dir`, `basename`. Returns texture paths + bounds + frame/vertex counts. Registered in `toolHandlers()`, listed in `buildToolsList()` with full descriptions, marked as `isHeavyTool()` so it runs through the deferred execution path. Sentry breadcrumbs (`ai.tool_call` + `file.import` + `file.export`).

11 new tests

  • 5 standalone — singleton instance, error paths (no selection, empty anim, empty outputDir, empty availableAnimations).
  • 6 scene-fixture — refresh from selection, happy-path bake + isBaking transitions, missing-anim error path, rgba16+godot routing (verifies both the 16-bit PNG and the `.gdshader` are produced), selection-change refresh.

Manual smoke

  • Open the app, select an animated entity, expand Animations → Bake VAT, fill in the output dir, click Bake → output appears.
  • `echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | qtmesheditor --mcp` lists `bake_vat` with the expected arg schema.

Test plan

  • CI Linux/Xvfb: all 11 new VATBakerController tests pass; existing 30+ VATBaker tests still pass.
  • CI macOS/Windows: build clean.
  • Manual: bake via Inspector + bake via MCP both produce equivalent output for the same args.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a new "Bake VAT" panel to the Properties UI with controls for selecting animations, setting frame rate, choosing texture format and target platform, toggling normal map baking, specifying output directory, and triggering bake operations.
    • Added support for viewing bake results, including generated textures on success or error messages on failure.
    • Extended server capabilities to support VAT baking operations via external tools.

Review Change Stack

Closes the final piece of #371 (parent epic: #517 / Slice E).
Surfaces the existing VATBaker via the two user-facing channels
QtMeshEditor expects: the QML Inspector and the MCP server.

### `VATBakerController` (QML_SINGLETON)

QObject wrapper around `VATBaker::bake()`:
- `availableAnimations` — read-only list of clip names on the
  currently-selected entity. Refreshes on `selectionChanged`
  and on the QML mount.
- `isBaking` — true between `bake()` call and `bakeFinished`.
  QML uses it to disable the button.
- `bake(animationName, fps, encoding, target, bakeNormals,
       outputDir, basename)` — validates args, builds
  `VATBaker::Options`, calls `VATBaker::bake`, emits
  `bakeFinished(bool ok, QString posTexture, QString error)`.
  Synchronous for slice 4; the Ogre animation state can't
  safely be sampled off-thread (it races with Manager's
  per-frame updates), so a future slice would split the
  sampling out into a producer/consumer pair. The QML
  surface already exposes the `isBaking` flag so adding the
  off-thread sampler is a behavior change, not an API one.

### Inspector UI

New "Bake VAT" subgroup inside the Animations section
(`qml/PropertiesPanel.qml`). Fields:
- Anim picker (auto-populated from `VATBakerController.availableAnimations`)
- FPS spinbox (1..120, default 30)
- Format dropdown (`rgba8` / `rgba16`)
- Target dropdown (`agnostic` / `unity` / `unreal` / `godot`)
- Normals checkbox
- Output dir text field (no native picker for slice 4 — Inspector
  text-fields are standard here; a follow-up can wire in
  QFileDialog for parity with the Save dialogs elsewhere)
- Bake button + last-result status line (green ✓ on success,
  red ✗ + error on failure)

Bake button disables itself while `isBaking` is true.

### MCP `bake_vat` tool

`MCPServer::toolBakeVat(args)`:
- Args mirror the CLI subcommand: `file`, `anim`, `fps`,
  `encoding`, `target`, `normals`, `output_dir`, `basename`.
- Required: `file`, `anim`, `output_dir`.
- Loads via `MeshImporterExporter::importer` (same path as CLI),
  bakes via `VATBaker::bake`, returns a JSON content result with
  the texture paths, frame/vertex counts, bounds, target, and
  encoding.
- Registered in `toolHandlers()` and listed in `buildToolsList()`
  with per-arg descriptions.
- Listed in `isHeavyTool()` so it runs through the deferred
  execution path (Qt event loop, not re-entrant from
  MCP-stdin processing).
- Sentry breadcrumbs: `ai.tool_call` on entry, then `file.import`
  before the mesh load and `file.export` before the bake.

### Tests (11 new, in `VATBakerController_test.cpp`)

Standalone (no Ogre):
- `InstanceIsSingleton`
- `BakeRefusedWhenNothingSelected` (error path)
- `BakeRefusesEmptyAnimName`
- `BakeRefusesEmptyOutputDir`
- `AvailableAnimationsEmptyWithoutSelection`

Scene fixture:
- `RefreshAnimationsFindsSelectionAnim`
- `BakeKicksOffAndEmitsFinished` — happy path + isBaking
  transitions
- `BakeReportsErrorForMissingAnim`
- `EncodingAndTargetStringsRoutedThrough` — confirms
  the rgba16 + godot path produces both the 16-bit PNG and
  the `.gdshader` template
- `AvailableAnimationsRefreshesOnSelectionChange`

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

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@fernandotonon has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 38 minutes and 26 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b01472fb-9a17-43eb-aaee-ae2d91526b70

📥 Commits

Reviewing files that changed from the base of the PR and between c9b1c97 and 699b8f0.

📒 Files selected for processing (6)
  • qml/PropertiesPanel.qml
  • src/MCPServer.cpp
  • src/MCPServer.h
  • src/VATBakerController.cpp
  • src/VATBakerController.h
  • src/VATBakerController_test.cpp
📝 Walkthrough

Walkthrough

This PR introduces a complete VAT baking workflow by adding a QML singleton controller (VATBakerController) that manages animation selection and bake state, wiring it into the Inspector UI, integrating it with the MCP server as a new bake_vat tool, and providing comprehensive test coverage.

Changes

VAT Baking Integration

Layer / File(s) Summary
VATBakerController: State Management & Baking Logic
src/VATBakerController.h, src/VATBakerController.cpp
QML singleton controller with availableAnimations, isBaking, and progress properties. Listens to SelectionSet::selectionChanged to refresh animation lists from the first selected entity's Ogre skeleton. bake() method validates inputs (animation name, output directory, existing selection), normalizes encoding/target strings, logs Sentry breadcrumbs, calls VATBaker::bake() synchronously, updates progress counters, and emits bakeFinished() with success/error results.
QML UI & Application Singleton Registration
src/mainwindow.cpp, qml/PropertiesPanel.qml
Registers VATBakerController as a QML singleton in PropertiesPanel namespace. Adds "Bake VAT" UI subgroup with ComboBox for animation selection, SpinBox for FPS, ComboBoxes for encoding/texture format/target, CheckBox for "Bake normals" toggle, TextField for output directory, and a button to trigger VATBakerController.bake(...). UI binds to controller properties and signals to update animation list and display bake results (success with position texture path or failure with error message).
MCP Server Tool Handler: bake_vat Registration & Implementation
src/MCPServer.h, src/MCPServer.cpp
Adds new MCP tool "bake_vat" handler. MCPServer::toolBakeVat() validates required parameters (file, animation, output_dir), checks file existence, imports mesh via MeshImporterExporter, snapshots pre-existing entities for RAII cleanup, verifies skeleton presence, constructs VATBaker::Options, calls VATBaker::bake(), and returns indented JSON with output paths, bounds, and frame/vertex counts on success or structured error results on failure. Tool is classified as "heavy" and documented in buildToolsList() JSON schema.
Build System & Test Suite Integration
src/CMakeLists.txt, tests/CMakeLists.txt, src/VATBakerController_test.cpp
Adds VATBakerController.cpp to both main and test build targets. Test suite covers singleton lifecycle, input validation (rejecting empty selection/animation name/output directory with bakeFinished failure), animation list refresh from Ogre skeletal states, successful bake with progress/completion signals, missing animation error handling, encoding/target-specific output artifact verification (.gdshader for Godot target), and animation list change signaling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • fernandotonon/QtMeshEditor#371: Main changes implement the VAT baker controller and MCP tool handler (VATBakerController, MCPServer::toolBakeVat) that directly implement the VAT exporter workflow described in the issue.
  • fernandotonon/QtMeshEditor#522: Main changes implement VAT baking UI, controller, and MCP integration that directly address the VAT export family functionality described in the issue.

Possibly related PRs

  • fernandotonon/QtMeshEditor#566: VATBakerController.bake() normalizes and passes encoding and bakeNormals parameters into VATBaker::bake() which relies on the RGBA16 and normal-texture options added in this PR.
  • fernandotonon/QtMeshEditor#567: VATBakerController passes target parameter to VATBaker::bake() and MCPServer::toolBakeVat() expects target-specific output paths (Unity .meta, Godot .gdshader) which depend on the target-aware output logic from this PR.
  • fernandotonon/QtMeshEditor#432: New "Bake VAT" UI is integrated into qml/PropertiesPanel.qml and mainwindow QML singleton wiring, overlapping with the PropertiesPanel refactoring in this PR.

Poem

🐰 A baker so deft, with a VAT up its sleeve,
Now textures dance smooth as the meshes believe!
With animations baked into every pale frame,
The models all shimmer—let the vertex dances came!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main addition: VAT baking support in slice 4, including both the Inspector subgroup and MCP tool.
Description check ✅ Passed The description comprehensively covers all major components (VATBakerController, Inspector UI, MCP tool), testing approach, and manual verification, closely following the template structure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/vat-slice-4-inspector-mcp

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 104682ea5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/MCPServer.cpp Outdated
Comment on lines +4123 to +4128
auto& entities = Manager::getSingleton()->getEntities();
Ogre::Entity* entity = nullptr;
for (auto* obj : entities) {
if (obj && obj->getMovableType() == "Entity") {
entity = static_cast<Ogre::Entity*>(obj);
break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bake the entity imported for this request

After importing file, the tool iterates Manager::getEntities() and bails out on the first entity in the scene graph rather than the entity that was just imported. If the editor already has entities loaded (or the import creates multiple entities), bake_vat can bake the wrong mesh or fail with an animation-not-found error even though the requested file/clip is valid.

Useful? React with 👍 / 👎.

Comment thread src/MCPServer.cpp Outdated
// Load via MeshImporterExporter (same path the CLI subcommand uses).
SentryReporter::addBreadcrumb("file.import",
QString("Importing %1 for VAT bake").arg(filePath));
MeshImporterExporter::importer({filePath});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clean up temporary imported entities after bake_vat

This import path adds scene nodes/entities to the live Manager state, but toolBakeVat has no cleanup on success or failure. In a long-lived MCP session, repeated calls will accumulate imported meshes, increasing memory usage and contaminating subsequent tool behavior (including which entity is selected by later scans).

Useful? React with 👍 / 👎.

Two findings from Codex on PR #568, both real:

P1 — `toolBakeVat` picked the first entity from
`Manager::getEntities()` after import, but the editor may
already have entities loaded when MCP runs alongside the GUI.
That meant `bake_vat` could bake the wrong mesh or fail with
"animation not found" even though the requested file/clip was
valid. Snapshot `getEntities()` *before* the import and pick
only the entities that newly appeared. Same pattern
`toolOptimizeMesh` already uses.

P2 — `toolBakeVat` didn't clean up the entities it imported, so
in a long-lived MCP session every bake accumulated scene state
(memory + selection contamination). Add an RAII `ImportCleanup`
struct that destroys the imported scene nodes on every exit
path. Again same pattern as `toolOptimizeMesh`.

Also wraps the importer call in try/catch so a malformed input
file surfaces as a clean MCP error instead of propagating an
exception out of the tool handler.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
src/VATBakerController.h (1)

31-71: ⚡ Quick win

Align the header contract with the actual synchronous bake flow.

The public comments still describe worker-thread orchestration and “no bakeFinished on start failure”, but the current implementation is synchronous and emits bakeFinished for most validation failures. Please update the contract text so QML callers don’t build the wrong assumptions.

💡 Suggested comment update
- *   - `bake(...)` kicks off the bake on a worker QThread so the UI
- *     doesn't freeze on long anims. Progress is emitted as
+ *   - `bake(...)` runs bake synchronously in this slice. Progress is emitted as
  *     `bakeProgress(int done, int total)` after each frame; the
  *     terminal signal is `bakeFinished(bool ok, QString posTexture,
  *     QString error)`.
@@
-    /// Start a bake on a worker thread. Returns false synchronously if
-    /// the bake can't even start (no selection, no animation, bake
-    /// already in progress); in that case `bakeFinished` is NOT
-    /// emitted. Returns true if the worker was kicked off; the result
-    /// arrives via `bakeFinished`.
+    /// Start a bake synchronously. Returns false if validation fails.
+    /// Validation failures may emit `bakeFinished(false, ...)` depending
+    /// on the failure path; successful start emits final result via
+    /// `bakeFinished(...)` before returning.
🤖 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 `@src/VATBakerController.h` around lines 31 - 71, The header comment for
VATBakerController is out of sync: it says bake(...) runs on a worker QThread
and that bakeFinished is not emitted on synchronous validation failures, but the
implementation runs synchronously and does emit bakeFinished for most validation
failures. Update the class-level documentation (around VATBakerController, and
the Q_INVOKABLE refreshAnimations / bake contract text) to state that the
current bake flow is synchronous for validation/error cases, that
bakeFinished(bool ok, QString posTexture, QString error) may be emitted even
when the bake fails to start, and clarify the true meaning of isBaking,
availableAnimations, progressDone/progressTotal and when
bakeProgress/bakeFinished are emitted so QML callers handle both sync and async
outcomes correctly.
🤖 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.

Inline comments:
In `@qml/PropertiesPanel.qml`:
- Around line 549-759: The VAT subgroup Rectangle (containing id vatCol and
bakeMa) is currently nested inside a fixed-height Row (the small 26px toolbar
Row) which causes horizontal overflow and vertical overlap; move the entire
Rectangle (the VAT block starting with "Rectangle { width: parent.width - 16 ...
}" that contains property vatCol and MouseArea bakeMa) out of that fixed-height
Row and place it as a sibling (e.g., directly inside the parent
Column/container) so it can size vertically normally and not be constrained by
the toolbar Row; ensure any anchors referencing the old Row are updated to use
the new sibling parent so enabled/disabled logic (VATBakerController.isBaking,
vatCol.outputDir, animPicker, bakeMa) continues to work.

In `@src/MCPServer.cpp`:
- Around line 4124-4168: Delta tracking and subsequent bake target selection are
fragile because Manager::getEntities() can return non-Entity movables and you
unconditionally cast and use imported.first() as the bake target; change
discovery to filter safely by checking obj->getMovableType()=="Entity" before
casting to Ogre::Entity*, build the imported list only from those safe-cast
entities (avoid relying on raw getEntities() entries that might be ManualObject
or other types), then choose the bake target by scanning that filtered list for
an entity that hasSkeleton() (and preferably also has animation clips if
available) instead of using imported.first(); update any references around
MeshImporterExporter::importer, the imported list construction, and the bake
site that currently uses entity/hasSkeleton() so the code skips non-Entity
objects and selects a valid skeletoned entity or returns an explicit error if
none found.

In `@src/MCPServer.h`:
- Around line 206-210: The MCP interface was extended by adding the
toolBakeVat(const QJsonObject &args) method, so update the MCP protocol version
constant SERVER_VERSION in MCPServer.h to a new appropriate semantic version
(e.g., bump patch or minor such as "1.7.1" or "1.8.0") so clients can detect the
capability change; find the SERVER_VERSION declaration in the same header
(symbol SERVER_VERSION) and update its string literal and any related
comments/documentation mentioning the MCP protocol version to match the new
value.

In `@src/VATBakerController_test.cpp`:
- Around line 89-93: The SetUp method in VATBakerControllerSceneTest currently
asserts tryInitOgre() but doesn't assert mesh-loading capability; add an
assertion ASSERT_TRUE(canLoadMeshFiles()) inside
VATBakerControllerSceneTest::SetUp() (alongside the existing
ASSERT_TRUE(tryInitOgre()) and before clearSelection()) so failures to load mesh
fixtures fail loudly in CI; locate the SetUp implementation to insert the call
to canLoadMeshFiles().

In `@src/VATBakerController.cpp`:
- Around line 100-123: The validation branches in bake() (checks for
animationName, outputDir, SelectionSet::getSingleton(), empty entities, and
entity->hasSkeleton()) currently emit bakeFinished and return without telemetry;
add a Sentry breadcrumb before each early exit by calling
SentryReporter::addBreadcrumb with a consistent category (e.g.,
"bake.validation") and a clear message describing the failure (e.g., "missing
animationName", "missing outputDir", "no SelectionSet", "no entity selected",
"selected entity has no skeleton") immediately before each emit
bakeFinished(false, ...) so all user-facing validation failures are tracked.
- Around line 154-157: Reset m_progressDone and m_progressTotal before emitting
the initial bakeProgress signal: set m_progressDone = 0 and m_progressTotal = 0
prior to calling emit bakeProgress(0, /*totalSentinel*/0) so QML property-bound
listeners never observe stale values; update the sequence in VATBakerController
(referencing m_progressDone, m_progressTotal, and bakeProgress) accordingly.

---

Nitpick comments:
In `@src/VATBakerController.h`:
- Around line 31-71: The header comment for VATBakerController is out of sync:
it says bake(...) runs on a worker QThread and that bakeFinished is not emitted
on synchronous validation failures, but the implementation runs synchronously
and does emit bakeFinished for most validation failures. Update the class-level
documentation (around VATBakerController, and the Q_INVOKABLE refreshAnimations
/ bake contract text) to state that the current bake flow is synchronous for
validation/error cases, that bakeFinished(bool ok, QString posTexture, QString
error) may be emitted even when the bake fails to start, and clarify the true
meaning of isBaking, availableAnimations, progressDone/progressTotal and when
bakeProgress/bakeFinished are emitted so QML callers handle both sync and async
outcomes correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb7ffb3d-bbc9-4a64-bf0b-9cdb141dd5b6

📥 Commits

Reviewing files that changed from the base of the PR and between c92e4c1 and c9b1c97.

📒 Files selected for processing (9)
  • qml/PropertiesPanel.qml
  • src/CMakeLists.txt
  • src/MCPServer.cpp
  • src/MCPServer.h
  • src/VATBakerController.cpp
  • src/VATBakerController.h
  • src/VATBakerController_test.cpp
  • src/mainwindow.cpp
  • tests/CMakeLists.txt

Comment thread qml/PropertiesPanel.qml
Comment thread src/MCPServer.cpp
Comment thread src/MCPServer.h
Comment thread src/VATBakerController_test.cpp
Comment thread src/VATBakerController.cpp
Comment thread src/VATBakerController.cpp Outdated
Six findings from CodeRabbit on PR #568 (against commit c9b1c97).
All real:

**Critical (MCP)** — `toolBakeVat` used `Manager::getEntities()`
for both delta tracking and bake-target selection. That getter
returns raw `MovableObject*` which would crash if non-Entity
attachments (gizmos, manual objects, mask overlays, ring objects)
were in the scene, and bailing on `imported.first()` could
return a false "no skeleton" when one of the *other* imported
entities had a valid one. Replace with a `collectEntitiesSafe()`
helper that walks scene nodes and filters by
`getMovableType() == "Entity"`, then pick the bake target by
scanning for `hasSkeleton()` rather than list order.

**Major (QML)** — the VAT subgroup was a child of the 26 px
horizontal `Row` that hosts the Merge Animations button, so the
panel overflowed horizontally and overlapped the next scene-tree
row vertically. Wrap the merge `Row` + the VAT `Rectangle` in a
shared `Column` so each lays out vertically.

**Major (test fixture)** — `VATBakerControllerSceneTest::SetUp()`
only asserted `tryInitOgre()`; add `canLoadMeshFiles()` so an
incomplete CI environment fails loudly at setup rather than
later in the first test step.

**Major (telemetry)** — `bake()`'s validation-failure exit paths
emitted `bakeFinished(false, …)` but no Sentry breadcrumb, leaving
gaps in the `ui.action` flow. Add a `"VAT bake refused: <reason>"`
breadcrumb at each refuse site (empty anim name, empty outputDir,
missing SelectionSet, no entity selected, no skeleton).

**Minor (MCP version)** — `bake_vat` extends the MCP tool surface;
bump `SERVER_VERSION` from 1.7.0 → 1.8.0 so clients can detect
the new capability.

**Minor (signal race)** — `bake()` emitted the initial
`bakeProgress(0, 0)` *before* resetting the bound member
variables. QML listeners bound to the `progressDone` /
`progressTotal` properties could read stale values. Reset
members first, then emit.

**Nit (header docs)** — the class docstring still described the
bake as running on a worker QThread and said `bakeFinished` is
not emitted on validation failures. Both are false in the slice 4
implementation. Rewrite the contract block + the per-method
comment to match the actual synchronous flow so QML callers
don't build wrong assumptions.

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

Copy link
Copy Markdown

@fernandotonon
fernandotonon merged commit 7d34fa7 into master May 17, 2026
20 checks passed
@fernandotonon
fernandotonon deleted the feat/vat-slice-4-inspector-mcp branch May 17, 2026 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant