Skip to content

feat(#864/#865): PartOps Slices E+F — explode/join CLI+MCP parity + docs - #938

Merged
fernandotonon merged 4 commits into
masterfrom
feat/partops-slice-ef-864-865
Aug 3, 2026
Merged

feat(#864/#865): PartOps Slices E+F — explode/join CLI+MCP parity + docs#938
fernandotonon merged 4 commits into
masterfrom
feat/partops-slice-ef-864-865

Conversation

@fernandotonon

@fernandotonon fernandotonon commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

PartOps epic #859, final slices — E (#864) CLI/MCP parity for segmented
part operations, and F (#865) docs / tests / polish. Completes the epic.

Slice E — CLI/MCP parity

CLI (qtmesh segment):

qtmesh segment model.fbx --explode-parts -o scene.glb
qtmesh segment model.fbx --explode-parts --explode-distance 0.3 --solidify -o scene.glb

Splits into per-part submeshes, explodes each into its own scene node offset
outward, and exports the multi-node scene. Usage error (exit 2) without -o or
with a negative/non-numeric distance.

MCP:

Tool Args Notes
explode_mesh_parts entity_name?, distance? undoable via ExplodePartsCommand
join_mesh_parts entity_names? (omit → all mesh entities) undoable via JoinPartsCommand

Both run through the same undoable commands as the GUI buttons, are registered
as heavy tools, and map to the segmentation gamification cluster.
segment_mesh already returns face_labels unconditionally (the epic's
return_face_labels).

Descoped: --print-split / prepare_print_split (the 3D-print pegs were
removed earlier this epic — no reliable flat cut plane on organic joints, and
Meshy/Tripo ship no discrete pegs either).

Slice F — docs, tests, polish

  • New docs/PART_OPS.md — full workflow (split / explode /
    join / solidify), all three surfaces (GUI / CLI / MCP), export behaviour, and
    the documented limitations: body-centric labels, model-unit dimensions, static
    join (skeletons not reconciled), thin-shell solidify, no pegs/boolean.
  • README feature bullet + CLAUDE.md CLI/MCP/PartOps updates.
  • Sentry breadcrumbs already present: mesh.parts.{segment_preview, split_segments, explode, join}.

Tests

3 new CLI explode coverage tests (usage errors + a multi-node-scene check that
parses the exported .glb JSON chunk and asserts >1 node with outward
translations — reimport-free, so it avoids glTF same-material merging and scene
lights). Full PartOps suite 35 green. App builds clean; CLI explode verified
end-to-end on Rumba (12 parts at offsets).

Closes #864, #865.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added CLI support for exploding segmented mesh parts with configurable separation distance and multi-node export.
    • Added MCP operations to explode mesh parts and join multiple meshes.
    • Added validation, structured results, and undo support for mesh-part operations.
  • Documentation

    • Documented segmentation, splitting, exploding, joining, and solidification workflows across GUI, CLI, and MCP.
  • Tests

    • Added coverage for explosion validation, output generation, and separated part placement.

Slice E (#864) — CLI/MCP parity for segmented part operations:
- CLI `qtmesh segment <file> --explode-parts [--explode-distance <d>] [--solidify]
  -o scene.glb`: splits into per-part submeshes, then explodes each into its own
  scene node offset outward, and exports the multi-node scene via sceneExporter.
  Usage error (exit 2) without -o or with a bad distance.
- MCP `explode_mesh_parts` ({entity_name?, distance?}) and `join_mesh_parts`
  ({entity_names?} — omit to join all mesh entities), both undoable via the same
  ExplodePartsCommand / JoinPartsCommand the GUI uses; registered heavy + mapped
  to the `segmentation` gamification cluster.
- `segment_mesh` already returns `face_labels` unconditionally (the epic's
  `return_face_labels`).
- Print-split/pegs descoped (removed earlier this epic — no reliable flat cut
  plane on organic joints; Meshy/Tripo ship no discrete pegs either).

Slice F (#865) — docs/tests/polish:
- New docs/PART_OPS.md: full workflow (split/explode/join/solidify), all three
  surfaces, and the documented limitations (body-centric labels, model-unit
  dimensions, static join, thin-shell solidify, no pegs/boolean).
- README feature bullet + CLAUDE.md CLI/MCP/PartOps updates.
- Breadcrumbs already present: mesh.parts.{segment_preview,split_segments,
  explode,join}.

Tests: 3 new CLI explode coverage tests (usage errors + a multi-node-scene glb
that parses the exported .glb JSON chunk and asserts >1 node with outward
offsets — reimport-free so it doesn't hit glTF same-material merging or scene
lights). Full PartOps suite 35 green. App builds clean; CLI explode verified
end-to-end on Rumba (12 parts, offset nodes).

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

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b0fc502-68e3-4b03-8671-094d32be8293

📥 Commits

Reviewing files that changed from the base of the PR and between 4a41dc6 and a093b96.

📒 Files selected for processing (1)
  • CMakeLists.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • CMakeLists.txt

📝 Walkthrough

Walkthrough

The PR adds CLI support for exploding segmented mesh parts and MCP tools for exploding and joining mesh entities. It adds validation, undoable command execution, structured results, multi-node export coverage, PartOps documentation, and a pinned stb revision update.

Changes

PartOps interface parity

Layer / File(s) Summary
CLI part explosion
src/CLIPipeline.cpp, src/CLIPipeline_cmdsplitparts_coverage_test.cpp, CLAUDE.md
The segment command accepts --explode-parts and --explode-distance. It validates arguments, creates offset scene nodes, exports the multi-node scene, reports results, and tests error and output cases.
MCP explode and join tools
src/MCPServer.cpp, src/MCPServer.h, docs/PART_OPS.md, README.md, CLAUDE.md
MCP exposes undoable explode_mesh_parts and join_mesh_parts operations with registration, schemas, entity selection, structured results, and documentation for CLI, GUI, and MCP behavior.

Build dependency update

Layer / File(s) Summary
stb FetchContent revision
CMakeLists.txt
The pinned stb revision changes, and comments document the previous shallow-clone failure and the new revision.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPClient
  participant MCPServer
  participant PartOpsCommands
  participant Scene

  MCPClient->>MCPServer: Call explode_mesh_parts or join_mesh_parts
  MCPServer->>PartOpsCommands: Execute undoable mesh-part command
  PartOpsCommands->>Scene: Create offset nodes or join mesh entities
  Scene-->>MCPServer: Return operation counts
  MCPServer-->>MCPClient: Return structured JSON result
Loading

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR covers CLI explode and MCP explode/join, but issue #864 also requires print-split and prepare_print_split functionality. Implement the remaining print-split requirements or update issue #864 to document their removal before merging.
Out of Scope Changes check ⚠️ Warning The stb dependency pin update is unrelated to the linked PartOps objectives, and solidification is not part of issue #864 scope. Remove the unrelated dependency change or link it to a relevant issue, and confirm solidification is intentionally approved scope.
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the PartOps explode/join CLI and MCP parity work and documentation updates.
Description check ✅ Passed The description provides a detailed summary, technical changes, tests, scope decisions, and linked issue references.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/partops-slice-ef-864-865

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.

@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: 1c4ac5cf12

ℹ️ 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/CLIPipeline.cpp
Comment thread src/MCPServer.cpp Outdated
Comment thread src/MCPServer.cpp Outdated

@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: 3

🧹 Nitpick comments (1)
src/MCPServer.cpp (1)

4906-4951: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Negative distance is silently clamped instead of rejected, unlike the CLI.

args.value("distance").toDouble(0.15) followed by if (distance < 0.0) distance = 0.0; silently coerces a negative value to 0.0. The CLI counterpart (CLIPipeline.cpp, --explode-distance parsing) rejects negative values outright with Error: --explode-distance must be a non-negative number. and exit code 2. For a PR whose objective is CLI/MCP parity, the same bad input should produce the same outcome on both surfaces: either both reject it, or both document the clamp.

♻️ Proposed fix (reject to match the CLI)
         double distance = args.value("distance").toDouble(0.15);
-        if (distance < 0.0) distance = 0.0;
+        if (distance < 0.0)
+            return makeErrorResult("Error: distance must be a non-negative number");
🤖 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/MCPServer.cpp` around lines 4906 - 4951, Update
MCPServer::toolExplodeMeshParts to reject negative distance values instead of
clamping them to zero. Preserve the existing default and valid non-negative
distance behavior, and return an error matching the CLI’s non-negative distance
validation outcome before creating or pushing ExplodePartsCommand.
🤖 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 `@src/CLIPipeline.cpp`:
- Around line 10706-10711: Move the outputPath validation for explodeParts next
to the existing split-parts validation, before initOgreHeadless() and any
segmentation work, so --explode-parts without -o exits immediately with the same
error and status. Remove the redundant outputPath check from the later
explodeParts block while preserving its remaining processing.
- Around line 10706-10784: After splitEntity succeeds, remove the original scene
node owning entity before calling explodeEntity or sceneExporter, while
retaining the temporary srcNode/splitEnt needed for explosion. Ensure the
original fused entity is detached and destroyed so only the exploded part nodes
remain in the exported scene.

In `@src/MCPServer.cpp`:
- Around line 4961-4988: Update MCPServer::toolJoinMeshParts to validate every
requested entity_names entry: return an error immediately for unknown names and
reject duplicate names before adding them to names. Preserve the existing entity
filtering and automatic all-entity behavior when entity_names is absent or
empty, ensuring JoinPartsCommand receives only unique, valid entities.

---

Nitpick comments:
In `@src/MCPServer.cpp`:
- Around line 4906-4951: Update MCPServer::toolExplodeMeshParts to reject
negative distance values instead of clamping them to zero. Preserve the existing
default and valid non-negative distance behavior, and return an error matching
the CLI’s non-negative distance validation outcome before creating or pushing
ExplodePartsCommand.
🪄 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 Plus

Run ID: 127e5dd3-296d-400e-a7c5-9bfa964a429c

📥 Commits

Reviewing files that changed from the base of the PR and between 970b145 and 1c4ac5c.

📒 Files selected for processing (7)
  • CLAUDE.md
  • README.md
  • docs/PART_OPS.md
  • src/CLIPipeline.cpp
  • src/CLIPipeline_cmdsplitparts_coverage_test.cpp
  • src/MCPServer.cpp
  • src/MCPServer.h

Comment thread src/CLIPipeline.cpp Outdated
Comment thread src/CLIPipeline.cpp
Comment thread src/MCPServer.cpp
fernandotonon and others added 2 commits August 2, 2026 11:59
CI was failing on EVERY platform at the CMake configure step:
`fatal: unable to read tree (31c1ad3745…)` while cloning nothings/stb — the
pinned commit was force-pushed out of upstream, so the shallow clone couldn't
resolve it. This breaks master too, not just this PR.

Re-pin to the current upstream master head 2c980bb5…. stb_image.h's .hdr decode
API is unchanged; HDR/HdrEquirectLoader.cpp (the only consumer) compiles and the
app builds clean against the new pin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CLI --explode-parts: destroy the ORIGINAL imported source node (not just the
  temp split node) before sceneExporter, which walks every entity-bearing node —
  otherwise the exported scene overlaid an intact un-exploded mesh on top of the
  parts, doubling geometry (P1). Verified: exploded Rumba glb now has 12 meshes
  (was 13).
- CLI --explode-parts: move the -o requirement check up-front, before
  segmentation (which may download an ONNX model), matching --split-parts — so a
  missing -o fails fast (Major).
- MCP join_mesh_parts: reject an entity_names list with an UNRESOLVED name
  (previously silently joined the valid subset) or a DUPLICATE name (previously
  duplicated geometry + broke undo). Validate all resolve + are unique (Major).
- MCP explode_mesh_parts / join_mesh_parts: map to the catalogued `ai_assist`
  gamification cluster (like the sibling segment tools) instead of the
  non-existent `segmentation` key, so usage events aren't dropped (P2).

34 PartOps tests green; app builds clean.

Co-Authored-By: Claude Opus 4.8 (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: 1

🤖 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 `@CMakeLists.txt`:
- Around line 434-437: Update the comment immediately above GIT_TAG in the stb
dependency block to remove the inaccurate force-push/rebase and “dropped from
upstream” explanation, while retaining the immutable
2c980bb59875b0d32144a71867fbdebb2f77cd20 pin and documenting only the verified
CI fetch failure reason or the new pin.
🪄 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 Plus

Run ID: 5ffbd988-8677-4f32-b779-4507846f2489

📥 Commits

Reviewing files that changed from the base of the PR and between 1c4ac5c and 4a41dc6.

📒 Files selected for processing (3)
  • CMakeLists.txt
  • src/CLIPipeline.cpp
  • src/MCPServer.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/MCPServer.cpp
  • src/CLIPipeline.cpp

Comment thread CMakeLists.txt Outdated
The previous comment asserted a force-push/rebase cause that isn't verifiable;
state only what was observed (the shallow-clone "unable to read tree" failure in
CI) and that the new pin clones cleanly (CodeRabbit).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fernandotonon
fernandotonon merged commit 51e7b9f into master Aug 3, 2026
13 checks passed
@fernandotonon
fernandotonon deleted the feat/partops-slice-ef-864-865 branch August 3, 2026 03:24
@sonarqubecloud

sonarqubecloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

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.

PartOps Slice E — CLI/MCP parity for segmented part operations

1 participant