Skip to content

feat(mcp): assembly mutates in shared cad_submit map - #63

Merged
8 commits merged into
jackControls:mainfrom
jeffglousher:feat/mcp-assembly-submit-map
Aug 26, 2026
Merged

feat(mcp): assembly mutates in shared cad_submit map#63
8 commits merged into
jackControls:mainfrom
jeffglousher:feat/mcp-assembly-submit-map

Conversation

@jeffglousher

@jeffglousher jeffglousher commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Stacks on feat(mcp): UI-owned inbox apply (#11 slice) #60 (feat/mcp-ui-owned-apply / nbcad-mcp-mutate) and brings in the assembly ToolSpecs from feat(mcp): host-neutral assembly component tools #62.
  • Registers the six assembly modeling mutates in the shared MUTATES map so cad_submit accepts them while attached (UI inbox apply path).
  • assembly_document / assembly_solution stay read-safe inspect tools (not inbox mutates).
  • Adds Assembly focus pack + disclosure tags; headless roundtrip + classifier coverage.

Tools added to crates/mcp-mutate (engine_method matches host.rs)

MCP name engine_method payload
assembly_create_component assembly_create_component Object
assembly_update_component assembly_update_component Object
assembly_create_occurrence assembly_create_occurrence Object
assembly_update_occurrence assembly_update_occurrence Object
assembly_set_occurrence_pose assembly_set_occurrence_pose Object
assembly_set_occurrence_grounded assembly_set_occurrence_grounded Object

Stacking / merge order

GitHub could not use feat/mcp-ui-owned-apply as base on jackControls/noBS-CAD (branch only exists on the fork / #60 head). Please merge after #60 (this branch is based on that tip). Includes #62 assembly ToolSpecs so ToolSpec ↔ mutate map stay in sync; #62 alone is not required if this lands after #60.

Do not merge before #60.

Test plan

  • cargo test -p nbcad-mcp-mutate
  • cargo test in mcp-server (42 passed), including:
    • assembly_create_component_is_in_lookup_mutate
    • assembly_create_component_accepted_by_cad_submit_classifier
    • assembly_component_occurrence_grounded_roundtrip
    • tool_spec_mutates_match_shared_inbox_map
    • every_shared_mutate_is_accepted_by_cad_submit_classifier
  • Jack: confirm merge-after-feat(mcp): UI-owned inbox apply (#11 slice) #60

@jeffglousher

Copy link
Copy Markdown
Collaborator Author

Sitrep: stacks on #60 (shared mcp-mutate / cad_submit map) and carries the assembly ToolSpecs from #62. Six assembly mutates are now in MUTATES so attached cad_submit will not reject them. Inspect-only: assembly_document, assembly_solution. Please review; do not merge before #60.

@jackControls jackControls left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Requesting changes on the current stacked head. There are two data-loss blockers and one revision-accounting issue:

  1. The carried assembly update schemas still allow destructive partial replacement: assembly_update_component requires only id/name, and assembly_update_occurrence only id/name/component_id, while serde defaults omitted ownership, transforms, parent, visibility, and grounding before the complete records are replaced. Require full replacement fields or use patch DTOs with preservation tests.

  2. For assembly and other non-solid inbox results, applyInboxNow falls back to loadDocument(). That action unconditionally writes dirty: false, so an MCP-applied live edit can be treated as saved and the user can close without an unsaved-changes warning. Refresh the affected state while preserving/setting dirty: true.

  3. The native inbox apply already increments engine_revision, then the store changes caused by applyInboxNow reach the subscription and call mcp_session_bridge_note_mutation, incrementing it again. Suppress the local mutation note for inbox-originated synchronization (and prefer a targeted refresh over the full document reload).

This also stacks on open #60 and carries #62 functionality; please rebase/deduplicate after the dependencies are corrected. I did not merge.

@jeffglousher

Copy link
Copy Markdown
Collaborator Author

Addressed Jack’s three #63 blockers (local only; not merging):

  1. Assembly update destructive partials — Cherry-picked feat(mcp): host-neutral assembly component tools #62’s b6d6543 (ComponentDefinitionPatchDto / ComponentOccurrencePatchDto). Host merge preserves omitted fields; ToolSpecs require only id. Tests: update_component_and_occurrence_patches_preserve_omitted_fields, assembly_update_component_rename_preserves_bodies_and_lcs, assembly_update_occurrence_rename_preserves_pose_parent_flags.

  2. dirty:false after inbox applyapplyInboxNow no longer calls loadDocument(). Non-solid results use refreshAfterInboxApply (assembly-targeted path) with dirty: true. Solid updates still go through applySolidUpdate (already dirty:true).

  3. Double engine_revision — While inboxApplying, the store subscription skips mcp_session_bridge_note_mutation. Native apply’s bump remains the single advance; inbox-originated UI sync does not note again.

Commits: 6e6cdfa (cherry-pick #62 patch DTOs), 19800b9 (dirty + suppress). Still stacks on #60; please re-review.

jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 22, 2026
Stack STEP import + solid move/copy ToolSpecs (jackControls#61) onto the fixed
assembly submit-map tip (jackControls#63 @ 19800b9) so cad_submit accepts
solid_import_step, solid_edit_import_step, solid_move_copy, and
solid_edit_move_copy while attached. cad_script and cad_compare_solids
stay read-safe control helpers (not inbox mutates).

Upgrade cad_script to jackControls#61 @ 52266b2 behavior: attach/refresh seeds a
cad_load_project_model baseline, and the e2e test actually replays the
dumped script on a fresh CadServer (detach after attach so jackControls#60
session_read_only still holds). Bump MODELING_TOOL_COUNT and keep
ToolSpec↔map sync.

@jackControls jackControls left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Re-reviewed at 19800b9. The latest PR-local fix looks correct: inbox refresh preserves dirty state and suppresses the duplicate revision note while apply is in progress. I cannot approve the stacked PR yet because it inherits PR60's unresolved atomic revision race and stuck conflicted/malformed head-of-queue behavior. Its MCP check is also failing on the inherited formatting diff. Please resolve/rebase those upstream blockers.

@jeffglousher
jeffglousher force-pushed the feat/mcp-assembly-submit-map branch from 19800b9 to 45324f1 Compare August 23, 2026 14:23
@jeffglousher

Copy link
Copy Markdown
Collaborator Author

Reset onto #60 tip 30785fe (cherry-picked assembly map + patch DTO + dirty/suppress). New tip: 45324f1.

@jackControls jackControls left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Re-reviewed at 45324f1. The PR-local work is sound: assembly mutates use the shared cad_submit map, dirty state remains set after inbox apply, the double revision bump is gone, and all 47 MCP-server tests pass.

This stack still inherits the two P1 coherency races from PR60: a mutation can occur between snapshot export and bridge write so stale geometry is published at the current revision, and switching retained project tabs does not rebind/advance the bridge so an op authored for tab A can dispatch through active_mut() into tab B. Assembly mutations make the second failure especially destructive. Please rebase after PR60 fixes those races and carries deterministic regression tests.

jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 25, 2026
Rebase jackControls#63 onto jackControls#60 tip f241739. Mutate map entries and ToolSpecs already
landed with jackControls#60/jackControls#62; keep unique classifier/lookup coverage so cad_submit
accepts the six assembly modeling mutates while assembly_document and
assembly_solution stay read-safe inspect tools.
@jeffglousher
jeffglousher force-pushed the feat/mcp-assembly-submit-map branch from 45324f1 to 48222e0 Compare August 25, 2026 14:33
@jeffglousher

Copy link
Copy Markdown
Collaborator Author

Rebased onto #60 tip f241739 (P1 publish + project-session races closed). Did not merge. Stayed off Jack #65/#66/#67.

New head: 48222e0

Unique vs #60 (map entries + ToolSpecs already on the #60/#62 stack):

  • refreshAfterInboxApply with dirty: true — assembly/non-solid inbox apply no longer falls back to loadDocument() (which cleared dirty)
  • dedicated assembly lookup + cad_submit classifier tests (assembly_document / assembly_solution stay inspect-only)

Tests: cargo test -p nbcad-mcp-mutate 5/5; cargo test in mcp-server 47/47.

Please re-review. Still merge-after-#60.

jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 25, 2026
Rebase jackControls#63 onto jackControls#60 tip f241739. Mutate map entries and ToolSpecs already
landed with jackControls#60/jackControls#62; keep unique classifier/lookup coverage so cad_submit
accepts the six assembly modeling mutates while assembly_document and
assembly_solution stay read-safe inspect tools.
@jeffglousher

Copy link
Copy Markdown
Collaborator Author

Rebased onto new #60 tip 5526bf0 (itself on #65/#66). Clean rebase — leftover is still just assembly cad_submit map + dirty-refresh (refreshAfterInboxApply, no loadDocument). Jack's project-tab / i18n shell untouched.

New tip: 9cb5a63. mcp-mutate 5, mcp-server 47 green. Not merging.

cad_submit writes inbox/<seq>.json; the desktop applies via host::handle
and publishes. MCP does not write model.json (no last-writer-wins).
Address Jack's three jackControls#60 blockers:

1. call_tool enforces session_read_only for direct mutates while attached
   (cad_submit / inspect / export stay allowed); writeback:true attach is
   rejected. Regression covers attach → direct mutate rejected →
   cad_submit accepted → detach restores headless mutation.

2. WindowPublisher.engine_revision is the authoritative OCC gate, advanced
   atomically with live engine apply and on immediate UI mutation notes
   (not heartbeat-debounce alone). Deterministic tests cover stale-base
   after UI note and two same-base ops.

3. Shared nbcad-mcp-mutate name→engine-method+payload map drives both
   cad_submit accept-list and Tauri inbox dispatch; unsupported names are
   rejected before enqueue; failed applies dead-letter to inbox/failed/
   so the queue cannot wedge. Table-driven coverage of accepted mutates.
Advance session engine_revision in run_ui_mutation under the publisher
lock across the live engine call so inbox OCC cannot race a later JS
note. Dead-letter conflicting and malformed inbox heads to inbox/failed/
so the queue cannot wedge; next sequence can apply. Sync jackControls#61 mutates
(move_copy/import_step) into the shared map after rebase onto main.
Reserve captures engine_revision and write rejects a snapshot if it moved during export. Bind the bridge to the active native project-session identity so an A inbox op cannot apply to B.
jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 25, 2026
Rebase jackControls#63 onto jackControls#60 tip f241739. Mutate map entries and ToolSpecs already
landed with jackControls#60/jackControls#62; keep unique classifier/lookup coverage so cad_submit
accepts the six assembly modeling mutates while assembly_document and
assembly_solution stay read-safe inspect tools.
@jeffglousher
jeffglousher force-pushed the feat/mcp-assembly-submit-map branch from 9cb5a63 to 5c2ebc7 Compare August 25, 2026 14:59
@jeffglousher

Copy link
Copy Markdown
Collaborator Author

Rebased onto new #60 tip 25bdf45 (itself on a0015f0 / #67). Clean rebase — leftover is still dirty-refresh (refreshAfterInboxApply, no loadDocument) + assembly cad_submit classifier tests. Jack's constraint-glyph / viewport shell untouched. New tip: 5c2ebc7. mcp-mutate 5, mcp-server 47 green. Not merging.

@jackControls jackControls left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the updates. I rechecked the current head (5c2ebc7) and the dirty-state correction looks good. One refresh-state issue remains, and this branch also inherits the unresolved bridge blockers from PR60:

  1. A targeted assembly refresh leaves an existing jointMotionPreview active. refreshSessionFromMcp() advances jointMotionPreviewGeneration and clears jointPreviewSolution/mechanismPreview, but does not clear the already stored jointMotionPreview. Both viewport paths prefer jointMotionPreview.solution over the newly refreshed assemblySolution, so an external assembly mutation can be applied successfully while the old motion pose continues to render and its controls remain stale. Please set jointMotionPreview: null in the assembly-refresh path and add a state-level regression test that begins with an active motion preview.

  2. This PR is stacked on the current PR60 head and therefore includes PR60's two outstanding P1 concurrency issues: the cross-tab publication reservation/write identity race and the non-atomic MCP inbox sequence allocation. Please rebase once those are corrected.

The focused build/tests pass, but they do not cover the stored-preview refresh case.

jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 25, 2026
assembly_create_joint/update return a joint DTO, so applyInboxNow no
longer falls through to loadDocument() (dirty:false). Port jackControls#63
refreshAfterInboxApply with dirty:true and clear stale jointMotionPreview.
jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 25, 2026
Drop the extra helper; applyInboxNow uses the same scene+document guard
as jackControls#63 so joint DTOs take refreshAfterInboxApply (dirty:true).
Carry reserved session_id + project_session_id on the write path so a delayed A export cannot consume B's reservation. cad_submit allocates inbox sequences with create_new so concurrent submits cannot share a seq.
Rebase jackControls#63 onto jackControls#60 tip f241739. Mutate map entries and ToolSpecs already
landed with jackControls#60/jackControls#62; keep unique classifier/lookup coverage so cad_submit
accepts the six assembly modeling mutates while assembly_document and
assembly_solution stay read-safe inspect tools.
applyInboxNow no longer falls back to loadDocument() (which cleared
dirty). Assembly ops get a targeted refreshAfterInboxApply with
dirty:true; other non-solid results refresh live state the same way.
inboxApplying still guards re-entry; native apply already owns the
engine_revision bump.
@jeffglousher
jeffglousher force-pushed the feat/mcp-assembly-submit-map branch from 5c2ebc7 to c7aba14 Compare August 25, 2026 19:24
@jeffglousher

Copy link
Copy Markdown
Collaborator Author

Rebased onto #60 5447175 (identity-bound publish write + exclusive inbox seq). Leftover still dirty-refresh + assembly classifier tests.

jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 25, 2026
assembly_create_joint/update return a joint DTO, so applyInboxNow no
longer falls through to loadDocument() (dirty:false). Port jackControls#63
refreshAfterInboxApply with dirty:true and clear stale jointMotionPreview.
jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 25, 2026
Drop the extra helper; applyInboxNow uses the same scene+document guard
as jackControls#63 so joint DTOs take refreshAfterInboxApply (dirty:true).

@jackControls jackControls left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the update—the inherited publish/inbox races and dirty-state path are now addressed. One stale-preview blocker remains: refreshAfterInboxApply invalidates jointMotionPreviewGeneration but does not clear jointMotionPreview. The viewport resolves jointMotionPreview?.solution before assemblySolution, so after an attached assembly mutation it can continue rendering the old motion pose despite the refreshed solution. Please add jointMotionPreview: null beside jointPreviewSolution: null (as the combined PR already does) and ideally cover the targeted refresh path with a regression test.

refreshAfterInboxApply invalidated jointMotionPreviewGeneration but left
the stored preview in place. Viewport prefers jointMotionPreview.solution
over assemblySolution, so an attached assembly mutate could keep rendering
the old motion pose. Clear it beside jointPreviewSolution.
@jeffglousher

Copy link
Copy Markdown
Collaborator Author

Addressed the remaining stale-preview blocker.

refreshAfterInboxApply now sets jointMotionPreview: null beside jointPreviewSolution: null on the targeted assembly path (viewport prefers jointMotionPreview?.solution over the refreshed assemblySolution). Generation is still invalidated.

Cheap regression: assembly_inbox_refresh_clears_joint_motion_preview locks the assembly inbox-refresh block in appStore.ts.

New tip: e94e27e. mcp-mutate 5/5; new source-lock + classifier tests green. Not merging until Approved.

jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 26, 2026
assembly_create_joint/update return a joint DTO, so applyInboxNow no
longer falls through to loadDocument() (dirty:false). Port jackControls#63
refreshAfterInboxApply with dirty:true and clear stale jointMotionPreview.
jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 26, 2026
Drop the extra helper; applyInboxNow uses the same scene+document guard
as jackControls#63 so joint DTOs take refreshAfterInboxApply (dirty:true).
jeffglousher added a commit to jeffglousher/noBS-CAD that referenced this pull request Aug 26, 2026
Sixth-pass: no applyInboxAll loadDocument path; leftover refreshAfterInboxApply
stays the single jackControls#63 dirty:true contract. Lock cad_refresh vs cad_load_project_model
joint parity and a part-document joint inbox typed reject.
jackControls
jackControls previously approved these changes Aug 26, 2026
@jeffglousher
jeffglousher dismissed jackControls’s stale review August 26, 2026 02:21

The merge-base changed after approval.

@jeffglousher jeffglousher closed this pull request by merging all changes into jackControls:main in df875a0 Aug 26, 2026
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.

2 participants