app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
← Commit history
The mechanical layer on 1.12.8 with the restart fixes (1.13.0-dev.1, add-in 1.0.23)
Merges the exe-owned mechanical layer (61 typed operations through fusion_mechanical, with
fusion_mech_capabilities and fusion_mech_operation_status) onto 1.12.8 plus the busy-add-in and
safe-restart commits. The layer touches the existing core in two places only: the "mechanical"
add-in command in addin.rs and its dispatch in main.rs. Closes keep 1.12.8's behaviour (refused
for a changed cloud document unless save or saveToClose); the dev pin's own close variant is
dropped. The dev-build changelog moves to docs/MECHANICAL-HISTORY.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
31 files changed
+26579−3
diff truncated
BRIDGE_VERSION+1−1CHANGELOG.md+22addin/AdomBridge/AdomBridge.manifest+1−1addin/AdomBridge/commands/__init__.py+2addin/AdomBridge/commands/mechanical/__init__.py+127addin/AdomBridge/commands/mechanical/assemblies.py+458addin/AdomBridge/commands/mechanical/assembly_state.py+108addin/AdomBridge/commands/mechanical/bill_of_materials.py+141addin/AdomBridge/commands/mechanical/configurations.py+326addin/AdomBridge/commands/mechanical/core.py+430addin/AdomBridge/commands/mechanical/exploded_preview.py+485addin/AdomBridge/commands/mechanical/exploded_preview_prototype.py+580addin/AdomBridge/commands/mechanical/exploded_view.py+220addin/AdomBridge/commands/mechanical/exporting.py+93addin/AdomBridge/commands/mechanical/features.py+238addin/AdomBridge/commands/mechanical/geometry.py+279addin/AdomBridge/commands/mechanical/handoff.py+68addin/AdomBridge/commands/mechanical/joint_state.py+196addin/AdomBridge/commands/mechanical/journal.py+197addin/AdomBridge/commands/mechanical/motion_links.py+157addin/AdomBridge/commands/mechanical/operations.py+242addin/AdomBridge/commands/mechanical/purchased.py+142addin/AdomBridge/commands/mechanical/readback.py+286addin/AdomBridge/commands/mechanical/regular_joints.py+488addin/AdomBridge/commands/mechanical/render_scene.py+345addin/AdomBridge/commands/mechanical/schema.json+6993addin/AdomBridge/commands/mechanical/sketch_edits.py+148addin/AdomBridge/commands/mechanical/sketches.py+234addin/AdomBridge/commands/mechanical/transactions.py+157bridge.json+4−1contracts/describe.json+13411BRIDGE_VERSION+1−1@@ -1 +1 @@-1.12.8+1.13.0-dev.1
CHANGELOG.md+22@@ -3,6 +3,28 @@ All notable changes to the Adom Bridge Fusion 360 bridge, newest first. Canonical: https://wiki.adom.inc/adom/fusion-bridge ยท GitHub backup: `adom-inc/fusion-bridge` +## Unreleased: mechanical layer, 1.13.0-dev.1 (2026-10-28)++The typed mechanical layer (`fusion_mechanical`, 61 operations; `fusion_mech_capabilities`;+`fusion_mech_operation_status`) on 1.12.8 with the restart fixes of 1.12.9. Development build, not released.+Its development history is in [docs/MECHANICAL-HISTORY.md](docs/MECHANICAL-HISTORY.md).++- The exe owns the layer's validation, durable journal and replies, as it does for the rest of the bridge; the+ add-in only runs the Fusion API work. Capabilities and operation status answer from files, so they never wait+ on Fusion's main thread.+- Readbacks the 2026-10-27 certification run needed (CSWA parts and the feature matrix, built through typed+ operations only):+ - `inspect` lists `constructionPlanes` per component, the three origin planes included, with refs. A plane+ reference can be read again after a write moves the revision, and an origin plane's ref resolves through its+ component (`originPlane` xy/xz/yz), so base planes work wherever a construction plane is accepted+ (`construction_axis` two_planes, `sketch_create`, feature tools).+ - `inspect_entity` on a sketch returns its `frame` (origin, x, y and normal in component space) and each+ profile's `centroidComponent` next to its sketch-space centroid.+ - Parameter rows name their `kind` (model or user) and the `owner` that made a model parameter (feature,+ dimension or joint, with its role), so `feature_edit` no longer needs a parameter-list diff.+ - `material_list` without `libraryId` searches the document's materials and then every loaded library; each+ row names the `libraryId` that `material_assign` needs.+ ## 1.12.9 (unreleased) The bridge no longer restarts Fusion over a busy add-in, and a restart it does need no longer kills Fusion over
addin/AdomBridge/AdomBridge.manifest+1−1@@ -6,7 +6,7 @@ "description": { "": "Adom Desktop Conduit bridge add-in. Exposes a localhost HTTP API for external automation of Fusion 360." },- "version": "1.0.20",+ "version": "1.0.23", "runOnStartup": true, "supportedOS": "windows" }
addin/AdomBridge/commands/__init__.py+2@@ -65,8 +65,10 @@ from .silkscreen_capture import handle_take_silkscreen_screenshot from .modeling import handle_run_modeling_script from .assembly_bom import handle_assembly_bom from .physical_properties import handle_physical_properties+from .mechanical import handle_mechanical COMMAND_HANDLERS = {+ "mechanical": handle_mechanical, "activity_banner": handle_activity_banner, # App state "get_app_state": handle_get_app_state,
addin/AdomBridge/commands/mechanical/__init__.pyadded+127@@ -0,0 +1,127 @@+"""Typed mechanical operations, executed for the bridge exe.++The exe (rust/bridge/src/mech.rs) validates every request against schema.json, admits and journals+writes, and serves capabilities and status itself. This package runs the Fusion API work on the main+thread. A write carries the exe's dispatch: it runs only if this add-in claims the dispatch first,+and its exact result is also left for the exe in case the reply is lost (handoff.py).+"""+import json+from pathlib import Path+import threading++import adsk.core++from .core import SESSION_ID, MechanicalError, document_identity, require+from .operations import OPERATIONS, WRITES+from .journal import Journal, JournalError+from . import handoff, transactions, sketches, features, assemblies, purchased, bill_of_materials, geometry, sketch_edits, regular_joints, motion_links, configurations, render_scene, exploded_view, exploded_preview++for _name, _handler in {**sketches.OPERATIONS, **features.OPERATIONS, **assemblies.OPERATIONS,+ **geometry.OPERATIONS, **sketch_edits.OPERATIONS, **regular_joints.OPERATIONS, **motion_links.OPERATIONS}.items():+ OPERATIONS[_name] = lambda app, args, fn=_handler: transactions.execute(app, args, fn)+ WRITES.add(_name)+OPERATIONS.update(assemblies.READS)+OPERATIONS.update(geometry.READS)+OPERATIONS.update(sketch_edits.READS)+OPERATIONS.update(configurations.READS)+OPERATIONS.update(render_scene.READS)+OPERATIONS.update(exploded_view.READS)+# Presentation effects have explicit ownership/disposal, not model PTransaction.+for _name, _handler in {**exploded_preview.WRITES, **exploded_preview.READS}.items():+ OPERATIONS[_name] = lambda app, args, fn=_handler: fn(app, args, _history)+WRITES.update(exploded_preview.WRITES)+OPERATIONS.update(purchased.OPERATIONS)+OPERATIONS.update(bill_of_materials.OPERATIONS)+WRITES.update(purchased.WRITES | bill_of_materials.WRITES)+OPERATIONS["transaction_probe"] = transactions.probe+WRITES.add("transaction_probe")++SOURCE_VERSION = json.loads((Path(__file__).resolve().parents[2] / "AdomBridge.manifest").read_text(encoding="utf-8"))["version"]+SCHEMA = json.loads((Path(__file__).with_name("schema.json")).read_text(encoding="utf-8"))+++class _Proofs:+ """Exploded previews prove earlier writes from the exe's journal (read-only here)."""+ def __init__(self):+ self.journal = Journal()++ def store(self):+ return self.journal+++_history = _Proofs()+++def error_result(error):+ if isinstance(error, MechanicalError):+ return {"success": False, "sessionId": SESSION_ID, "errorCode": error.code,+ "error": str(error), **error.details}+ if isinstance(error, JournalError):+ return {"success": False, "sessionId": SESSION_ID, "errorCode": error.code, "error": str(error)}+ return {"success": False, "sessionId": SESSION_ID, "errorCode": "mechanical_failed",+ "error": str(error), "recovery": {"verified": False, "strategy": "none"}}+++def observed_document(app):+ require(threading.current_thread() is threading.main_thread(), "wrong_thread", "Document observation requires the Fusion main thread.")+ doc = app.activeDocument+ if doc is None:+ return None+ data_file = doc.dataFile+ return {"documentId": document_identity(doc), "creationId": doc.creationId,+ "dataFileId": data_file.id if data_file is not None else None}+++def publish_session():+ handoff.publish_session(SESSION_ID, SOURCE_VERSION, transactions.capability())+++transactions.on_probe = publish_session+++def handle_mechanical(app, args):+ dispatch = args.pop("_dispatch", None) if isinstance(args, dict) else None+ op = args.get("op") if isinstance(args, dict) else None+ if op not in OPERATIONS:+ return error_result(MechanicalError("unsupported_operation", "Discover supported operations with fusion_mech_capabilities."))+ if not (op in WRITES or (op == "health" and args.get("recompute", False))):+ try:+ return {"success": True, "sessionId": SESSION_ID, "operation": op, "data": OPERATIONS[op](app, args)}+ except Exception as error:+ return error_result(error)+ dispatch_id = handoff.dispatch_id(dispatch)+ if dispatch_id is None:+ return error_result(MechanicalError("dispatch_required", "Writes are validated and journaled by the bridge exe; send them through fusion_mechanical."))+ try:+ require(args.get("sessionId") == SESSION_ID, "session_mismatch", "Old-session history is readable, but native execution requires the current add-in session.")+ document = observed_document(app)+ require(handoff.claim(dispatch_id, document, args), "dispatch_expired", "The bridge gave up on this dispatch before it started; it was not executed.")+ except Exception as error:+ return {"success": False, "dispatchId": dispatch_id, "nativeExecutionStarted": False, "result": error_result(error)}+ try:+ result = {"success": True, "sessionId": SESSION_ID, "operation": op, "data": OPERATIONS[op](app, args)}+ except Exception as error:+ result = error_result(error)+ result["operationId"] = args["operationId"]+ try:+ handoff.publish_result(dispatch_id, result)+ except (OSError, TypeError, ValueError):+ pass # the reply below still carries it+ return {"success": result["success"], "dispatchId": dispatch_id, "nativeExecutionStarted": True,+ "observedDocument": document, "result": result}+++def _publish_at_load():+ """Inside Fusion the session is published as the add-in loads; offline tests have no app."""+ try:+ if adsk.core.Application.get() is None:+ return+ except AttributeError:+ return+ try:+ publish_session()+ except Exception:+ pass # the exe then answers writes with session_mismatch; reads are unaffected+++_publish_at_load()
addin/AdomBridge/commands/mechanical/assemblies.pyadded+458@@ -0,0 +1,458 @@+"""Typed component definitions, placements, joint motion and interference."""+import math++import adsk.core+import adsk.fusion++from .core import (identity, items, number, point_mm, ref, require, resolve, same, target)+from .assembly_state import GROUP, MOTION_SCALES, joint_data, joint_motion, metadata, rigid_group_data+from .sketches import collection, component, named+++def hybrid(design):+ enum = getattr(adsk.fusion, "DesignIntentTypes", None)+ require(enum is not None and hasattr(design, "designIntent"), "unsupported_capability", "This operation requires an inspectable Hybrid design intent.")+ require(design.designIntent == enum.HybridDesignIntentType, "hybrid_design_required", "Internal components require Hybrid design intent. Create a new Hybrid design; existing intent is never changed implicitly.")+++def transform(spec=None):+ spec = spec or {}+ matrix = adsk.core.Matrix3D.create()+ angle = number(spec.get("angle", 0), "angle")+ axis = spec.get("axis", [0, 0, 1])+ vector = adsk.core.Vector3D.create(*axis)+ require(vector.length > 1e-9, "invalid_input", "Rotation axis must be nonzero.")+ require(matrix.setToRotation(math.radians(angle), vector, adsk.core.Point3D.create(0, 0, 0)), "invalid_transform", "Fusion refused rotation transform.")+ matrix.translation = adsk.core.Vector3D.create(*[number(n, "position") / 10 for n in spec.get("position", [0, 0, 0])])+ return matrix+++def occurrence_data(occ, value):+ matrix = list(occ.transform2.asArray())+ for index in (3, 7, 11):+ matrix[index] *= 10+ return {"ref": ref(occ, value), "component": {"ref": ref(occ.component, value), "id": identity(occ.component), **metadata(occ.component)},+ "path": occ.fullPathName, "transform": matrix, "transformLayout": "row-major", "translationUnit": "mm",+ "space": "assembly_context", "grounded": occ.isGrounded, "groundedToParent": occ.isGroundToParent}+++def require_transform(actual, expected, message):+ require_transform_values(list(actual.asArray()), list(expected.asArray()), message)+++def require_transform_values(actual_values, expected_values, message):+ require(len(actual_values) == len(expected_values) == 16+ and all(math.isfinite(a) and math.isfinite(b) and abs(a-b) < 1e-8+ for a, b in zip(actual_values, expected_values)), "placement_refused", message)+++def created_occurrence_contexts(design, parent, occurrence):+ """Find placed root proxies without reading a definition-local token."""+ if same(parent, design.rootComponent):+ return [occurrence]+ native = getattr(occurrence, "nativeObject", None) or occurrence+ all_occurrences = items(design.rootComponent.allOccurrences)+ contexts = [o for o in all_occurrences if same(getattr(o, "nativeObject", None) or o, native)]+ if not contexts:+ parents = [o for o in all_occurrences if same(o.component, parent)]+ contexts = [native.createForAssemblyContext(o) for o in parents]+ require(contexts and all(o is not None and o.isValid for o in contexts), "occurrence_context_failed", "Could not obtain placed root-context instances for the new occurrence.")+ return contexts+++def context_placements(design, parent, contexts, expected):+ local_values = list(expected.asArray())+ require_transform_values(local_values, local_values, "Requested transform must be a complete finite matrix.")+ if same(parent, design.rootComponent):+ return [(context, local_values) for context in contexts]+ parents_by_path = {}+ for candidate in items(design.rootComponent.allOccurrences):+ if same(candidate.component, parent):+ parents_by_path.setdefault(candidate.fullPathName, []).append(candidate)+ placements, seen_parents = [], set()+ for context in contexts:+ parent_context = context.assemblyContext+ parent_matches = parents_by_path.get(parent_context.fullPathName, []) if parent_context is not None else []+ require(parent_context is not None and same(parent_context.component, parent)+ and len(parent_matches) == 1 and same(parent_matches[0], parent_context),+ "occurrence_context_failed", "New occurrence's parent must identify an exact root-context instance.")+ require(parent_context.fullPathName not in seen_parents, "occurrence_context_failed", "New occurrence has duplicate parent contexts.")+ seen_parents.add(parent_context.fullPathName)+ parent_values = list(parent_context.transform2.asArray())+ require_transform_values(parent_values, parent_values, "Parent transform must be a complete finite matrix.")+ # Row-major matrices: parent root pose composed with requested local pose.+ world_values = [sum(parent_values[row*4+k] * local_values[k*4+column] for k in range(4))+ for row in range(4) for column in range(4)]+ placements.append((context, world_values))+ require(seen_parents == set(parents_by_path), "occurrence_context_failed", "New occurrence must resolve in every placed parent instance.")+ return placements+++def place_created_occurrence(design, parent, occurrence, expected):+ # Paste New may leave a pending pose and reused parent instances can inherit+ # the source pose. Transform overrides must use root proxies, never the local+ # occurrence. Apply every instance and capture once before shared recompute.+ contexts = created_occurrence_contexts(design, parent, occurrence)+ for context, values in context_placements(design, parent, contexts, expected):+ matrix = expected+ if not same(parent, design.rootComponent):+ matrix = adsk.core.Matrix3D.create()+ require(matrix.setWithArray(values), "placement_failed", "Could not construct the requested root-context placement.")+ context.transform2 = matrix+ require_transform_values(list(context.transform2.asArray()), values, "Fusion refused the requested occurrence transform.")+ captured = False+ if design.snapshots.hasPendingSnapshot:+ require(design.snapshots.add() is not None, "placement_failed", "Could not capture the new occurrence positions.")+ captured = True+ return captured+++def created_occurrence_result(design, parent, occurrence, value, expected=None):+ """Return and verify every placed instance in root assembly coordinates.++ The local transform can retain its base pose even when captured positions+ are correct. Verify the computed root proxies, each against its exact parent.+ """+ contexts = created_occurrence_contexts(design, parent, occurrence)+ if expected is not None:+ for context, values in context_placements(design, parent, contexts, expected):+ require_transform_values(list(context.transform2.asArray()), values,+ "Recompute did not retain the requested placement in every parent instance.")+ rows = [occurrence_data(o, value) for o in contexts]+ return {"occurrence": rows[0], "affectedOccurrences": rows,+ "placementSpace": "parent_component", "referenceSpace": "root_assembly",+ "placementScope": "all_current_parent_instances", "editScope": "parent_component_definition"}+++def parent_component(design, value, args):+ return component(design, value, {"component": args["parent"]} if "parent" in args else {})+++def component_create(design, value, args):+ hybrid(design)+ parent = parent_component(design, value, args)+ expected = transform(args.get("transform"))+ occ = parent.occurrences.addNewComponent(expected)+ require(occ and occ.isValid, "creation_failed", "Component creation failed.")+ named(occ.component, args)+ if args.get("metadata"):+ apply_metadata(occ.component, args["metadata"])+ captured = place_created_occurrence(design, parent, occ, expected)+ return lambda fresh: {**created_occurrence_result(design, parent, occ, fresh, expected), "positionCaptured": captured}+++def occurrence_insert(design, value, args):+ hybrid(design)+ parent = parent_component(design, value, args)+ comp = resolve(design, value, args["component"], ("Component",))+ require(not same(comp, design.rootComponent), "invalid_component", "Root component cannot be instanced.")+ # Avoid cyclic containment before asking the kernel to insert.+ descendants = [comp] + [o.component for o in items(comp.allOccurrences)]+ require(not any(same(parent, c) for c in descendants), "cyclic_assembly", "A component cannot contain itself or its ancestor.")+ method = parent.occurrences.addNewComponentCopy if args.get("independentCopy", False) else parent.occurrences.addExistingComponent+ expected = transform(args.get("transform"))+ occ = method(comp, expected)+ require(occ and occ.isValid, "creation_failed", "Occurrence insertion failed.")+ captured = place_created_occurrence(design, parent, occ, expected)+ return lambda fresh: {**created_occurrence_result(design, parent, occ, fresh, expected), "positionCaptured": captured}+++def occurrence_update(design, value, args):+ require(any(k in args for k in ("transform", "grounded", "groundedToParent")), "invalid_input", "Occurrence update requires a placement or grounding change.")+ occ = resolve(design, value, args["occurrence"], ("Occurrence",))+ if "grounded" in args:+ occ.isGrounded = args["grounded"]+ if "groundedToParent" in args:+ occ.isGroundToParent = args["groundedToParent"]+ if "transform" in args:+ require(not occ.isGrounded and not occ.isGroundToParent, "grounded_occurrence", "Unground the occurrence explicitly before moving it.")+ expected = transform(args["transform"])+ occ.transform2 = expected+ actual = occ.transform2.asArray()+ require(all(abs(a-b) < 1e-8 for a,b in zip(actual, expected.asArray())), "placement_refused", "Constraints prevented the requested occurrence transform.")+ if design.snapshots.hasPendingSnapshot:+ require(design.snapshots.add() is not None, "placement_failed", "Could not capture occurrence position in the timeline.")+ require("grounded" not in args or occ.isGrounded == args["grounded"], "grounding_refused", "Grounding did not match the request.")+ require("groundedToParent" not in args or occ.isGroundToParent == args["groundedToParent"], "grounding_refused", "Parent grounding did not match the request.")+ def result(fresh):+ if "transform" in args:+ expected = transform(args["transform"])+ require(all(abs(a-b) < 1e-8 for a,b in zip(occ.transform2.asArray(), expected.asArray())),+ "placement_refused", "Recompute did not retain the requested occurrence transform.")+ require("grounded" not in args or occ.isGrounded == args["grounded"], "grounding_refused", "Recompute did not retain grounding.")+ require("groundedToParent" not in args or occ.isGroundToParent == args["groundedToParent"], "grounding_refused", "Recompute did not retain parent grounding.")+ return {"occurrence": occurrence_data(occ, fresh)}+ return result+++def occurrence_delete(design, value, args):+ occ = resolve(design, value, args["occurrence"], ("Occurrence",))+ path = occ.fullPathName+ require(occ.deleteMe(), "delete_failed", "Fusion refused occurrence deletion.")+ return {"deletedPath": path}+++def apply_metadata(comp, changes):+ for key in ("name", "partNumber", "description"):+ if key in changes:+ setattr(comp, key, changes[key])+ import json+ for key in changes.keys() - {"name", "partNumber", "description"}:+ require(comp.attributes.add(GROUP, key, json.dumps(changes[key], sort_keys=True)) is not None, "metadata_failed", "Could not persist " + key)+++def component_metadata(design, value, args):+ comp = component(design, value, args)+ apply_metadata(comp, args["changes"])+ return lambda fresh: {"component": {"ref": ref(comp, fresh), **metadata(comp)}}+++def require_root_context(design, entity):+ context = entity if entity.objectType.endswith("::Occurrence") else getattr(entity, "assemblyContext", None)+ if context is not None:+ require(any(same(o, context) and o.fullPathName == context.fullPathName for o in items(design.rootComponent.allOccurrences)),+ "root_context_required", "Assembly analysis and joints require a root-context occurrence reference from inspect.")+ else:+ owner = getattr(entity, "parentComponent", None) or getattr(entity, "component", None)+ if hasattr(entity, "body"):+ owner = entity.body.parentComponent+ if hasattr(entity, "parentSketch"):+ owner = entity.parentSketch.parentComponent+ require(owner is not None and same(owner, design.rootComponent), "occurrence_context_required", "Non-root geometry requires a specific occurrence-context reference.")+++def joint_geometry(design, value, raw):+ entity = resolve(design, value, raw["entity"], ("SketchPoint", "ConstructionPoint", "BRepVertex", "BRepEdge", "BRepFace"))+ require_root_context(design, entity)+ kind = entity.objectType.split("::")[-1]+ default = "middle" if kind == "BRepFace" and entity.geometry.objectType.endswith(("::Cylinder", "::Cone")) else "center"+ key = getattr(adsk.fusion.JointKeyPointTypes, {"center": "CenterKeyPoint", "start": "StartKeyPoint", "middle": "MiddleKeyPoint", "end": "EndKeyPoint"}[raw.get("keyPoint", default)])+ if kind in ("SketchPoint", "ConstructionPoint", "BRepVertex"):+ result = adsk.fusion.JointGeometry.createByPoint(entity)+ elif kind == "BRepEdge":+ result = adsk.fusion.JointGeometry.createByCurve(entity, key)+ elif entity.geometry.objectType.endswith("::Plane"):+ result = adsk.fusion.JointGeometry.createByPlanarFace(entity, None, key)+ else:+ result = adsk.fusion.JointGeometry.createByNonPlanarFace(entity, key)+ require(result is not None, "invalid_joint_geometry", "Fusion could not infer the joint origin.")+ return result+++def set_limits(motion, name, values, scale):+ require(hasattr(motion, name), "wrong_joint_type", "Joint does not support " + name)+ limits = getattr(motion, name)+ minimum = values.get("minimum", limits.minimumValue * scale)+ maximum = values.get("maximum", limits.maximumValue * scale)+ minimum_enabled = values.get("minimumEnabled", "minimum" in values or limits.isMinimumValueEnabled)+ maximum_enabled = values.get("maximumEnabled", "maximum" in values or limits.isMaximumValueEnabled)+ require(not (minimum_enabled and maximum_enabled) or minimum <= maximum, "invalid_limits", "Minimum joint limit must not exceed maximum.")+ rest = values.get("rest", limits.restValue * scale)+ rest_enabled = values.get("restEnabled", "rest" in values or limits.isRestValueEnabled)+ require(not rest_enabled or (not minimum_enabled or rest >= minimum) and (not maximum_enabled or rest <= maximum), "invalid_limits", "Rest position must lie within enabled limits.")+ limits.isMinimumValueEnabled = False+ limits.isMaximumValueEnabled = False+ for key in ("minimum", "maximum", "rest"):+ if key in values:+ setattr(limits, key + "Value", number(values[key], key) / scale)+ limits.isMinimumValueEnabled = minimum_enabled+ limits.isMaximumValueEnabled = maximum_enabled+ limits.isRestValueEnabled = rest_enabled+++def configure_joint(design, joint, args):+ if "name" in args:+ joint.name = args["name"]+ if "suppressed" in args:+ joint.isSuppressed = args["suppressed"]+ motion = joint_motion(joint)+ for prefix, scale in MOTION_SCALES.items():+ if prefix + "Limits" in args:+ set_limits(motion, prefix + "Limits", args[prefix + "Limits"], scale)+ if prefix + "Value" in args:+ require(hasattr(motion, prefix + "Value"), "wrong_joint_type", "Joint does not support " + prefix)+ number_value = number(args[prefix + "Value"], prefix + "Value") / scale+ limits = getattr(motion, prefix + "Limits")+ require(not limits.isMinimumValueEnabled or number_value >= limits.minimumValue, "joint_limit_exceeded", "Drive value is below minimum.")+ require(not limits.isMaximumValueEnabled or number_value <= limits.maximumValue, "joint_limit_exceeded", "Drive value is above maximum.")+ setattr(motion, prefix + "Value", number_value)+ # Joint value and suppression setters alter pending assembly state. Capture+ # once, after all requested changes, so computeAll replays that state instead+ # of resetting it. Rest values and limit flags stay intact.+ captured = False+ if ("suppressed" in args or any(prefix + "Value" in args for prefix in MOTION_SCALES)) and design.snapshots.hasPendingSnapshot:+ require(design.snapshots.add() is not None, "joint_capture_failed", "Could not capture the edited assembly state.")+ captured = True+ return captured+++def joint_result(joint, value, args):+ result = joint_data(joint, value)+ for field in ("name", "suppressed"):+ if field in args:+ require(result[field] == args[field], "joint_edit_refused", "Joint did not retain requested " + field + ".")+ for prefix in MOTION_SCALES:+ for suffix in ("Value", "Limits"):+ key = prefix + suffix+ if key in args:+ require(key in result, "joint_readback_failed", "Joint motion readback is unavailable.")+ if suffix == "Value":+ require(math.isclose(result[key], args[key], rel_tol=1e-8, abs_tol=1e-6), "joint_drive_refused", "Joint did not reach the requested drive value.")+ else:+ expected_limits = dict(args[key])+ for field in ("minimum", "maximum", "rest"):+ if field in expected_limits and field + "Enabled" not in expected_limits:+ expected_limits[field + "Enabled"] = True+ for field, expected in expected_limits.items():+ actual = result[key][field]+ require(actual == expected if isinstance(expected, bool) else math.isclose(actual, expected, rel_tol=1e-8, abs_tol=1e-6), "joint_limits_refused", "Joint limit did not read back: " + field)+ return {"joint": result}+++def rigid_group_result(group, value, args):+ result = rigid_group_data(group, value)+ for field in ("name", "suppressed"):+ if field in args:+ require(result[field] == args[field], "rigid_group_edit_refused", "Rigid group did not retain requested " + field + ".")+ return {"rigidGroup": result}+++def joint_create(design, value, args):+ first, second = [resolve(design, value, args[key], ("Occurrence",)) for key in ("occurrenceOne", "occurrenceTwo")]+ require(not same(first, second), "invalid_joint", "Joint requires two distinct occurrences.")+ for occurrence in (first, second):+ require_root_context(design, occurrence)+ geometry = joint_geometry(design, value, args["geometry"]) if args["type"] != "rigid" else None+ group = design.rootComponent.asBuiltJoints+ inp = group.createInput(first, second, geometry)+ method = getattr(inp, {"rigid": "setAsRigidJointMotion", "revolute": "setAsRevoluteJointMotion", "slider": "setAsSliderJointMotion", "cylindrical": "setAsCylindricalJointMotion", "pin_slot": "setAsPinSlotJointMotion", "planar": "setAsPlanarJointMotion", "ball": "setAsBallJointMotion"}[args["type"]])+ direction = getattr(adsk.fusion.JointDirections, args.get("axis", "z").upper() + "AxisJointDirection")+ if args["type"] == "rigid":+ ok = method()+ elif args["type"] == "ball":+ ok = method(adsk.fusion.JointDirections.ZAxisJointDirection, adsk.fusion.JointDirections.XAxisJointDirection)+ elif args["type"] == "pin_slot":+ require(args.get("axis", "z") != args.get("slideAxis", "x"), "invalid_joint_axes", "Pin-slot rotation and slide directions must differ.")+ ok = method(direction, getattr(adsk.fusion.JointDirections, args.get("slideAxis", "x").upper() + "AxisJointDirection"))+ else:+ ok = method(direction)+ require(ok, "joint_failed", "Joint motion definition failed.")+ joint = group.add(inp)+ require(joint and joint.isValid, "joint_failed", "As-built joint creation failed.")+ captured = configure_joint(design, joint, args)+ return lambda fresh: {**joint_result(joint, fresh, args), "positionCaptured": captured}+++def joint_edit(design, value, args):+ require(any(k in args for k in ("name", "suppressed")) or any(prefix + suffix in args for prefix in MOTION_SCALES for suffix in ("Limits", "Value")), "invalid_input", "Joint edit requires a change.")+ joint = resolve(design, value, args["joint"], ("Joint", "AsBuiltJoint"))+ from .motion_links import coupling_snapshot, verify_coupling+ driven = any(prefix + "Value" in args for prefix in MOTION_SCALES)+ coupling = coupling_snapshot(design) if driven else []+ captured = configure_joint(design, joint, args)+ return lambda fresh: {**joint_result(joint, fresh, args), "positionCaptured": captured,+ "motionLinksVerified": verify_coupling(coupling)}+++def joint_delete(design, value, args):+ joint = resolve(design, value, args["joint"], ("Joint", "AsBuiltJoint"))+ name = joint.name+ require(joint.deleteMe(), "delete_failed", "Fusion refused joint deletion.")+ return {"deletedJoint": name}+++def rigid_group_create(design, value, args):+ occurrences = [resolve(design, value, raw, ("Occurrence",)) for raw in args["occurrences"]]+ require(len({identity(o) for o in occurrences}) == len(occurrences), "duplicate_selection", "Rigid group occurrences must be distinct.")+ for occurrence in occurrences:+ require_root_context(design, occurrence)+ group = design.rootComponent.rigidGroups.add(collection(occurrences), args.get("includeChildren", True))+ named(group, args)+ return lambda fresh: rigid_group_result(group, fresh, args)+++def rigid_group_edit(design, value, args):+ require("name" in args or "suppressed" in args, "invalid_input", "Rigid group edit requires a change.")+ group = resolve(design, value, args["group"], ("RigidGroup",))+ if "name" in args:+ group.name = args["name"]+ if "suppressed" in args:+ group.isSuppressed = args["suppressed"]+ captured = False+ if "suppressed" in args and design.snapshots.hasPendingSnapshot:+ require(design.snapshots.add() is not None, "rigid_group_capture_failed", "Could not capture the edited rigid group state.")+ captured = True+ return lambda fresh: {**rigid_group_result(group, fresh, args), "positionCaptured": captured}+++def rigid_group_delete(design, value, args):+ group = resolve(design, value, args["group"], ("RigidGroup",))+ name = group.name+ require(group.deleteMe(), "delete_failed", "Fusion refused rigid group deletion.")+ return {"deletedRigidGroup": name}+++def assembly_properties(app, args):+ _, design, value = target(app, args)+ entity = resolve(design, value, args["entity"], ("Component", "Occurrence", "BRepBody")) if args.get("entity") else design.rootComponent+ props = entity.getPhysicalProperties(adsk.fusion.CalculationAccuracy.HighCalculationAccuracy)+ volume = props.volume * 1000+ is_occurrence = entity.objectType.endswith("::Occurrence")+ return {"target": value, "entity": ref(entity, value), "mass": props.mass, "volume": volume,+ "area": props.area * 100, "centerOfMass": point_mm(props.centerOfMass),+ "averageDensity": props.mass / volume if volume else None,+ "units": {"mass": "kg", "volume": "mm3", "area": "mm2", "centerOfMass": "mm", "averageDensity": "kg/mm3"},+ "scope": "body" if entity.objectType.endswith("::BRepBody") else "component_subtree",+ "space": "assembly_context" if is_occurrence or getattr(entity, "assemblyContext", None) else "component",+ "materialBasis": "Fusion assigned/default material; supplier provenance does not certify modeled material density"}+++def interference(app, args):+ _, design, value = target(app, args)+ entities = [resolve(design, value, r, ("Occurrence", "BRepBody")) for r in args["entities"]]+ selected_bodies = set()+ walked = 0+ def body_instances(entity, depth=0):+ nonlocal walked+ walked += 1+ require(depth <= 100 and walked <= 20000, "inspection_budget", "Interference assembly exceeds traversal budget.")+ if entity.objectType.endswith("::BRepBody"):+ native = getattr(entity, "nativeObject", None) or entity+ context = getattr(entity, "assemblyContext", None)+ return {(identity(native), context.fullPathName if context else "<root>")}+ result = set()+ for body in items(entity.bRepBodies):+ walked += 1+ require(walked <= 20000, "inspection_budget", "Interference assembly exceeds shared body traversal budget.")+ native = getattr(body, "nativeObject", None) or body+ result.add((identity(native), entity.fullPathName))+ for child in items(entity.childOccurrences):+ result.update(body_instances(child, depth + 1))+ require(len(result) <= 20000, "inspection_budget", "Interference assembly exceeds body budget.")+ return result+ for index, entity in enumerate(entities):+ require(not any(same(entity, earlier) for earlier in entities[:index]), "duplicate_selection", "Interference entities must be distinct.")+ require_root_context(design, entity)+ instances = body_instances(entity)+ require(not selected_bodies.intersection(instances), "overlapping_selection", "Selections include the same physical body instance more than once; choose disjoint occurrences/bodies.")+ selected_bodies.update(instances)+ require(len(selected_bodies) <= 20000, "inspection_budget", "Interference selection exceeds physical body instance budget.")+ inp = design.createInterferenceInput(collection(entities))+ inp.areCoincidentFacesIncluded = args.get("includeTouching", False)+ results = design.analyzeInterference(inp)+ require(results is not None, "interference_failed", "Fusion did not return an interference result.")+ rows = []+ for result in items(results):+ body = result.interferenceBody+ volume = body.volume * 1000 if body else 0.0+ rows.append({"entityOne": ref(result.entityOne, value), "entityTwo": ref(result.entityTwo, value),+ "volume": volume, "classification": "overlap" if volume > args.get("volumeTolerance", 1e-6) else "touching"})+ return {"target": value, "complete": True, "results": rows, "volumeUnit": "mm3", "bodiesCreated": False}+++OPERATIONS = {"component_create": component_create, "occurrence_insert": occurrence_insert,+ "occurrence_update": occurrence_update, "occurrence_delete": occurrence_delete,+ "component_metadata": component_metadata, "joint_create": joint_create, "joint_edit": joint_edit,+ "joint_delete": joint_delete, "rigid_group_create": rigid_group_create,+ "rigid_group_edit": rigid_group_edit, "rigid_group_delete": rigid_group_delete}+READS = {"interference": interference, "assembly_properties": assembly_properties}
addin/AdomBridge/commands/mechanical/assembly_state.pyadded+108@@ -0,0 +1,108 @@+"""Assembly readback shared by inspection, stale guards and recovery witnesses."""+import json+import math+import adsk.fusion++from .core import concrete, identity, items, ref, require++GROUP = "AdomMechanical"+MOTION_SCALES = {"rotation": 180 / math.pi, "slide": 10, "primarySlide": 10, "secondarySlide": 10,+ "pitch": 180 / math.pi, "roll": 180 / math.pi, "yaw": 180 / math.pi}+++def attributes(entity):+ attrs = getattr(entity, "attributes", None)+ return {attr.name: attr.value for attr in items(attrs) if attr.groupName == GROUP} if attrs else {}+++def metadata(component):+ raw = attributes(component)+ result = {"name": component.name, "partNumber": getattr(component, "partNumber", ""),+ "description": getattr(component, "description", ""), "attributes": raw}+ for name in ("supplier", "supplierPartNumber", "sourceUrl", "sourceFile", "sha256", "asPurchased", "excludeFromBom"):+ if name in raw:+ result[name] = json.loads(raw[name])+ material = getattr(component, "material", None)+ result["material"] = {"id": material.id, "name": material.name} if material else None+ return result+++def limits_data(limits, scale):+ return {"minimumEnabled": limits.isMinimumValueEnabled, "minimum": limits.minimumValue * scale,+ "maximumEnabled": limits.isMaximumValueEnabled, "maximum": limits.maximumValue * scale,+ "restEnabled": limits.isRestValueEnabled, "rest": limits.restValue * scale}+++def joint_motion(joint):+ joint = concrete(joint)+ base = joint.jointMotion+ require(base is not None, "unsupported_inferred_joint", "Inferred joint motion is unavailable; redefine it in Fusion before typed edits.")+ kind = base.objectType.split("::")[-1]+ require(kind in ("RigidJointMotion", "RevoluteJointMotion", "SliderJointMotion", "CylindricalJointMotion",+ "PinSlotJointMotion", "PlanarJointMotion", "BallJointMotion"), "unsupported_joint_motion", "Cannot inspect joint motion type: " + kind)+ cls = getattr(adsk.fusion, kind, None)+ require(cls is not None, "unsupported_joint_motion", "Fusion motion wrapper is unavailable: " + kind)+ motion = cls.cast(base)+ require(motion is not None, "unsupported_joint_motion", "Fusion could not cast joint motion: " + kind)+ return motion+++def joint_data(joint, value=None):+ joint = concrete(joint)+ regular = joint.objectType.endswith("::Joint")+ motion = joint_motion(joint)+ result = {"name": joint.name, "type": joint.objectType, "motionType": motion.objectType,+ "suppressed": joint.isSuppressed,+ "occurrenceOneId": identity(joint.occurrenceOne) if joint.occurrenceOne else None,+ "occurrenceTwoId": identity(joint.occurrenceTwo) if joint.occurrenceTwo else None}+ for name, scale in MOTION_SCALES.items():+ if hasattr(motion, name + "Limits"):+ result[name + "Limits"] = limits_data(getattr(motion, name + "Limits"), scale)+ if hasattr(motion, name + "Value"):+ result[name + "Value"] = getattr(motion, name + "Value") * scale+ for name in ("rotationAxisVector", "slideDirectionVector", "normalDirectionVector", "primarySlideDirectionVector", "secondarySlideDirectionVector", "pitchDirectionVector", "yawDirectionVector", "rollDirectionVector"):+ if hasattr(motion, name):+ vector = getattr(motion, name)+ result[name] = [vector.x, vector.y, vector.z] if vector else None+ from .joint_state import motion_axes_state+ result["axes"] = motion_axes_state(motion, value)+ if hasattr(joint, "transform"):+ matrix = joint.transform+ result["transform"] = list(matrix.asArray()) if matrix else None+ if regular and matrix:+ for index in (3, 7, 11):+ result["transform"][index] *= 10+ if regular:+ from .joint_state import regular_state+ result.update(regular_state(joint, value))+ if value:+ result["ref"] = ref(joint, value)+ result["units"] = {"length": "mm", "angle": "deg", "transformTranslation": "mm" if regular else "cm"}+ return result+++def rigid_group_data(group, value=None):+ result = {"name": group.name, "suppressed": group.isSuppressed,+ "occurrenceIds": [identity(o) for o in items(group.occurrences)]}+ if value:+ result["ref"] = ref(group, value)+ result["occurrenceRefs"] = [ref(o, value) for o in items(group.occurrences)]+ return result+++def component_state(comp):+ result = metadata(comp)+ result["joints"] = [joint_data(joint) for name in ("joints", "asBuiltJoints")+ for joint in items(getattr(comp, name))] if hasattr(comp, "joints") else []+ result["rigidGroups"] = [rigid_group_data(group) for group in items(comp.rigidGroups)] if hasattr(comp, "rigidGroups") else []+ result["bodyMaterials"] = [{"id": body.material.id, "name": body.material.name} if body.material else None+ for body in items(comp.bRepBodies)] if all(hasattr(b, "material") for b in items(comp.bRepBodies)) else []+ from .joint_state import component_joint_state+ result.update(component_joint_state(comp))+ return result+++def occurrence_state(occurrence):+ return {"name": getattr(occurrence, "name", None), "grounded": occurrence.isGrounded,+ "groundedToParent": getattr(occurrence, "isGroundToParent", None),+ "attributes": attributes(occurrence)}
addin/AdomBridge/commands/mechanical/bill_of_materials.pyadded+141@@ -0,0 +1,141 @@+"""Definition identity, nested instance quantities, purchased collapse and CSV."""+import csv+from pathlib import Path+import tempfile+import adsk.fusion++from .assembly_state import metadata+from .core import identity, items, occurrence_suppression, ref, require, target+from .exporting import publish_staged+from .configurations import provenance, snapshot as configuration_snapshot, source_link+++def generate(design, value, args):+ configuration = configuration_snapshot(design, strict=True)+ table = configuration["table"]+ columns = {column["id"]: column for column in table["columns"]} if table else {}+ suppressed = [columns[cell["columnId"]]["source"] for cell in table["cells"]+ if cell["rowId"] == table["activeRowId"] and cell["type"] == "ConfigurationSuppressCell"+ and cell["isEffectivelySuppressed"]] if table else []+ def suppression(occurrence):+ if occurrence_suppression(occurrence) is True:+ return "suppressed"+ for source in suppressed:+ if source["objectType"].endswith("::Occurrence"):+ candidate = source_link(design, occurrence)+ if candidate["entityId"] == source["entityId"] and (source["occurrenceId"] is None or candidate["occurrenceId"] == source["occurrenceId"]):+ return "configuration_suppressed"+ return None+ parts, instances, excluded = {}, [], []+ physical = {"mass": 0.0, "volume": 0.0, "bodyInstances": 0}+ accuracy = adsk.fusion.CalculationAccuracy.HighCalculationAccuracy+ budget = {"visited": 0}+ def visit(comp, depth, ancestry):+ budget["visited"] += 1+ require(depth <= 100 and budget["visited"] <= 20000, "bom_budget", "Assembly exceeds combined purchasing/physical traversal budget.")+ key = identity(comp)+ require(key not in ancestry, "cyclic_assembly", "Assembly contains cyclic component containment.")+ return key+ # Physical totals are independent of purchasing collapse. Count direct bodies+ # once per physical instance, including root bodies and repeated children.+ def physical_walk(comp, occurrence=None, depth=0, ancestry=()):+ key = visit(comp, depth, ancestry)+ bodies = items(occurrence.bRepBodies if occurrence else comp.bRepBodies)+ for body in bodies:+ props = body.getPhysicalProperties(accuracy)+ physical["mass"] += props.mass+ physical["volume"] += props.volume * 1000+ physical["bodyInstances"] += 1+ children = items(occurrence.childOccurrences if occurrence else comp.occurrences)+ for child in children:+ if suppression(child) is None:+ physical_walk(child.component, child, depth + 1, ancestry + (key,))++ def walk(comp, occurrence=None, parent_path=None, depth=0, ancestry=()):+ key = visit(comp, depth, ancestry)+ path = occurrence.fullPathName if occurrence else "<root>"+ data = metadata(comp)+ reason = suppression(occurrence) if occurrence else None+ if reason:+ excluded.append({"path": path, "reason": reason})+ return+ if data.get("excludeFromBom", False):+ excluded.append({"path": path, "reason": "excludeFromBom"})+ return+ children = items(occurrence.childOccurrences if occurrence else comp.occurrences)+ purchased = bool(data.get("asPurchased", False)) and not args.get("explodePurchased", False)+ row = {"path": path, "parentPath": parent_path, "depth": depth, "componentId": key, "metadata": data,+ "bodyCount": comp.bRepBodies.count, "childCount": len(children), "asPurchased": purchased,+ "componentRef": ref(comp, value), "occurrenceRef": ref(occurrence, value) if occurrence else None}+ instances.append(row)+ if comp.bRepBodies.count or purchased:+ if key not in parts:+ props = comp.getPhysicalProperties(accuracy) if purchased else None+ direct_properties = [] if purchased else [b.getPhysicalProperties(accuracy) for b in items(comp.bRepBodies)]+ parts[key] = {"componentId": key, **data, "quantity": 0, "occurrencePaths": [],+ "scope": "component_subtree" if purchased else "direct_component_bodies",+ "unitMass": props.mass if props else sum(p.mass for p in direct_properties),+ "unitVolume": props.volume * 1000 if props else sum(p.volume * 1000 for p in direct_properties)}+ parts[key]["quantity"] += 1+ parts[key]["occurrencePaths"].append(path)+ if not purchased:+ for child in children:+ walk(child.component, child, path, depth + 1, ancestry + (key,))++ walk(design.rootComponent)+ physical_walk(design.rootComponent)+ rows = list(parts.values())+ return {"target": value, "complete": True, "configuration": provenance(configuration),+ "configurationSuppression": [cell for cell in table["cells"] if cell["rowId"] == table["activeRowId"] and cell["type"] == "ConfigurationSuppressCell"] if table else [],+ "parts": rows, "instances": instances, "excluded": excluded,+ "partCount": len(rows), "totalPurchasingInstances": sum(r["quantity"] for r in rows),+ "physical": {**physical, "scope": "all unsuppressed direct-body instances; BOM exclusions do not remove physical mass",+ "materialBasis": "Fusion assigned/default material; supplier provenance does not certify modeled material density"},+ "units": {"mass": "kg", "volume": "mm3"}, "calculationAccuracy": "high",+ "identityScope": "running add-in session; supplier metadata persists in native archives"}+++def bom(app, args):+ _, design, value = target(app, args)+ return generate(design, value, args)+++def bom_export(app, args):+ _, design, value = target(app, args, True, model_write=False)+ destination = Path(args["filename"])+ require(destination.is_absolute() and destination.suffix.lower() == ".csv" and not destination.is_symlink() and not destination.is_dir(), "invalid_destination", "BOM destination must be an absolute regular .csv path.")+ require(args.get("overwrite", False) or not destination.exists(), "destination_exists", "Pass overwrite:true to replace an existing CSV.")+ result = generate(design, value, args)+ destination.parent.mkdir(parents=True, exist_ok=True)+ published = {}+ try:+ with tempfile.TemporaryDirectory(prefix=".adom-bom-", dir=str(destination.parent)) as folder:+ staged = Path(folder) / "bom.csv"+ keys = ["componentId", "partNumber", "name", "description", "quantity", "supplier", "supplierPartNumber", "sourceUrl", "sha256", "unitMass", "unitVolume", "scope"]+ rows = result["parts"]+ if args.get("mode", "flat") == "structured":+ keys = ["path", "parentPath", "depth", "componentId", "name", "partNumber", "description", "quantity", "bodyCount", "childCount", "asPurchased", "supplier", "supplierPartNumber", "sourceUrl", "sha256"]+ rows = [{**row["metadata"], **row, "quantity": 1} for row in result["instances"]]+ configuration = result["configuration"]+ context = {"documentId": value["documentId"], "revision": value["revision"],+ "configurationTableId": configuration["tableId"], "configurationRowId": configuration["rowId"],+ "configurationRowName": configuration["rowName"]}+ keys += list(context)+ rows = [{**row, **context} for row in rows]+ with staged.open("w", encoding="utf-8", newline="") as stream:+ writer = csv.writer(stream)+ writer.writerow(keys)+ for part in rows:+ values = [part.get(key, "") for key in keys]+ writer.writerow(["'" + v if isinstance(v, str) and v[:1] in ("=", "+", "-", "@") else v for v in values])+ published.update(publish_staged(staged, destination, args.get("overwrite", False)))+ except Exception as exc:+ if not published.get("published"):+ raise+ published["cleanupWarning"] = str(exc)+ return {"target": value, "partCount": result["partCount"], "mode": args.get("mode", "flat"),+ "calculationAccuracy": result["calculationAccuracy"], "configuration": result["configuration"], **published}+++OPERATIONS = {"bom": bom, "bom_export": bom_export}+WRITES = {"bom_export"}
addin/AdomBridge/commands/mechanical/configurations.pyadded+326@@ -0,0 +1,326 @@+"""Read-only configuration graph; incomplete scope never authorizes a write.++Table IDs are native stable identifiers. Controlled sources use fresh API equality,+with tokens used only to re-resolve wrappers invalidated by recompute. No method in+this module converts a design, activates a row, or writes a configuration cell.+"""+import math++import adsk.core+import adsk.fusion++from .core import MAX_ENTITIES, MechanicalError, identity, items, require, same++MAX_ROWS, MAX_COLUMNS, MAX_CELLS = 128, 128, 4096+_sources = []+CELL_TYPES = {+ "ConfigurationParameterColumn": "ConfigurationParameterCell",+ "ConfigurationSuppressColumn": "ConfigurationSuppressCell",+ "ConfigurationVisibilityColumn": "ConfigurationVisibilityCell",+ "ConfigurationPropertyColumn": "ConfigurationPropertyCell",+}+++def concrete(entity):+ require(entity is not None and entity.isValid, "configuration_read_failed", "Configuration object is missing or invalid.")+ namespace = adsk.core if entity.objectType.startswith("adsk::core::") else adsk.fusion+ cls = getattr(namespace, entity.objectType.split("::")[-1], None)+ require(cls is not None and hasattr(cls, "cast"), "configuration_read_failed", "Concrete configuration wrapper is unavailable.", objectType=entity.objectType)+ result = cls.cast(entity)+ require(result is not None and result.isValid, "configuration_read_failed", "Concrete configuration cast failed.")+ return result+++def bounded(collection, maximum):+ require(0 <= collection.count <= maximum, "configuration_budget", "Configuration inspection budget exceeded.")+ return [concrete(collection.item(index)) for index in range(collection.count)]+++def native_id(entity):+ value = entity.id+ require(isinstance(value, str) and bool(value), "configuration_read_failed", "Native configuration ID is missing.")+ return value+++def flag(value):+ require(type(value) is bool, "configuration_read_failed", "Native configuration flag is not a Boolean.")+ return value+++def source_link(design, entity):+ entity = concrete(entity)+ context = getattr(entity, "assemblyContext", None)+ native = concrete(getattr(entity, "nativeObject", None) or entity)+ kind = native.objectType.split("::")[-1]+ owner_scope = "component"+ if kind.endswith("Parameter"):+ require(any(same(native, p) for p in items(design.allParameters)), "configuration_source_mismatch", "Controlled parameter is outside this design.")+ if kind == "UserParameter":+ require(same(native.design, design), "configuration_source_mismatch", "User parameter belongs to another design.")+ owner, owner_scope = design, "design"+ else:+ require(kind == "ModelParameter", "configuration_scope_unsupported", "Only user and model parameter ownership is supported.")+ owner = concrete(native.component)+ require(any(same(owner, c) for c in items(design.allComponents)), "configuration_source_mismatch", "Model parameter component is outside this design.")+ else:+ from .geometry import owner as component_owner+ owner = native if kind == "Component" else component_owner(native)+ if kind == "Occurrence":+ owner = native.component+ require(owner is not None and any(same(owner, c) for c in items(design.allComponents)),+ "configuration_source_mismatch", "Controlled source owner is outside this design.")+ key = None+ token = getattr(native, "entityToken", None)+ if token:+ for saved in _sources:+ if not saved["design"].isValid or not same(saved["design"], design) or saved["type"] != native.objectType:+ continue+ matches = design.findEntityByToken(saved["token"])+ require(len(matches) <= MAX_ENTITIES, "configuration_budget", "Configuration source lookup exceeds budget.")+ if any(candidate.isValid and same(candidate, native) for candidate in matches):+ require(all(candidate.isValid and candidate.objectType == native.objectType and same(candidate, native) for candidate in matches),+ "configuration_source_mismatch", "Controlled source token resolves ambiguously.")+ key = saved["id"]+ break+ if key is None:+ key = identity(native)+ if token:+ require(len(_sources) < MAX_ENTITIES, "identity_budget", "Configuration source identity budget exhausted.")+ _sources.append({"design": design, "type": native.objectType, "token": token, "id": key})+ return {"entityId": key, "objectType": native.objectType, "ownerId": identity(owner), "ownerScope": owner_scope,+ "occurrenceId": identity(context) if context is not None else None}+++def diagnostic_read(result, field, reader):+ """Retain stable table cells when a preview guard is unavailable.++ The missing guard remains an explicit read error, so strict preflight and+ recovery cannot infer that unavailable preview state is absent.+ """+ try:+ return reader()+ except Exception as error:+ result["readErrors"].append({"code": error.code if isinstance(error, MechanicalError) else "configuration_read_failed",+ "field": field, "message": str(error)})+ return {"unavailable": True}+++def child_ids(row):+ children = row.childRows+ require(len(children) <= MAX_ROWS, "configuration_budget", "Configuration hierarchy exceeds budget.")+ return [native_id(concrete(child)) for child in children]+++def property_link(design, prop):+ prop = concrete(prop)+ group = concrete(prop.parent)+ require(group.objectType == "adsk::core::PropertyGroup", "configuration_scope_unsupported", "Only component property groups are supported.")+ owner = concrete(group.parent)+ require(owner.objectType == "adsk::fusion::Component", "configuration_scope_unsupported", "Property must belong to a component.")+ return {"id": native_id(prop), "name": prop.name, "objectType": prop.objectType,+ "groupId": native_id(group), "owner": source_link(design, owner), "readOnly": flag(prop.isReadOnly)}+++def numeric_cell(design, cell, unit):+ value = cell.value+ require(isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value),+ "configuration_read_failed", "Configuration numeric value is not finite.")+ scales = {"mm": 10.0, "deg": 180.0 / math.pi, "kg": 1.0, "mm2": 100.0, "mm3": 1000.0, "": 1.0}+ # A bare number is valid in almost any supplied default unit; it does not+ # establish dimensionlessness. Fusion documents an empty native unit for it.+ canonical = "" if unit == "" else next((candidate for candidate in scales if candidate+ and design.unitsManager.isValidExpression("1 " + candidate, unit)), None)+ require(canonical is not None, "configuration_scope_unsupported",+ "Configuration numeric units must be length, angle, mass, area, volume or unitless.", displayUnit=unit)+ result = {"internalValue": value, "internalUnitSystem": "Fusion database units (cm, rad, kg)", "displayUnit": unit}+ # convert returns -1 on ExpressionError, which is also a legitimate signed+ # cell value. Probe a positive basis against the known database scale, then+ # calculate signed values with that verified linear scale. Temperature and+ # other unsupported families never enter a speculative conversion fallback.+ scale = scales[canonical]+ basis = design.unitsManager.convert(1.0, "internalUnits", canonical)+ require(isinstance(basis, (int, float)) and not isinstance(basis, bool) and math.isfinite(basis)+ and basis > 0 and math.isclose(basis, scale, rel_tol=1e-12, abs_tol=1e-12),+ "configuration_read_failed", "Configuration unit conversion basis failed validation.", unit=canonical or "unitless")+ converted = value * scale+ require(math.isfinite(converted), "configuration_read_failed", "Converted configuration value is not finite.")+ result.update(value=converted, unit=canonical or "unitless")+ return result+++def read_table(design, result):+ table = concrete(design.configurationTopTable)+ rows, columns = bounded(table.rows, MAX_ROWS), bounded(table.columns, MAX_COLUMNS)+ require(len(rows) * len(columns) <= MAX_CELLS, "configuration_budget", "Configuration cell budget exceeded.")+ require(rows and len({native_id(row) for row in rows}) == len(rows), "configuration_read_failed", "Missing rows or duplicate configuration row IDs.")+ require(len({native_id(column) for column in columns}) == len(columns), "configuration_read_failed", "Duplicate configuration column IDs.")+ active = concrete(table.activeRow)+ require(any(same(active, row) for row in rows), "configuration_read_failed", "Active row is outside ordinary rows.")+ data = {"id": native_id(table), "name": table.name, "activeRowId": native_id(active),+ "activeRowName": active.name, "rows": [], "testRow": None, "columns": [], "cells": []}+ result["table"] = data+ custom = diagnostic_read(result, "top.hasCustomColumns", lambda: flag(table.hasCustomColumns))+ def read_test_row():+ row = table.testRow+ if row is None:+ return None+ row = concrete(row)+ require(all(not same(row, normal) and native_id(row) != native_id(normal) for normal in rows),+ "configuration_read_failed", "Test row overlaps an ordinary row.")+ return row+ test_row = diagnostic_read(result, "top.testRow", read_test_row)+ test_id = native_id(test_row) if test_row is not None and not isinstance(test_row, dict) else test_row+ result["preview"] = {"hasCustomColumns": custom, "testRowId": test_id}+ if custom is True:+ result["unsupported"].append("custom columns")+ all_rows = rows + ([test_row] if isinstance(test_id, str) else [])+ require(len(all_rows) <= MAX_ROWS and len(all_rows) * len(columns) <= MAX_CELLS,+ "configuration_budget", "Configuration table including its test row exceeds budget.")+ themes = bounded(table.customThemeTables, MAX_COLUMNS)+ for name in ("appearanceTable", "materialTable", "plasticRuleTable", "sheetMetalRuleTable"):+ theme = concrete(getattr(table, name))+ if theme.columns.count:+ themes.append(theme)+ result["themeTables"] = [{"id": native_id(t), "objectType": t.objectType,+ "rowCount": t.rows.count, "columnCount": t.columns.count} for t in themes]+ if themes:+ result["unsupported"].append("theme-table contents")+ for row in all_rows:+ require(same(row.parentTable, table), "configuration_read_failed", "Configuration row belongs to another table.")+ def parent_id():+ parent = row.parentRow+ return native_id(concrete(parent)) if parent is not None else None+ hierarchy = {"parentId": diagnostic_read(result, "row." + row.id + ".parentRow", parent_id),+ "childIds": diagnostic_read(result, "row." + row.id + ".childRows", lambda: child_ids(row)),+ "isTestRow": diagnostic_read(result, "row." + row.id + ".isTestRow", lambda: flag(row.isTestRow))}+ reserved = isinstance(test_id, str) and same(row, test_row)+ if isinstance(hierarchy["parentId"], str) or (isinstance(hierarchy["childIds"], list) and hierarchy["childIds"]):+ result["unsupported"].append("hierarchical row: " + row.id)+ if type(hierarchy["isTestRow"]) is bool and hierarchy["isTestRow"] is not reserved:+ result["unsupported"].append("test-row flag disagrees with native table membership: " + row.id)+ row_data = {"id": row.id, "name": row.name, "index": row.index, "hierarchy": hierarchy,+ "scope": "test" if reserved else "ordinary"}+ if reserved:+ data["testRow"] = row_data+ else:+ data["rows"].append(row_data)+ for column in columns:+ require(same(column.parentTable, table), "configuration_read_failed", "Configuration column belongs to another table.")+ kind = column.objectType.split("::")[-1]+ count_label = "column." + column.id + ".rowCount"+ def row_count():+ count = column.rowCount+ require(type(count) is int and 0 <= count <= MAX_ROWS, "configuration_read_failed", "Invalid column row count.")+ return count+ metadata = {"id": column.id, "index": column.index, "title": column.title, "type": kind,+ "rowCount": diagnostic_read(result, count_label, row_count)}+ data["columns"].append(metadata)+ if type(metadata["rowCount"]) is int:+ diagnostic_read(result, count_label,+ lambda: require(metadata["rowCount"] == len(all_rows), "configuration_read_failed",+ "Column row count differs from the ordinary plus reserved test-row inventory."))+ if kind not in CELL_TYPES:+ metadata["supported"] = False+ result["unsupported"].append("column type: " + kind)+ continue+ label = "column." + column.id+ if kind == "ConfigurationParameterColumn":+ metadata.update(source=diagnostic_read(result, label + ".source", lambda: source_link(design, column.parameter)),+ displayUnit=diagnostic_read(result, label + ".displayUnit", lambda: column.parameter.unit))+ elif kind == "ConfigurationSuppressColumn":+ metadata["source"] = diagnostic_read(result, label + ".source", lambda: source_link(design, column.feature))+ elif kind == "ConfigurationVisibilityColumn":+ metadata["source"] = diagnostic_read(result, label + ".source", lambda: source_link(design, column.entity))+ else:+ metadata["property"] = diagnostic_read(result, label + ".property", lambda: property_link(design, column.parentProperty))+ for row in all_rows:+ label = "cell." + row.id + "." + column.id+ entry = {"rowId": row.id, "columnId": column.id, "type": CELL_TYPES[kind],+ "scope": "test" if isinstance(test_id, str) and same(row, test_row) else "ordinary"}+ data["cells"].append(entry)+ def addressed_cell():+ cell = concrete(column.getCellByRowId(row.id))+ require(cell.objectType == "adsk::fusion::" + CELL_TYPES[kind], "configuration_read_failed", "Cell type disagrees with its column.")+ require(same(concrete(cell.parentRow), row) and same(concrete(cell.parentColumn), column),+ "configuration_read_failed", "Cell address readback mismatch.")+ return cell+ cell = diagnostic_read(result, label + ".address", addressed_cell)+ if isinstance(cell, dict):+ entry["unavailable"] = True+ continue+ if kind == "ConfigurationParameterColumn":+ entry.update(expression=diagnostic_read(result, label + ".expression", lambda: cell.expression),+ valueType=diagnostic_read(result, label + ".valueType", lambda: int(cell.valueType)))+ if entry["valueType"] == adsk.fusion.ParameterValueTypes.NumericParameterValueType:+ measured = diagnostic_read(result, label + ".numeric", lambda: numeric_cell(design, cell, column.parameter.unit))+ if measured.get("unavailable"):+ entry["numeric"] = measured+ else:+ entry.update(measured)+ elif entry["valueType"] == adsk.fusion.ParameterValueTypes.TextParameterValueType:+ entry["textValue"] = diagnostic_read(result, label + ".textValue", lambda: cell.textValue)+ elif not isinstance(entry["valueType"], dict):+ diagnostic_read(result, label + ".valueType", lambda: require(False, "configuration_read_failed", "Unknown parameter-cell value type."))+ elif kind == "ConfigurationSuppressColumn":+ entry.update(isSuppressed=diagnostic_read(result, label + ".isSuppressed", lambda: flag(cell.isSuppressed)),+ isEffectivelySuppressed=diagnostic_read(result, label + ".isEffectivelySuppressed", lambda: flag(cell.isEffectivelySuppressed)))+ elif kind == "ConfigurationVisibilityColumn":+ entry["isVisible"] = diagnostic_read(result, label + ".isVisible", lambda: flag(cell.isVisible))+ else:+ def property_value():+ value = cell.value+ require(isinstance(value, str), "configuration_scope_unsupported", "Only textual component property cells are supported.")+ return value+ entry["value"] = diagnostic_read(result, label + ".value", property_value)+++def snapshot(design, strict=False):+ result = {"configured": None, "isConfiguration": None, "configurationRowId": None,+ "complete": False, "table": None, "configuredOccurrences": [],+ "unsupported": [], "readErrors": [], "configurationWritesEnabled": False,+ "scope": "loaded Design ordinary and reserved test rows; no activation or recompute"}+ try:+ result["configured"] = flag(design.isConfiguredDesign)+ result["isConfiguration"] = flag(design.isConfiguration)+ row_id = design.configurationRowId+ require(isinstance(row_id, str), "configuration_read_failed", "Native design configuration row ID is not a string.")+ result["configurationRowId"] = row_id+ require(bool(row_id) == result["isConfiguration"], "configuration_read_failed", "Native design configuration flag and row ID disagree.")+ if result["isConfiguration"]:+ result["unsupported"].append("generated single-configuration design: source-table identity and durable lifecycle are unsupported")+ if result["configured"]:+ read_table(design, result)+ # A standard design can still contain externally configured occurrences.+ for occurrence in items(design.rootComponent.allOccurrences):+ if flag(occurrence.isConfiguration):+ row = concrete(occurrence.configurationRow)+ data_file = occurrence.configuredDataFile+ require(data_file is not None and data_file.isValid, "configuration_read_failed", "Configured occurrence source file is unavailable.")+ result["configuredOccurrences"].append({"occurrenceId": identity(occurrence), "path": occurrence.fullPathName,+ "tableId": native_id(concrete(row.parentTable)), "rowId": native_id(row), "rowName": row.name,+ "sourceFileId": data_file.id, "sourceVersion": data_file.versionNumber})+ if result["configuredOccurrences"]:+ result["unsupported"].append("configured inserted occurrences: source-table witness and cloud lifecycle are deferred")+ except Exception as error:+ result["readErrors"].append({"code": error.code if isinstance(error, MechanicalError) else "configuration_read_failed", "message": str(error)})+ result["complete"] = not result["unsupported"] and not result["readErrors"]+ if strict:+ require(result["complete"], "configuration_scope_unsupported", "A complete supported configuration witness is required before model mutation or complete BOM export.", configuration=result)+ return result+++def provenance(graph):+ table = graph["table"]+ return {"configured": graph["configured"], "isConfiguration": graph["isConfiguration"],+ "tableId": table["id"] if table else None,+ "rowId": graph["configurationRowId"] if graph["isConfiguration"] else table["activeRowId"] if table else None, "rowName": table["activeRowName"] if table else None,+ "scope": "current active configuration only", "graphComplete": graph["complete"]}+++def inspect(app, args):+ from .core import target+ _, design, value = target(app, args)+ return {"target": value, "configuration": snapshot(design)}+++READS = {"configuration_inspect": inspect}
addin/AdomBridge/commands/mechanical/core.pyadded+430@@ -0,0 +1,430 @@+"""Main-thread mechanical identity, revisions, references and health.++Session IDs are capabilities for this running add-in, not persistent CAD IDs. Tokens+are only resolver inputs: they are never compared to decide entity identity.+"""+import hashlib+import json+import math+import threading+import uuid++import adsk.core+import adsk.fusion++SESSION_ID = str(uuid.uuid4())+_documents = []+_entities = []+MAX_ENTITIES = 20000+UNITS = {"length": "mm", "angle": "deg", "mass": "kg", "area": "mm2", "volume": "mm3"}+++class MechanicalError(Exception):+ def __init__(self, code, message, **details):+ super().__init__(message)+ self.code, self.details = code, details+++def require(condition, code, message, **details):+ if not condition:+ raise MechanicalError(code, message, **details)+++def items(collection):+ require(collection.count <= MAX_ENTITIES, "inspection_budget", "Collection exceeds inspection budget.")+ return [collection.item(i) for i in range(collection.count)]+++def same(a, b):+ return a is b or a == b+++def concrete(entity):+ """Cast Base-returning Fusion getters before reading subclass context/state."""+ if entity is None:+ return None+ cls = getattr(adsk.fusion, entity.objectType.split("::")[-1], None)+ if cls is not None and hasattr(cls, "cast"):+ entity = cls.cast(entity)+ require(entity is not None, "unsupported_entity_wrapper", "Could not cast the advertised Fusion entity type.")+ return entity+++def identity(entity):+ """Object equality, never name or token equality. Retained for this session only."""+ for obj, key in _entities:+ if obj.isValid and same(obj, entity):+ return key+ require(len(_entities) < MAX_ENTITIES, "identity_budget", "Session entity budget exhausted.")+ key = str(uuid.uuid4())+ _entities.append((entity, key))+ return key+++def document_identity(doc):+ require(threading.current_thread() is threading.main_thread(), "wrong_thread", "Document identity requires the Fusion main thread.")+ require(doc is not None and doc.isValid, "wrong_document", "Document identity requires a live document.")+ for previous, key in _documents:+ if previous.isValid and same(previous, doc):+ return key+ key = SESSION_ID + ":" + str(uuid.uuid4())+ _documents.append((doc, key))+ return key+++def active(app):+ require(threading.current_thread() is threading.main_thread(), "wrong_thread", "Mechanical API requires the Fusion main thread.")+ doc = app.activeDocument+ design = adsk.fusion.Design.cast(app.activeProduct)+ require(doc and design, "no_design", "The active product must be a Fusion Design.")+ require(same(design.parentDocument, doc), "wrong_document", "Active product and document disagree.")+ return doc, design, document_identity(doc)+++def digest(value):+ return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode()).hexdigest()+++def _revision_document_observation(design, document):+ """Bounded, independently readable facts even when one native getter fails."""+ result = {"complete": True, "errors": []}+ for field, getter in (+ ("designValid", lambda: design.isValid),+ ("documentValid", lambda: document.isValid),+ ("ownerVerified", lambda: same(design.parentDocument, document)),+ ("isSaved", lambda: document.isSaved),+ ("isModified", lambda: document.isModified),+ ):+ try:+ value = getter()+ require(type(value) is bool, "revision_unavailable", "Native document observation is not boolean.")+ result[field] = value+ except Exception as error:+ result[field] = None+ result["complete"] = False+ result["errors"].append({"field": field, "error": str(error)[:512]})+ return result+++def revision(design):+ """Initialize all mesh collections, then capture the unchanged revision graph.++ Fusion can advance ancestor revision IDs on the first child meshBodies read.+ All components must be initialized before ANY component revisionId is sampled.+ Native IDs still detect true equal-volume/topology changes; no target is rebased.+ """+ require(threading.current_thread() is threading.main_thread(), "wrong_thread", "Mechanical revision requires the Fusion main thread.")+ from .assembly_state import component_state, occurrence_state+ from .readback import construction_state, feature_data+ from .configurations import snapshot as configuration_snapshot+ document, observations = None, []++ def observe(stage):+ state = _revision_document_observation(design, document)+ observations.append({"stage": stage, **state})+ return state++ def checked(state, before=None):+ require(state["complete"] and all(state[key] is True for key in ("designValid", "documentValid", "ownerVerified")),+ "revision_unavailable", "Exact Design/document ownership or flags are unavailable.")+ if before is not None:+ require(all(state[key] == before[key] for key in ("isSaved", "isModified")),+ "revision_document_changed", "Document saved/modified flags changed during revision observation.")++ try:+ document = design.parentDocument+ before = observe("before_initialization")+ checked(before)+ collection = design.allComponents+ size = collection.count+ require(type(size) is int and 0 <= size <= MAX_ENTITIES, "inspection_budget", "Component collection exceeds inspection budget.")+ component_handles = [collection.item(index) for index in range(size)]+ mesh_collections = [] # Retain all native collection handles through the full digest.+ for comp in component_handles:+ require(comp is not None and comp.isValid is True and same(comp.parentDesign, design),+ "revision_unavailable", "Component does not belong to the exact Design.")+ meshes = comp.meshBodies+ mesh_collections.append(meshes) # Retain before count/validation can fail.+ require(meshes is not None and meshes.isValid is True and meshes.objectType == "adsk::fusion::MeshBodies",+ "revision_unavailable", "Native mesh collection is invalid or has an unexpected type.")+ mesh_count = meshes.count+ require(type(mesh_count) is int and 0 <= mesh_count <= MAX_ENTITIES,+ "inspection_budget", "Mesh collection exceeds inspection budget.")+ checked(observe("after_initialization"), before)+ components = []+ for comp in component_handles:+ components.append([identity(comp), str(comp.revisionId), comp.name,+ [[identity(b), str(b.revisionId)] for b in items(comp.bRepBodies)],+ [[identity(s), str(s.revisionId)] for s in items(comp.sketches)],+ component_state(comp), [feature_data(f, order=i) for i, f in enumerate(items(comp.features))] if hasattr(comp, "features") else [], construction_state(comp)])+ occurrences = [[identity(o), identity(o.component), list(o.transform2.asArray()),+ occurrence_suppression(o), occurrence_state(o)] for o in items(design.rootComponent.allOccurrences)]+ params = [[p.name, p.expression, p.unit] for p in items(design.allParameters)]+ result = digest([components, occurrences, params, design.timeline.count, design.timeline.markerPosition,+ configuration_snapshot(design)])+ checked(observe("after_digest"), before)+ return result+ except Exception as error:+ observe("after_failure")+ initial = observations[0] if observations and observations[0]["stage"] == "before_initialization" else None+ changed = sorted({key for state in observations for key in ("isSaved", "isModified")+ if initial is not None and type(initial.get(key)) is bool and type(state.get(key)) is bool+ and state[key] != initial[key]})+ effect = "changed" if changed else "unchanged" if initial is not None and all(state["complete"] for state in observations) else "unknown"+ details = dict(getattr(error, "details", {}))+ details.update(revisionObservation={"documentFlagEffect": effect, "changedFlags": changed,+ "observations": observations}, rollbackClaimed=False)+ raise MechanicalError(getattr(error, "code", "revision_unavailable"), str(error)[:1024], **details) from error+++def occurrence_suppression(occurrence):+ timeline = occurrence.timelineObject+ return bool(timeline.isSuppressed) if timeline is not None else None+++def target(app, args, write=False, model_write=True):+ doc, design, key = active(app)+ wanted = args.get("target")+ require(isinstance(wanted, dict), "target_required", "Pass target from mechanical inspect.")+ require(wanted.get("documentId") == key, "wrong_document", "Target is not the active document.", activeDocumentId=key)+ if write:+ from .configurations import snapshot as configuration_snapshot+ configuration = configuration_snapshot(design, strict=True)+ if model_write:+ require(not configuration["configured"], "configuration_mutation_unverified",+ "Configured-design model writes remain disabled until native configuration recovery and archive gates pass; inspection and current-row BOM/export remain available.")+ rev = revision(design)+ if write or "revision" in wanted:+ require(wanted.get("revision") == rev, "stale_revision", "The model changed; inspect it before a new operation.", currentRevision=rev)+ return doc, design, {"documentId": key, "revision": rev}+++def current_target(app):+ doc, design, key = active(app)+ return {"documentId": key, "revision": revision(design)}+++ORIGIN_PLANES = {"xy": "xYConstructionPlane", "xz": "xZConstructionPlane", "yz": "yZConstructionPlane"}+++def origin_plane_key(entity):+ """'xy', 'xz' or 'yz' when entity is one of its component's three origin planes, else None."""+ if getattr(entity, "objectType", "") != "adsk::fusion::ConstructionPlane":+ return None+ comp = getattr(entity, "component", None) or getattr(entity, "parent", None)+ for key, attr in ORIGIN_PLANES.items():+ if comp is not None and same(getattr(comp, attr, None), entity):+ return key+ return None+++def ref(entity, target_value, occurrence=None):+ entity = concrete(entity)+ if entity.objectType == "adsk::fusion::Profile":+ from .readback import profile_ref+ sketch = entity.parentSketch+ matches = [index for index, profile in enumerate(items(sketch.profiles)) if same(profile, entity)]+ require(len(matches) == 1, "ambiguous_reference", "Profile must belong uniquely to its sketch.")+ return profile_ref(sketch, entity, matches[0], target_value)+ is_occurrence = entity.objectType.endswith("::Occurrence")+ native = entity if is_occurrence else (getattr(entity, "nativeObject", None) or entity)+ context = None if is_occurrence else (occurrence or getattr(entity, "assemblyContext", None))+ result = {"documentId": target_value["documentId"], "revision": target_value["revision"],+ "token": native.entityToken, "objectType": native.objectType,+ "occurrenceToken": context.entityToken if context else None,+ "occurrencePath": context.fullPathName if context else (entity.fullPathName if is_occurrence else None)}+ if native.objectType == "adsk::fusion::Component" and same(native, native.parentDesign.rootComponent):+ # Fusion can emit a root token that findEntityByToken cannot resolve.+ # Bind this explicit root reference to its live API identity instead.+ result["rootComponentId"] = identity(native)+ plane = origin_plane_key(native)+ if plane:+ # an origin plane resolves through its component, not its token, so a base plane is a+ # reference like any other plane (construction_axis two_planes, sketch_create, a split tool)+ result["originPlane"] = plane+ result["component"] = ref(native.component, target_value)+ return result+++def occurrence_context(design, entity, path):+ entity = concrete(entity)+ require(entity.objectType.endswith("::Occurrence"), "invalid_context", "Context must resolve to an occurrence.")+ native = getattr(entity, "nativeObject", None) or entity+ candidates = [o for o in items(design.rootComponent.allOccurrences)+ if o.fullPathName == path and same(getattr(o, "nativeObject", None) or o, native)]+ if candidates:+ require(len(candidates) == 1, "ambiguous_context", "Occurrence path must identify one instance.")+ return candidates[0]+ # Native definition references have a path relative to their owner. Only+ # retain that context when the token and saved path both resolve exactly.+ require(entity.fullPathName == path, "invalid_context", "Occurrence path changed.")+ return entity+++def resolve(design, target_value, value, allowed=()):+ require(isinstance(value, dict), "invalid_reference", "An entity reference object is required.")+ require(value.get("documentId") == target_value["documentId"] and value.get("revision") == target_value["revision"],+ "stale_reference", "Reference is from another document or revision.")+ if value.get("objectType") == "adsk::fusion::Profile":+ # Bind profile selection to a uniquely+ # resolved sketch, this exact aggregate revision and geometric witness.+ from .readback import profile_signature+ require(not allowed or "Profile" in allowed, "wrong_entity_type", "Operation does not accept profiles.")+ require(isinstance(value.get("sketchToken"), str), "invalid_reference", "Profile sketch token is required.")+ sketches = design.findEntityByToken(value["sketchToken"])+ require(len(sketches) == 1 and sketches[0].objectType.endswith("::Sketch"), "ambiguous_reference", "Profile sketch must resolve uniquely.")+ index = value.get("profileIndex")+ require(isinstance(index, int) and not isinstance(index, bool) and 0 <= index < sketches[0].profiles.count,+ "invalid_reference", "Profile index is no longer valid.")+ profile = sketches[0].profiles.item(index)+ require(digest(profile_signature(profile)) == value.get("fingerprint"), "stale_reference", "Profile geometry changed.")+ return profile+ if value.get("originPlane") is not None:+ require(value.get("objectType") == "adsk::fusion::ConstructionPlane" and value["originPlane"] in ORIGIN_PLANES,+ "invalid_reference", "An origin plane reference names xy, xz or yz.")+ require(not allowed or "ConstructionPlane" in allowed, "wrong_entity_type", "Operation does not accept this entity type.")+ comp = resolve(design, target_value, value.get("component"), ("Component",))+ entity = getattr(comp, ORIGIN_PLANES[value["originPlane"]])+ require(entity is not None and entity.isValid, "invalid_reference", "Origin plane is not available.")+ return entity+ if "rootComponentId" in value:+ entity = design.rootComponent+ require(value.get("objectType") == "adsk::fusion::Component"+ and value["rootComponentId"] == identity(entity)+ and not value.get("occurrenceToken") and not value.get("occurrencePath"),+ "invalid_reference", "Root reference does not identify this active design's root component.")+ else:+ require(isinstance(value.get("token"), str) and value["token"], "invalid_reference", "Reference token is missing.")+ matches = design.findEntityByToken(value["token"])+ require(len(matches) <= MAX_ENTITIES, "inspection_budget", "Token lookup exceeds inspection budget.")+ if len(matches) > 1 and value.get("objectType") == "adsk::fusion::Component":+ # Repeated definitions can return duplicate API wrappers. Only+ # actual Component equality makes them one entity; face splits and+ # occurrence instances retain the strict one-result rule below.+ first = matches[0]+ for candidate in matches:+ require(candidate.isValid and candidate.objectType == "adsk::fusion::Component"+ and same(first, candidate), "ambiguous_reference",+ "Component token resolves to distinct or invalid entities.", matchCount=len(matches))+ matches = [first]+ require(len(matches) == 1, "ambiguous_reference", "Token must resolve to exactly one live entity.", matchCount=len(matches))+ entity = concrete(matches[0])+ require(entity.isValid and entity.objectType == value.get("objectType"), "invalid_reference", "Entity type or validity changed.")+ require(not allowed or entity.objectType.split("::")[-1] in allowed, "wrong_entity_type", "Operation does not accept this entity type.")+ if entity.objectType.endswith("::Occurrence") and value.get("occurrencePath"):+ entity = occurrence_context(design, entity, value["occurrencePath"])+ if value.get("occurrenceToken"):+ contexts = design.findEntityByToken(value["occurrenceToken"])+ require(len(contexts) == 1, "ambiguous_context", "Occurrence context must resolve uniquely.")+ context = occurrence_context(design, contexts[0], value.get("occurrencePath"))+ owner = getattr(entity, "parentComponent", None)+ if owner is None and hasattr(entity, "component"):+ owner = entity.component+ if owner is None and hasattr(entity, "parentSketch"):+ owner = entity.parentSketch.parentComponent+ if owner is None and hasattr(entity, "body"):+ owner = entity.body.parentComponent+ require(owner and same(owner, context.component), "invalid_context", "Entity is not owned by this occurrence component.")+ entity = entity.createForAssemblyContext(context)+ require(entity and entity.isValid, "invalid_context", "Could not create occurrence-context proxy.")+ return entity+++def health(design, recompute=False):+ computed = None+ if recompute:+ computed = bool(design.computeAll())+ faults, unknown, suppressed = [], [], []+ pending = [(str(index), item) for index, item in enumerate(items(design.timeline))]+ while pending:+ index, item = pending.pop(0)+ try:+ if getattr(item, "isGroup", False):+ group = adsk.fusion.TimelineGroup.cast(item)+ pending.extend((index + "." + str(i), child) for i, child in enumerate(items(group)))+ continue+ if getattr(item, "isSuppressed", False):+ suppressed.append({"index": index, "name": item.name})+ continue+ state = item.healthState+ if state != adsk.fusion.FeatureHealthStates.HealthyFeatureHealthState:+ faults.append({"index": index, "name": item.name, "state": int(state),+ "message": item.errorOrWarningMessage})+ except Exception as exc:+ unknown.append({"index": index, "error": str(exc)})+ return {"healthy": computed is not False and not faults and not unknown,+ "computeSucceeded": computed, "complete": not unknown, "faults": faults,+ "unknown": unknown, "suppressed": suppressed, "timelineCount": design.timeline.count}+++def require_healthy(design, recompute=True):+ state = health(design, recompute)+ require(state["healthy"], "unhealthy_model", "Recompute or feature health check failed.", health=state)+ return state+++def point_mm(point):+ return [point.x * 10, point.y * 10, point.z * 10]+++def body_data(body):+ pp = body.physicalProperties+ box = body.boundingBox+ return {"name": body.name, "volume": pp.volume * 1000, "area": pp.area * 100,+ "mass": pp.mass, "centerOfMass": point_mm(pp.centerOfMass),+ "bounds": {"min": point_mm(box.minPoint), "max": point_mm(box.maxPoint)},+ "faces": body.faces.count, "edges": body.edges.count, "isSolid": body.isSolid,+ "space": "component", "units": UNITS}+++def parameter_data(design, parameter):+ unit = parameter.unit+ # Fusion numeric values always use its internal units (cm/rad/kg), regardless+ # of the display unit. Conversion occurs here, never in individual handlers.+ value, normalized = None, unit+ for canonical in ("mm", "deg", "kg", ""):+ try:+ if design.unitsManager.isValidExpression("1 " + canonical, unit):+ value = design.unitsManager.convert(parameter.value, "internalUnits", canonical)+ normalized = canonical or "unitless"+ break+ except Exception:+ continue+ row = {"name": parameter.name, "expression": parameter.expression,+ "value": value, "unit": normalized, "displayUnit": unit}+ # which feature (or sketch dimension, or joint) owns a model parameter, so feature_edit can be+ # pointed at it without diffing parameter lists; a user parameter has no owner+ owner = None+ try:+ created_by = getattr(parameter, "createdBy", None)+ if created_by is not None:+ owner = {"name": getattr(created_by, "name", None), "objectType": created_by.objectType}+ role = getattr(parameter, "role", None)+ if role:+ owner["role"] = role+ except Exception:+ owner = None+ row["kind"] = "model" if parameter.objectType.endswith("::ModelParameter") else "user"+ row["owner"] = owner+ return row+++def recovery_snapshot(design):+ """Semantic recovery witness; native revision IDs legitimately change on undo."""+ from .readback import construction_state, feature_data, sketch_data, topology_witness+ from .assembly_state import component_state, occurrence_state+ from .configurations import snapshot as configuration_snapshot+ configuration = configuration_snapshot(design, strict=True)+ return {"configuration": configuration,+ "parameters": [[p.name, p.expression, p.unit] for p in items(design.allParameters)],+ "components": [[identity(c), c.name, [topology_witness(b) for b in items(c.bRepBodies)],+ [sketch_data(s) for s in items(c.sketches)],+ [feature_data(f, order=i) for i, f in enumerate(items(c.features))], component_state(c), construction_state(c)] for c in items(design.allComponents)],+ "occurrences": [[identity(o), list(o.transform2.asArray()), occurrence_state(o)] for o in items(design.rootComponent.allOccurrences)],+ "timeline": design.timeline.count, "timelineMarker": design.timeline.markerPosition, "health": health(design)}+++def number(value, name, positive=False):+ require(isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value), "invalid_input", name + " must be a finite number.")+ require(not positive or value > 0, "invalid_input", name + " must be positive.")+ return float(value)
addin/AdomBridge/commands/mechanical/exploded_preview.pyadded+485@@ -0,0 +1,485 @@+"""Session-owned public controller for finite exploded presentation graphics.++Direct synchronous WRITES use the mechanical durable journal, not PTransaction.+History receipts describe past completion; this registry describes live resources.+No document create/close, global Undo, timer, capture or cross-session adoption.+"""+import copy+import hashlib+import json+from pathlib import Path+import time+import uuid++import adsk.fusion++from . import core+from . import exploded_preview_prototype as backend+from .core import MechanicalError, SESSION_ID, require, same+from .journal import fingerprint+from .exploded_view import request_arguments++SCHEMA_VERSION = 1+BACKEND_SOURCE_DIGEST = hashlib.sha256(Path(backend.__file__).read_bytes()).hexdigest()+MAX_ACTIVE = 4+MAX_ENTRIES = 128+MAX_PLAN_BYTES = 2 * 1024 * 1024+MAX_TOTAL_PLAN_BYTES = 8 * 1024 * 1024+MAX_TOTAL_BODIES = 512+++def bounded_text(value, name, maximum=128):+ require(isinstance(value, str) and 0 < len(value) <= maximum, 'invalid_input', name + ' must be a bounded nonempty string.')+ return value+++def exact_target(value):+ require(isinstance(value, dict) and set(value) == {'documentId', 'revision'}, 'invalid_input', 'An exact documentId/revision target is required.')+ for name in value:+ bounded_text(value[name], name, 256)+ return copy.deepcopy(value)+++def flags(doc):+ result = (doc.isSaved, doc.isModified)+ require(all(type(v) is bool for v in result), 'preview_state_unavailable', 'Document flags are unavailable.')+ return result+++def known_identity(app, document_id):+ """Resolve only an already-issued session identity; no revision adoption."""+ backend.main_thread()+ matches = [doc for doc, key in core._documents if key == document_id]+ require(len(matches) == 1 and matches[0].isValid, 'preview_document_unavailable', 'Exact session document is unavailable.')+ doc = matches[0]+ require(sum(same(item, doc) for item in backend.bounded(app.documents, 128)) == 1,+ 'preview_document_unavailable', 'Exact session document is not uniquely open.')+ design = adsk.fusion.Design.cast(doc.products.itemByProductType('DesignProductType'))+ require(design is not None and design.isValid and same(design.parentDocument, doc),+ 'preview_document_unavailable', 'Exact document has no matching Design.')+ require(core.document_identity(doc) == document_id, 'preview_document_unavailable', 'Session document identity differs.')+ return doc, design+++def known_document(app, wanted):+ wanted = exact_target(wanted)+ doc, design = known_identity(app, wanted['documentId'])+ require(core.revision(design) == wanted['revision'],+ 'stale_revision', 'Exact document identity/revision changed.')+ return doc, design+++def blank_model(design):+ root = design.rootComponent+ require(all(backend.count(getattr(root, field), backend.MAX_BODIES) == 0+ for field in ('bRepBodies', 'meshBodies', 'occurrences', 'sketches', 'features'))+ and backend.count(design.allComponents, 1) == 1 and same(design.allComponents.item(0), root)+ and backend.count(design.allParameters, 0) == 0 and backend.count(design.timeline, 0) == 0+ and backend.count(root.customGraphicsGroups, 0) == 0,+ 'preview_not_blank', 'Presentation must be a blank Design with no graphics before reservation.')+++def retained_json_bytes(*objects):+ """Count every distinct retained serialized string, including duplicate content."""+ seen = set()+ total = 0+ for obj in objects:+ for key, value in vars(obj).items():+ if key.endswith('_json') and isinstance(value, str) and id(value) not in seen:+ seen.add(id(value))+ total += len(value.encode('utf-8'))+ return total+++def terminal_record(history, operation_id, operation=None):+ record = history.store().lookup(SESSION_ID, operation_id)+ require(isinstance(record, dict) and record.get('sessionId') == SESSION_ID+ and record.get('operationId') == operation_id and record.get('phase') in ('succeeded', 'failed', 'not_executed'),+ 'preview_history_unresolved', 'A coherent current-session terminal operation record is required.')+ result = record.get('result')+ require(isinstance(result, dict) and result.get('sessionId') == SESSION_ID and result.get('operationId') == operation_id+ and type(result.get('success')) is bool and result['success'] == (record['phase'] == 'succeeded'),+ 'preview_history_unresolved', 'Terminal record identity/result is incoherent.')+ if operation is not None:+ require(record.get('operation') == operation and result.get('operation') == operation,+ 'preview_ownership', 'Document creation proof is not the required operation.')+ return record+++class Controller:+ def __init__(self):+ self.epoch = str(uuid.uuid4())+ self.entries = {}+ self.claims = {}++ def guard(self, args):+ backend.main_thread()+ require(args.get('sessionId') == SESSION_ID, 'session_mismatch', 'Preview control requires the current session.')++ def entry(self, app, args):+ self.guard(args)+ key = bounded_text(args.get('previewId'), 'previewId')+ entry = self.entries.get(key)+ require(entry is not None and entry['epoch'] == self.epoch and entry['sessionId'] == SESSION_ID,+ 'preview_session_expired', 'No live controller capability identifies this preview; history does not rebind native resources.')+ require(same(entry['app'], app), 'preview_ownership', 'Preview belongs to another application.')+ return entry++ def ownership(self, entry):+ backend.main_thread()+ require(entry['epoch'] == self.epoch and entry['sessionId'] == SESSION_ID+ and self.entries.get(entry['previewId']) is entry+ and self.claims.get(entry['target']['documentId']) == entry['previewId'],+ 'preview_ownership', 'Exclusive live presentation lease is unavailable.')+ doc, design = known_identity(entry['app'], entry['target']['documentId'])+ require(same(doc, entry['document']) and same(design, entry['design']) and doc.isSaved is False+ and doc.creationId == entry['creationId'],+ 'preview_ownership', 'Retained native presentation identity differs.')+ return {'success': True, 'ownerId': entry['previewId'], 'sessionId': SESSION_ID,+ 'documentId': entry['target']['documentId']}++ def lease(self, entry, cleanup=False):+ if entry['lease'] is None:+ entry['lease'] = backend.OwnedPresentationLease(entry['app'], entry['document'], entry['design'],+ entry['previewId'], lambda: self.ownership(entry), expected_state=entry['initialPresentationState'])+ entry['lease'].check(cleanup=cleanup)+ return entry['lease']++ def reserve(self, app, args, history):+ self.guard(args)+ wanted = exact_target(args.get('target'))+ source = exact_target(args.get('sourceTarget'))+ require(wanted['documentId'] != source['documentId'], 'preview_ownership', 'Source and presentation must be distinct documents.')+ creation_id = bounded_text(args.get('creationOperationId'), 'creationOperationId')+ record = terminal_record(history, creation_id, 'new_design')+ result = record['result']+ data = result.get('data')+ require(record['phase'] == 'succeeded' and isinstance(data, dict) and data.get('target') == wanted+ and isinstance(data.get('document'), dict) and data['document'].get('saved') is False,+ 'preview_ownership', 'Committed new_design result does not identify this unsaved presentation target.')+ creation = bounded_text(data['document'].get('creationId'), 'creationId', 256)+ previous = [item for item in self.entries.values() if item['target']['documentId'] == wanted['documentId']]+ predecessor = previous[-1] if previous else None+ if predecessor is None:+ doc, design = known_document(app, wanted) # Initial claim remains strict against real new_design.+ else:+ require(predecessor['state'] == 'disposed' and self.last_history(predecessor, history)['terminalRecorded']+ and predecessor['priorUnresolved'] is None and predecessor['finalPresentationState'] is not None,+ 'preview_history_unresolved', 'Previous presentation use lacks coherent durable verified disposal/release evidence.')+ require(predecessor['creationOperationId'] == creation_id and predecessor['creationDigest'] == record['requestDigest']+ and predecessor['target'] == wanted and predecessor['epoch'] == self.epoch,+ 'preview_ownership', 'Reuse must preserve its exact original creation proof and controller epoch.')+ doc, design = known_identity(app, wanted['documentId'])+ require(same(doc, predecessor['document']) and same(design, predecessor['design'])+ and same(design.rootComponent, predecessor['lease'].root_component),+ 'preview_ownership', 'Disposed presentation native identity changed.')+ require(same(app.activeDocument, doc) and doc.creationId == creation and doc.isSaved is False,+ 'preview_ownership', 'Claim requires the exact active unsaved document returned by new_design.')+ blank_model(design)+ initial_state = backend.presentation_observation(doc, design)+ require(initial_state['revision'] == wanted['revision'] if predecessor is None+ else initial_state == predecessor['finalPresentationState'],+ 'stale_revision', 'Presentation changed outside the verified creation/disposal chain.')+ source_doc, source_design = known_document(app, source)+ source_flags = flags(source_doc)+ require(wanted['documentId'] not in self.claims, 'preview_ownership', 'Presentation already has an exclusive preview lease.')+ require(len(self.entries) < MAX_ENTRIES and len(self.claims) < MAX_ACTIVE+ and sum(e['planBytes'] for e in self.entries.values()) + MAX_PLAN_BYTES <= MAX_TOTAL_PLAN_BYTES+ and sum(e['bodyBudget'] for e in self.entries.values()) + backend.MAX_BODIES <= MAX_TOTAL_BODIES,+ 'preview_capacity', 'Session preview capacity is full; retained resources are never evicted.')+ key = self.epoch + ':' + str(uuid.uuid4())+ entry = {'previewId': key, 'epoch': self.epoch, 'sessionId': SESSION_ID, 'app': app,+ 'document': doc, 'design': design, 'target': wanted, 'sourceTarget': source,+ 'sourceDocument': source_doc, 'sourceDesign': source_design, 'sourceFlags': source_flags,+ 'creationOperationId': creation_id, 'creationDigest': record['requestDigest'], 'creationId': creation,+ 'generation': 0, 'state': 'reserved', 'lease': None, 'backend': None,+ 'initialPresentationState': initial_state, 'finalPresentationState': None,+ 'predecessorPreviewId': predecessor['previewId'] if predecessor is not None else None,+ 'planBytes': MAX_PLAN_BYTES, 'bodyBudget': backend.MAX_BODIES,+ 'planId': None, 'samplesDigest': None, 'sampleCount': None, 'sampleIndex': None,+ 'activationIssued': False, 'factoryMayHaveBeenIssued': False, 'nativeWorkInFlight': False,+ 'lastOperationId': args['operationId'], 'lastOperation': args['op'], 'lastRequestDigest': fingerprint(args), 'lastReturned': False,+ 'lastExpectedResultDigest': None,+ 'lastObserved': None, 'priorUnresolved': None, 'disposition': None,+ 'disposalAttempted': False, 'disposalOutcome': 'not_attempted'}+ self.entries[key] = entry+ self.claims[wanted['documentId']] = key+ return entry++ def begin(self, entry, args):+ require(not entry['nativeWorkInFlight'], 'preview_in_progress', 'A native preview invocation is still in flight.')+ entry['generation'] += 1+ entry['lastOperationId'] = args['operationId']+ entry['lastOperation'] = args['op']+ entry['lastExpectedResultDigest'] = None+ entry['lastRequestDigest'] = fingerprint(args)+ entry['lastReturned'] = False+ entry['nativeWorkInFlight'] = True++ def last_history(self, entry, history):+ if entry['lastOperationId'] is None:+ return {'terminalRecorded': False, 'reason': 'no_effect_operation'}+ try:+ record = terminal_record(history, entry['lastOperationId'])+ require(record.get('requestDigest') == entry['lastRequestDigest']+ and record['phase'] in ('succeeded', 'failed') and entry['lastExpectedResultDigest'] is not None+ and core.digest(record['result']) == entry['lastExpectedResultDigest'],+ 'preview_history_unresolved', 'Last operation digest or observed terminal result differs.')+ return {'terminalRecorded': True, 'phase': record['phase'], 'operationId': entry['lastOperationId'],+ 'requestDigest': entry['lastRequestDigest']}+ except Exception as error:+ return {'terminalRecorded': False, 'operationId': entry['lastOperationId'],+ 'requestDigest': entry['lastRequestDigest'], 'errorCode': getattr(error, 'code', 'preview_history_unresolved'),+ 'error': str(error)[:512]}++ def existing(self, app, args, history, disposal=False):+ entry = self.entry(app, args)+ require(exact_target(args.get('target')) == entry['target'], 'preview_ownership', 'Presentation target differs from its lease.')+ require(type(args.get('expectedGeneration')) is int and args['expectedGeneration'] == entry['generation'],+ 'preview_generation_conflict', 'Preview generation changed; inspect before another intent.',+ currentGeneration=entry['generation'])+ require(entry['state'] != 'disposed' and not entry['nativeWorkInFlight'], 'preview_state', 'Preview is disposed or in flight.')+ if disposal:+ require(not entry['disposalAttempted'], 'preview_disposal_unresolved',+ 'A prior deletion attempt remains unresolved; a new operation ID cannot authorize another delete.')+ self.lease(entry, cleanup=disposal)+ previous = self.last_history(entry, history)+ if not previous['terminalRecorded']:+ require(disposal and entry['lastReturned'] and entry['lastObserved'] is not None+ and entry['backend'] is not None and entry['backend'].root is not None+ and entry['backend'].state in ('ready', 'retained_failure'),+ 'preview_history_unresolved', 'Prior operation history is unresolved; automatic continuation is disabled.',+ priorOperation=previous)+ entry['backend']._ownership() # Explicit cleanup exception, never adoption.+ previous['observedResultDigest'] = core.digest(entry['lastObserved'])+ if not disposal:+ require(entry['state'] == 'ready', 'preview_state', 'Only a verified ready preview can be sampled or shown.')+ return entry, None if previous['terminalRecorded'] else previous++ def source_check(self, entry):+ doc, design = known_document(entry['app'], entry['sourceTarget'])+ require(same(doc, entry['sourceDocument']) and same(design, entry['sourceDesign'])+ and flags(doc) == entry['sourceFlags'], 'preview_source_changed', 'Source identity/revision/flags changed.')++ def compact(self, entry, scope='operation_completion'):+ return {'kind': 'EXPLODED_PREVIEW', 'receiptScope': scope, 'controllerSchemaVersion': SCHEMA_VERSION,+ 'backendSourceDigest': BACKEND_SOURCE_DIGEST, 'previewId': entry['previewId'], 'controllerEpoch': self.epoch,+ 'sessionId': SESSION_ID, 'presentationTarget': copy.deepcopy(entry['target']),+ 'sourceTarget': copy.deepcopy(entry['sourceTarget']), 'creationOperationId': entry['creationOperationId'],+ 'presentationTargetScope': 'immutable_creation_anchor',+ 'lastVerifiedPresentationRevision': entry['lease'].model_revision if entry['lease'] is not None else entry['initialPresentationState']['revision'],+ 'presentationVerificationSequence': entry['lease'].sequence if entry['lease'] is not None else 0,+ 'presentationFailureObservation': copy.deepcopy(entry['lease'].failure_observation) if entry['lease'] is not None else None,+ 'predecessorPreviewId': entry['predecessorPreviewId'],+ 'observedEmptyTarget': {'documentId': entry['target']['documentId'], 'revision': entry['finalPresentationState']['revision']} if entry['finalPresentationState'] is not None else None,+ 'planId': entry['planId'], 'samplesDigest': entry['samplesDigest'], 'sampleCount': entry['sampleCount'],+ 'state': entry['state'], 'generation': entry['generation'],+ 'sampleIndex': entry['sampleIndex'] if entry['state'] == 'ready' else None,+ 'lastVerifiedSampleIndex': entry['sampleIndex'],+ 'pose': ('sample' if entry['sampleIndex'] is not None else 'home') if entry['state'] == 'ready' else 'unverified',+ 'disposalAttempted': entry['disposalAttempted'], 'disposalOutcome': entry['disposalOutcome'],+ 'lastOperationId': entry['lastOperationId'], 'nativeWorkInFlight': entry['nativeWorkInFlight'],+ 'documentDisposition': 'open_for_caller', 'disposition': entry['disposition'],+ 'sourceMoved': False, 'sourceVisibilityWritten': False, 'rollbackClaimed': False,+ 'rendered': False, 'storyboardCreated': False, 'visualQualified': False,+ 'archivePersistence': 'UNVERIFIED', 'priorOperationUnresolved': entry['priorUnresolved'] is not None,+ 'priorUnresolvedOperation': copy.deepcopy(entry['priorUnresolved'])}++ def release(self, entry, disposition):+ require(self.claims.get(entry['target']['documentId']) == entry['previewId'], 'preview_ownership', 'Exclusive claim changed.')+ require(entry['lease'] is not None, 'preview_ownership', 'Verified presentation lease is required for release.')+ entry['finalPresentationState'] = copy.deepcopy(entry['lease'].check())+ del self.claims[entry['target']['documentId']]+ entry.update(state='disposed', disposition=disposition, planBytes=0, bodyBudget=0)+ entry['backend'] = None # Drop large stored samples/geometry; retain bounded tombstone/native doc proof.++ def no_graphics_release(self, entry):+ require(not entry['factoryMayHaveBeenIssued'], 'preview_reconciliation_required', 'Graphics creation may have been issued.')+ self.lease(entry)+ require(backend.count(entry['design'].rootComponent.customGraphicsGroups, 0) == 0,+ 'preview_reconciliation_required', 'No-graphics absence was not verified.')+ self.release(entry, 'reservation_released_no_graphics')++ def failure(self, entry, error):+ entry['state'] = 'retained_failure'+ recovery_error = None+ if not entry['factoryMayHaveBeenIssued']:+ try:+ self.no_graphics_release(entry)+ except Exception as cleanup_error:+ recovery_error = str(cleanup_error)[:512]+ observed = {'errorCode': getattr(error, 'code', 'preview_native_failed'), 'error': str(error)[:2048],+ 'effectState': 'possible_graphics' if entry['factoryMayHaveBeenIssued'] else 'activation_only' if entry['activationIssued'] else 'none',+ 'preview': self.compact(entry), 'recoveryError': recovery_error}+ observed['preview']['nativeWorkInFlight'] = False # Synchronous native effect already returned/threw.+ entry['lastObserved'] = observed+ entry['lastExpectedResultDigest'] = core.digest({+ 'success': False, 'sessionId': SESSION_ID, 'operationId': entry['lastOperationId'],+ 'errorCode': observed['errorCode'], 'error': observed['error'], 'effectState': observed['effectState'],+ 'preview': observed['preview'], 'recoveryError': recovery_error, 'rollbackClaimed': False})+ raise MechanicalError(observed['errorCode'], observed['error'], effectState=observed['effectState'],+ preview=observed['preview'], recoveryError=recovery_error, rollbackClaimed=False) from error++ def create(self, app, args, history):+ self.guard(args)+ bounded_text(args.get('operationId'), 'operationId')+ bounded_text(args.get('expectedPlanId'), 'expectedPlanId', 64)+ planner = copy.deepcopy(args['planner'])+ planner.update(op='exploded_view_plan', sessionId=SESSION_ID, target=exact_target(args['sourceTarget']))+ # Validate pure planner mathematics before reserving resources/activation.+ request_arguments(planner)+ entry = self.reserve(app, args, history)+ try:+ self.lease(entry)+ self.source_check(entry)+ self.begin(entry, args)+ entry['state'] = 'creating'+ entry['activationIssued'] = True+ require(entry['sourceDocument'].activate() is True and same(app.activeDocument, entry['sourceDocument']),+ 'preview_activation_refused', 'Exact source activation was not acknowledged.')+ self.source_check(entry)+ prepared = backend.prepare(app, planner, args['expectedPlanId'])+ preview = backend.Preview(prepared, entry['lease'])+ entry['backend'] = preview # Retain before any subsequent failure or graphics effect.+ byte_count = retained_json_bytes(prepared, preview)+ require(byte_count <= MAX_PLAN_BYTES, 'preview_capacity', 'Total retained plan/sample JSON exceeds its reserved memory bound.')+ entry.update(planId=prepared.plan_id, samplesDigest=preview.samples_digest,+ sampleCount=len(json.loads(preview._sample_json)), planBytes=byte_count)+ entry['factoryMayHaveBeenIssued'] = True+ preview.create()+ self.source_check(entry)+ entry.update(state='ready', bodyBudget=sum(len(n['graphics']) for n in preview.nodes))+ result = self.compact(entry)+ result['nativeWorkInFlight'] = False+ entry['lastObserved'] = copy.deepcopy(result)+ entry['lastExpectedResultDigest'] = core.digest({+ 'success': True, 'sessionId': SESSION_ID, 'operationId': entry['lastOperationId'],+ 'operation': entry['lastOperation'], 'data': result})+ return result+ except Exception as error:+ self.failure(entry, error)+ finally:+ entry['nativeWorkInFlight'] = False+ entry['lastReturned'] = True++ def action(self, action, app, args, history):+ self.guard(args)+ bounded_text(args.get('operationId'), 'operationId')+ if action == 'sample':+ require(type(args.get('sampleIndex')) is int and args['sampleIndex'] >= 0,+ 'invalid_input', 'Sample index must be a nonnegative integer.')+ entry, unresolved = self.existing(app, args, history, disposal=action == 'dispose')+ preview = entry['backend']+ if action == 'sample':+ require(args['sampleIndex'] < entry['sampleCount'], 'invalid_input', 'Sample index exceeds the immutable stored samples.')+ if action != 'dispose':+ self.source_check(entry)+ preview._guard(); preview._graphics()+ elif preview is not None:+ preview._ownership()+ if action == 'show':+ require(any(same(app.activeDocument, doc) for doc in (entry['sourceDocument'], entry['document'])),+ 'preview_activation_refused', 'Another active document intervened; no activation performed.')+ self.begin(entry, args)+ entry['priorUnresolved'] = unresolved+ entry['state'] = 'updating'+ try:+ if action == 'sample':+ preview.sample(args['sampleIndex'])+ entry['sampleIndex'] = args['sampleIndex']+ self.source_check(entry)+ entry['state'] = 'ready'+ elif action == 'show':+ preview.activate()+ self.source_check(entry)+ entry['state'] = 'ready'+ elif action == 'dispose':+ if preview is None:+ self.no_graphics_release(entry)+ else:+ entry['disposalAttempted'] = True # Before native deletion; never retry uncertain effects.+ entry['disposalOutcome'] = 'unknown'+ preview.dispose()+ entry['disposalOutcome'] = 'verified_absent'+ self.release(entry, 'graphics_disposed')+ result = self.compact(entry)+ result['nativeWorkInFlight'] = False+ entry['lastObserved'] = copy.deepcopy(result)+ entry['lastExpectedResultDigest'] = core.digest({+ 'success': True, 'sessionId': SESSION_ID, 'operationId': entry['lastOperationId'],+ 'operation': entry['lastOperation'], 'data': result})+ return result+ except Exception as error:+ self.failure(entry, error)+ finally:+ entry['nativeWorkInFlight'] = False+ entry['lastReturned'] = True++ def inspect(self, app, args, history):+ entry = self.entry(app, args)+ result = self.compact(entry, 'live_observation')+ result['observedAt'] = time.time()+ result['history'] = self.last_history(entry, history)+ result['usable'] = False+ result['lastKnownDocumentDisposition'] = result['documentDisposition']+ result['documentDisposition'] = 'unknown_current'+ errors = []+ if entry['state'] == 'disposed':+ result['receiptScope'] = 'registry_tombstone'+ result['nativeResourceRights'] = False+ result['nativeObservation'] = 'NOT_PERFORMED'+ result['observationComplete'] = False+ return result+ try:+ self.lease(entry, cleanup=entry['state'] == 'retained_failure')+ result['presentationOwnershipVerified'] = True+ result['documentDisposition'] = 'verified_open_owned'+ preview = entry['backend']+ if preview is not None:+ preview._ownership()+ result['graphicsOwnershipVerified'] = True+ preview._graphics()+ result['graphicsReadbackVerified'] = True+ else:+ result['graphicsCount'] = backend.count(entry['design'].rootComponent.customGraphicsGroups, 1)+ except Exception as error:+ errors.append({'scope': 'presentation', 'code': getattr(error, 'code', 'preview_read_failed'), 'message': str(error)[:512]})+ try:+ self.source_check(entry)+ if entry['backend'] is not None:+ entry['backend'].prepared.check()+ result['sourceUnchanged'] = True+ except Exception as error:+ errors.append({'scope': 'source', 'code': getattr(error, 'code', 'preview_read_failed'), 'message': str(error)[:512]})+ result['errors'] = errors+ result['observationComplete'] = not errors+ result['usable'] = not errors and entry['state'] == 'ready' and result['history']['terminalRecorded'] and not entry['nativeWorkInFlight']+ return result+++_controller = Controller()+++def create(app, args, history):+ return _controller.create(app, args, history)+++def sample(app, args, history):+ return _controller.action('sample', app, args, history)+++def show(app, args, history):+ return _controller.action('show', app, args, history)+++def dispose(app, args, history):+ return _controller.action('dispose', app, args, history)+++def inspect(app, args, history):+ return _controller.inspect(app, args, history)+++WRITES = {'exploded_preview_create': create, 'exploded_preview_sample': sample,+ 'exploded_preview_show': show, 'exploded_preview_dispose': dispose}+READS = {'exploded_preview_inspect': inspect}
addin/AdomBridge/commands/mechanical/exploded_preview_prototype.pyadded+580@@ -0,0 +1,580 @@+"""UNREGISTERED prototype: exact-owned, finite CustomGraphics presentation copies.++No document creation/close, source setters, timers, capture, durable replay or public+operation registration. The native qualification harness supplies an owned blank+presentation lease. All native access, including failed-resource cleanup, stays on+the main thread. Retain the Preview object if create/sample/dispose raises.+"""+import copy+import json+import math+import threading+import uuid++import adsk.core++from . import core+from .core import (SESSION_ID, concrete, digest, document_identity, require,+ resolve, revision, same, target)+from .exploded_view import IDENTITY, exploded_view_plan++PROTOTYPE_ONLY = True+MAX_INSTANCES = 64+MAX_BODIES = 128+MAX_TOPOLOGY = 4096+MAX_VERTICES = 16384+_PREPARED = object()+++def main_thread():+ require(threading.current_thread() is threading.main_thread(), 'wrong_thread',+ 'Preview native access requires the main thread.')+++def count(collection, limit=MAX_TOPOLOGY):+ count = collection.count+ require(type(count) is int and 0 <= count <= limit, 'preview_budget', 'Native collection exceeds the preview bound.')+ return count+++def bounded(collection, limit=MAX_TOPOLOGY):+ return [concrete(collection.item(i)) for i in range(count(collection, limit))]+++def finite(value):+ require(type(value) in (int, float) and math.isfinite(value) and abs(value) <= 1e12,+ 'preview_geometry_invalid', 'Native geometry contains a nonfinite or excessive coordinate.')+ return value+++def point(value):+ return [finite(value.x), finite(value.y), finite(value.z)]+++def body_witness(body, transient):+ require(body is not None and body.isValid and body.objectType == 'adsk::fusion::BRepBody'+ and body.isTransient is transient and body.isSolid is True,+ 'preview_geometry_unsupported', 'Preview requires valid solid BRep bodies with the expected native/copy provenance.')+ box = body.boundingBox+ low, high = point(box.minPoint), point(box.maxPoint)+ require(all(a <= b for a, b in zip(low, high)), 'preview_geometry_invalid', 'Invalid native body bounds.')+ volume = finite(body.volume)+ require(volume > 0, 'preview_geometry_unsupported', 'Solid BRep volume must be positive.')+ return {'volumeCm3': volume, 'boundsCm': [low, high],+ 'faceCount': count(body.faces), 'edgeCount': count(body.edges),+ 'verticesCm': [point(v.geometry) for v in bounded(body.vertices)]}+++def equivalent(left, right):+ """Only geometry floats use tolerance; identity/counts/booleans remain exact."""+ if type(left) in (int, float) and type(right) in (int, float):+ return math.isclose(left, right, rel_tol=1e-10, abs_tol=1e-10)+ if type(left) is not type(right):+ return False+ if isinstance(left, dict):+ return left.keys() == right.keys() and all(equivalent(left[k], right[k]) for k in left)+ if isinstance(left, list):+ return len(left) == len(right) and all(equivalent(a, b) for a, b in zip(left, right))+ return left == right+++def intended_native_values(values):+ require(isinstance(values, list) and len(values) == 16 and all(type(v) in (int, float) and math.isfinite(v) for v in values),+ 'preview_plan_invalid', 'Stored sample matrix is invalid.')+ values = list(values)+ for index in (3, 7, 11):+ values[index] /= 10. # Public root/world mm -> native model cm, exactly once.+ return values+++def native_matrix(values):+ intended = intended_native_values(values)+ result = adsk.core.Matrix3D.create()+ require(result is not None and result.setWithArray(list(intended)) is True,+ 'preview_transform_refused', 'Native matrix construction failed.')+ require(equivalent(list(result.asArray()), intended),+ 'preview_transform_refused', 'Native matrix construction altered the intended stored pose.')+ return result+++def matrix_values(entity):+ result = list(entity.transform.asArray())+ require(len(result) == 16 and all(type(v) in (int, float) and math.isfinite(v) for v in result),+ 'preview_transform_refused', 'Native graphics transform is unavailable.')+ return result+++def source_geometry(design, value, plan):+ """Proxy visibility first, native definition geometry second; flat instances."""+ require(len(plan['instances']) <= MAX_INSTANCES, 'preview_budget', 'Preview supports at most 64 physical instances.')+ rows = [(None, design.rootComponent, None)]+ for row in plan['instances']:+ occurrence = resolve(design, value, row['occurrence'], ('Occurrence',))+ component = resolve(design, value, row['component'], ('Component',))+ require(same(occurrence.component, component) and occurrence.fullPathName == row['path'],+ 'preview_source_changed', 'Instance/component attachment changed.')+ require(occurrence.isVisible is True, 'preview_visibility_unsupported', 'Hidden or effectively hidden occurrences are unsupported.')+ timeline = occurrence.timelineObject+ require(timeline is None or timeline.isSuppressed is False,+ 'preview_visibility_unsupported', 'Suppressed occurrences are unsupported.')+ rows.append((row['instanceId'], component, occurrence))+ output, body_count, vertices = [], 0, 0+ for key, component, occurrence in rows:+ require(component is not None and component.isValid and same(component.parentDesign, design),+ 'preview_source_changed', 'Component is not owned by the source Design.')+ require(count(component.meshBodies, MAX_BODIES) == 0,+ 'preview_geometry_unsupported', 'Mesh bodies are unsupported; no partial geometry omission is permitted.')+ bodies, witnesses = [], []+ for native in bounded(component.bRepBodies, MAX_BODIES):+ body_count += 1+ require(body_count <= MAX_BODIES, 'preview_budget', 'Preview exceeds 128 body copies.')+ require(native.nativeObject is None and same(native.parentComponent, component),+ 'preview_geometry_unsupported', 'Copy inputs must be definition-space native bodies.')+ visible = concrete(native.createForAssemblyContext(occurrence)) if occurrence is not None else native+ require(visible is not None and visible.isValid and visible.isVisible is True,+ 'preview_visibility_unsupported', 'Hidden or effectively hidden bodies are unsupported.')+ if occurrence is not None:+ require(same(visible.nativeObject, native) and same(visible.assemblyContext, occurrence),+ 'preview_source_changed', 'Body proxy does not belong to the exact instance.')+ witness = body_witness(native, False)+ vertices += len(witness['verticesCm'])+ require(vertices <= MAX_VERTICES, 'preview_budget', 'Preview exceeds 16384 copied vertices.')+ bodies.append(native); witnesses.append(witness)+ output.append({'instanceId': key, 'bodies': bodies, 'witnesses': witnesses})+ require(body_count > 0, 'preview_geometry_unsupported', 'Source has no supported solid bodies.')+ return output+++class Prepared:+ """Server-only preparation; callers receive identifiers, never writable poses."""+ def __init__(self, app, args, expected_plan_id):+ main_thread()+ args = copy.deepcopy(args)+ result = exploded_view_plan(app, args)+ require(result['planId'] == expected_plan_id, 'preview_plan_mismatch', 'Regenerated plan differs from the expected plan.')+ self.app = app+ self.document, self.design, value = target(app, args)+ self._result_json = json.dumps(result, sort_keys=True, allow_nan=False)+ self._target = copy.deepcopy(value)+ self._flags = (self.document.isSaved, self.document.isModified)+ require(all(type(v) is bool for v in self._flags), 'preview_source_changed', 'Source document flags are unavailable.')+ self._geometry = source_geometry(self.design, value, result['plan'])+ self._witness = [r['witnesses'] for r in self._geometry]+ self._seal = _PREPARED+ self.check()++ @property+ def plan_id(self):+ return json.loads(self._result_json)['planId']++ def check(self):+ main_thread()+ result = json.loads(self._result_json)+ require(result['plan']['sessionId'] == SESSION_ID, 'session_mismatch', 'Preview belongs to an earlier add-in session.')+ require(self.document.isValid and self.design.isValid and same(self.design.parentDocument, self.document)+ and sum(same(d, self.document) for d in bounded(self.app.documents, 128)) == 1,+ 'preview_source_changed', 'Exact source document is no longer open.')+ require(document_identity(self.document) == self._target['documentId']+ and revision(self.design) == self._target['revision']+ and all(type(v) is bool for v in (self.document.isSaved, self.document.isModified))+ and (self.document.isSaved, self.document.isModified) == self._flags,+ 'preview_source_changed', 'Source identity, revision or flags changed.')+ current = source_geometry(self.design, self._target, result['plan'])+ require([r['witnesses'] for r in current] == self._witness,+ 'preview_source_changed', 'Source geometry/visibility changed.')+ # Refresh handles through the guarded refs, never use a stale body wrapper.+ self._geometry = current+++def prepare(app, planner_args, expected_plan_id):+ return Prepared(app, planner_args, expected_plan_id)+++def all_attributes(entity):+ rows = []+ for attribute in bounded(entity.attributes, 256):+ row = [attribute.groupName, attribute.name, attribute.value]+ require(all(isinstance(v, str) and len(v) <= 4096 for v in row),+ 'preview_budget', 'Presentation attribute is unbounded.')+ rows.append(row)+ return sorted(rows)+++def origin_witness(root):+ """The seven built-in origin entities are outside construction collections."""+ rows = []+ for field, kind in (('xConstructionAxis', 'ConstructionAxis'), ('yConstructionAxis', 'ConstructionAxis'),+ ('zConstructionAxis', 'ConstructionAxis'), ('xYConstructionPlane', 'ConstructionPlane'),+ ('xZConstructionPlane', 'ConstructionPlane'), ('yZConstructionPlane', 'ConstructionPlane'),+ ('originConstructionPoint', 'ConstructionPoint')):+ entity = concrete(getattr(root, field))+ require(entity is not None and entity.isValid is True and entity.objectType == 'adsk::fusion::' + kind+ and same(entity.component, root) and entity.nativeObject is None and entity.isDeletable is False,+ 'preview_not_blank', 'Exact built-in presentation origin is unavailable.')+ visibility = [entity.isLightBulbOn, entity.isVisible]+ require(all(type(value) is bool for value in visibility), 'preview_not_blank', 'Origin visibility is unavailable.')+ geometry = entity.geometry+ values = point(geometry) if kind == 'ConstructionPoint' else [point(geometry.origin),+ point(geometry.direction if kind == 'ConstructionAxis' else geometry.normal)]+ rows.append([field, core.identity(entity), entity.name, visibility, values, all_attributes(entity)])+ return rows+++def presentation_observation(document, design):+ main_thread()+ flags = None+ try:+ flags = [document.isSaved, document.isModified]+ return _presentation_observation(document, design, flags)+ except Exception as error:+ observations = {'beforeFlags': flags, 'afterFlags': None, 'flagReadError': None}+ try:+ observations['afterFlags'] = [document.isSaved, document.isModified]+ except Exception as flag_error:+ observations['flagReadError'] = str(flag_error)[:512]+ valid = all(isinstance(value, list) and len(value) == 2 and all(type(v) is bool for v in value)+ for value in (flags, observations['afterFlags']))+ observations['flagEffect'] = ('unchanged' if flags == observations['afterFlags'] else 'changed') if valid else 'unknown'+ details = dict(getattr(error, 'details', {}))+ details.update(presentationObservation=observations, rollbackClaimed=False)+ raise core.MechanicalError(getattr(error, 'code', 'preview_revision_unavailable'), str(error)[:1024], **details) from error+++def _presentation_observation(document, design, flags):+ """Complete bounded empty-model payload; only the root's opaque revision varies.++ Rebuild the exact current core aggregate for this one-component empty case,+ and prove its digest equals the unmodified core revision before and after.+ No global monkeypatch, target refresh, or source revision exemption.+ """+ from .assembly_state import component_state+ from .readback import construction_state+ from .configurations import snapshot+ require(document.isValid is True and design.isValid is True and same(design.parentDocument, document),+ 'preview_ownership', 'Exact presentation document/Design is unavailable.')+ require(all(type(v) is bool for v in flags) and flags[0] is False,+ 'preview_not_blank', 'Presentation flags must be known and unsaved.')+ before = revision(design)+ root = design.rootComponent+ require(root is not None and root.isValid is True and root.objectType == 'adsk::fusion::Component'+ and same(root.parentDesign, design), 'preview_ownership', 'Exact presentation root is unavailable.')+ components = bounded(design.allComponents, 1)+ require(len(components) == 1 and same(components[0], root), 'preview_not_blank', 'Presentation must have exactly its root.')+ empty_fields = ('bRepBodies', 'meshBodies', 'occurrences', 'allOccurrences', 'sketches', 'features',+ 'joints', 'asBuiltJoints', 'jointOrigins', 'rigidGroups', 'motionLinks',+ 'constructionAxes', 'constructionPlanes', 'constructionPoints', 'modelParameters',+ 'canvases', 'decals')+ for name in empty_fields:+ require(count(getattr(root, name), 0) == 0, 'preview_not_blank', 'Presentation contains unsupported ' + name + '.')+ require(count(design.allParameters, 0) == 0 and count(design.timeline, 0) == 0+ and type(design.timeline.markerPosition) is int and design.timeline.markerPosition == 0+ and count(design.snapshots, 0) == 0 and design.snapshots.hasPendingSnapshot is False,+ 'preview_not_blank', 'Presentation contains model parameters, history or positions.')+ configuration = snapshot(design, strict=True)+ require(configuration['complete'] is True and configuration['configured'] is False+ and configuration['isConfiguration'] is False, 'preview_not_blank', 'Configured presentations are unsupported.')+ attributes = {name: all_attributes(entity) for name, entity in (('document', document), ('design', design), ('root', root))}+ require(all(isinstance(value, str) and len(value) <= 4096 for value in (root.name, root.partNumber, root.description)),+ 'preview_not_blank', 'Presentation metadata is unavailable or unbounded.')+ folders = {}+ for name in ('isBodiesFolderLightBulbOn', 'isConstructionFolderLightBulbOn', 'isOriginFolderLightBulbOn',+ 'isSketchFolderLightBulbOn', 'isJointsFolderLightBulbOn', 'isJointOriginsFolderLightBulbOn',+ 'isCanvasFolderLightBulbOn', 'isDecalFolderLightBulbOn'):+ value = getattr(root, name)+ require(type(value) is bool, 'preview_not_blank', 'Presentation visibility is unavailable.')+ folders[name] = value+ native_revision = root.revisionId+ require(isinstance(native_revision, str) and 0 < len(native_revision) <= 256,+ 'preview_not_blank', 'Native presentation revision is unavailable.')+ payload = [[[core.identity(root), native_revision, root.name, [], [], component_state(root), [], construction_state(root)]],+ [], [], design.timeline.count, design.timeline.markerPosition, configuration]+ require(digest(payload) == before, 'preview_revision_unavailable', 'Empty presentation payload does not match the actual aggregate.')+ stable_payload = copy.deepcopy(payload)+ stable_payload[0][0][1] = None # The sole classified, opaque native revision slot; never a missing-data sentinel.+ witness = {'aggregate': stable_payload, 'attributes': attributes, 'folders': folders, 'origin': origin_witness(root),+ 'documentName': document.name, 'creationId': document.creationId,+ 'designType': int(design.designType), 'designIntent': int(design.designIntent)}+ wire = json.dumps(witness, sort_keys=True, allow_nan=False)+ require(len(wire.encode('utf-8')) <= 65536, 'preview_budget', 'Presentation witness exceeds its bound.')+ after = revision(design)+ final_flags = [document.isSaved, document.isModified]+ require(all(type(v) is bool for v in final_flags) and final_flags == flags and after == before,+ 'preview_revision_unavailable', 'Presentation observation changed or could not establish a stable revision/flags.')+ return {'revision': after, 'nativeRevision': native_revision, 'witnessDigest': digest(witness), 'flags': flags,+ 'rootId': core.identity(root)}+++class OwnedPresentationLease:+ """Exact native doc + independent ownership receipt; no name-based selection.++ verify_owner() must return success:true, ownerId, sessionId, documentId after+ checking the existing native run registry. This prototype never creates,+ registers, archives or closes a document; the qualification harness owns that.+ """+ def __init__(self, app, document, design, owner_id, verify_owner, expected_state=None):+ main_thread()+ require(isinstance(owner_id, str) and 1 <= len(owner_id) <= 128 and callable(verify_owner),+ 'preview_ownership', 'A bounded owner ID and exact ownership verifier are required.')+ self.app, self.document, self.design = app, document, design+ self.root_component = design.rootComponent+ self.owner_id, self.verify_owner = owner_id, verify_owner+ self.document_id = document_identity(document)+ self.session_id = SESSION_ID+ self.sequence = 0+ self._effect = None+ self.cleanup_state = None+ self.failure_observation = None+ observed = self.observe()+ require(expected_state is None or observed == expected_state,+ 'preview_not_blank', 'Presentation changed after reservation.')+ self._accept(observed)++ def observe(self):+ main_thread()+ require(self.session_id == SESSION_ID, 'session_mismatch', 'Presentation lease belongs to another session.')+ receipt = self.verify_owner()+ require(isinstance(receipt, dict) and set(receipt) == {'success', 'ownerId', 'sessionId', 'documentId'}+ and receipt.get('success') is True and receipt.get('ownerId') == self.owner_id+ and receipt.get('sessionId') == SESSION_ID and receipt.get('documentId') == self.document_id,+ 'preview_ownership', 'Independent exact-document ownership was not verified.')+ doc, design = self.document, self.design+ require(doc.isValid is True and design.isValid is True and same(design.parentDocument, doc)+ and document_identity(doc) == self.document_id and same(design.rootComponent, self.root_component)+ and sum(same(d, doc) for d in bounded(self.app.documents, 128)) == 1,+ 'preview_ownership', 'Owned presentation document/root is unavailable or differs.')+ return presentation_observation(doc, design)++ def _accept(self, observed):+ self.verified_state = copy.deepcopy(observed)+ self.model_revision = observed['revision']++ def check(self, cleanup=False):+ main_thread()+ observed = self.observe()+ expected = self.cleanup_state if cleanup and self.cleanup_state is not None else self.verified_state+ require(observed == expected, 'preview_not_blank', 'Presentation revision or non-graphics model changed.')+ return observed++ def begin_effect(self, cleanup=False):+ observed = self.check(cleanup=cleanup)+ require(self._effect is None, 'preview_state', 'A presentation effect is already in flight.')+ self._effect = copy.deepcopy(observed)+ return self._effect++ def _after_effect(self, token):+ require(token is self._effect and token is not None, 'preview_state', 'Effect does not belong to the current lease.')+ observed = self.observe()+ # Full payload digest is already proven by observe(). Only the opaque+ # root Component revision slot may differ; flags and every witness field stay exact.+ require(all(observed[key] == token[key] for key in ('witnessDigest', 'flags', 'rootId')),+ 'preview_not_blank', 'Owned graphics changed non-graphics presentation state.',+ presentationBefore=token, presentationAfter=observed, rollbackClaimed=False)+ return observed++ def finish_effect(self, token):+ observed = self._after_effect(token)+ self._accept(observed)+ self.sequence += 1+ self.cleanup_state = None+ self._effect = None++ def failed_effect(self, token, ownership):+ if token is None or self._effect is not token:+ return # No effect, or the graphics boundary already completed.+ self.cleanup_state = None+ try:+ ownership()+ self.cleanup_state = self._after_effect(token)+ self.failure_observation = {'cleanupOnly': True, 'observedRevision': self.cleanup_state['revision']}+ except Exception as error:+ self.failure_observation = {'cleanupOnly': False, 'error': str(error)[:512],+ 'presentationObservation': getattr(error, 'details', {}).get('presentationObservation'),+ 'observedState': getattr(error, 'details', {}).get('presentationAfter')}+ finally:+ self._effect = None+++class Preview:+ """Retain this object on failure: .state/.root identify partial owned resources."""+ def __init__(self, prepared, lease):+ main_thread()+ require(isinstance(prepared, Prepared) and prepared._seal is _PREPARED and isinstance(lease, OwnedPresentationLease),+ 'preview_ownership', 'Use server preparation and an exact-owned presentation lease.')+ require(same(prepared.app, lease.app) and not same(prepared.document, lease.document),+ 'preview_ownership', 'Source and presentation must be different documents in the same application.')+ self.prepared, self.lease = prepared, lease+ self.preview_id = str(uuid.uuid4())+ self.root = None+ self.nodes = []+ self.state = 'prepared'+ self.sample_index = None+ self._root_id_written = False+ self._delete_issued = False+ self._sample_json = json.dumps(json.loads(prepared._result_json)['samples'], sort_keys=True, allow_nan=False)+ self.samples_digest = digest(json.loads(self._sample_json))++ def _guard(self):+ main_thread()+ self.lease.check()+ self.prepared.check()++ def _ownership(self):+ require(self.root is not None and self.root.isValid is True+ and self.root.objectType == 'adsk::fusion::CustomGraphicsGroup'+ and (not self._root_id_written or self.root.id == 'AdomExplodedPreview:' + self.preview_id),+ 'preview_ownership', 'Exact retained graphics root is unavailable or changed.')+ require(same(self.lease.design.rootComponent, self.lease.root_component),+ 'preview_ownership', 'Exact retained presentation root Component changed.')+ roots = bounded(self.lease.root_component.customGraphicsGroups, 1)+ require(len(roots) == 1 and same(roots[0], self.root), 'preview_ownership', 'Presentation graphics inventory changed.')+ children = bounded(self.root, MAX_INSTANCES + 1)+ require(len(children) == len(self.nodes), 'preview_ownership', 'Owned child inventory changed.')+ for actual, node in zip(children, self.nodes):+ group = node['group']+ require(same(actual, group) and group.isValid and same(group.parent, self.root),+ 'preview_ownership', 'Exact owned graphics subgroup changed.')+ bodies = bounded(group, MAX_BODIES)+ require(len(bodies) == len(node['graphics']) and all(same(a, b) and a.isValid and same(a.parent, group)+ for a, b in zip(bodies, node['graphics'])),+ 'preview_ownership', 'Exact owned graphics body inventory changed.')++ def _graphics(self):+ self._ownership()+ require(self.root.isSelectable is False and self.root.isChildrenSelectable is False+ and self.root.isVisible is True and matrix_values(self.root) == IDENTITY,+ 'preview_ownership', 'Owned root graphics properties changed.')+ for node in self.nodes:+ group = node['group']+ require(group.id == node['id'] and group.isVisible is True+ and equivalent(matrix_values(group), node['matrix']),+ 'preview_ownership', 'Owned graphics group or last applied transform changed.')+ require(len(node['graphics']) == len(node['witnesses']), 'preview_copy_mismatch', 'Incomplete geometry copying.')+ for graphic, witness in zip(node['graphics'], node['witnesses']):+ require(graphic.isVisible is True and matrix_values(graphic) == IDENTITY+ and equivalent(body_witness(graphic.bRepBody, True), witness),+ 'preview_copy_mismatch', 'Owned copied geometry changed or copy readback differs.')++ def create(self):+ main_thread()+ require(self.state == 'prepared', 'preview_state', 'Creation cannot be retried or replayed.')+ self._guard()+ require(self.lease.design.rootComponent.customGraphicsGroups.count == 0,+ 'preview_not_blank', 'Presentation already contains graphics.')+ token = self.lease.begin_effect()+ self.state = 'creating'+ try:+ self.root = self.lease.design.rootComponent.customGraphicsGroups.add()+ require(self.root is not None and self.root.isValid, 'preview_create_unknown', 'Graphics root creation could not be verified.')+ self.root.id = 'AdomExplodedPreview:' + self.preview_id+ require(self.root.id == 'AdomExplodedPreview:' + self.preview_id, 'preview_create_unknown', 'Root ownership ID setter was ignored.')+ self._root_id_written = True+ self.root.isSelectable = False+ self.root.isChildrenSelectable = False+ result = json.loads(self.prepared._result_json)+ homes = {r['instanceId']: r['homeMatrix'] for r in result['plan']['instances']}+ for row in self.prepared._geometry:+ group = self.root.addGroup()+ require(group is not None, 'preview_create_unknown', 'Graphics subgroup creation could not be verified.')+ node = {'group': group, 'id': row['instanceId'] or 'fixed-root', 'graphics': [],+ 'witnesses': row['witnesses'], 'matrix': list(IDENTITY), 'instanceId': row['instanceId']}+ self.nodes.append(node) # Keep exact ownership before ANY native getter/setter.+ require(group.isValid, 'preview_create_unknown', 'Returned graphics subgroup is invalid.')+ group.id = node['id']+ for body in row['bodies']:+ graphic = group.addBRepBody(body)+ require(graphic is not None, 'preview_create_unknown', 'Independent BRep graphics creation could not be verified.')+ node['graphics'].append(graphic) # Retain before even isValid may throw.+ require(graphic.isValid, 'preview_create_unknown', 'Returned graphics body is invalid.')+ pose = homes.get(row['instanceId'], IDENTITY)+ node['matrix'] = intended_native_values(pose)+ group.transform = native_matrix(pose)+ self._graphics()+ self.lease.finish_effect(token)+ self._guard()+ self.state = 'ready'+ # A new presentation normally became active in the harness. Return to+ # the exact source; never steal another intervening active document.+ current = self.lease.app.activeDocument+ if same(current, self.lease.document):+ require(self.prepared.document.activate() is True and same(self.lease.app.activeDocument, self.prepared.document),+ 'preview_activation_refused', 'Could not return to the exact source document.')+ else:+ require(same(current, self.prepared.document), 'preview_activation_refused', 'Another active document intervened; no activation performed.')+ self._guard()+ return self.receipt()+ except Exception:+ self.state = 'retained_failure'+ self.lease.failed_effect(token, self._ownership)+ raise++ def sample(self, index):+ main_thread()+ samples = json.loads(self._sample_json)+ require(type(index) is int and 0 <= index < len(samples), 'invalid_input', 'Sample requires an exact stored integer index.')+ require(self.state == 'ready', 'preview_state', 'Sampling requires a verified ready preview.')+ self._guard(); self._graphics()+ poses = {r['instanceId']: r['matrix'] for r in samples[index]['poses']}+ token = self.lease.begin_effect()+ self.state = 'sampling'+ try:+ for node in self.nodes:+ pose = poses[node['instanceId']] if node['instanceId'] is not None else IDENTITY+ node['matrix'] = intended_native_values(pose)+ node['group'].transform = native_matrix(pose)+ self._graphics(); self.lease.finish_effect(token); self._guard()+ self.sample_index = index+ self.state = 'ready'+ return self.receipt()+ except Exception:+ self.state = 'retained_failure'+ self.lease.failed_effect(token, self._ownership)+ raise++ def activate(self):+ main_thread()+ require(self.state == 'ready', 'preview_state', 'Only a verified ready preview can be activated.')+ self._guard(); self._graphics()+ require(any(same(self.lease.app.activeDocument, doc) for doc in (self.prepared.document, self.lease.document)),+ 'preview_activation_refused', 'Another active document intervened; no activation performed.')+ require(self.lease.document.activate() is True and same(self.lease.app.activeDocument, self.lease.document),+ 'preview_activation_refused', 'Presentation activation was not acknowledged.')+ self._guard(); self._graphics()+ # No foreground/viewport/camera settings or automatic capture.+ return self.receipt()++ def dispose(self):+ main_thread()+ self.lease.check(cleanup=True) # Source may have closed; disposal still requires exact owned presentation.+ require(self.state in ('ready', 'retained_failure'), 'preview_state', 'Only retained owned graphics can be disposed.')+ # Failed samples may have partial transforms or a stale/closed source.+ # Disposal still requires every native descendant to be exactly owned.+ self._ownership()+ require(not self._delete_issued, 'preview_dispose_unknown', 'An earlier native deletion attempt remains unresolved.')+ token = self.lease.begin_effect(cleanup=True)+ self.state = 'disposing'+ try:+ self._delete_issued = True+ require(self.root.deleteMe() is True, 'preview_dispose_unknown', 'Owned graphics deletion was not acknowledged.')+ require(self.lease.design.rootComponent.customGraphicsGroups.count == 0,+ 'preview_dispose_unknown', 'Owned graphics absence could not be verified.')+ require(self.root.isValid is False, 'preview_dispose_unknown', 'Deleted retained root remains valid.')+ self.lease.finish_effect(token)+ self.state = 'disposed'+ return self.receipt()+ except Exception:+ self.state = 'retained_failure'+ self.lease._effect = None # Delete outcome cannot authorize another attempt.+ self.lease.cleanup_state = None+ raise++ def receipt(self):+ main_thread()+ return {'prototypeOnly': True, 'previewId': self.preview_id, 'planId': self.prepared.plan_id,+ 'samplesDigest': self.samples_digest, 'sessionId': self.lease.session_id,+ 'sourceTarget': copy.deepcopy(self.prepared._target), 'presentationDocumentId': self.lease.document_id,+ 'state': self.state, 'sampleIndex': self.sample_index, 'groupCount': len(self.nodes),+ 'bodyCopyCount': sum(len(n['graphics']) for n in self.nodes),+ 'sourceMoved': False, 'sourceVisibilityWritten': False, 'storyboardCreated': False,+ 'visualQualified': False, 'archivePersistence': 'UNVERIFIED',+ 'copyWitness': 'solid volume, bounds, topology counts and indexed vertex coordinates; appearances/textures unqualified',+ 'documentCleanup': 'owned caller responsibility; no automatic close or global undo'}
addin/AdomBridge/commands/mechanical/exploded_view.pyadded+220@@ -0,0 +1,220 @@+"""Read-only exploded assembly plans. Translation math never moves native objects."""+import json+import math+import threading++from .core import SESSION_ID, concrete, digest, identity, ref, require, resolve, same, target+from .configurations import provenance, snapshot as configuration_snapshot++MAX_INSTANCES = 256+MAX_STEPS = 32+MAX_MOVES = 128+MAX_SAMPLES = 64+MAX_POSES = 4096+MAX_OUTPUT_BYTES = 2 * 1024 * 1024+IDENTITY = [1.,0.,0.,0., 0.,1.,0.,0., 0.,0.,1.,0., 0.,0.,0.,1.]+++def bounded(collection):+ count = collection.count+ require(type(count) is int and 0 <= count <= MAX_INSTANCES, 'plan_budget', 'Occurrence collection exceeds 256 instances or has an invalid count.')+ return [concrete(collection.item(i)) for i in range(count)]+++def text(value, limit=2048):+ require(isinstance(value,str) and len(value)<=limit, 'plan_identity_unavailable', 'Instance metadata is unavailable or exceeds its bound.')+ return value+++def matrix(value):+ out = list(value.asArray())+ require(len(out)==16 and all(type(v) in (int,float) and math.isfinite(v) for v in out),+ 'plan_frame_invalid', 'Home pose must be a complete finite matrix.')+ require(all(abs(out[12+i]-v)<=1e-9 for i,v in enumerate((0,0,0,1))), 'plan_frame_invalid', 'Home pose is not affine.')+ axes = [[out[r*4+c] for r in range(3)] for c in range(3)]+ require(all(abs(sum(v*v for v in axis)-1)<=1e-6 for axis in axes)+ and all(abs(sum(a*b for a,b in zip(axes[i],axes[j])))<=1e-6 for i,j in ((0,1),(0,2),(1,2))),+ 'plan_frame_invalid', 'Home pose is not orthonormal; scaled instances are unsupported.')+ a,b,c=axes+ cross=[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]+ require(sum(x*y for x,y in zip(cross,c))>1-1e-6, 'plan_frame_invalid', 'Reflected home poses are unsupported.')+ for index in (3,7,11):+ out[index] *= 10+ require(math.isfinite(out[index]) and abs(out[index])<=1e9, 'plan_frame_invalid', 'Home translation exceeds 1e9 mm.')+ return out+++def capture(design, value):+ """Capture the actual root-context tree; never infer parents from names."""+ root = design.rootComponent+ candidates = bounded(root.allOccurrences)+ by_path = {}+ for occurrence in candidates:+ require(occurrence is not None and occurrence.isValid and occurrence.objectType=='adsk::fusion::Occurrence',+ 'plan_identity_unavailable', 'Invalid occurrence in root-context inventory.')+ path=text(occurrence.fullPathName)+ require(path and path not in by_path, 'ambiguous_context', 'Every root-context occurrence needs a unique full path.')+ by_path[path]=occurrence+ rows, seen, handles = [], set(), {}+ def walk(occurrence,parent,depth):+ require(depth<=32, 'plan_budget', 'Occurrence hierarchy exceeds 32 levels.')+ path=text(occurrence.fullPathName)+ require(path in by_path and same(occurrence,by_path[path]) and path not in seen,+ 'ambiguous_context', 'Child traversal does not identify one exact root-context instance.')+ occurrence=by_path[path]+ context=concrete(occurrence.assemblyContext)+ require((parent is None and context is None) or+ (parent is not None and context is not None and text(context.fullPathName)==parent['path']+ and same(context,handles[parent['instanceId']])),+ 'invalid_context', 'Occurrence parent context disagrees with the native hierarchy.')+ comp=concrete(occurrence.component)+ require(comp is not None and comp.isValid and same(comp.parentDesign,design), 'wrong_component', 'Occurrence component belongs to another design.')+ comp_id=identity(comp)+ instance_id=digest([SESSION_ID,value['documentId'],path,comp_id])+ row={'instanceId':instance_id,'path':path,'occurrence':ref(occurrence,value),+ 'parentInstanceId':parent['instanceId'] if parent else None,+ 'componentId':comp_id,'component':ref(comp,value),'componentName':text(comp.name,256),+ 'partNumber':text(comp.partNumber,256),'homeMatrix':matrix(occurrence.transform2)}+ rows.append(row);seen.add(path);handles[instance_id]=occurrence+ for child in sorted(bounded(occurrence.childOccurrences),key=lambda item:text(item.fullPathName)):+ walk(child,row,depth+1)+ for occurrence in sorted(bounded(root.occurrences),key=lambda item:text(item.fullPathName)):+ walk(occurrence,None,1)+ require(seen==set(by_path), 'invalid_context', 'Native hierarchy did not cover every root-context occurrence.')+ # Numbers are presentation labels within this source-bound plan, not BOM quantities.+ labels={}+ for row in rows:+ labels.setdefault(row['componentId'],'C'+str(len(labels)+1).zfill(3))+ row['label']={'item':labels[row['componentId']],'instance':row['path'],+ 'componentId':row['componentId'],'partNumber':row['partNumber'],+ 'scope':'presentation component label; not an engineering BOM item number'}+ root_bodies=root.bRepBodies.count+ require(type(root_bodies) is int and 0<=root_bodies<=MAX_INSTANCES,'plan_budget','Root body inventory exceeds its bound.')+ root_group={'componentId':identity(root),'component':ref(root,value),'bodyCount':root_bodies,+ 'homeMatrix':list(IDENTITY),'selectable':False,'scope':'fixed root-owned direct bodies'}+ return {'instances':rows,'rootBodyGroup':root_group}, handles+++def request_arguments(args):+ require(args.get('unit')=='mm', 'invalid_input', 'Exploded planning distances require explicit mm.')+ playback=args.get('playback','explode')+ require(playback in ('explode','assemble'), 'invalid_input', 'playback must be explode or assemble.')+ steps=args.get('steps')+ require(isinstance(steps,list) and 1<=len(steps)<=MAX_STEPS,'invalid_input','Supply 1โ32 ordered steps.')+ total=0.;count=0+ for step in steps:+ require(isinstance(step,dict) and set(step)<= {'durationSeconds','moves','title','instruction'} and {'durationSeconds','moves'}<=set(step),+ 'invalid_input','Each step requires durationSeconds and moves.')+ duration=step['durationSeconds']+ require(type(duration) in (int,float) and math.isfinite(duration) and 0<duration<=3600,+ 'invalid_input','Step duration must be finite, positive and at most 3600 seconds.')+ if 'title' in step:require(isinstance(step['title'],str) and len(step['title'])<=256,'invalid_input','Step title must be at most 256 characters.')+ if 'instruction' in step:require(isinstance(step['instruction'],str) and len(step['instruction'])<=1024,'invalid_input','Step instruction must be at most 1024 characters.')+ end=total+duration+ require(math.isfinite(end) and end>total,+ 'invalid_input', 'Step duration is too small to represent after preceding steps.')+ total=end+ moves=step['moves'];require(isinstance(moves,list) and 1<=len(moves)<=MAX_MOVES,'invalid_input','Each step requires 1โ128 moves.')+ count+=len(moves)+ for move in moves:+ require(isinstance(move,dict) and set(move)=={'occurrence','direction','distance','frame'}, 'invalid_input','Move fields must be occurrence, direction, distance and frame.')+ require(move['frame'] in ('world','parent','local'),'invalid_input','Unknown translation frame.')+ direction=move['direction'];distance=move['distance']+ require(isinstance(direction,list) and len(direction)==3 and all(type(v) in (int,float) and math.isfinite(v) and abs(v)<=1e9 for v in direction),+ 'invalid_input','Direction requires three finite bounded numbers.')+ require(math.hypot(*direction)>1e-12,'invalid_input','Direction must be nonzero.')+ require(type(distance) in (int,float) and math.isfinite(distance) and 0<distance<=1e6,+ 'invalid_input','Distance must be positive and at most 1e6 mm; use direction for sign.')+ require(count<=MAX_MOVES and total<=3600,'plan_budget','Plan exceeds 128 moves or 3600 seconds.')+ times=args.get('sampleTimes',[])+ require(isinstance(times,list) and len(times)<=MAX_SAMPLES-2 and all(type(v) in (int,float) and math.isfinite(v) and 0<=v<=total for v in times),+ 'invalid_input','Supply at most 62 finite sample times within the plan duration.')+ # Endpoints always appear, and exact endpoint handling preserves home matrices.+ return total, sorted(set([0.,total]+times)), playback+++def compile_steps(design,value,snapshot,handles,steps):+ lookup={row['instanceId']:row for row in snapshot['instances']}+ by_path={row['path']:row for row in snapshot['instances']}+ result=[];start=0.+ for index,step in enumerate(steps):+ compiled=[];seen=set()+ for move in step['moves']:+ occurrence=resolve(design,value,move['occurrence'],('Occurrence',))+ path=text(occurrence.fullPathName);row=by_path.get(path)+ require(row is not None and same(occurrence,handles[row['instanceId']]), 'invalid_context','Selected reference is not this exact root-context instance.')+ key=row['instanceId'];require(key not in seen,'invalid_input','An instance may occur only once within a simultaneous step.');seen.add(key)+ norm=math.hypot(*move['direction']);direction=[v/norm for v in move['direction']]+ frame=IDENTITY if move['frame']=='world' else row['homeMatrix'] if move['frame']=='local' else lookup[row['parentInstanceId']]['homeMatrix'] if row['parentInstanceId'] else IDENTITY+ world=[sum(frame[r*4+c]*direction[c] for c in range(3))*move['distance'] for r in range(3)]+ compiled.append({'instanceId':key,'frame':move['frame'],'direction':direction,'distance':move['distance'],'worldTranslation':world})+ end=start+step['durationSeconds']+ result.append({'index':index,'title':step.get('title','Step '+str(index+1)), 'instruction':step.get('instruction',''), 'startSeconds':start,'endSeconds':end,+ 'durationSeconds':step['durationSeconds'],'moves':compiled})+ start=end+ return result+++def sample_plan(snapshot,steps,times,total,playback):+ """Pure deterministic sampling from immutable root home matrices."""+ require(len(snapshot['instances'])*len(times)<=MAX_POSES,'plan_budget','Plan samples exceed 4096 instance poses.')+ results=[]+ for time in times:+ forward=time if playback=='explode' else total-time+ own={row['instanceId']:[0.,0.,0.] for row in snapshot['instances']}+ progress=[]+ for step in steps:+ fraction=0. if forward<=step['startSeconds'] else 1. if forward>=step['endSeconds'] else (forward-step['startSeconds'])/step['durationSeconds']+ progress.append({'stepIndex':step['index'],'fraction':fraction})+ for move in step['moves']:+ for axis in range(3):own[move['instanceId']][axis]+=move['worldTranslation'][axis]*fraction+ composed={};poses=[]+ for row in snapshot['instances']:+ key=row['instanceId'];parent=composed[row['parentInstanceId']] if row['parentInstanceId'] else [0.,0.,0.]+ offset=[own[key][axis]+parent[axis] for axis in range(3)];composed[key]=offset+ pose=list(row['homeMatrix'])+ if forward!=0:+ for axis,index in enumerate((3,7,11)):pose[index]+=offset[axis]+ require(all(math.isfinite(v) and abs(v)<=2e9 for v in pose),'plan_frame_invalid','Sample pose exceeds finite output bounds.')+ poses.append({'instanceId':key,'matrix':pose,'translationFromHome':offset})+ results.append({'timeSeconds':time,'forwardTimeSeconds':forward,'stepProgress':progress,'poses':poses})+ return results+++def exploded_view_plan(app,args):+ require(threading.current_thread() is threading.main_thread(),'wrong_thread','Exploded plan native reads require the main thread.')+ require(args.get('sessionId')==SESSION_ID,'session_mismatch','Exploded planning requires the current session.')+ wanted=args.get('target')+ require(isinstance(wanted,dict) and isinstance(wanted.get('documentId'),str) and isinstance(wanted.get('revision'),str),+ 'invalid_input','Exploded planning requires exact target documentId and revision.')+ total,times,playback=request_arguments(args)+ document,design,value=target(app,args)+ flags=(document.isSaved,document.isModified)+ require(all(type(v) is bool for v in flags),'plan_state_unavailable','Document flags are unavailable.')+ config=configuration_snapshot(design,strict=True)+ snapshot,handles=capture(design,value)+ steps=compile_steps(design,value,snapshot,handles,args['steps'])+ samples=sample_plan(snapshot,steps,times,total,playback)+ after,_=capture(design,value)+ after_flags=(document.isSaved,document.isModified)+ require(after==snapshot and configuration_snapshot(design,strict=True)==config and all(type(v) is bool for v in after_flags) and after_flags==flags,+ 'plan_source_changed','Model instance, configuration or document state changed during planning.')+ _,_,current=target(app,args)+ require(current==value,'plan_source_changed','Model target changed during planning.')+ plan={'schemaVersion':1,'sessionId':SESSION_ID,'target':value,'configuration':provenance(config),+ 'configurationGraphDigest':digest(config),'units':{'translation':'mm','time':'s'},'space':'root/world',+ 'matrixLayout':'row-major; column-vector multiplication; translation indices 3,7,11',+ 'framePolicy':'directions normalized; axes fixed at home; parent translations inherited once; explicit child translations add',+ 'durationSeconds':total,'steps':steps,**snapshot}+ result={'kind':'PRESENTATION_PLAN','planId':digest(plan),'plan':plan,'playback':playback,'samples':samples,+ 'modelUnchanged':True,'nativeMoved':False,'graphicsCreated':False,'rendered':False,'storyboardCreated':False,+ 'bomQuantities':'not computed or changed; labels link component identities and exact instance paths',+ 'limits':['Explicit directions only; no automatic layout, rotation interpolation, collision or physical disassembly proof.',+ 'Root-owned direct bodies are a fixed group and cannot be selected as occurrences.',+ 'No geometry copies, graphics preview, trails, video, native storyboard or archive-persistence claim.',+ 'Plan identity is bound to this session and model revision; no cross-session reference replay.']}+ require(len(json.dumps(result,allow_nan=False).encode('utf-8'))<=MAX_OUTPUT_BYTES,'plan_budget','Plan exceeds its 2 MiB output bound.')+ return result+++READS={'exploded_view_plan':exploded_view_plan}
addin/AdomBridge/commands/mechanical/exporting.pyadded+93@@ -0,0 +1,93 @@+"""Single-file exports: preflight, isolated staging, verified atomic publication."""+import hashlib+import os+from pathlib import Path+import tempfile++import adsk.fusion++from .core import MechanicalError, require++FORMATS = {+ "step": ("createSTEPExportOptions", (".step", ".stp"), "file"),+ "stl": ("createSTLExportOptions", (".stl",), "entity"),+ "3mf": ("createC3MFExportOptions", (".3mf",), "entity"),+ "f3d": ("createFusionArchiveExportOptions", (".f3d",), "file"),+ "iges": ("createIGESExportOptions", (".igs", ".iges"), "file"),+ "sat": ("createSATExportOptions", (".sat",), "file"),+ "smt": ("createSMTExportOptions", (".smt",), "file"),+ "usdz": ("createUSDExportOptions", (".usdz",), "file"),+ "fbx": ("createFBXExportOptions", (".fbx",), "file"),+ "skp": ("createSketchUpExportOptions", (".skp",), "file"),+}+++def publish_staged(staged, destination, overwrite):+ require(staged.is_file() and staged.stat().st_size > 0, "empty_export", "Exporter produced no nonempty file.")+ size = staged.stat().st_size+ sha = hashlib.sha256(staged.read_bytes()).hexdigest()+ result = {"outputPath": str(destination), "fileSize": size, "sha256": sha,+ "published": True, "overwrite": overwrite}+ if overwrite:+ os.replace(str(staged), str(destination))+ else:+ # Atomic exclusive publication. Checking exists then replace has a race that+ # could overwrite another writer's file. A hardlink never replaces it.+ os.link(str(staged), str(destination))+ try:+ staged.unlink()+ except OSError as exc:+ # The hardlink is the publication commit point. A leftover staging+ # link is cleanup work, not a failed export or preserved destination.+ result["cleanupWarning"] = str(exc)+ return result+++def export_design(design, args):+ fmt = args.get("format", "step").lower()+ require(fmt in FORMATS, "unsupported_format", "Single-file export format is unavailable. Sketch DXF requires an explicit sketch; OBJ sidecars need a bundle exporter.")+ method, suffixes, mode = FORMATS[fmt]+ manager = design.exportManager+ factory = getattr(manager, method, None)+ require(callable(factory), "unsupported_capability", method + " is unavailable in this Fusion build.")+ raw = args.get("outputPath")+ require(isinstance(raw, str) and raw, "invalid_path", "outputPath is required.")+ destination = Path(raw)+ require(destination.is_absolute(), "invalid_path", "outputPath must be absolute on the Fusion host.")+ require(destination.suffix.lower() in suffixes, "extension_mismatch", "File extension does not match requested format.")+ overwrite = args.get("overwrite", False)+ require(isinstance(overwrite, bool), "invalid_input", "overwrite must be a boolean.")+ require(not destination.exists() or overwrite, "destination_exists", "Destination exists; pass overwrite:true to atomically replace it.")+ require(not destination.is_symlink() and not destination.is_dir(), "invalid_path", "Destination must be a regular file path.")+ destination.parent.mkdir(parents=True, exist_ok=True)+ # Entire unique directory prevents the exporter seeing an existing file and+ # raising an overwrite dialog. Same filesystem permits atomic publication.+ out = None+ try:+ with tempfile.TemporaryDirectory(prefix=".adom-export-", dir=str(destination.parent)) as folder:+ staged = Path(folder) / ("model" + destination.suffix)+ options = factory(design.rootComponent, str(staged)) if mode == "entity" else factory(str(staged))+ require(options is not None, "export_preflight_failed", "Exporter did not create options.")+ if hasattr(options, "isViewFit"):+ options.isViewFit = False+ if fmt in ("stl", "3mf"):+ options.sendToPrintUtility = False+ options.isOneFilePerBody = False+ if fmt == "stl":+ refinement = args.get("refinement", "medium")+ settings = {"low": "MeshRefinementLow", "medium": "MeshRefinementMedium", "high": "MeshRefinementHigh"}+ require(refinement in settings, "invalid_input", "refinement must be low, medium or high.")+ options.meshRefinement = getattr(adsk.fusion.MeshRefinementSettings, settings[refinement])+ # STL has no embedded unit declaration; request millimetres explicitly.+ require(hasattr(options, "unitType"), "unsupported_capability", "STL millimetre unit selection is unavailable.")+ options.unitType = adsk.fusion.DistanceUnits.MillimeterDistanceUnits+ require(bool(manager.execute(options)), "export_failed", "Fusion exporter returned false; destination was preserved.")+ out = publish_staged(staged, destination, overwrite)+ out.update({"format": fmt, "externalEffect": "file_published", "modelRollbackApplies": False})+ if fmt == "stl":+ out["lengthUnit"] = "mm"+ except Exception as exc:+ if out is None:+ raise+ out["cleanupWarning"] = str(exc)+ return out
addin/AdomBridge/commands/mechanical/features.pyadded+238@@ -0,0 +1,238 @@+"""Solid feature operations; selection always resolves against the guarded model."""+import math++import adsk.core+import adsk.fusion++from .core import items, parameter_data, ref, require, resolve, same+from .readback import feature_data, topology_data+from .sketches import axis, collection, component, named, plane, point, quantity+++def owner(entity):+ if hasattr(entity, "body"):+ return entity.body.parentComponent+ if hasattr(entity, "parentComponent"):+ return entity.parentComponent+ if hasattr(entity, "parentSketch"):+ return entity.parentSketch.parentComponent+ return None+++def selected(design, value, args, name, types, comp):+ raw = args.get(name, [])+ require(isinstance(raw, list) and raw, "invalid_selection", name + " requires at least one reference.")+ result = [resolve(design, value, r, types) for r in raw]+ for entity in result:+ require(getattr(entity, "assemblyContext", None) is None, "definition_reference_required", "Feature authoring requires native definition references.")+ require(same(owner(entity), comp), "wrong_component", "Selected entity belongs to another component; pass its component reference.")+ return result+++def operation(value):+ return getattr(adsk.fusion.FeatureOperations, {"new_body": "NewBodyFeatureOperation", "join": "JoinFeatureOperation",+ "cut": "CutFeatureOperation", "intersect": "IntersectFeatureOperation"}[value])+++def factory(features, name):+ result = getattr(features, name, None)+ require(result is not None, "unsupported_capability", name + " is unavailable in this Fusion build.")+ return result+++def feature_create(design, value, args):+ comp = component(design, value, args)+ features, kind = comp.features, args["type"]+ q = lambda key, unit="mm", positive=True: quantity(design, args[key], unit, positive)+ select = lambda key, types: selected(design, value, args, key, types, comp)+ operation_name = args.get("operation", "new_body")+ if operation_name == "join" and kind in ("extrude", "revolve", "loft", "sweep"):+ require(comp.bRepBodies.count == 1, "ambiguous_join", "Join requires one existing body in the component; create a new body then use combine for explicit multi-body selection.")+ if kind == "extrude":+ profiles = select("profiles", ("Profile",))+ group = factory(features, "extrudeFeatures")+ inp = group.createInput(collection(profiles), operation(operation_name))+ if args.get("direction", "positive") == "symmetric":+ require(inp.setSymmetricExtent(q("distance"), True, quantity(design, args.get("taper", 0), "deg")), "invalid_extent", "Symmetric extrusion extent was refused.")+ else:+ extent = adsk.fusion.DistanceExtentDefinition.create(q("distance"))+ direction = getattr(adsk.fusion.ExtentDirections, "NegativeExtentDirection" if args.get("direction") == "negative" else "PositiveExtentDirection")+ require(inp.setOneSideExtent(extent, direction, quantity(design, args.get("taper", 0), "deg")), "invalid_extent", "Extrusion extent was refused.")+ if operation_name in ("cut", "intersect"):+ inp.participantBodies = select("bodies", ("BRepBody",))+ elif kind == "revolve":+ profiles = select("profiles", ("Profile",))+ group = factory(features, "revolveFeatures")+ inp = group.createInput(collection(profiles), axis(design, value, comp, args["axis"]), operation(operation_name))+ require(inp.setAngleExtent(False, q("angle", "deg")), "invalid_extent", "Revolve angle was refused.")+ if operation_name in ("cut", "intersect"):+ inp.participantBodies = select("bodies", ("BRepBody",))+ elif kind == "fillet":+ group = factory(features, "filletFeatures")+ inp = group.createInput()+ inp.edgeSetInputs.addConstantRadiusEdgeSet(collection(select("edges", ("BRepEdge",))), q("radius"), args.get("tangentChain", False))+ elif kind == "chamfer":+ group = factory(features, "chamferFeatures")+ require(hasattr(group, "createInput2"), "unsupported_capability", "Modern chamfer input API unavailable.")+ inp = group.createInput2()+ inp.chamferEdgeSets.addEqualDistanceChamferEdgeSet(collection(select("edges", ("BRepEdge",))), q("distance"), args.get("tangentChain", False))+ elif kind == "shell":+ group = factory(features, "shellFeatures")+ inp = group.createInput(collection(select("faces", ("BRepFace",))), args.get("tangentChain", False))+ inp.insideThickness = q("thickness")+ elif kind == "hole":+ group = factory(features, "holeFeatures")+ inp = group.createSimpleInput(q("diameter"))+ require(inp.setPositionBySketchPoints(collection(select("points", ("SketchPoint",)))), "hole_position_failed", "Hole positions were refused.")+ require(inp.setDistanceExtent(q("depth")), "invalid_extent", "Hole depth was refused.")+ inp.participantBodies = select("bodies", ("BRepBody",))+ elif kind == "loft":+ group = factory(features, "loftFeatures")+ inp = group.createInput(operation(operation_name))+ profiles = select("profiles", ("Profile",))+ require(len(profiles) >= 2, "invalid_selection", "Loft requires at least two profiles.")+ for profile in profiles:+ inp.loftSections.add(profile)+ inp.isSolid = True+ if operation_name in ("cut", "intersect"):+ inp.participantBodies = select("bodies", ("BRepBody",))+ elif kind == "sweep":+ group = factory(features, "sweepFeatures")+ profiles = select("profiles", ("Profile",))+ require(len(profiles) == 1, "invalid_selection", "Sweep requires exactly one profile.")+ path = adsk.fusion.Path.create(collection(select("path", ("SketchLine", "SketchArc", "SketchFittedSpline", "BRepEdge"))), adsk.fusion.ChainedCurveOptions.noChainedCurves)+ require(path is not None, "invalid_path", "Sweep path must form a connected curve chain.")+ inp = group.createInput(profiles[0], path, operation(operation_name))+ if operation_name in ("cut", "intersect"):+ inp.participantBodies = select("bodies", ("BRepBody",))+ elif kind == "combine":+ group = factory(features, "combineFeatures")+ target_body = resolve(design, value, args["body"], ("BRepBody",))+ require(getattr(target_body, "assemblyContext", None) is None, "definition_reference_required", "Combine requires a native target body reference.")+ require(same(owner(target_body), comp), "wrong_component", "Target body is in another component.")+ tools = select("tools", ("BRepBody",))+ require(not any(same(target_body, tool) for tool in tools), "invalid_selection", "Target cannot also be a tool body.")+ inp = group.createInput(target_body, collection(tools))+ require(operation_name in ("join", "cut", "intersect"), "invalid_input", "Combine operation must be join, cut or intersect.")+ inp.operation = operation(operation_name)+ inp.isKeepToolBodies = args.get("keepTools", True)+ elif kind == "split":+ group = factory(features, "splitBodyFeatures")+ bodies = select("bodies", ("BRepBody",))+ require(len(bodies) == 1, "invalid_selection", "Split requires one body.")+ tool = plane(design, value, comp, args["plane"])+ inp = group.createInput(bodies[0], tool, args.get("extend", True))+ elif kind == "move":+ group = factory(features, "moveFeatures")+ require(hasattr(group, "createInput2"), "unsupported_capability", "Modern move input API unavailable.")+ inp = group.createInput2(collection(select("bodies", ("BRepBody",))))+ matrix = adsk.core.Matrix3D.create()+ if args.get("angle", 0):+ vector = args.get("axis", [0, 0, 1])+ rotation_axis = adsk.core.Vector3D.create(*vector)+ require(rotation_axis.length > 1e-9, "invalid_input", "Rotation axis must be nonzero.")+ angle_value = design.unitsManager.evaluateExpression(str(args["angle"]) + " deg", "deg")+ require(matrix.setToRotation(angle_value, rotation_axis, point(args.get("origin", [0, 0, 0]))), "invalid_transform", "Rotation matrix creation failed.")+ translation = point(args.get("translation", [0, 0, 0]))+ existing = matrix.translation+ matrix.translation = adsk.core.Vector3D.create(existing.x + translation.x, existing.y + translation.y, existing.z + translation.z)+ require(inp.defineAsFreeMove(matrix), "invalid_transform", "Move transform was refused.")+ elif kind == "mirror":+ group = factory(features, "mirrorFeatures")+ inp = group.createInput(collection(select("bodies", ("BRepBody",))), plane(design, value, comp, args["plane"]))+ inp.isCombine = False+ elif kind == "rectangular_pattern":+ if any(key in args for key in ("axis2", "count2", "spacing2")):+ require("count2" in args and "spacing2" in args, "invalid_input", "Second pattern direction requires count2 and spacing2; axis2 defaults to y.")+ group = factory(features, "rectangularPatternFeatures")+ inp = group.createInput(collection(select("bodies", ("BRepBody",))), axis(design, value, comp, args["axis"]),+ q("count", ""), q("spacing"), adsk.fusion.PatternDistanceType.SpacingPatternDistanceType)+ inp.quantityTwo = quantity(design, 1, "", True)+ inp.isSymmetricInDirectionOne = False+ inp.isSymmetricInDirectionTwo = False+ if "count2" in args:+ require(inp.setDirectionTwo(axis(design, value, comp, args.get("axis2", "y")), q("count2", ""), q("spacing2")), "pattern_failed", "Second pattern direction was refused.")+ elif kind == "circular_pattern":+ group = factory(features, "circularPatternFeatures")+ inp = group.createInput(collection(select("bodies", ("BRepBody",))), axis(design, value, comp, args["axis"]))+ inp.quantity = q("count", "")+ inp.totalAngle = quantity(design, args.get("angle", 360), "deg", True)+ inp.isSymmetric = False+ elif kind == "draft":+ group = factory(features, "draftFeatures")+ inp = group.createInput(select("faces", ("BRepFace",)), plane(design, value, comp, args["plane"]), args.get("tangentChain", False))+ require(inp.setSingleAngle(False, q("angle", "deg", False)), "draft_failed", "Draft angle was refused.")+ elif kind == "thread":+ group = factory(features, "threadFeatures")+ create_info = getattr(getattr(adsk.fusion, "ThreadInfo", None), "create", None)+ require(callable(create_info), "unsupported_capability", "ThreadInfo.create is unavailable.")+ info = create_info(False, args["internal"], args["threadType"], args["designation"], args["threadClass"], True)+ require(info is not None, "invalid_thread", "Requested thread standard/designation/class is unavailable.")+ inp = group.createInput(collection(select("faces", ("BRepFace",))), info)+ inp.isModeled = args.get("modeled", False)+ inp.isFullLength = True+ else:+ require(False, "unsupported_feature", "This feature type is not supported.")+ feature = named(group.add(inp), args)+ return lambda fresh: {"feature": feature_data(feature, fresh),+ "bodies": [topology_data(body, fresh) for body in items(feature.bodies)]}+++def feature_edit(design, value, args):+ require(any(key in args for key in ("name", "suppressed", "parameters")), "invalid_input", "Specify a feature property to edit.")+ feature = resolve(design, value, args["feature"])+ timeline = getattr(feature, "timelineObject", None)+ require(timeline is not None, "wrong_entity_type", "Feature must have a timeline object.")+ if "name" in args:+ feature.name = args["name"]+ if "suppressed" in args:+ timeline.isSuppressed = args["suppressed"]+ for name, expression in args.get("parameters", {}).items():+ parameter = design.allParameters.itemByName(name)+ require(parameter is not None and same(getattr(parameter, "createdBy", None), feature), "wrong_parameter", "Parameter is not owned by this feature: " + name)+ require(design.unitsManager.isValidExpression(expression, parameter.unit), "invalid_expression", "Invalid parameter expression.")+ parameter.expression = expression+ return lambda fresh: {"feature": feature_data(feature, fresh)}+++def entity_delete(design, value, args):+ entity = resolve(design, value, args["entity"])+ kind = entity.objectType.split("::")[-1]+ require(kind in ("Sketch", "SketchLine", "SketchCircle", "SketchArc", "SketchPoint", "SketchFittedSpline", "ConstructionPlane") or kind.endswith("Feature"),+ "unsupported_delete", "Delete supports sketches, sketch geometry, construction planes and timeline features.")+ description = {"name": getattr(entity, "name", None), "type": entity.objectType}+ timeline = getattr(entity, "timelineObject", None)+ if timeline:+ require(timeline.isDeletable, "delete_refused", "Fusion reports this timeline entity cannot be deleted independently.")+ require(timeline.deleteObject(), "delete_failed", "Feature deletion failed.")+ else:+ require(entity.deleteMe(), "delete_failed", "Entity deletion failed.")+ return {"deleted": description}+++def parameter_batch(design, value, args):+ changed = []+ for change in args["changes"]:+ name, action = change["name"], change["action"]+ parameter = design.allParameters.itemByName(name)+ if action == "create":+ require(parameter is None, "parameter_exists", "Parameter exists: " + name)+ parameter = design.userParameters.add(name, quantity(design, change["expression"], change["unit"]), change["unit"], change.get("comment", ""))+ require(parameter is not None, "parameter_create_failed", "Parameter creation failed.")+ else:+ require(parameter is not None, "parameter_missing", "Parameter not found: " + name)+ if action == "delete":+ user = design.userParameters.itemByName(name)+ require(user is not None, "delete_refused", "Only user parameters can be deleted.")+ require(user.deleteMe(), "delete_refused", "Parameter is in use or cannot be deleted.")+ changed.append({"name": name, "deleted": True})+ continue+ expression = change["expression"]+ require(design.unitsManager.isValidExpression(expression, parameter.unit), "invalid_expression", "Invalid expression for " + name)+ parameter.expression = expression+ changed.append(parameter_data(design, parameter))+ return {"parameters": changed}+++OPERATIONS = {"feature_create": feature_create, "feature_edit": feature_edit,+ "entity_delete": entity_delete, "parameter_batch": parameter_batch}
addin/AdomBridge/commands/mechanical/geometry.pyadded+279@@ -0,0 +1,279 @@+"""Bounded geometry selection, engineering measurements and construction axes."""+import math++import adsk.core+import adsk.fusion++from .core import (MAX_ENTITIES, UNITS, items, number, point_mm, ref, require,+ resolve, same, target)+from .readback import concrete_geometry, geometry_data+from .sketches import component, named, point+++def native(entity):+ return getattr(entity, "nativeObject", None) or entity+++def owner(entity):+ entity = native(entity)+ if hasattr(entity, "body"):+ return entity.body.parentComponent+ if hasattr(entity, "parentSketch"):+ return entity.parentSketch.parentComponent+ return getattr(entity, "parentComponent", None) or getattr(entity, "component", None)+++def root_entity(design, entity):+ """Never guess which instance of a non-root definition the caller means."""+ if entity.objectType.endswith("::Occurrence"):+ require(any(same(entity, item) for item in items(design.rootComponent.allOccurrences)),+ "root_context_required", "Occurrence must be resolved in the root assembly context.")+ else:+ context = getattr(entity, "assemblyContext", None)+ require(context is not None or same(owner(entity), design.rootComponent),+ "occurrence_required", "Root-space geometry in a child component requires an occurrence-context reference.")+ if context is not None:+ require(any(same(context, item) for item in items(design.rootComponent.allOccurrences)),+ "root_context_required", "Geometry context must be a root assembly occurrence.")+ return entity+++def finite_point(value):+ require(value is not None, "measurement_failed", "Native measurement has no witness point.")+ result = point_mm(value)+ require(all(math.isfinite(v) for v in result), "measurement_failed", "Native witness point is not finite.")+ return result+++def minimum(manager, first, second):+ result = manager.measureMinimumDistance(first, second)+ require(result is not None and result.isValid, "measurement_failed", "Native minimum-distance measurement failed.")+ distance = number(result.value, "native distance") * 10+ require(distance >= 0, "measurement_failed", "Native minimum distance is negative.")+ points = [finite_point(result.positionOne), finite_point(result.positionTwo)]+ separation = math.dist(*points)+ require(math.isclose(distance, separation, abs_tol=1e-5, rel_tol=1e-7),+ "measurement_failed", "Native distance does not agree with its witness positions.")+ return distance, points+++MEASURABLE = ("Occurrence", "BRepBody", "BRepFace", "BRepEdge", "BRepVertex",+ "ConstructionPlane", "ConstructionAxis", "ConstructionPoint", "SketchPoint",+ "SketchLine", "SketchArc", "SketchCircle", "SketchEllipse", "SketchEllipticalArc",+ "SketchFittedSpline", "SketchControlPointSpline", "SketchFixedSpline")+POINT_TYPES = ("BRepVertex", "ConstructionPoint", "SketchPoint")+++def angle_direction(entity):+ # Sketch geometry is in sketch coordinates; worldGeometry includes the+ # sketch transform and the resolved occurrence context.+ geometry = concrete_geometry(entity.worldGeometry if entity.objectType.endswith("::SketchLine")+ else entity.geometry)+ kind = geometry.objectType.split("::")[-1]+ if kind == "Plane":+ vector, meaning = geometry.normal, "normal"+ elif kind == "Line3D":+ vector, meaning = geometry.startPoint.vectorTo(geometry.endPoint), "line"+ else:+ require(kind == "InfiniteLine3D", "measurement_failed", "Cannot establish the native angle direction.")+ vector, meaning = geometry.direction, "line"+ return {"kind": meaning, "vector": unit_vector([vector.x, vector.y, vector.z])}+++def angle_witness(entities, value, points):+ a = [points[0][i] - points[1][i] for i in range(3)]+ b = [points[2][i] - points[1][i] for i in range(3)]+ if math.hypot(*a) > 1e-12 and math.hypot(*b) > 1e-12:+ expected = math.degrees(math.acos(max(-1, min(1, sum(x*y for x, y in zip(unit_vector(a), unit_vector(b)))))))+ require(math.isclose(value, expected, abs_tol=1e-5), "measurement_failed", "Angle disagrees with native witness positions.")+ return {"method": "native_points", "nativePointWitnessDegenerate": False}+ # Fusion returns three zero points for valid parallel-plane angles. Keep+ # those original points, and establish parallelism from native geometry.+ # Never accept a degenerate three-point angle or synthesize witness points.+ require(len(entities) == 2 and all(e.objectType.split("::")[-1] not in POINT_TYPES for e in entities),+ "invalid_direction", "Point angle witness is degenerate.")+ require(min(abs(value), abs(value - 180)) <= 1e-5,+ "measurement_failed", "Degenerate native points do not establish this angle.")+ directions = [angle_direction(entity) for entity in entities]+ dot = sum(x*y for x, y in zip(directions[0]["vector"], directions[1]["vector"]))+ cosine = min(1, abs(dot))+ deviation = math.degrees(math.acos(cosine) if directions[0]["kind"] == directions[1]["kind"]+ else math.asin(cosine))+ require(deviation <= 1e-5, "measurement_failed", "Degenerate angle points disagree with native geometry directions.")+ return {"method": "native_geometry_directions", "nativePointWitnessDegenerate": True,+ "directions": directions, "parallelDeviationDegrees": deviation}+++def measure(app, args):+ _, design, value = target(app, args)+ entities = [root_entity(design, resolve(design, value, item, MEASURABLE)) for item in args["entities"]]+ mode = args["type"]+ if mode == "distance":+ require(len(entities) == 2, "invalid_input", "Distance requires exactly two entities.")+ result, witnesses = minimum(app.measureManager, *entities)+ validation = {"method": "native_points"}+ unit = "mm"+ else:+ kinds = [e.objectType.split("::")[-1] for e in entities]+ if any(kind in POINT_TYPES for kind in kinds):+ require(len(entities) == 3 and all(kind in POINT_TYPES for kind in kinds),+ "invalid_input", "Point angles require exactly three points, with the second as apex.")+ else:+ require(len(entities) == 2, "invalid_input", "Line/plane angles require two entities.")+ for entity, kind in zip(entities, kinds):+ geometry = getattr(entity, "geometry", None)+ require(kind in ("ConstructionPlane", "ConstructionAxis", "SketchLine") or+ kind == "BRepEdge" and geometry.objectType.endswith("::Line3D") or+ kind == "BRepFace" and geometry.objectType.endswith("::Plane"),+ "unsupported_geometry", "Angle inputs must be linear or planar, or three points.")+ measured = app.measureManager.measureAngle(*entities)+ require(measured is not None and measured.isValid, "measurement_failed", "Native angle measurement failed.")+ result = math.degrees(number(measured.value, "native angle"))+ witnesses = [finite_point(getattr(measured, name)) for name in ("positionOne", "positionTwo", "positionThree")]+ require(0 <= result <= 180 + 1e-7, "measurement_failed", "Native angle is outside 0..180 degrees.")+ validation = angle_witness(entities, result, witnesses)+ unit = "deg"+ return {"target": value, "type": mode, "value": result, "unit": unit,+ "frame": {"space": "root", "lengthUnit": "mm"}, "witnessPoints": witnesses,+ "witnessValidation": validation,+ "entities": [ref(entity, value) for entity in entities]}+++def unit_vector(vector):+ length = math.sqrt(sum(number(v, "direction") ** 2 for v in vector))+ require(length > 1e-12, "invalid_direction", "Direction or angle witness is degenerate.")+ return [v / length for v in vector]+++def transformed_direction(vector, context):+ xyz = [vector.x, vector.y, vector.z]+ if context is not None:+ matrix = context.transform2.asArray()+ xyz = [sum(matrix[row*4 + col] * xyz[col] for col in range(3)) for row in range(3)]+ return unit_vector(xyz)+++def query_row(entity, kind, context, value):+ base = native(entity)+ geometry = getattr(base, "geometry", None)+ if geometry is not None: geometry = concrete_geometry(geometry)+ row = {"ref": ref(base, value, context), "type": geometry.objectType.split("::")[-1] if geometry else "degenerate"}+ direction = None+ if kind == "faces":+ row["area"] = base.area * 100+ if geometry.objectType.endswith("::Plane"):+ ok, direction = base.evaluator.getNormalAtPoint(base.pointOnFace)+ require(ok, "geometry_unavailable", "Could not evaluate planar face normal.")+ elif kind == "edges":+ row["length"] = base.length * 10+ if geometry and geometry.objectType.endswith("::Line3D"):+ direction = geometry.startPoint.vectorTo(geometry.endPoint)+ if geometry and hasattr(geometry, "radius"):+ row["radius"] = geometry.radius * 10+ if direction:+ row["direction"] = transformed_direction(direction, context)+ return row+++def near_geometry(manager, entity, kind, pick, frame_context, design):+ candidate = entity.createForAssemblyContext(frame_context) if frame_context else entity+ if frame_context is None and not same(owner(entity), design.rootComponent):+ # MeasureManager rejects native B-Rep in a child definition. A temporary+ # copy preserves its trimmed geometry in component space without moving+ # or adding anything to the design. Vertices are transient points.+ candidate = (entity.geometry if kind == "vertices" else+ adsk.fusion.TemporaryBRepManager.get().copy(entity))+ require(candidate is not None, "measurement_failed", "Could not copy component geometry for measurement.")+ distance, witnesses = minimum(manager, point(pick), candidate)+ # Native temporary-body measurements can reverse the two witness positions.+ # Bind the pick by coordinates instead of assuming positionTwo is on the CAD.+ matching = [i for i, witness in enumerate(witnesses) if math.dist(pick, witness) <= 1e-5]+ require(matching, "measurement_failed", "Native distance has no witness at the requested pick.")+ return distance, witnesses[1 - matching[0]]+++def geometry_query(app, args):+ _, design, value = target(app, args)+ body = resolve(design, value, args["body"], ("BRepBody",))+ space, kind, filters = args["space"], args["kind"], args.get("filters", {})+ if space == "root":+ root_entity(design, body)+ context = getattr(body, "assemblyContext", None)+ frame_context = context if space == "root" else None+ source = native(body)+ selected, collection = [], getattr(source, kind)+ require(collection.count <= MAX_ENTITIES, "inspection_budget", "Body topology exceeds query budget.")+ allowed_filters = {"type", "near"} | ({"area", "radius", "direction"} if kind == "faces" else+ {"length", "radius", "direction"} if kind == "edges" else set())+ require(not (set(filters) - allowed_filters), "invalid_input", "Filter is not supported for the selected topology kind.")+ for key in ("area", "radius", "length"):+ if key in filters:+ limits = filters[key]+ require(limits.get("min", 0) <= limits.get("max", math.inf), "invalid_input", "Filter minimum exceeds maximum.")+ direction_filter = filters.get("direction")+ desired = unit_vector(direction_filter["vector"]) if direction_filter else None+ for entity in items(collection):+ # Reference context and filter frame are separate: component-space+ # queries on an instance still return references to that exact instance.+ row = query_row(entity, kind, frame_context, value)+ row["ref"] = ref(entity, value, context)+ if "type" in filters and row["type"] != filters["type"]: continue+ if any(key in filters and (key not in row or not filters[key].get("min", 0) <= row[key] <= filters[key].get("max", math.inf))+ for key in ("area", "radius", "length")): continue+ if desired:+ if "direction" not in row: continue+ dot = sum(a*b for a, b in zip(desired, row["direction"]))+ if direction_filter.get("orientation", "same") == "parallel": dot = abs(dot)+ if dot < math.cos(math.radians(direction_filter.get("tolerance", 0.01))) - 1e-12: continue+ if "near" in filters:+ distance, nearest = near_geometry(app.measureManager, entity, kind, filters["near"]["point"], frame_context, design)+ if distance > filters["near"]["tolerance"] + 1e-7: continue+ row.update(distanceToPick=distance, nearestPoint=nearest)+ selected.append(row)+ require(len(selected) <= args.get("maxResults", 256), "query_budget", "Query result exceeds maxResults; narrow the filters.")+ if args.get("selection", "one") == "one":+ require(len(selected) == 1, "selection_empty" if not selected else "ambiguous_selection",+ "Geometry query must select exactly one entity.", matchCount=len(selected))+ return {"target": value, "matches": selected, "count": len(selected), "complete": True,+ "frame": {"space": space, "component": ref(source.parentComponent, value),+ "occurrencePath": context.fullPathName if context else None}, "units": UNITS}+++def axis_data(axis, value=None):+ result = {"name": axis.name, "geometry": geometry_data(axis.geometry), "space": "component",+ "healthState": int(axis.healthState), "message": axis.errorOrWarningMessage,+ "parametric": axis.isParametric, "lengthUnit": "mm"}+ if value: result["ref"] = ref(axis, value)+ return result+++def construction_axis(design, value, args):+ comp = component(design, value, args)+ methods = {"edge": ("setByEdge", ("BRepEdge", "SketchLine", "SketchArc", "SketchCircle"), 1),+ "circular_face": ("setByCircularFace", ("BRepFace",), 1),+ "two_planes": ("setByTwoPlanes", ("BRepFace", "ConstructionPlane"), 2),+ "two_points": ("setByTwoPoints", POINT_TYPES, 2)}+ method, allowed, count = methods[args["type"]]+ require(len(args["entities"]) == count, "invalid_input", "Incorrect number of axis references.")+ entities = [resolve(design, value, item, allowed) for item in args["entities"]]+ require(count != 2 or not same(entities[0], entities[1]), "duplicate_entity", "Axis references must identify distinct entities.")+ cross_component = any(not same(owner(entity), comp) or getattr(entity, "assemblyContext", None) for entity in entities)+ inp = comp.constructionAxes.createInput()+ if cross_component:+ for entity in entities: root_entity(design, entity)+ if not same(comp, design.rootComponent):+ require(args.get("creationOccurrence"), "creation_occurrence_required", "Cross-component axes need an explicit occurrence of the target component.")+ context = root_entity(design, resolve(design, value, args["creationOccurrence"], ("Occurrence",)))+ require(same(context.component, comp), "wrong_component", "Creation occurrence does not instantiate the target component.")+ inp.creationOccurrence = context+ else:+ require(not args.get("creationOccurrence"), "invalid_input", "Root axes do not use a creation occurrence.")+ else:+ require(not args.get("creationOccurrence"), "invalid_input", "Native same-component axis references do not use a creation occurrence.")+ require(getattr(inp, method)(*entities), "axis_failed", "Fusion rejected the construction axis definition.")+ axis = named(comp.constructionAxes.add(inp), args)+ return lambda fresh: {"axis": axis_data(axis, fresh)}+++READS = {"geometry_query": geometry_query, "measure": measure}+OPERATIONS = {"construction_axis": construction_axis}
addin/AdomBridge/commands/mechanical/handoff.pyadded+68@@ -0,0 +1,68 @@+"""Files the bridge exe and this add-in share for mechanical writes (rust/bridge/src/mech.rs).++The exe validates, admits and journals every write; this add-in only executes it. Three small+files carry what the exe cannot observe from outside Fusion:+- session.json: this add-in session's ID and transaction-probe state, for capabilities and+ the exe's session check. Trusted by the exe only while the Fusion process that wrote it runs.+- dispatch/<id>.claim: created exclusively before a write executes. The exe creates it only to+ expire a dispatch it gave up on, so exactly one side wins and "never ran" stays provable.+- dispatch/<id>.result.json: the exact result, kept for the exe when its reply is lost.+"""+import json+import os+import re+import time++from .journal import storage_root++PROTOCOL = "exe-journal-1"+_DISPATCH_ID = re.compile(r"[0-9a-f]{32}")+++def _write_atomically(path, data):+ staged = path.with_name(path.name + ".tmp")+ with open(staged, "wb") as stream:+ stream.write(data)+ stream.flush()+ os.fsync(stream.fileno())+ os.replace(staged, path)+++def publish_session(session_id, source_version, transactions):+ root = storage_root()+ root.mkdir(parents=True, exist_ok=True)+ _write_atomically(root / "session.json", json.dumps({+ "protocol": PROTOCOL, "sessionId": session_id, "pid": os.getpid(), "startedAt": time.time(),+ "sourceVersion": source_version, "transactions": transactions}).encode("utf-8"))+++def dispatch_id(dispatch):+ """The exe's dispatch ID, or None. Never a path: 32 lowercase hex characters only."""+ value = dispatch.get("dispatchId") if isinstance(dispatch, dict) else None+ return value if isinstance(value, str) and _DISPATCH_ID.fullmatch(value) else None+++def claim(dispatch_id, document, args):+ """Win the dispatch before executing. False means the exe already expired it."""+ folder = storage_root() / "dispatch"+ folder.mkdir(parents=True, exist_ok=True)+ try:+ descriptor = os.open(folder / (dispatch_id + ".claim"), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)+ except FileExistsError:+ return False+ # The claim is owned from here on; its contents only help the exe reconcile later.+ try:+ with os.fdopen(descriptor, "w", encoding="utf-8") as stream:+ stream.write(json.dumps({"state": "started", "sessionId": args.get("sessionId"),+ "operationId": args.get("operationId"), "observedDocument": document, "at": time.time()}))+ stream.flush()+ os.fsync(stream.fileno())+ except (OSError, TypeError, ValueError):+ pass+ return True+++def publish_result(dispatch_id, result):+ folder = storage_root() / "dispatch"+ _write_atomically(folder / (dispatch_id + ".result.json"),+ json.dumps(result, sort_keys=True, separators=(",", ":"), allow_nan=False).encode("ascii"))
addin/AdomBridge/commands/mechanical/joint_state.pyadded+196@@ -0,0 +1,196 @@+"""Semantic attachment graph and frames for regular joints, origins and links.++Entity relationships use native API equality through session identities. Transient+JointGeometry wrappers and token strings never serve as graph identity.+"""+import math+import adsk.fusion++from .core import MAX_ENTITIES, concrete, identity, items, number, point_mm, ref, require, same++_source_entities = []+++def source_identity(entity):+ """Re-resolve token locators before comparing API identity after recompute.++ Fusion can invalidate an old BRep wrapper while preserving its model entity.+ A token is only a lookup locator; fresh native equality is the identity proof.+ """+ from .geometry import owner+ require(entity is not None and entity.isValid, 'lost_joint_geometry', 'Attachment native source is invalid.')+ component = owner(entity)+ if component is None:+ return identity(entity)+ design = component.parentDesign+ token = entity.entityToken+ for saved in _source_entities:+ if saved['type'] != entity.objectType or not saved['owner'].isValid or not same(saved['owner'], component):+ continue+ candidates = [concrete(candidate) for candidate in design.findEntityByToken(saved['token'])]+ require(len(candidates) <= MAX_ENTITIES, 'inspection_budget', 'Attachment token lookup exceeds inspection budget.')+ matches = [candidate for candidate in candidates if candidate.isValid and same(candidate, entity)]+ if matches:+ require(len(candidates) == 1 and candidates[0].objectType == entity.objectType,+ 'ambiguous_reference', 'Attachment identity token no longer resolves uniquely.', matchCount=len(candidates))+ return saved['id']+ require(len(_source_entities) < MAX_ENTITIES, 'identity_budget', 'Session attachment identity budget exhausted.')+ result = identity(entity)+ _source_entities.append({'owner': component, 'type': entity.objectType, 'token': token, 'id': result})+ return result+++def entity_link(entity, value=None):+ entity = concrete(entity)+ if entity is None:+ return None+ require(entity.isValid, "lost_joint_geometry", "Joint attachment is no longer valid.")+ native = getattr(entity, "nativeObject", None) or entity+ context = getattr(entity, "assemblyContext", None)+ result = {"entityId": source_identity(native), "objectType": native.objectType,+ "occurrenceId": identity(context) if context is not None else None}+ if value is not None:+ result["ref"] = ref(entity, value)+ return result+++def vector_data(vector):+ require(vector is not None, "joint_frame_unavailable", "Joint direction is unavailable.")+ return [number(getattr(vector, axis), "joint direction") for axis in "xyz"]+++def matrix_data(matrix):+ require(matrix is not None, "joint_frame_unavailable", "Joint frame is unavailable.")+ result = [number(v, "joint transform") for v in matrix.asArray()]+ require(len(result) == 16, "joint_frame_unavailable", "Joint frame must be a 4 by 4 matrix.")+ for i in (3, 7, 11):+ result[i] *= 10+ return result+++def parameter_state(parameter, unit):+ require(parameter is not None, "unsupported_inferred_joint", "Joint parameter is unavailable (inferred joints are not editable).")+ scale = 180 / math.pi if unit == "deg" else 10+ return {"expression": parameter.expression, "value": number(parameter.value, "joint parameter") * scale, "unit": unit}+++def geometry_state(geometry, value=None):+ geometry = concrete(geometry)+ require(geometry is not None and geometry.isValid, "lost_joint_geometry", "Joint geometry is unavailable.")+ if geometry.objectType.endswith("::JointOrigin"):+ return {"origin": entity_link(geometry, value)}+ require(geometry.objectType.endswith("::JointGeometry"), "unsupported_joint_geometry", "Unsupported joint attachment wrapper.")+ result = {"geometryType": int(geometry.geometryType), "keyPointType": int(geometry.keyPointType),+ "frameSpace": "native_joint_geometry_api", "originUnit": "mm",+ "entityOne": entity_link(geometry.entityOne, value), "entityTwo": entity_link(geometry.entityTwo, value),+ "origin": point_mm(geometry.origin),+ "primaryAxis": vector_data(geometry.primaryAxisVector),+ "secondaryAxis": vector_data(geometry.secondaryAxisVector),+ "thirdAxis": vector_data(geometry.thirdAxisVector)}+ # These parameters distinguish analytical face and tangent attachment sites.+ kinds = adsk.fusion.JointGeometryTypes+ if geometry.geometryType in (kinds.JointTangentBRepFaceGeometry, kinds.JointTangentBRepFaceEdgeGeometry):+ for name in ("tangentFaceType", "tangentFaceParamOne", "tangentFaceParamTwo"):+ result[name] = number(getattr(geometry, name), name)+ for name in ("planeOne", "planeTwo"):+ plane = getattr(geometry, name)+ result[name] = entity_link(plane, value)+ return result+++def regular_state(joint, value=None):+ return {"geometryOne": geometry_state(joint.geometryOrOriginOne, value),+ "geometryTwo": geometry_state(joint.geometryOrOriginTwo, value),+ "geometryOneTransform": matrix_data(joint.geometryOneTransform),+ "geometryTwoTransform": matrix_data(joint.geometryTwoTransform),+ "angle": parameter_state(joint.angle, "deg"),+ "offset": parameter_state(joint.offset, "mm"),+ "offsetX": parameter_state(joint.offsetX, "mm"),+ "offsetY": parameter_state(joint.offsetY, "mm"),+ "flipped": bool(joint.isFlipped), "locked": bool(joint.isLocked),+ "ownerId": identity(joint.parentComponent),+ "frameSpace": "native_joint_api", "transformTranslationUnit": "mm"}+++def origin_data(origin, value=None):+ origin = concrete(origin)+ result = {"name": origin.name, "geometry": geometry_state(origin.geometry, value),+ "flipped": bool(origin.isFlipped), "transform": matrix_data(origin.transform),+ "xAxisEntity": entity_link(origin.xAxisEntity, value),+ "zAxisEntity": entity_link(origin.zAxisEntity, value),+ "primaryAxis": vector_data(origin.primaryAxisVector),+ "secondaryAxis": vector_data(origin.secondaryAxisVector),+ "thirdAxis": vector_data(origin.thirdAxisVector),+ "ownerId": identity(origin.parentComponent),+ "space": "root" if getattr(origin, "assemblyContext", None) else "component",+ "units": {"length": "mm", "angle": "deg", "transformTranslation": "mm"}}+ for name in ("angle", "offsetX", "offsetY", "offsetZ"):+ result[name] = parameter_state(getattr(origin, name), "deg" if name == "angle" else "mm")+ if value is not None:+ result["ref"] = ref(origin, value)+ return result+++# Public motion names remain independent of Autodesk enum integer values.+LINK_MOTIONS = {+ "RevoluteJointMotion": {"rotation": "RevoluteJointRotateMotionType"},+ "SliderJointMotion": {"slide": "SliderJointSlideMotionType"},+ "CylindricalJointMotion": {"rotation": "CylindricalJointRotateMotionType", "slide": "CylindricalJointSlideMotionType"},+ "PinSlotJointMotion": {"rotation": "PinSlotJointRotateMotionType", "slide": "PinSlotJointSlideMotionType"},+ "PlanarJointMotion": {"rotation": "PlanarJointRotateMotionType", "primarySlide": "PlanarJointSlideOneMotionType", "secondarySlide": "PlanarJointSlideTwoMotionType"},+ "BallJointMotion": {"pitch": "BallJointPitchMotionType", "roll": "BallJointRollMotionType", "yaw": "BallJointYawMotionType"}}+++def link_motion_name(joint, enum):+ import adsk.fusion+ from .assembly_state import joint_motion+ names = LINK_MOTIONS.get(joint_motion(joint).objectType.split("::")[-1], {})+ matches = [name for name, member in names.items() if getattr(adsk.fusion.JointMotionTypes, member) == enum]+ require(len(matches) == 1, "unsupported_link_motion", "Motion link has an unsupported joint degree of freedom.")+ return matches[0]+++def link_data(link, value=None):+ link = concrete(link)+ first, second = concrete(link.jointOne), concrete(link.jointTwo)+ names = [link_motion_name(joint, enum) for joint, enum in+ ((first, link.motionOne), (second or first, link.motionTwo))]+ result = {"name": link.name, "suppressed": bool(link.isSuppressed), "reversed": bool(link.isReversed),+ "jointOne": entity_link(first, value), "jointTwo": entity_link(second, value),+ "sameJoint": second is None or same(first, second),+ "motionOne": names[0], "motionTwo": names[1], "healthState": int(link.healthState),+ "message": link.errorOrWarningMessage}+ for suffix, name in zip(("One", "Two"), names):+ result["value" + suffix] = parameter_state(getattr(link, "value" + suffix), "mm" if "lide" in name else "deg")+ if value is not None:+ result["ref"] = ref(link, value)+ return result+++def component_joint_state(comp):+ return {"jointOrigins": [origin_data(o) for o in items(comp.jointOrigins)] if hasattr(comp, "jointOrigins") else [],+ "motionLinks": [link_data(o) for o in items(comp.motionLinks)] if hasattr(comp, "motionLinks") else []}+++MOTION_AXES = {+ "RigidJointMotion": (),+ "RevoluteJointMotion": (("rotationAxis", "customRotationAxisEntity"),),+ "CylindricalJointMotion": (("rotationAxis", "customRotationAxisEntity"),),+ "SliderJointMotion": (("slideDirection", "customSlideDirectionEntity"),),+ "PinSlotJointMotion": (("rotationAxis", "customRotationAxisEntity"), ("slideDirection", "customSlideDirectionEntity")),+ "PlanarJointMotion": (("normalDirection", "customNormalDirectionEntity"), ("primarySlideDirection", "customPrimarySlideDirectionEntity")),+ "BallJointMotion": (("pitchDirection", "customPitchDirectionEntity"), ("yawDirection", "customYawDirectionEntity"))}+++def motion_axes_state(motion, value=None):+ kind = motion.objectType.split("::")[-1]+ require(kind in MOTION_AXES, "unsupported_joint_motion", "Unknown joint direction contract.")+ result = {}+ for direction, source in MOTION_AXES[kind]:+ enum = getattr(motion, direction)+ custom = getattr(motion, source) if enum == adsk.fusion.JointDirections.CustomJointDirection else None+ require(enum != adsk.fusion.JointDirections.CustomJointDirection or custom is not None,+ "lost_joint_geometry", "Custom joint axis has no source entity.")+ result[direction] = int(enum)+ result[source] = entity_link(custom, value)+ return result
addin/AdomBridge/commands/mechanical/journal.pyadded+197@@ -0,0 +1,197 @@+"""Read-only access to the durable operation journal the bridge exe writes.++The exe (rust/bridge/src/mech_store.rs) is the only writer: it admits, dispatches and records every+write. Exploded previews read terminal records here as proof of earlier operations in this session.+Every read re-validates digest, identity, phase and canonical encoding, as the writer does, and+nothing here creates, migrates or resets storage.+"""+import copy+import hashlib+import json+import math+import os+import re+from pathlib import Path+import stat+import sys+import threading++STORAGE_VERSION = 1+APPLICATION_ID = 0x41444D4D+MAX_DATABASE_BYTES = 64 * 1024 * 1024+MAX_RECORD_BYTES = 4 * 1024 * 1024+MAX_REQUEST_BYTES = 1024 * 1024+TERMINAL = frozenset(('succeeded', 'failed', 'not_executed'))+PHASES = frozenset(('admitted', 'dispatched', 'executing')) | TERMINAL+++class JournalError(Exception):+ def __init__(self, code, message):+ super().__init__(message)+ self.code = code+++def require(condition, code, message):+ if not condition:+ raise JournalError(code, message)+++def canonical(value, maximum=MAX_RECORD_BYTES):+ try:+ data = json.dumps(value, sort_keys=True, separators=(',', ':'), allow_nan=False).encode('utf-8')+ except (TypeError, ValueError, RecursionError) as error:+ raise JournalError('operation_record_invalid', 'Operation record is not canonical JSON: ' + str(error)) from error+ require(len(data) <= maximum, 'operation_record_too_large', 'Operation record exceeds its durable byte limit.')+ return data+++def fingerprint(request):+ return hashlib.sha256(canonical(request, MAX_REQUEST_BYTES)).hexdigest()+++def storage_root():+ if os.name == 'nt':+ base = os.environ.get('LOCALAPPDATA')+ require(bool(base), 'operation_storage_unavailable', 'LOCALAPPDATA is unavailable.')+ return Path(base) / 'Adom' / 'FusionBridge' / 'mechanical'+ if sys.platform == 'darwin':+ return Path.home() / 'Library' / 'Application Support' / 'Adom' / 'FusionBridge' / 'mechanical'+ return Path.home() / '.local' / 'state' / 'adom' / 'fusion-bridge' / 'mechanical'+++class Journal:+ def __init__(self, directory=None, *, max_bytes=MAX_DATABASE_BYTES, max_record_bytes=MAX_RECORD_BYTES):+ # Constructor injection is for offline tests; no public verb accepts it.+ self.directory = Path(directory) if directory is not None else storage_root()+ self.path = self.directory / 'operations-v1.sqlite3'+ self.max_bytes, self.max_record_bytes = max_bytes, max_record_bytes+ self.lock = threading.RLock()++ def _initialization_link(self, info):+ """Account for both links during exclusive initialization publication.++ A normal external hardlink is still refused: exactly two links must be+ accounted for by the public file and one regular, same-inode staging+ file inside this validated app-owned directory. No open staging writer+ remains when initialization publishes it.+ """+ if info.st_nlink != 2:+ return False+ matches = []+ for staged in self.directory.glob('.operations-v1-*.tmp'):+ if not re.fullmatch(r'\.operations-v1-[0-9a-f]{32}\.tmp', staged.name):+ continue+ try:+ other = staged.lstat()+ except FileNotFoundError:+ continue+ if (stat.S_ISREG(other.st_mode) and not (getattr(other, 'st_file_attributes', 0) & 0x400)+ and (other.st_dev, other.st_ino) == (info.st_dev, info.st_ino)):+ matches.append(staged)+ # If the publisher has already unlinked, verify the final file's fresh+ # single-link state instead of rejecting the completed transition.+ if not matches:+ fresh = self.path.lstat()+ return (fresh.st_dev, fresh.st_ino) == (info.st_dev, info.st_ino) and fresh.st_nlink == 1+ return len(matches) == 1++ def _paths(self, create):+ require(self.directory.is_absolute() and '..' not in self.directory.parts,+ 'operation_storage_invalid_path', 'Operation storage must be an absolute app-owned path.')+ for item in reversed((self.directory, *self.directory.parents)):+ if not item.exists() and not item.is_symlink():+ continue+ info = item.lstat()+ require(stat.S_ISDIR(info.st_mode) and not stat.S_ISLNK(info.st_mode)+ and not (getattr(info, 'st_file_attributes', 0) & 0x400),+ 'operation_storage_invalid_path', 'Operation storage cannot traverse links, junctions or non-directories.')+ if create:+ self.directory.mkdir(mode=0o700, parents=True, exist_ok=True)+ if self.directory.exists() and hasattr(os, 'getuid'):+ info = self.directory.stat()+ require(info.st_uid == os.getuid() and not (info.st_mode & 0o022),+ 'operation_storage_invalid_path', 'Operation storage must be owned by this user and not writable by others.')+ for suffix in ('', '-journal', '-wal', '-shm'):+ item = Path(str(self.path) + suffix)+ if not item.exists() and not item.is_symlink():+ continue+ info = item.lstat()+ links_safe = info.st_nlink == 1 or (not suffix and self._initialization_link(info))+ require(stat.S_ISREG(info.st_mode) and not stat.S_ISLNK(info.st_mode) and links_safe+ and not (getattr(info, 'st_file_attributes', 0) & 0x400),+ 'operation_storage_invalid_path', 'Operation storage files cannot be links or special files.')++ def _connect(self, create):+ try:+ import sqlite3+ except ImportError as error:+ raise JournalError('operation_storage_unavailable', 'This Python runtime lacks SQLite support.') from error+ self._paths(create)+ if not self.path.exists():+ return None+ connection = sqlite3.connect(self.path.as_uri() + '?mode=rw', uri=True, timeout=2, isolation_level=None)+ try:+ # Existing-history reads may recover SQLite's hot rollback journal+ # after a process crash. mode=rw never creates missing storage.+ connection.execute('PRAGMA synchronous=FULL')+ app_id = connection.execute('PRAGMA application_id').fetchone()[0]+ version = connection.execute('PRAGMA user_version').fetchone()[0]+ require(app_id == APPLICATION_ID, 'operation_storage_corrupt', 'Storage is not an operation journal; it will not be reset.')+ require(version == STORAGE_VERSION, 'operation_storage_version', 'Unsupported operation journal version; automatic migration is disabled.')+ columns = connection.execute('PRAGMA table_info(operations)').fetchall()+ require([row[1] for row in columns] == ['session_id', 'operation_id', 'record_json', 'record_digest'],+ 'operation_storage_corrupt', 'Operation journal schema is invalid.')+ require(self.path.stat().st_size <= self.max_bytes, 'operation_storage_full', 'Operation journal exceeds its database byte limit.')+ return connection+ except BaseException:+ connection.close()+ raise++ def _decode(self, row, session_id, operation_id):+ if row is None:+ return None+ raw = bytes(row[0])+ require(len(raw) <= self.max_record_bytes and hashlib.sha256(raw).hexdigest() == row[1],+ 'operation_storage_corrupt', 'Operation record digest or size is invalid.')+ try:+ record = json.loads(raw)+ valid = (record['storageVersion'] == STORAGE_VERSION and record['sessionId'] == session_id+ and record['operationId'] == operation_id and record['phase'] in PHASES+ and isinstance(record['requestDigest'], str) and len(record['requestDigest']) == 64+ and isinstance(record['events'], list) and 1 <= len(record['events']) <= 6+ and math.isfinite(record['createdAt']) and math.isfinite(record['updatedAt']))+ if record['phase'] in TERMINAL:+ valid = valid and isinstance(record['result'], dict) and type(record['result'].get('success')) is bool+ valid = valid and (record['result']['success'] == (record['phase'] == 'succeeded'))+ else:+ valid = valid and record['result'] is None+ except (ValueError, TypeError, KeyError) as error:+ raise JournalError('operation_storage_corrupt', 'Operation record is malformed.') from error+ require(valid and canonical(record, self.max_record_bytes) == raw,+ 'operation_storage_corrupt', 'Operation record identity, phase or canonical encoding is invalid.')+ return record++ def _read(self, connection, session_id, operation_id):+ return self._decode(connection.execute('SELECT record_json,record_digest FROM operations WHERE session_id=? AND operation_id=?',+ (session_id, operation_id)).fetchone(), session_id, operation_id)++ def _run(self, create, action):+ with self.lock:+ connection = None+ try:+ connection = self._connect(create)+ if connection is None:+ return None+ return copy.deepcopy(action(connection))+ except JournalError:+ raise+ except Exception as error:+ # Do not reset corrupt databases, retry a possibly committed+ # operation, or claim native effects were rolled back here.+ raise JournalError('operation_storage_failed', type(error).__name__ + ': ' + str(error)) from error+ finally:+ if connection is not None:+ connection.close()++ def lookup(self, session_id, operation_id):+ return self._run(False, lambda connection: self._read(connection, session_id, operation_id))
addin/AdomBridge/commands/mechanical/motion_links.pyadded+157@@ -0,0 +1,157 @@+"""Typed native coupling with exact joint/degree-of-freedom readback."""+import math++import adsk.fusion++from .assembly_state import joint_motion+from .core import concrete, items, number, require, resolve, same+from .joint_state import LINK_MOTIONS, entity_link, link_data, link_motion_name+from .sketches import quantity+++def root_joint(design, value, raw):+ joint = resolve(design, value, raw, ("Joint", "AsBuiltJoint"))+ require(getattr(joint, "assemblyContext", None) is None and same(joint.parentComponent, design.rootComponent),+ "root_joint_required", "Motion links currently require root-owned joints.")+ require(not joint.isSuppressed, "suppressed_joint", "Unsuppress a joint before coupling its motion.")+ return joint+++def motion_enum(joint, name):+ family = joint_motion(joint).objectType.split("::")[-1]+ members = LINK_MOTIONS.get(family, {})+ require(name in members, "wrong_joint_type", "The selected joint does not have motion " + name + ".")+ enums = getattr(adsk.fusion, "JointMotionTypes", None)+ require(enums is not None and hasattr(enums, members[name]), "unsupported_capability", "This Fusion build does not expose native motion links.")+ return getattr(enums, members[name])+++def inputs(design, first, second, args):+ require(not same(first, second) or args["motionOne"] != args["motionTwo"], "invalid_motion_link", "Two different joint degrees of freedom are required.")+ enums, values, expected = [], [], {}+ for suffix, joint in (("One", first), ("Two", second)):+ motion = args["motion" + suffix]+ enums.append(motion_enum(joint, motion))+ unit = "mm" if "lide" in motion else "deg"+ inp = quantity(design, args["value" + suffix], unit, positive=True)+ magnitude = design.unitsManager.evaluateExpression(inp.stringValue, unit)+ require(magnitude > 0, "invalid_motion_link", "Link magnitudes must be positive; use reversed for opposite direction.")+ values.append(inp)+ expected["value" + suffix] = magnitude * (10 if unit == "mm" else 180 / math.pi)+ return enums, values, expected+++def result(link, fresh, args, first, second, expected):+ data = link_data(link, fresh)+ # A same-joint link can expose either null or the first joint again as its+ # native second member. Canonicalize only the effective member for equality;+ # link_data preserves the actual native jointTwo representation.+ actual_first, actual_second = concrete(link.jointOne), concrete(link.jointTwo)+ require(entity_link(actual_first) == entity_link(first)+ and entity_link(actual_second or actual_first) == entity_link(second or first),+ "motion_link_refused", "Linked joint identities did not read back.")+ for key in ("name", "suppressed", "reversed", "motionOne", "motionTwo"):+ require(key not in args or data[key] == args[key], "motion_link_refused", "Motion link did not retain " + key)+ for key, wanted in expected.items():+ require(math.isclose(data[key]["value"], wanted, rel_tol=1e-8, abs_tol=1e-6), "motion_link_refused", "Motion-link ratio did not read back.")+ require(link.healthState == adsk.fusion.FeatureHealthStates.HealthyFeatureHealthState, "unhealthy_motion_link", "Motion link is not healthy.")+ return {"motionLink": data}+++def motion_value(joint, enum):+ name = link_motion_name(joint, enum)+ motion = joint_motion(joint)+ require(hasattr(motion, name + "Value"), "motion_link_unverifiable", "Linked motion has no readable value.")+ return number(getattr(motion, name + "Value"), "linked motion")+++def coupling_snapshot(design):+ collection = getattr(design.rootComponent, "motionLinks", None)+ if collection is None:+ return []+ result = []+ for link in items(collection):+ if link.isSuppressed:+ continue+ first, second = concrete(link.jointOne), concrete(link.jointTwo or link.jointOne)+ require(not first.isSuppressed and not second.isSuppressed, "motion_link_unverifiable", "An active link references a suppressed joint.")+ result.append((link, motion_value(first, link.motionOne), motion_value(second, link.motionTwo), link_data(link)))+ return result+++def verify_coupling(before):+ verified = []+ for link, one, two, state in before:+ require(link.isValid and not link.isSuppressed, "motion_link_refused", "Driving unexpectedly removed or suppressed a motion link.")+ first, second = concrete(link.jointOne), concrete(link.jointTwo or link.jointOne)+ require(link_data(link) == state, "motion_link_refused", "Driving unexpectedly changed motion-link membership or settings.")+ delta_one = motion_value(first, link.motionOne) - one+ delta_two = motion_value(second, link.motionTwo) - two+ value_one, value_two = number(link.valueOne.value, "link magnitude"), number(link.valueTwo.value, "link magnitude")+ require(abs(value_one) > 1e-12 and abs(value_two) > 1e-12, "motion_link_unverifiable", "Link magnitude cannot be zero.")+ require(math.isclose(delta_one/value_one, (-1 if link.isReversed else 1)*delta_two/value_two, abs_tol=1e-7, rel_tol=1e-7),+ "motion_link_drive_refused", "Driven motion did not propagate according to the active link ratio.")+ verified.append({"name": link.name, "jointOne": entity_link(first), "jointTwo": entity_link(second), "ratioVerified": True})+ return verified+++def motion_link_create(design, value, args):+ first = root_joint(design, value, args["jointOne"])+ second = root_joint(design, value, args["jointTwo"]) if "jointTwo" in args else None+ require(second is None or not same(first, second), "invalid_motion_link", "Omit jointTwo to couple two motions of jointOne.")+ enums, values, expected = inputs(design, first, second or first, args)+ collection = getattr(design.rootComponent, "motionLinks", None)+ require(collection is not None, "unsupported_capability", "This Fusion build has no motionLinks collection.")+ inp = collection.createInput(first, second)+ require(inp is not None, "motion_link_failed", "Fusion did not return MotionLinkInput.")+ inp.motionOne, inp.motionTwo = enums+ inp.valueOne, inp.valueTwo = values+ inp.isReversed = args.get("reversed", False)+ link = collection.add(inp)+ require(link is not None and link.isValid, "motion_link_failed", "Fusion did not create a motion link.")+ if "name" in args:+ link.name = args["name"]+ return lambda fresh: result(link, fresh, {"reversed": False, **args}, first, second, expected)+++def motion_link_edit(design, value, args):+ link = resolve(design, value, args["link"], ("MotionLink",))+ require(getattr(link, "assemblyContext", None) is None and same(link.parentComponent, design.rootComponent),+ "root_joint_required", "Edit the root-owned motion link.")+ require(set(args) & {"name", "suppressed", "reversed", "motionOne", "motionTwo", "valueOne", "valueTwo"}, "invalid_input", "Supply a motion-link change.")+ first, second = concrete(link.jointOne), concrete(link.jointTwo)+ coupling = set(args) & {"motionOne", "motionTwo", "valueOne", "valueTwo", "reversed"}+ expected = {}+ effective = dict(args)+ if coupling:+ old = link_data(link)+ changes = {key: args.get(key, old[key]) for key in ("motionOne", "motionTwo", "reversed")}+ changes.update({key: args.get(key, old[key]["expression"]) for key in ("valueOne", "valueTwo")})+ effective.update(changes)+ enums, values, expected = inputs(design, first, second or first, changes)+ # Unlike regular-joint redefinition, setMotionData requires the link to+ # exist in the evaluated timeline. Native rollback before this item+ # removes its component context and fails InternalValidationError.+ require(design.timeline.markerPosition == design.timeline.count, "timeline_not_at_end",+ "Motion-link coupling edits require the timeline at the end.")+ require(link.setMotionData(enums[0], values[0], enums[1], values[1], changes["reversed"]),+ "motion_link_failed", "Fusion refused updated coupling data.")+ if "name" in args:+ link.name = args["name"]+ if "suppressed" in args:+ link.isSuppressed = args["suppressed"]+ if design.snapshots.hasPendingSnapshot:+ require(design.snapshots.add() is not None, "motion_link_capture_failed", "Could not capture link suppression.")+ return lambda fresh: result(link, fresh, effective, first, second, expected)+++def motion_link_delete(design, value, args):+ link = resolve(design, value, args["link"], ("MotionLink",))+ require(getattr(link, "assemblyContext", None) is None and same(link.parentComponent, design.rootComponent),+ "root_joint_required", "Delete the root-owned motion link.")+ name = link.name+ require(link.deleteMe(), "delete_failed", "Fusion refused motion-link deletion.")+ return {"deletedMotionLink": name}+++OPERATIONS = {name: globals()[name] for name in ("motion_link_create", "motion_link_edit", "motion_link_delete")}
addin/AdomBridge/commands/mechanical/operations.pyadded+242@@ -0,0 +1,242 @@+"""Typed mechanical operations. Add new operations here behind shared guards."""+import re++import adsk.core+import adsk.fusion++from .core import (MechanicalError, active, body_data, current_target, health,+ identity, items, number, parameter_data, recovery_snapshot,+ ref, require, require_healthy, resolve, target, UNITS)+from .exporting import export_design+++def inspect(app, args):+ from .assembly_state import metadata, joint_data, rigid_group_data+ doc, design, key = active(app)+ value = target(app, args)[2] if args.get("target") else current_target(app)+ bodies, components, occurrences, sketches, features, axes, planes = [], [], [], [], [], [], []+ for comp in items(design.allComponents):+ components.append({"id": identity(comp), **metadata(comp), "ref": ref(comp, value),+ "bodyCount": comp.bRepBodies.count, "sketchCount": comp.sketches.count})+ for body in items(comp.bRepBodies):+ row = body_data(body)+ row.update({"ref": ref(body, value), "componentId": identity(comp)})+ bodies.append(row)+ for sketch in items(comp.sketches):+ sketches.append({"ref": ref(sketch, value), "name": sketch.name, "componentId": identity(comp),+ "profileCount": sketch.profiles.count, "fullyConstrained": sketch.isFullyConstrained})+ for index, feature in enumerate(items(comp.features)):+ from .readback import feature_data+ features.append(feature_data(feature, value, order=index))+ from .geometry import axis_data+ axes.extend(axis_data(axis, value) for axis in items(comp.constructionAxes))+ # the three origin planes and every construction plane, so a plane reference can be read+ # again after a write (each write moves the revision) and a base plane is referenceable+ from .readback import plane_data+ for plane in [comp.xYConstructionPlane, comp.xZConstructionPlane, comp.yZConstructionPlane] + items(comp.constructionPlanes):+ planes.append({**plane_data(plane, value), "componentId": identity(comp)})+ for occ in items(design.rootComponent.allOccurrences):+ matrix = list(occ.transform2.asArray())+ # Matrix4x4.asArray is row-major; translation lies at 3,7,11.+ for i in (3, 7, 11):+ matrix[i] *= 10+ occurrences.append({"ref": ref(occ, value), "componentId": identity(occ.component),+ "path": occ.fullPathName, "transform": matrix,+ "transformLayout": "row-major", "translationUnit": "mm",+ "space": "root", "grounded": occ.isGrounded})+ from .joint_state import origin_data, link_data+ from .configurations import snapshot as configuration_snapshot+ configuration = configuration_snapshot(design)+ return {"target": value, "document": {"name": doc.name, "creationId": doc.creationId,+ "saved": doc.isSaved}, "designIntent": str(getattr(design, "designIntent", "unavailable")),+ "units": UNITS, "components": components, "bodies": bodies, "occurrences": occurrences,+ "sketches": sketches, "features": features, "constructionAxes": axes, "constructionPlanes": planes,+ "joints": [joint_data(j, value) for comp in items(design.allComponents)+ for name in ("joints", "asBuiltJoints") for j in items(getattr(comp, name))],+ "rigidGroups": [rigid_group_data(g, value) for comp in items(design.allComponents) for g in items(comp.rigidGroups)],+ "jointOrigins": [origin_data(o, value) for comp in items(design.allComponents) for o in items(comp.jointOrigins)],+ "motionLinks": [link_data(o, value) for comp in items(design.allComponents) for o in items(comp.motionLinks)] if hasattr(design.rootComponent, "motionLinks") else [],+ "parameters": [parameter_data(design, p) for p in items(design.allParameters)],+ "health": health(design), "complete": configuration["complete"], "configuration": configuration}+++def inspect_entity(app, args):+ _, design, value = target(app, args)+ entity = resolve(design, value, args.get("entity"))+ result = {"ref": ref(entity, value), "objectType": entity.objectType, "name": getattr(entity, "name", None)}+ if entity.objectType.endswith("::BRepBody"):+ from .readback import topology_data+ result.update(topology_data(entity, value))+ result["space"] = "assembly_context" if args["entity"].get("occurrenceToken") else "component"+ result["faceRefs"] = [ref(f, value) for f in items(entity.faces)]+ result["edgeRefs"] = [ref(e, value) for e in items(entity.edges)]+ elif entity.objectType.endswith("::Sketch"):+ from .readback import sketch_data+ result.update(sketch_data(entity, value))+ elif entity.objectType.endswith("::Occurrence"):+ result["bodyRefs"] = [ref(b, value, entity) for b in items(entity.component.bRepBodies)]+ result["childRefs"] = [ref(o, value) for o in items(entity.childOccurrences)]+ result["jointOriginRefs"] = [ref(o, value, entity) for o in items(entity.component.jointOrigins)]+ elif entity.objectType.endswith(("::AsBuiltJoint", "::Joint")):+ from .assembly_state import joint_data+ result.update(joint_data(entity, value))+ elif entity.objectType.endswith("::Component"):+ from .assembly_state import metadata+ result.update(metadata(entity))+ elif entity.objectType.endswith("::RigidGroup"):+ from .assembly_state import rigid_group_data+ result.update(rigid_group_data(entity, value))+ elif entity.objectType.endswith("::ConstructionAxis"):+ from .geometry import axis_data+ result.update(axis_data(entity, value))+ result["space"] = "root" if getattr(entity, "assemblyContext", None) else "component"+ elif entity.objectType.endswith("::JointOrigin"):+ from .joint_state import origin_data+ result.update(origin_data(entity, value))+ elif entity.objectType.endswith("::MotionLink"):+ from .joint_state import link_data+ result.update(link_data(entity, value))+ return {"target": value, "entity": result}+++def new_design(app, args):+ name = args.get("name", "Adom mechanical")+ require(isinstance(name, str) and 0 < len(name) <= 120, "invalid_input", "name must be 1..120 characters.")+ box = args.get("box")+ if box is not None:+ require(isinstance(box, dict), "invalid_input", "box must contain width, height and depth in mm.")+ dims = [number(box.get(n), n, positive=True) for n in ("width", "height", "depth")]+ previous = app.activeDocument+ doc = None+ try:+ doc = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)+ require(doc is not None, "create_failed", "Fusion did not create a document.")+ design = adsk.fusion.Design.cast(doc.products.itemByProductType("DesignProductType"))+ require(design is not None, "create_failed", "Created document has no Design product.")+ design.designType = adsk.fusion.DesignTypes.ParametricDesignType+ from . import transactions+ if not transactions.capability()["verified"]:+ transactions.probe(app, {"target": current_target(app)})+ if box is not None:+ create_box(design, dims)+ require_healthy(design)+ # Naming a never-saved Fusion document makes close(False) capable of+ # raising a save dialog. Keep the scratch document unnamed and store the+ # requested label as model metadata until an explicit save operation.+ design.rootComponent.attributes.add("AdomMechanical", "requestedName", name)+ result = inspect(app, {})+ result["document"]["requestedName"] = name+ return result+ except Exception as exc:+ recovery = {"strategy": "discard_new_unsaved_document", "verified": False}+ if doc is not None:+ try:+ require(not doc.isSaved, "recovery_refused", "New document unexpectedly saved.")+ require(doc.name.lower().startswith("untitled"), "recovery_refused", "Document was renamed; refusing a close that could prompt.")+ doc.close(False)+ recovery["verified"] = not doc.isValid+ if previous and previous.isValid:+ previous.activate()+ except Exception as close_error:+ recovery["error"] = str(close_error)+ raise MechanicalError("create_failed", str(exc), recovery=recovery)+++def create_box(design, dimensions):+ root = design.rootComponent+ for name, value in zip(("width", "height", "depth"), dimensions):+ design.userParameters.add(name, adsk.core.ValueInput.createByString(str(value) + " mm"), "mm", "Adom driving dimension")+ width, height, _ = [n / 10 for n in dimensions]+ sketch = root.sketches.add(root.xYConstructionPlane)+ sketch.name = "Base rectangle"+ origin = adsk.core.Point3D.create(0, 0, 0)+ lines = sketch.sketchCurves.sketchLines.addTwoPointRectangle(origin, adsk.core.Point3D.create(width, height, 0))+ first = lines.item(0)+ second = lines.item(1)+ orient = adsk.fusion.DimensionOrientations+ sx = sketch.sketchDimensions.addDistanceDimension(first.startSketchPoint, first.endSketchPoint,+ orient.HorizontalDimensionOrientation, adsk.core.Point3D.create(width / 2, -height / 3, 0))+ sy = sketch.sketchDimensions.addDistanceDimension(second.startSketchPoint, second.endSketchPoint,+ orient.VerticalDimensionOrientation, adsk.core.Point3D.create(width * 1.3, height / 2, 0))+ sx.parameter.expression, sy.parameter.expression = "width", "height"+ # addTwoPointRectangle at the origin supplies the origin coincidence itself.+ feature_input = root.features.extrudeFeatures.createInput(sketch.profiles.item(0), adsk.fusion.FeatureOperations.NewBodyFeatureOperation)+ extent = adsk.fusion.DistanceExtentDefinition.create(adsk.core.ValueInput.createByString("depth"))+ feature_input.setOneSideExtent(extent, adsk.fusion.ExtentDirections.PositiveExtentDirection)+ feature = root.features.extrudeFeatures.add(feature_input)+ feature.name = "Base extrusion"+ feature.bodies.item(0).name = "Block"+++def parameter_set(app, args):+ _, design, before_target = target(app, args, True)+ require_healthy(design, False)+ name, expression = args.get("name"), args.get("expression")+ require(isinstance(name, str) and isinstance(expression, str) and expression.strip(), "invalid_input", "name and expression are required strings.")+ parameter = design.allParameters.itemByName(name)+ require(parameter is not None, "parameter_missing", "No parameter named " + name)+ require(design.unitsManager.isValidExpression(expression, parameter.unit), "invalid_expression", "Expression is invalid for the parameter's unit.")+ before, old = recovery_snapshot(design), parameter.expression+ try:+ parameter.expression = expression+ state = require_healthy(design)+ except Exception as exc:+ recovery = {"strategy": "restore_parameter_expression", "verified": False,+ "scope": "parameter expressions, body metrics, occurrence transforms, counts and health"}+ try:+ parameter.expression = old+ require_healthy(design)+ recovery["verified"] = recovery_snapshot(design) == before+ except Exception as restore_error:+ recovery["error"] = str(restore_error)+ raise MechanicalError("parameter_edit_failed", str(exc), recovery=recovery, target=current_target(app))+ return {"target": current_target(app), "parameter": parameter_data(design, parameter),+ "health": state, "recoveryStrategy": "restore_parameter_expression", "transactional": False}+++def parameter_create(app, args):+ _, design, _ = target(app, args, True)+ require_healthy(design, False)+ name, expression, unit = args.get("name"), args.get("expression"), args.get("unit")+ require(isinstance(name, str) and re.fullmatch(r"[A-Za-z][A-Za-z0-9_]{0,63}", name), "invalid_input", "Use a short parameter identifier.")+ require(unit in ("mm", "deg", "kg", ""), "invalid_input", "unit must be mm, deg, kg or empty for unitless.")+ require(isinstance(expression, str) and design.unitsManager.isValidExpression(expression, unit), "invalid_expression", "Expression must match the declared unit.")+ require(design.allParameters.itemByName(name) is None, "parameter_exists", "Parameter already exists.")+ before = recovery_snapshot(design)+ parameter = None+ try:+ parameter = design.userParameters.add(name, adsk.core.ValueInput.createByString(expression), unit, args.get("comment", ""))+ require(parameter is not None, "parameter_create_failed", "Fusion returned no parameter.")+ state = require_healthy(design)+ except Exception as exc:+ recovery = {"strategy": "delete_created_parameter", "verified": False}+ try:+ created = parameter or design.userParameters.itemByName(name)+ if created:+ created.deleteMe()+ require_healthy(design)+ recovery["verified"] = recovery_snapshot(design) == before+ except Exception as restore_error:+ recovery["error"] = str(restore_error)+ raise MechanicalError("parameter_create_failed", str(exc), recovery=recovery)+ return {"target": current_target(app), "parameter": parameter_data(design, parameter), "health": state, "transactional": False}+++def check_health(app, args):+ _, design, _ = target(app, args, args.get("recompute", False))+ state = health(design, args.get("recompute", False))+ return {"target": current_target(app), "health": state}+++def export(app, args):+ _, design, value = target(app, args, True, model_write=False)+ require_healthy(design, False)+ result = export_design(design, args)+ result["target"] = value+ return result+++OPERATIONS = {"inspect": inspect, "inspect_entity": inspect_entity, "new_design": new_design,+ "parameter_set": parameter_set, "parameter_create": parameter_create,+ "health": check_health, "export": export}+WRITES = {"new_design", "parameter_set", "parameter_create", "export"}
addin/AdomBridge/commands/mechanical/purchased.pyadded+142@@ -0,0 +1,142 @@+"""Local supplier CAD ingestion and Fusion's native McMaster command."""+import hashlib+from pathlib import Path+from urllib.parse import urlparse++import adsk.core++from . import transactions+from .assemblies import apply_metadata, created_occurrence_result, hybrid, parent_component, place_created_occurrence, transform+from .assembly_state import metadata+from .core import MechanicalError, active, current_target, items, ref, require, resolve, target++MCM_COMMAND = "InsertMcMasterCarrComponentCommand"+++def file_hash(path):+ result = hashlib.sha256()+ with path.open("rb") as stream:+ for block in iter(lambda: stream.read(1024 * 1024), b""):+ result.update(block)+ return result.hexdigest()+++def preflight_file(args):+ path = Path(args["filename"])+ require(path.is_absolute() and path.is_file() and not path.is_symlink(), "invalid_source", "Supplier CAD must be an absolute regular local file.")+ require(path.suffix.lower() in (".step", ".stp", ".sat"), "unsupported_format", "Local supplier ingestion supports STEP and SAT.")+ require(0 < path.stat().st_size <= 512 * 1024 * 1024, "invalid_source", "Supplier CAD must be nonempty and no larger than 512 MiB.")+ provenance = args["provenance"]+ url = urlparse(provenance["sourceUrl"])+ require(url.scheme == "https" and url.hostname and not url.username and not url.password, "invalid_provenance", "sourceUrl must be an HTTPS supplier URL without credentials.")+ if provenance["supplier"].lower() in ("mcmaster", "mcmaster-carr"):+ require(url.hostname.lower() == "mcmaster.com" or url.hostname.lower().endswith(".mcmaster.com"), "invalid_provenance", "McMaster provenance must use the supplier's domain.")+ sha = file_hash(path)+ if "sha256" in args:+ require(args["sha256"].lower() == sha, "source_hash_mismatch", "Downloaded CAD does not match the expected SHA-256.")+ return path, sha+++def cad_import(app, args):+ # Source preflight runs before any transaction/model mutation. The importer+ # only reads this file; no cloud upload, browser download or file rewrite.+ target(app, args, True)+ path, sha = preflight_file(args)+ document_count = app.documents.count+ manager = app.importManager+ factory = manager.createSATImportOptions if path.suffix.lower() == ".sat" else manager.createSTEPImportOptions+ options = factory(str(path))+ require(options is not None, "import_unavailable", "Fusion refused import options.")+ options.isViewFit = False++ def mutate(design, value, request):+ hybrid(design)+ parent = parent_component(design, value, request)+ expected = transform(request.get("transform"))+ occurrence = parent.occurrences.addNewComponent(expected)+ require(occurrence is not None, "import_failed", "Could not create import wrapper component.")+ comp = occurrence.component+ comp.name = request.get("name", path.stem)+ imported = manager.importToTarget2(options, comp)+ require(imported is not None and imported.count > 0, "import_failed", "Fusion returned no imported objects.")+ require(app.documents.count == document_count, "import_document_side_effect", "Importer changed the document set; inspect documents before continuing.")+ components = [comp] + [o.component for o in items(comp.allOccurrences)]+ require(any(c.bRepBodies.count for c in components), "import_empty", "Imported CAD contains no BRep bodies.")+ require(file_hash(path) == sha, "source_changed", "Supplier CAD changed while import was running.")+ provenance = {**request["provenance"], "sourceFile": str(path), "sha256": sha,+ "asPurchased": request.get("asPurchased", True), "partNumber": request["provenance"]["supplierPartNumber"]}+ apply_metadata(comp, provenance)+ captured = place_created_occurrence(design, parent, occurrence, expected)+ return lambda fresh: {**created_occurrence_result(design, parent, occurrence, fresh, expected), "positionCaptured": captured,+ "source": {"filename": str(path), "sha256": sha},+ "importedObjectCount": imported.count, "externalEffects": "read-only source file; no cloud or document creation requested"}+ try:+ return transactions.execute(app, args, mutate)+ except MechanicalError as exc:+ exc.details["sourceFileModified"] = False+ exc.details["documentSetUnchanged"] = app.documents.count == document_count+ raise+++def mcmaster_discover(app, args):+ _, design, _ = active(app)+ command = app.userInterface.commandDefinitions.itemById(MCM_COMMAND)+ return {"target": current_target(app), "commandId": MCM_COMMAND, "available": command is not None,+ "name": command.name if command else None, "designIntent": str(getattr(design, "designIntent", "unavailable")),+ "requirements": ["Hybrid design", "online supplier access", "interactive selection and download"],+ "downloadedCadAlternative": "cad_import with local STEP/SAT and supplier provenance"}+++def mcmaster_open(app, args):+ _, design, value = target(app, args, True)+ hybrid(design)+ command = app.userInterface.commandDefinitions.itemById(MCM_COMMAND)+ require(command is not None, "mcmaster_unavailable", "Fusion's native McMaster command is not installed.")+ require(command.execute(), "mcmaster_launch_failed", "Fusion refused the native McMaster command.")+ return {"commandId": MCM_COMMAND, "launchRequested": True, "targetAtLaunch": value,+ "completion": "interactive; inspect after inserting or cancelling", "refreshRequired": True,+ "transactional": False, "recovery": "native command controls its own user interaction"}+++def material_list(app, args):+ _, design, value = target(app, args)+ libraries = [{"id": library.id, "name": library.name, "count": library.materials.count} for library in items(app.materialLibraries)]+ query = args.get("query", "").lower()+ if args.get("libraryId"):+ matches = [lib for lib in items(app.materialLibraries) if lib.id == args["libraryId"]]+ require(len(matches) == 1, "material_library_missing", "Material library ID must identify one loaded library.")+ sources = [(matches[0].id, matches[0].materials)]+ else:+ # the document's own materials first, then every loaded library: a new design holds only+ # the materials it uses, so a search for "copper" there used to find nothing. Each row+ # names the libraryId that material_assign needs (null: the document's own)+ sources = [(None, design.materials)] + [(lib.id, lib.materials) for lib in items(app.materialLibraries)]+ rows = [{"id": material.id, "name": material.name, "libraryId": library_id}+ for library_id, source in sources for material in items(source) if query in material.name.lower()]+ limit = args.get("limit", 100)+ return {"target": value, "libraries": libraries, "materials": rows[:limit], "complete": len(rows) <= limit,+ "matchingCount": len(rows), "libraryId": args.get("libraryId")}+++def material_assign(app, args):+ def mutate(design, value, request):+ entity = resolve(design, value, request["entity"], ("Component", "BRepBody"))+ require(getattr(entity, "assemblyContext", None) is None, "definition_reference_required", "Material assignment affects a native component or body definition.")+ source = design.materials+ if request.get("libraryId"):+ matches = [lib for lib in items(app.materialLibraries) if lib.id == request["libraryId"]]+ require(len(matches) == 1, "material_library_missing", "Material library ID must identify one loaded library.")+ source = matches[0].materials+ material = source.itemById(request["materialId"])+ require(material is not None, "material_missing", "Material ID was not found in the selected source.")+ entity.material = material+ def result(fresh):+ require(entity.material is not None and entity.material.id == material.id, "material_failed", "Assigned material ID did not read back; same display names are not material identity.")+ return {"entity": ref(entity, fresh), "material": {"id": entity.material.id, "name": entity.material.name}}+ return result+ return transactions.execute(app, args, mutate)+++OPERATIONS = {"cad_import": cad_import, "mcmaster_open": mcmaster_open, "material_assign": material_assign,+ "mcmaster_discover": mcmaster_discover, "material_list": material_list}+WRITES = {"cad_import", "mcmaster_open", "material_assign"}
addin/AdomBridge/commands/mechanical/readback.pyadded+286@@ -0,0 +1,286 @@+"""Detailed topology and sketch readback using resolvable guarded references."""+import json+import adsk.core+import adsk.fusion+from .core import body_data, digest, items, point_mm, ref, require, same+++def concrete_geometry(geometry):+ """Read subclass properties even when a getter returns a base API wrapper."""+ kind = getattr(adsk.core, geometry.objectType.split("::")[-1], None)+ if kind is not None and hasattr(kind, "cast"):+ geometry = kind.cast(geometry)+ require(geometry is not None, "unavailable_geometry", "Cannot cast native geometry to its advertised type.")+ return geometry+++def geometry_data(geometry):+ geometry = concrete_geometry(geometry)+ result = {"type": geometry.objectType}+ for name in ("startPoint", "endPoint", "center", "origin"):+ if hasattr(geometry, name):+ result[name] = point_mm(getattr(geometry, name))+ for name in ("normal", "direction", "majorAxis", "minorAxis"):+ if hasattr(geometry, name):+ p = getattr(geometry, name)+ result[name] = [p.x, p.y, p.z]+ for name in ("radius", "majorRadius", "minorRadius"):+ if hasattr(geometry, name):+ result[name] = getattr(geometry, name) * 10+ if geometry.objectType.endswith("::NurbsCurve3D"):+ ok, controls, degree, knots, rational, weights, periodic = geometry.getData()+ require(ok, "unavailable_geometry", "Cannot read complete spline geometry for the semantic witness.")+ result.update(controlPoints=[point_mm(p) for p in controls], knots=list(knots), weights=list(weights),+ degree=degree, rational=rational, periodic=periodic)+ return result+++def edge_geometry_data(edge):+ geometry = edge.geometry+ if geometry is not None:+ return geometry_data(geometry)+ require(edge.isDegenerate, "unavailable_geometry", "Nondegenerate BRep edge has no readable curve geometry.")+ vertices = [point_mm(vertex.geometry) for vertex in (edge.startVertex, edge.endVertex) if vertex is not None]+ require(vertices, "unavailable_geometry", "Degenerate BRep edge has no readable vertex position.")+ return {"type": "degenerate", "isDegenerate": True, "vertices": vertices}+++def profile_signature(profile):+ props = profile.areaProperties()+ return {"area": props.area * 100, "centroid": point_mm(props.centroid), "loops": profile.profileLoops.count}+++def profile_ref(sketch, profile, index, value):+ return {**value, "objectType": "adsk::fusion::Profile", "sketchToken": sketch.entityToken,+ "profileIndex": index, "fingerprint": digest(profile_signature(profile))}+++def curve_data(curve, value=None):+ result = {"type": curve.objectType, "construction": curve.isConstruction,+ "fixed": curve.isFixed, "geometry": geometry_data(curve.geometry)}+ if value:+ result["ref"] = ref(curve, value)+ for name in ("startSketchPoint", "endSketchPoint", "centerSketchPoint"):+ if hasattr(curve, name):+ point = getattr(curve, name)+ result[name] = {"position": point_mm(point.geometry)}+ if value:+ result[name]["ref"] = ref(point, value)+ if hasattr(curve, "fitPoints"):+ result["fitPoints"] = [point_mm(p.geometry) for p in items(curve.fitPoints)]+ return result+++def frame_data(matrix):+ """A Matrix3D as a frame: origin in mm and three unit directions, in component space."""+ origin, x, y, z = matrix.getAsCoordinateSystem()+ return {"origin": point_mm(origin), "xDirection": [x.x, x.y, x.z], "yDirection": [y.x, y.y, y.z],+ "normal": [z.x, z.y, z.z], "space": "component", "lengthUnit": "mm"}+++def to_component(sketch, point):+ """A sketch-space point in its component's space, without changing the original."""+ moved = point.copy()+ moved.transformBy(sketch.transform)+ return point_mm(moved)+++def plane_data(plane, value=None):+ """A construction plane (the three origin planes included): its frame in component space."""+ from .core import origin_plane_key+ geometry = plane.geometry+ result = {"name": plane.name, "originPlane": origin_plane_key(plane), "space": "component", "lengthUnit": "mm",+ "origin": point_mm(geometry.origin), "normal": [geometry.normal.x, geometry.normal.y, geometry.normal.z]}+ for name in ("uDirection", "vDirection"):+ if hasattr(geometry, name):+ v = getattr(geometry, name)+ result[name] = [v.x, v.y, v.z]+ if value:+ result["ref"] = ref(plane, value)+ return result+++def sketch_data(sketch, value=None):+ curves, points = items(sketch.sketchCurves), items(sketch.sketchPoints)+ constraints, dimensions = items(sketch.geometricConstraints), items(sketch.sketchDimensions)+ graph = SketchGraph(curves, points, constraints)+ result = {"name": sketch.name, "space": "sketch", "lengthUnit": "mm",+ "fullyConstrained": sketch.isFullyConstrained,+ "curves": [curve_data(curve, value) for curve in curves],+ "points": [{"position": point_mm(p.geometry), "fixed": p.isFixed, **({"ref": ref(p, value)} if value else {})}+ for p in points],+ "curvePointLinks": [graph.curve_points(curve) for curve in curves],+ "constraints": [constraint_data(constraint, graph) for constraint in constraints],+ "dimensions": [dimension_data(dimension, graph) for dimension in dimensions]}+ if value:+ # where the sketch sits: its x, y and normal in component space, so a sketch-space point+ # (every curve, point and centroid above) maps to the model without guessing the plane+ result["frame"] = frame_data(sketch.transform)+ result["ref"] = ref(sketch, value)+ result["originRef"] = ref(sketch.originPoint, value)+ result["profiles"] = [{**profile_signature(profile),+ "centroidComponent": to_component(sketch, profile.areaProperties().centroid),+ "ref": profile_ref(sketch, profile, index, value)}+ for index, profile in enumerate(items(sketch.profiles))]+ return result+++CONSTRAINT_LINKS = {+ "CoincidentConstraint": ("point", "entity"),+ "HorizontalConstraint": ("line",), "VerticalConstraint": ("line",),+ "HorizontalPointsConstraint": ("pointOne", "pointTwo"),+ "VerticalPointsConstraint": ("pointOne", "pointTwo"),+ "ParallelConstraint": ("lineOne", "lineTwo"), "CollinearConstraint": ("lineOne", "lineTwo"),+ "PerpendicularConstraint": ("lineOne", "lineTwo"),+ "EqualConstraint": ("curveOne", "curveTwo"), "TangentConstraint": ("curveOne", "curveTwo"),+ "SmoothConstraint": ("curveOne", "curveTwo"), "ConcentricConstraint": ("entityOne", "entityTwo"),+ "MidPointConstraint": ("point", "midPointCurve"),+ "SymmetryConstraint": ("entityOne", "entityTwo", "symmetryLine"),+ "OffsetConstraint": (),+}+DIMENSION_LINKS = {+ "SketchLinearDimension": ("entityOne", "entityTwo"),+ "SketchAngularDimension": ("lineOne", "lineTwo"),+ "SketchRadialDimension": ("entity",), "SketchDiameterDimension": ("entity",),+ "SketchOffsetDimension": ("line", "entityTwo"),+ "SketchOffsetCurvesDimension": ("offsetConstraint",),+ "SketchConcentricCircleDimension": ("circleOne", "circleTwo"),+ "SketchEllipseMajorRadiusDimension": ("ellipse",), "SketchEllipseMinorRadiusDimension": ("ellipse",),+ "SketchLinearDiameterDimension": ("line", "entityTwo"),+ "SketchTangentDistanceDimension": ("entityOne", "circleOrArc"),+}+++class SketchGraph:+ """Links use local inventory nodes resolved by API equality, never tokens.++ The associated geometry inventories are part of the same witness. Native+ reordering may conservatively refuse recovery; retargeting distinct nodes+ cannot disappear merely because their current positions coincide.+ """+ def __init__(self, curves, points, constraints):+ self.nodes = {"curves": curves, "points": points, "constraints": constraints}+ self.comparisons = 0+ self.direct = {}+ for name, entities in self.nodes.items():+ for index, entity in enumerate(entities):+ self.direct.setdefault(id(entity), []).append({"collection": name, "index": index})++ def node(self, entity):+ matches = self.direct.get(id(entity))+ if matches is None:+ self.comparisons += sum(len(entities) for entities in self.nodes.values())+ require(self.comparisons <= 1000000, "inspection_budget", "Native sketch graph equality lookup exceeds inspection budget.")+ matches = [{"collection": name, "index": index}+ for name, entities in self.nodes.items() for index, item in enumerate(entities)+ if entity is not None and same(entity, item)]+ require(len(matches) == 1, "unsupported_constraint_graph",+ "Sketch graph link is null, ambiguous or outside the readable native sketch inventory.")+ return matches[0]++ def links(self, item, fields):+ try:+ return {name: self.node(getattr(item, name)) for name in fields}+ except AttributeError as exc:+ require(False, "unsupported_constraint_graph", "Native constraint relationship is unavailable.",+ objectType=item.objectType, detail=str(exc))++ def curve_points(self, curve):+ result = self.links(curve, [name for name in ("startSketchPoint", "endSketchPoint", "centerSketchPoint") if hasattr(curve, name)])+ if hasattr(curve, "fitPoints"):+ result["fitPoints"] = [self.node(p) for p in items(curve.fitPoints)]+ return result+++def graph_item(item, supported):+ kind = item.objectType.split("::")[-1]+ require(kind in supported, "unsupported_constraint_graph",+ "No complete recovery graph reader for this native constraint/dimension type.", objectType=item.objectType)+ subtype = getattr(adsk.fusion, kind, None)+ if subtype is not None:+ item = subtype.cast(item)+ require(item is not None, "unsupported_constraint_graph", "Cannot cast native sketch relationship.")+ require(item.isValid, "unsupported_constraint_graph", "Native sketch relationship is no longer valid.")+ return item, kind+++def constraint_data(constraint, graph):+ constraint, kind = graph_item(constraint, CONSTRAINT_LINKS)+ result = {"type": constraint.objectType}+ fields = CONSTRAINT_LINKS[kind]+ if kind == "PerpendicularConstraint" and hasattr(constraint, "curve"):+ # September 2026 added line/curve perpendicularity. Earlier runtimes+ # expose only the two-line relation; do not invent the missing link.+ fields = ("line", "curve")+ result["links"] = graph.links(constraint, fields)+ if kind == "OffsetConstraint":+ # Offset links and sign belong to the recovery witness, beyond the+ # number of constraints and generated curve geometry.+ result.update(distance=constraint.distance * 10, topologyMatched=constraint.isTopologyMatched,+ parents=sorted([graph.node(curve) for curve in constraint.parentCurves], key=lambda row: row["index"]),+ children=sorted([graph.node(curve) for curve in constraint.childCurves], key=lambda row: row["index"]))+ return result+++def dimension_data(dimension, graph):+ dimension, kind = graph_item(dimension, DIMENSION_LINKS)+ parameter = dimension.parameter+ result = {"type": dimension.objectType, "name": parameter.name if parameter else None,+ "expression": parameter.expression if parameter else None, "driving": dimension.isDriving,+ "value": dimension.value * (180 / 3.141592653589793 if kind == "SketchAngularDimension" else 10),+ "unit": "deg" if kind == "SketchAngularDimension" else "mm",+ "links": graph.links(dimension, DIMENSION_LINKS[kind])}+ if kind == "SketchLinearDimension": result["orientation"] = int(dimension.orientation)+ return result+++def construction_state(component):+ from .geometry import axis_data+ return [axis_data(axis) for axis in items(component.constructionAxes)] if hasattr(component, "constructionAxes") else []+++def topology_data(body, value):+ result = body_data(body)+ result["ref"] = ref(body, value)+ result["faces"] = [{"ref": ref(face, value), "area": face.area * 100,+ "point": point_mm(face.pointOnFace), "surface": geometry_data(face.geometry),+ "edges": [ref(edge, value) for edge in items(face.edges)]}+ for face in items(body.faces)]+ result["edges"] = [{"ref": ref(edge, value), "length": edge.length * 10,+ "curve": edge_geometry_data(edge)} for edge in items(body.edges)]+ return result+++def topology_witness(body):+ result = body_data(body)+ # Compare geometry rather than revision or transient topology IDs after undo.+ result["faceGeometry"] = sorted([+ {"area": f.area * 100, "point": point_mm(f.pointOnFace), "surface": geometry_data(f.geometry)}+ for f in items(body.faces)], key=lambda row: json.dumps(row, sort_keys=True))+ result["edgeGeometry"] = sorted([+ {"length": e.length * 10, "curve": edge_geometry_data(e)} for e in items(body.edges)],+ key=lambda row: json.dumps(row, sort_keys=True))+ return result+++def feature_data(feature, value=None, order=None):+ result = {"name": feature.name, "type": feature.objectType}+ if order is not None:+ result["componentFeatureOrder"] = order+ if value:+ result["ref"] = ref(feature, value)+ timeline = getattr(feature, "timelineObject", None)+ if timeline:+ result["suppressed"] = timeline.isSuppressed+ try:+ result["index"] = timeline.index+ except RuntimeError as exc:+ # Imported/native grouped features can have a valid timeline object+ # whose index getter rejects its documented non-indexed position.+ # Only this known getter failure is optional; retain name, type,+ # suppression and component collection order in semantic witnesses.+ if "InternalValidationError : res >= 0" not in str(exc):+ raise+ require(timeline.isValid, "invalid_timeline", "Feature timeline object is no longer valid.")+ result.update(index=None, indexUnavailable="native_timeline_position_unavailable")+ return result
addin/AdomBridge/commands/mechanical/regular_joints.pyadded+488@@ -0,0 +1,488 @@+"""Geometry-solving joints and reusable origins; all writes use PTransaction."""+from contextlib import contextmanager+import math++import adsk.core+import adsk.fusion++from .core import concrete, identity, items, ref, require, require_healthy, resolve, same+from .geometry import native, owner, root_entity+from .joint_state import (geometry_state, matrix_data, origin_data,+ regular_state)+from .sketches import component, named, quantity+from .assemblies import configure_joint, joint_result++GEOMETRY_TYPES = ("BRepFace", "BRepEdge", "BRepVertex", "SketchPoint", "ConstructionPoint",+ "SketchLine", "SketchArc", "SketchCircle", "SketchEllipse", "SketchEllipticalArc")+MOTION_METHODS = {"rigid": "Rigid", "revolute": "Revolute", "slider": "Slider", "cylindrical": "Cylindrical",+ "pin_slot": "PinSlot", "planar": "Planar", "ball": "Ball"}+++@contextmanager+def before_timeline(design, entity):+ """Restore the exact caller marker, including on native setter exceptions."""+ position = design.timeline.markerPosition+ timeline = entity.timelineObject+ require(timeline is not None and timeline.isValid, "timeline_unavailable", "This edit requires a valid parametric timeline item.")+ try:+ require(timeline.rollTo(True), "timeline_roll_failed", "Could not roll immediately before the joint item.")+ yield+ finally:+ design.timeline.markerPosition = position+ require(design.timeline.markerPosition == position, "timeline_restore_failed", "Could not restore the original timeline marker.")+++def geometry_from_entity(entity, key_name=None, edge=None):+ entity, edge = concrete(entity), concrete(edge)+ kind = entity.objectType.split("::")[-1]+ geometry = getattr(entity, "geometry", None)+ shape = geometry.objectType.split("::")[-1] if geometry is not None else ""+ default = "middle" if kind == "BRepFace" and shape in ("Cylinder", "Cone") else "center"+ key_name = key_name or default+ key = getattr(adsk.fusion.JointKeyPointTypes, {"center": "CenterKeyPoint", "start": "StartKeyPoint",+ "middle": "MiddleKeyPoint", "end": "EndKeyPoint"}[key_name])+ factory = adsk.fusion.JointGeometry+ if kind in ("BRepVertex", "SketchPoint", "ConstructionPoint"):+ require(key_name == "center" and edge is None, "invalid_joint_geometry", "Point attachments have no edge/key-point choice.")+ result = factory.createByPoint(entity)+ elif kind == "BRepEdge" or kind.startswith("Sketch"):+ require(edge is None, "invalid_joint_geometry", "An edge qualifier belongs only to a planar face.")+ require(shape in ("Line3D", "Arc3D", "Circle3D", "Ellipse3D", "EllipticalArc3D"),+ "unsupported_joint_geometry", "Use a line or analytical arc/circle/ellipse attachment.")+ require(shape not in ("Circle3D", "Ellipse3D") or key_name == "center", "invalid_joint_geometry", "Closed circles/ellipses require center.")+ result = factory.createByCurve(entity, key)+ elif kind == "BRepFace" and shape == "Plane":+ require(edge is not None or key_name == "center", "invalid_joint_geometry", "A planar face requires center or one of its explicit edges.")+ if edge is not None:+ require(any(same(native(item), native(edge)) for item in items(entity.edges)), "invalid_joint_geometry", "Selected edge does not belong to this face.")+ result = factory.createByPlanarFace(entity, edge, key)+ else:+ require(kind == "BRepFace" and shape in ("Cylinder", "Cone", "Sphere", "Torus") and edge is None,+ "unsupported_joint_geometry", "Joint face attachment requires a plane, cylinder, cone, sphere or torus.")+ result = factory.createByNonPlanarFace(entity, key)+ require(result is not None and result.isValid, "invalid_joint_geometry", "Fusion did not create joint geometry.")+ return result+++def selector(design, value, raw, definition=None):+ if "origin" in raw:+ require(definition is None, "invalid_joint_geometry", "A joint origin must be attached to geometry, not another origin.")+ origin = resolve(design, value, raw["origin"], ("JointOrigin",))+ root_entity(design, origin)+ return origin+ entity = resolve(design, value, raw["entity"], GEOMETRY_TYPES)+ edge = resolve(design, value, raw["edge"], ("BRepEdge",)) if "edge" in raw else None+ for item in (entity, edge):+ if item is None:+ continue+ if definition is None:+ root_entity(design, item)+ else:+ require(getattr(item, "assemblyContext", None) is None and same(owner(item), definition),+ "wrong_component", "Joint-origin inputs require native geometry in the selected component definition.")+ if edge is not None:+ require(same(getattr(entity, "assemblyContext", None), getattr(edge, "assemblyContext", None)),+ "invalid_context", "Face and edge must refer to the same occurrence instance.")+ return geometry_from_entity(entity, raw.get("keyPoint"), edge)+++def attachment_context(geometry):+ geometry = concrete(geometry)+ entity = concrete(geometry if geometry.objectType.endswith("::JointOrigin") else geometry.entityOne)+ require(entity is not None and entity.isValid, "lost_joint_geometry", "Attachment has no live source entity.")+ return getattr(entity, "assemblyContext", None)+++def check_pair(design, first, second, contexts=None):+ contexts = list(contexts) if contexts is not None else [attachment_context(g) for g in (first, second)]+ require(not same(*contexts), "invalid_joint", "Joint attachments must belong to distinct root/component instances.")+ for context in contexts:+ if context is not None:+ root_entity(design, context)+ if all(context is not None for context in contexts):+ a, b = [context.fullPathName for context in contexts]+ require(not a.startswith(b + "+") and not b.startswith(a + "+"), "invalid_joint", "Cannot joint an occurrence to its own descendant.")+ require(not all(context is None or context.isGrounded for context in contexts),+ "grounded_conflict", "Both joint sides are grounded; unground the intended movable instance explicitly.")+ return contexts+++def set_motion(subject, args):+ kind = args["type"]+ require(not (kind in ("rigid", "ball") and ("axis" in args or "slideAxis" in args)), "invalid_joint_axes", "This joint motion has no configurable axis choice.")+ require(kind == "pin_slot" or "slideAxis" not in args, "invalid_joint_axes", "slideAxis is specific to pin-slot joints.")+ method = getattr(subject, "setAs" + MOTION_METHODS[kind] + "JointMotion")+ directions = adsk.fusion.JointDirections+ axis = getattr(directions, args.get("axis", "z").upper() + "AxisJointDirection")+ if kind == "rigid":+ ok = method()+ elif kind == "ball":+ ok = method(directions.ZAxisJointDirection, directions.XAxisJointDirection)+ elif kind == "pin_slot":+ require(args.get("axis", "z") != args.get("slideAxis", "x"), "invalid_joint_axes", "Pin-slot rotation and slide axes must differ.")+ ok = method(axis, getattr(directions, args.get("slideAxis", "x").upper() + "AxisJointDirection"))+ else:+ ok = method(axis)+ require(ok, "joint_failed", "Fusion refused the requested motion definition.")+++def lock_arguments(args, joint=None):+ """Rigid motion has no lock setter; its implicit native lock flag is false."""+ kind = args.get("type")+ if kind is None:+ motion = joint.jointMotion+ require(motion is not None, "unsupported_inferred_joint", "Cannot determine the joint's native motion definition.")+ rigid = motion.objectType == "adsk::fusion::RigidJointMotion"+ else:+ rigid = kind == "rigid"+ if rigid:+ require(not args.get("locked", False), "unsupported_joint_lock", "Rigid joints cannot be locked; omit locked or use false.")+ # Verify the implicit flag even when a rigid redefinition omitted it.+ return {**args, "locked": False}+ return args+++def apply_lock(joint, args):+ if "locked" not in args:+ return+ motion = joint.jointMotion+ require(motion is not None, "unsupported_inferred_joint", "Cannot apply a lock without a native motion definition.")+ if motion.objectType == "adsk::fusion::RigidJointMotion":+ require(not args["locked"], "joint_edit_refused", "The native joint remained rigid and cannot accept a lock.")+ # Native rigid setters throw even for false. setAsRigidJointMotion+ # clears an existing moving-joint lock; regular_result checks readback.+ return+ joint.isLocked = args["locked"]+++def expected_parameters(design, args, names):+ result = {}+ for name in names:+ if name in args:+ unit = "deg" if name == "angle" else "mm"+ inp = quantity(design, args[name], unit)+ number = design.unitsManager.evaluateExpression(inp.stringValue, unit)+ result[name] = (inp, number * (180 / math.pi if unit == "deg" else 10))+ return result+++def verify_parameters(actual, expected):+ for name, (_, wanted) in expected.items():+ require(math.isclose(actual[name]["value"], wanted, abs_tol=1e-6, rel_tol=1e-8),+ "joint_parameter_refused", "Joint did not retain the requested " + name + ".")+++def capture_pending(design):+ if design.snapshots.hasPendingSnapshot:+ require(design.snapshots.add() is not None, "joint_capture_failed", "Could not capture pending joint state.")+ return True+ return False+++def source_reference(entity, value):+ entity = concrete(entity)+ if entity is None:+ return None+ component = owner(entity)+ require(component is not None, 'unsupported_joint_geometry', 'Attachment source has no readable component owner.')+ return {'reference': ref(entity, value), 'ownerId': identity(component)}+++def capture_attachment(geometry, value, context=None):+ """Freeze graph locators/scalars before solve; retain no transient geometry."""+ geometry = concrete(geometry)+ state = geometry_state(geometry)+ result = {'type': geometry.objectType, 'context': ref(context, value) if context is not None else None}+ if geometry.objectType.endswith('::JointOrigin'):+ result['origin'] = source_reference(geometry, value)+ else:+ result.update({key: state[key] for key in ('geometryType', 'keyPointType')})+ result['sources'] = {key: source_reference(getattr(geometry, key), value)+ for key in ('entityOne', 'entityTwo', 'planeOne', 'planeTwo')}+ result['tangent'] = {key: state[key] for key in ('tangentFaceType', 'tangentFaceParamOne', 'tangentFaceParamTwo') if key in state}+ return result+++def resolve_source(design, value, saved):+ if saved is None:+ return None+ entity = resolve(design, value, saved['reference'])+ require(owner(entity) is not None and identity(owner(entity)) == saved['ownerId'],+ 'joint_attachment_refused', 'Attachment source changed component owner.')+ return entity+++def verify_attachment(design, value, actual, saved, occurrence=None, definition=False):+ """Compare fresh native API entities, never stale wrappers or token text."""+ actual = concrete(actual)+ require(actual is not None and actual.isValid and actual.objectType == saved['type'],+ 'joint_attachment_refused', 'Joint attachment type changed.')+ expected_context = resolve(design, value, saved['context'], ('Occurrence',)) if saved['context'] else None+ require(same(occurrence, expected_context), 'joint_attachment_refused', 'Joint resolved a different occurrence instance.')+ require(not definition or (occurrence is None and expected_context is None and 'origin' not in saved),+ 'joint_attachment_refused', 'Definition verification is only valid for native origin geometry.')+ context = attachment_context(actual)+ require(definition or context is None or same(context, occurrence),+ 'joint_attachment_refused', 'Attachment proxy disagrees with the joint occurrence.')+ if 'origin' in saved:+ require(same(native(actual), native(resolve_source(design, value, saved['origin']))),+ 'joint_attachment_refused', 'Joint origin identity changed.')+ return+ state = geometry_state(actual)+ require(all(state[key] == saved[key] for key in ('geometryType', 'keyPointType'))+ and all(state.get(key) == wanted for key, wanted in saved['tangent'].items()),+ 'joint_attachment_refused', 'Joint attachment definition changed.')+ for key, locator in saved['sources'].items():+ found, wanted = concrete(getattr(actual, key)), resolve_source(design, value, locator)+ require((found is None and wanted is None) or (found is not None and wanted is not None+ and found.objectType == wanted.objectType and same(native(found), native(wanted))),+ 'joint_attachment_refused', 'Joint attachment source did not read back: ' + key)+ found_context = getattr(found, 'assemblyContext', None) if found is not None else None+ require(found_context is None or (definition and same(found_context.component, owner(wanted)))+ or (not definition and same(found_context, occurrence)),+ 'joint_attachment_refused', 'Joint attachment source has a different occurrence context.')+++def validate_frame(matrix):+ require(len(matrix) == 16 and all(math.isfinite(v) for v in matrix), "joint_frame_unavailable", "Attachment frame is incomplete.")+ axes = [[matrix[row * 4 + col] for row in range(3)] for col in range(3)]+ require(all(math.isclose(sum(v*v for v in axis), 1, abs_tol=1e-6) for axis in axes)+ and all(abs(sum(a*b for a,b in zip(axes[i],axes[k]))) < 1e-6 for i,k in ((0,1),(0,2),(1,2))),+ "joint_frame_unavailable", "Attachment frame is not orthonormal.")+ cross = [axes[0][1]*axes[1][2]-axes[0][2]*axes[1][1], axes[0][2]*axes[1][0]-axes[0][0]*axes[1][2], axes[0][0]*axes[1][1]-axes[0][1]*axes[1][0]]+ require(sum(a*b for a,b in zip(cross,axes[2])) > 1-1e-6, "joint_frame_unavailable", "Attachment frame is not right-handed.")+++def attachment_frames(joint):+ # Native joint transforms already locate root-owned joint anchors in root+ # space. JointGeometry frames use another axis convention and can become+ # unreadable after compute even while isValid remains true. Do not rebuild+ # them from native faces or apply occurrence transforms a second time.+ require(getattr(joint, 'assemblyContext', None) is None+ and same(joint.parentComponent, joint.parentComponent.parentDesign.rootComponent),+ 'root_joint_required', 'Frame readback requires a root-owned joint.')+ matrices = [matrix_data(joint.geometryOneTransform), matrix_data(joint.geometryTwoTransform)]+ for matrix in matrices:+ validate_frame(matrix)+ return {"space": "native_joint_api", "originSpace": "root", "translationUnit": "mm", "transformLayout": "row-major",+ "source": "Joint.geometryOneTransform / Joint.geometryTwoTransform",+ "axisConvention": "native joint transform; distinct from transient JointGeometry axes",+ "one": matrices[0], "two": matrices[1]}+++def initial_alignment(joint, offset, angle, kind):+ frames = attachment_frames(joint)+ one, two = frames["one"], frames["two"]+ delta = [one[i]-two[i] for i in (3,7,11)]+ distance = math.sqrt(sum(v*v for v in delta))+ require(math.isclose(distance, abs(offset), abs_tol=1e-5, rel_tol=1e-7),+ "joint_alignment_failed", "Solved attachment origins do not match the requested offset magnitude.", actualDistance=distance, expectedDistance=abs(offset))+ if abs(offset) > 1e-7:+ axis = [two[i] for i in (2,6,10)]+ axial = sum(a*b for a,b in zip(delta,axis))+ require(math.sqrt(max(0, distance*distance-axial*axial)) < 1e-5,+ "joint_alignment_failed", "Joint offset is not along the attachment Z axis.")+ axes_checked = kind != "ball" and abs(angle) < 1e-9+ if axes_checked:+ dot = sum(one[i]*two[i] for i in (2,6,10))+ require(abs(abs(dot)-1) < 1e-6, "joint_alignment_failed", "Solved attachment Z axes are not collinear.")+ return {**frames, "originDistance": distance, "offsetMagnitudeVerified": True,+ "zeroAngleAxisCollinearityVerified": axes_checked,+ "orientationSenseVerification": "native angle/flip parameter readback; physical orientation requires independent occurrence geometry acceptance"}+++def poses(design):+ return [(o, list(o.transform2.asArray()), bool(o.isGrounded), bool(getattr(o, "isGroundToParent", False)))+ for o in items(design.rootComponent.allOccurrences)]+++def pose_result(design, before):+ live = items(design.rootComponent.allOccurrences)+ require(len(live) == len(before), "joint_structure_changed", "Joint solving unexpectedly changed the occurrence count.")+ changed = []+ for occurrence, old, grounded, ground_to_parent in before:+ require(occurrence.isValid and any(same(occurrence, o) for o in live), "joint_structure_changed", "Joint solving replaced an occurrence.")+ current = list(occurrence.transform2.asArray())+ moved = any(not math.isclose(a, b, abs_tol=1e-8, rel_tol=1e-8) for a, b in zip(current, old))+ require(bool(occurrence.isGrounded) == grounded and bool(getattr(occurrence, "isGroundToParent", False)) == ground_to_parent,+ "grounding_changed", "Joint solving changed grounding state.")+ require(not grounded or not moved, "grounded_conflict", "Joint solving moved a grounded occurrence.")+ if moved:+ changed.append({"occurrenceId": identity(occurrence), "path": occurrence.fullPathName,+ "before": [v * 10 if i in (3, 7, 11) else v for i, v in enumerate(old)],+ "after": matrix_data(occurrence.transform2)})+ return {"space": "root", "translationUnit": "mm", "transformLayout": "row-major", "changedOccurrences": changed,+ "groundedOccurrencesPreserved": True}+++def regular_result(design, joint, fresh, args, expected, attachments, before, original_value):+ result = joint_result(joint, fresh, args)+ state = result["joint"]+ verify_parameters(state, expected)+ for key, prop in (("flipped", "isFlipped"), ("locked", "isLocked")):+ require(key not in args or getattr(joint, prop) == args[key], "joint_edit_refused", "Joint did not retain " + key)+ if "type" in args:+ require(state["motionType"].endswith("::" + MOTION_METHODS[args["type"]] + "JointMotion"), "joint_edit_refused", "Joint motion type did not read back.")+ verify_motion_axes(state["axes"], args)+ contexts = (joint.occurrenceOne, joint.occurrenceTwo)+ for (prop, expected_attachment), context in zip(attachments.items(), contexts):+ verify_attachment(design, original_value, getattr(joint, prop), expected_attachment, context)+ result["placement"] = pose_result(design, before)+ result["attachmentFrames"] = attachment_frames(joint)+ return result+++def verify_motion_axes(actual, args):+ kind = args["type"]+ field = {"revolute": "rotationAxis", "slider": "slideDirection", "cylindrical": "rotationAxis",+ "pin_slot": "rotationAxis", "planar": "normalDirection"}.get(kind)+ expected = {field: args.get("axis", "z")} if field else {}+ if kind == "pin_slot":+ expected["slideDirection"] = args.get("slideAxis", "x")+ if kind == "ball":+ expected.update(pitchDirection="z", yawDirection="x")+ for key, axis in expected.items():+ require(actual[key] == getattr(adsk.fusion.JointDirections, axis.upper() + "AxisJointDirection"),+ "joint_axis_refused", "Joint did not retain the requested " + key + ".")+++def regular_joint_create(design, value, args):+ args = lock_arguments({"flipped": False, **args})+ require(not design.snapshots.hasPendingSnapshot, "pending_position_changes", "Capture or revert pending positions before regular-joint solving.")+ first, second = [selector(design, value, args[key]) for key in ("geometryOne", "geometryTwo")]+ expected_contexts = check_pair(design, first, second)+ attachments = {prop: capture_attachment(geometry, value, context) for prop, geometry, context in+ zip(('geometryOrOriginOne', 'geometryOrOriginTwo'), (first, second), expected_contexts)}+ before = poses(design)+ expected = expected_parameters(design, {"angle": 0, "offset": 0, **args}, ("angle", "offset"))+ inp = design.rootComponent.joints.createInput(first, second)+ require(inp is not None, "joint_failed", "Fusion did not return JointInput.")+ for name, (quantity_input, _) in expected.items():+ setattr(inp, name, quantity_input)+ inp.isFlipped = args.get("flipped", False)+ set_motion(inp, args)+ joint = named(design.rootComponent.joints.add(inp), args)+ require_healthy(design)+ alignment = initial_alignment(joint, expected["offset"][1], expected["angle"][1], args["type"])+ apply_lock(joint, args)+ configure_joint(design, joint, args)+ capture_pending(design)+ def result(fresh):+ row = regular_result(design, joint, fresh, args, expected, attachments, before, value)+ row["initialAlignment"] = alignment+ return row+ return result+++def regular_joint_edit(design, value, args):+ require(not design.snapshots.hasPendingSnapshot, "pending_position_changes", "Capture or revert pending positions before regular-joint redefinition.")+ joint = resolve(design, value, args["joint"], ("Joint",))+ changed = set(args) & {"geometryOne", "geometryTwo", "angle", "offset", "offsetX", "offsetY", "flipped", "locked", "type"}+ require(changed, "invalid_input", "Supply a geometry, offset, angle, flip, lock or motion-definition change.")+ args = lock_arguments(args, joint)+ require(getattr(joint, "assemblyContext", None) is None and same(joint.parentComponent, design.rootComponent),+ "root_joint_required", "Regular-joint redefinition currently requires a root-owned joint.")+ regular_state(joint) # Fail before rollback if attachment readback is incomplete.+ require("type" in args or not ({"axis", "slideAxis"} & set(args)), "invalid_input", "Axis changes require the motion type.")+ attachments = {prop: selector(design, value, args[key]) if key in args else getattr(joint, prop)+ for key, prop in (("geometryOne", "geometryOrOriginOne"), ("geometryTwo", "geometryOrOriginTwo"))}+ expected_contexts = [attachment_context(attachments[prop]) if key in args else getattr(joint, occurrence)+ for key,prop,occurrence in (("geometryOne","geometryOrOriginOne","occurrenceOne"),("geometryTwo","geometryOrOriginTwo","occurrenceTwo"))]+ check_pair(design, *attachments.values(), contexts=expected_contexts)+ saved_attachments = {prop: capture_attachment(geometry, value, context)+ for (prop, geometry), context in zip(attachments.items(), expected_contexts)}+ expected = expected_parameters(design, args, ("angle", "offset", "offsetX", "offsetY"))+ before = poses(design)+ with before_timeline(design, joint):+ for key, prop in (("geometryOne", "geometryOrOriginOne"), ("geometryTwo", "geometryOrOriginTwo")):+ if key in args:+ setattr(joint, prop, attachments[prop])+ if "type" in args:+ set_motion(joint, args)+ if "flipped" in args:+ joint.isFlipped = args["flipped"]+ for name, (quantity_input, _) in expected.items():+ getattr(joint, name).expression = quantity_input.stringValue+ apply_lock(joint, args)+ capture_pending(design)+ return lambda fresh: regular_result(design, joint, fresh, args, expected, saved_attachments, before, value)+++def origin_axes(design, value, args, comp):+ result = {}+ for key in ("xAxisEntity", "zAxisEntity"):+ if key in args:+ entity = resolve(design, value, args[key], ("ConstructionAxis", "SketchLine", "BRepEdge"))+ require(getattr(entity, "assemblyContext", None) is None and same(owner(entity), comp),+ "wrong_component", "Orientation axes must be native geometry in the origin's component.")+ require(entity.geometry.objectType.endswith(("::Line3D", "::InfiniteLine3D")), "invalid_axis", "Orientation requires straight linear geometry.")+ result[key] = entity+ return result+++def origin_result(design, origin, fresh, args, expected, geometry, axes, original_value):+ result = origin_data(origin, fresh)+ verify_parameters(result, expected)+ for key in ("name", "flipped"):+ require(key not in args or result[key] == args[key], "joint_origin_refused", "Origin did not retain " + key)+ # A native origin belongs to a definition, but Fusion can return its geometry+ # as a root-context proxy. Match native source and owner, not that incidental+ # instance. Regular joints keep the strict occurrence-context check above.+ verify_attachment(design, original_value, origin.geometry, geometry, definition=True)+ for key, locator in axes.items():+ require(same(native(concrete(getattr(origin, key))), native(resolve_source(design, original_value, locator))),+ "joint_origin_refused", "Origin axis did not read back.")+ return {"jointOrigin": result, "editScope": "component_definition"}+++def joint_origin_create(design, value, args):+ args = {"flipped": False, **args}+ comp = component(design, value, args)+ geometry = selector(design, value, args["geometry"], comp)+ axes = origin_axes(design, value, args, comp)+ saved_geometry = capture_attachment(geometry, value)+ saved_axes = {key: source_reference(entity, value) for key, entity in axes.items()}+ expected = expected_parameters(design, {"angle": 0, "offsetX": 0, "offsetY": 0, "offsetZ": 0, **args}, ("angle", "offsetX", "offsetY", "offsetZ"))+ inp = comp.jointOrigins.createInput(geometry)+ require(inp is not None, "joint_origin_failed", "Fusion did not return JointOriginInput.")+ inp.isFlipped = args.get("flipped", False)+ for name, (quantity_input, _) in expected.items():+ setattr(inp, name, quantity_input)+ for key, entity in axes.items():+ setattr(inp, key, entity)+ origin = named(comp.jointOrigins.add(inp), args)+ return lambda fresh: origin_result(design, origin, fresh, args, expected, saved_geometry, saved_axes, value)+++def joint_origin_edit(design, value, args):+ origin = resolve(design, value, args["origin"], ("JointOrigin",))+ require(getattr(origin, "assemblyContext", None) is None, "definition_reference_required", "Edit the native origin definition.")+ require(set(args) & {"name", "geometry", "flipped", "angle", "offsetX", "offsetY", "offsetZ", "xAxisEntity", "zAxisEntity"}, "invalid_input", "Supply an origin change.")+ geometry = selector(design, value, args["geometry"], origin.parentComponent) if "geometry" in args else origin.geometry+ axes = origin_axes(design, value, args, origin.parentComponent)+ saved_geometry = capture_attachment(geometry, value)+ saved_axes = {key: source_reference(entity, value) for key, entity in axes.items()}+ expected = expected_parameters(design, args, ("angle", "offsetX", "offsetY", "offsetZ"))+ with before_timeline(design, origin):+ if "geometry" in args:+ origin.geometry = geometry+ if "flipped" in args:+ origin.isFlipped = args["flipped"]+ for key, entity in axes.items():+ setattr(origin, key, entity)+ for name, (quantity_input, _) in expected.items():+ getattr(origin, name).expression = quantity_input.stringValue+ if "name" in args:+ origin.name = args["name"]+ return lambda fresh: origin_result(design, origin, fresh, args, expected, saved_geometry, saved_axes, value)+++def joint_origin_delete(design, value, args):+ origin = resolve(design, value, args["origin"], ("JointOrigin",))+ require(getattr(origin, "assemblyContext", None) is None, "definition_reference_required", "Delete the native origin definition.")+ name = origin.name+ require(origin.deleteMe(), "delete_failed", "Fusion refused joint-origin deletion.")+ return {"deletedJointOrigin": name}+++OPERATIONS = {name: globals()[name] for name in ("regular_joint_create", "regular_joint_edit", "joint_origin_create", "joint_origin_edit", "joint_origin_delete")}
addin/AdomBridge/commands/mechanical/render_scene.pyadded+345@@ -0,0 +1,345 @@+"""Bounded scene observation and detached camera preparation; never renders.++All native access occurs on the main thread. Missing fields remain explicit;+this is not a complete scene witness or evidence of native queue ownership.+"""+import inspect+import json+import math+import threading++from .core import SESSION_ID, MechanicalError, require, target++MAX_ENVIRONMENTS = 32+MAX_OUTPUT_BYTES = 128 * 1024+++def scene_observation(app, document, design, *, max_environments=16):+ import adsk.core+ import adsk.fusion++ def need(value, message):+ if not value: raise RuntimeError(message)++ need(threading.current_thread() is threading.main_thread(), 'Native render reads require the main thread')+ need(type(max_environments) is int and 0 <= max_environments <= MAX_ENVIRONMENTS, 'Invalid environment probe budget')+ need(document is not None and document.isValid and design is not None and design.isValid, 'Owned native design is invalid')+ need(app.activeDocument == document and design.parentDocument == document, 'Current native document/design identity differs')+ document_count = app.documents.count+ need(type(document_count) is int and 0 <= document_count <= 128, 'Open document inventory exceeds probe budget or has invalid count')+ need(sum(app.documents.item(i) == document for i in range(document_count)) == 1, 'Exact owned document is not uniquely open')+ result = {'schemaVersion':1, 'status':'OBSERVED', 'renderStarted':False, 'settingsWritten':False,+ 'sceneWitnessComplete':False, 'renderQualified':False, 'nativeQueueIdle':'UNVERIFIED', 'exclusiveSettingsOwnership':'UNVERIFIED',+ 'errors':[], 'classes':{}, 'camera':{}, 'rendering':{}, 'scene':{}, 'environments':{},+ 'gaps':['No render execution or RenderFuture instance was created.',+ 'No global renderer queue/coexistence proof.',+ 'Appearance, material, texture, light and occurrence-visibility graphs are outside this bounded probe.',+ 'Environment IDs and names do not prove custom environment content identity.',+ 'No admitted revision-to-pixels or full source snapshot claim.']}++ def text(value):+ need(isinstance(value,str) and len(value) <= 256, 'Native string exceeds probe budget or has wrong type')+ return value++ def number(value):+ need(type(value) in (int,float) and math.isfinite(value), 'Native scalar is not a finite number')+ return value++ def boolean(value):+ need(type(value) is bool, 'Native scalar is not Boolean')+ return value++ def enum(value):+ need(not isinstance(value,(bool,str)), 'Native enum has invalid type')+ result = int(value)+ need(result == value, 'Native enum is not an exact integer')+ return result++ def point(value, scale=10):+ return [number(number(getattr(value,axis)) * scale) for axis in ('x','y','z')]++ def read(path, callback, unit=None, native_unit=None):+ try:+ data = callback()+ row = {'available':True,'value':data}+ if unit is not None: row['unit'] = unit+ if native_unit is not None: row['nativeUnit'] = native_unit+ return row+ except Exception as error:+ reason = str(error)[:512]+ result['errors'].append({'field':path,'error':reason})+ return {'available':False,'error':reason}++ def skipped(reason): return {'available':None,'notRead':reason}++ def object_read(path, callback):+ try:+ value = callback()+ need(value is not None and value.isValid, 'Native object absent or invalid')+ text(value.objectType)+ return value, {'available':True,'objectType':value.objectType}+ except Exception as error:+ reason = str(error)[:512]+ result['errors'].append({'field':path,'error':reason})+ return None, {'available':False,'error':reason}++ def fields(destination, prefix, owner, specs):+ for name, converter, unit, native_unit in specs:+ destination[name] = read(prefix + '.' + name, lambda name=name,converter=converter:converter(getattr(owner,name)), unit, native_unit)++ result['documentBefore'] = {'modified':read('document.modified',lambda:boolean(document.isModified)),+ 'saved':read('document.saved',lambda:boolean(document.isSaved))}+ # Static class inspection does not invoke descriptors, constructors, start,+ # activation, cancel or any native future polling.+ class_members = {'RenderManager':('rendering','sceneSettings','renderEnvironments','lights','isRenderWorkspaceActive'),+ 'Rendering':('startLocalRender','aspectRatio','resolution','resolutionWidth','resolutionHeight','renderQuality','isBackgroundTransparent'),+ 'RenderFuture':('filename','imageWidth','imageHeight','progress','renderState','isValid'),+ 'SceneSettings':('backgroundType','backgroundEnvironment','backgroundSolidColor','isDepthOfFieldEnabled'),+ 'RenderEnvironment':('id','name','isCustomEnvironment')}+ for name,members in class_members.items():+ try:+ cls = getattr(adsk.fusion,name)+ availability = {}+ for member in members:+ try: inspect.getattr_static(cls,member); availability[member] = True+ except AttributeError: availability[member] = False+ result['classes'][name] = {'available':True,'members':availability,'methodInvocation':False}+ except Exception as error:+ result['classes'][name] = {'available':False,'error':str(error)[:512]}++ camera_enums = {name:read('CameraTypes.'+name,lambda name=name:enum(getattr(adsk.core.CameraTypes,name)))+ for name in ('OrthographicCameraType','PerspectiveCameraType','PerspectiveWithOrthoFacesCameraType')}+ background_enums = {name:read('RenderSceneBackgroundTypes.'+name,+ lambda name=name:enum(getattr(adsk.fusion.RenderSceneBackgroundTypes,name)))+ for name in ('SolidColorRenderSceneBackgroundType','EnvironmentRenderSceneBackgroundType')}+ result['enums'] = {'camera':camera_enums,'background':background_enums}++ viewport, result['viewport'] = object_read('activeViewport',lambda:app.activeViewport)+ camera = None+ if viewport is not None:+ camera, result['camera']['object'] = object_read('camera',lambda:viewport.camera)+ if camera is not None:+ fields(result['camera'],'camera',camera,[('cameraType',enum,'native CameraTypes enum',None),+ ('eye',point,'mm','cm'),('target',point,'mm','cm'),('upVector',lambda p:point(p,1),'unitless',None),+ ('isFitView',boolean,None,None),('isSmoothTransition',boolean,None,None)])+ result['camera']['space'] = 'world/root'+ kind = result['camera']['cameraType'].get('value')+ if kind is not None and kind == camera_enums['OrthographicCameraType'].get('value'):+ def extents():+ okay,width,height = camera.getExtents()+ need(okay is True, 'Orthographic camera extent read refused')+ return [number(number(width)*10),number(number(height)*10)]+ result['camera']['extents'] = read('camera.getExtents',extents,'mm','cm')+ result['camera']['perspectiveAngle'] = skipped('Orthographic camera')+ elif kind is not None and kind in (camera_enums['PerspectiveCameraType'].get('value'),camera_enums['PerspectiveWithOrthoFacesCameraType'].get('value')):+ result['camera']['perspectiveAngle'] = read('camera.perspectiveAngle',lambda:number(math.degrees(number(camera.perspectiveAngle))), 'deg', 'rad')+ result['camera']['extents'] = skipped('Perspective camera')+ else:+ result['errors'].append({'field':'camera.cameraType','error':'Unknown/unreadable camera mode; conditional lens getters not read'})+ result['camera']['extents'] = result['camera']['perspectiveAngle'] = skipped('Unknown camera mode')++ manager, result['renderManager'] = object_read('design.renderManager',lambda:design.renderManager)+ if manager is not None:+ matched = read('renderManager.parentDesign',lambda:boolean(manager.parentDesign == design))+ result['renderManager']['ownerMatches'] = matched+ if matched.get('value') is not True:+ result['errors'].append({'field':'renderManager.parentDesign','error':'Foreign/unreadable renderer owner; remaining renderer objects not read'})+ manager = None+ if manager is not None:+ result['renderManager']['workspaceActive'] = read('renderManager.isRenderWorkspaceActive',lambda:boolean(manager.isRenderWorkspaceActive))+ rendering, result['rendering']['object'] = object_read('renderManager.rendering',lambda:manager.rendering)+ if rendering is not None:+ fields(result['rendering'],'rendering',rendering,[('aspectRatio',enum,'native RenderAspectRatios enum',None),+ ('resolution',enum,'native RenderResolutions enum',None),('resolutionWidth',number,'px',None),+ ('resolutionHeight',number,'px',None),('renderQuality',number,'native quality (25..100)',None),+ ('isBackgroundTransparent',boolean,None,None)])+ scene, result['scene']['object'] = object_read('renderManager.sceneSettings',lambda:manager.sceneSettings)+ if scene is not None:+ specs = [('aspectRatio',enum,'native RenderAspectRatios enum',None),('aspectRatioWidth',number,'ratio',None),+ ('aspectRatioHeight',number,'ratio',None),('backgroundType',enum,'native RenderSceneBackgroundTypes enum',None),+ ('brightness',number,'lux',None),('cameraExposure',number,'EV',None),('cameraFocalLength',number,'mm','mm'),+ ('cameraType',enum,'native CameraTypes enum',None),('groundOffset',lambda n:number(number(n)*10),'mm','cm'),+ ('lightAngle',lambda n:number(math.degrees(number(n))),'deg','rad')]+ specs += [(name,boolean,None,None) for name in ('isDepthOfFieldEnabled','isGroundDisplayed','isGroundFlattened','isGroundReflections')]+ fields(result['scene'],'scene',scene,specs)+ for condition,names in (('isDepthOfFieldEnabled',('centerOfFocus','depthOfFieldBlur')),+ ('isGroundFlattened',('groundPosition',)),('isGroundReflections',('groundRoughness',))):+ enabled = result['scene'][condition].get('value')+ for name in names:+ if enabled is True:+ vector = name in ('centerOfFocus','groundPosition')+ fields(result['scene'],'scene',scene,[(name,point if vector else number,'mm' if vector else 'unitless','cm' if vector else None)])+ else: result['scene'][name] = skipped(condition + ' is false or unreadable')+ background = result['scene']['backgroundType'].get('value')+ if background is not None and background == background_enums['SolidColorRenderSceneBackgroundType'].get('value'):+ def color():+ value = scene.backgroundSolidColor+ return {name:number(getattr(value,name)) for name in ('red','green','blue','opacity')}+ result['scene']['backgroundSolidColor'] = read('scene.backgroundSolidColor',color,'RGBA 0..255; opacity ignored by scene')+ result['scene']['backgroundEnvironment'] = skipped('Solid background')+ elif background is not None and background == background_enums['EnvironmentRenderSceneBackgroundType'].get('value'):+ env, descriptor = object_read('scene.backgroundEnvironment',lambda:scene.backgroundEnvironment)+ if env is not None:+ fields(descriptor,'scene.backgroundEnvironment',env,[('id',text,None,None),('name',text,None,None),('isCustomEnvironment',boolean,None,None)])+ result['scene']['backgroundEnvironment'] = descriptor+ result['scene']['backgroundSolidColor'] = skipped('Environment background')+ else:+ result['errors'].append({'field':'scene.backgroundType','error':'Unknown/unreadable background mode; conditional getters not read'})+ result['scene']['backgroundEnvironment'] = result['scene']['backgroundSolidColor'] = skipped('Unknown background mode')+ environments, result['environments']['object'] = object_read('renderManager.renderEnvironments',lambda:manager.renderEnvironments)+ if environments is not None:+ count = read('environments.count',lambda:number(environments.count))+ result['environments']['count'] = count+ n = count.get('value');result['environments']['items'] = []+ if type(n) is int and n >= 0:+ result['environments']['truncated'] = n > max_environments+ for index in range(min(n,max_environments)):+ env, descriptor = object_read('environment.' + str(index),lambda index=index:environments.item(index))+ if env is not None:+ fields(descriptor,'environment.'+str(index),env,[('id',text,None,None),('name',text,None,None),('isCustomEnvironment',boolean,None,None)])+ result['environments']['items'].append(descriptor)+ else: result['errors'].append({'field':'environments.count','error':'Environment count is not a nonnegative integer'})+ result['documentAfter'] = {'modified':read('document.modified.after',lambda:boolean(document.isModified)),+ 'saved':read('document.saved.after',lambda:boolean(document.isSaved)),+ 'stillActive':read('document.active.after',lambda:boolean(app.activeDocument == document))}+ result['documentFlagsUnchanged'] = (result['documentBefore']['modified'].get('available') is True+ and result['documentBefore']['saved'].get('available') is True+ and result['documentBefore']['modified'] == result['documentAfter']['modified']+ and result['documentBefore']['saved'] == result['documentAfter']['saved']+ and result['documentAfter']['stillActive'].get('value') is True)+ classes_complete = all(row.get('available') is True and all(row.get('members',{}).values()) for row in result['classes'].values())+ result['observationComplete'] = not result['errors'] and classes_complete and not result['environments'].get('truncated',False)+ result['status'] = 'OBSERVED' if result['observationComplete'] else 'PARTIAL'+ need(len(json.dumps(result,allow_nan=False).encode()) <= MAX_OUTPUT_BYTES,'Render probe output exceeds byte budget')+ return result+++def camera_arguments(value):+ """Validate plain input before constructing any native temporary object."""+ require(isinstance(value, dict), 'invalid_input', 'camera must be an object.')+ common = {'type', 'space', 'unit', 'eye', 'target', 'upVector'}+ kind = value.get('type')+ lens = {'extents'} if kind == 'orthographic' else {'perspectiveAngle', 'angleUnit'}+ require(kind in ('orthographic', 'perspective') and set(value) == common | lens,+ 'invalid_input', 'Supply one supported camera type with its exact geometry and lens fields.')+ require(value['space'] == 'world' and value['unit'] == 'mm', 'invalid_input', 'Camera positions use world-space mm.')++ def scalar(x):+ require(type(x) in (int, float) and math.isfinite(x) and abs(x) <= 1e9,+ 'invalid_input', 'Camera geometry must be finite and bounded to 1e9 mm.')+ return x++ def vector(name):+ row = value[name]+ require(isinstance(row, list) and len(row) == 3, 'invalid_input', name + ' requires three numbers.')+ return [scalar(x) for x in row]++ eye, look, up = (vector(name) for name in ('eye', 'target', 'upVector'))+ direction = [b-a for a,b in zip(eye,look)]+ distance, up_length = math.hypot(*direction), math.hypot(*up)+ require(distance > 1e-9, 'invalid_input', 'Camera eye and target must differ.')+ require(abs(up_length-1) <= 1e-6 and abs(sum(a*b for a,b in zip(direction,up))/distance) <= 1e-6,+ 'invalid_input', 'upVector must be a unit vector perpendicular to the view direction.')+ if kind == 'orthographic':+ extents = value['extents']+ require(isinstance(extents,list) and len(extents)==2, 'invalid_input', 'Orthographic extents require width and height in mm.')+ require(all(scalar(x) >= 1e-6 for x in extents), 'invalid_input', 'Orthographic extents must be positive.')+ else:+ require(value['angleUnit']=='deg' and 0 < scalar(value['perspectiveAngle']) < 180,+ 'invalid_input', 'Perspective angle must be strictly between 0 and 180 degrees.')+ return value+++def detached_camera(value):+ import adsk.core+ value = camera_arguments(value)+ try:+ camera = adsk.core.Camera.create()+ require(camera is not None, 'camera_unavailable', 'Camera.create returned no detached camera.')+ # Autodesk FUS-263280: transient Camera.isValid can be false even when+ # the exact Camera wrapper retains its geometry. Keep that flag as+ # evidence; validate type and every requested value independently.+ validity_before = camera.isValid+ require(type(validity_before) is bool and isinstance(camera, adsk.core.Camera)+ and camera.objectType == adsk.core.Camera.classType(),+ 'camera_unavailable', 'Camera.create returned an unexpected wrapper/type or validity field.')+ camera.cameraType = getattr(adsk.core.CameraTypes, 'OrthographicCameraType' if value['type']=='orthographic' else 'PerspectiveCameraType')+ camera.eye = adsk.core.Point3D.create(*(x/10 for x in value['eye']))+ camera.target = adsk.core.Point3D.create(*(x/10 for x in value['target']))+ camera.upVector = adsk.core.Vector3D.create(*value['upVector'])+ camera.isFitView = False+ camera.isSmoothTransition = False+ if value['type'] == 'orthographic':+ require(camera.setExtents(*(x/10 for x in value['extents'])) is True,+ 'camera_readback_mismatch', 'Detached camera extent setter refused.')+ else:+ camera.perspectiveAngle = math.radians(value['perspectiveAngle'])+ def finite_native(number):+ require(type(number) in (int,float) and math.isfinite(number),+ 'camera_readback_mismatch', 'Detached camera returned a nonnumeric or nonfinite value.')+ return number+ result = {'type':value['type'], 'space':'world', 'unit':'mm',+ 'eye':[finite_native(getattr(camera.eye,k))*10 for k in ('x','y','z')],+ 'target':[finite_native(getattr(camera.target,k))*10 for k in ('x','y','z')],+ 'upVector':[finite_native(getattr(camera.upVector,k)) for k in ('x','y','z')]}+ expected_kind = getattr(adsk.core.CameraTypes, 'OrthographicCameraType' if value['type']=='orthographic' else 'PerspectiveCameraType')+ require(not isinstance(camera.cameraType,bool) and camera.cameraType == expected_kind and camera.isFitView is False and camera.isSmoothTransition is False,+ 'camera_readback_mismatch', 'Detached camera mode or behavior differs.')+ if value['type'] == 'orthographic':+ okay,width,height = camera.getExtents()+ require(okay is True, 'camera_readback_mismatch', 'Detached camera extent read refused.')+ result['extents'] = [finite_native(width)*10,finite_native(height)*10]+ else:+ result.update(perspectiveAngle=math.degrees(finite_native(camera.perspectiveAngle)), angleUnit='deg')+ # Tolerance must never turn invalid native geometry into a valid camera.+ try:+ camera_arguments(result)+ except MechanicalError as error:+ raise MechanicalError('camera_readback_mismatch', 'Detached camera returned invalid geometry or lens: ' + str(error))+ def equal(left,right):+ if isinstance(right,list):+ return isinstance(left,list) and len(left)==len(right) and all(equal(a,b) for a,b in zip(left,right))+ if type(right) in (int,float):+ return type(left) in (int,float) and math.isfinite(left) and math.isclose(left,right,rel_tol=1e-10,abs_tol=1e-8)+ return type(left) is type(right) and left==right+ require(all(equal(result.get(k),v) for k,v in value.items()), 'camera_readback_mismatch', 'Detached camera did not retain the requested geometry or lens.')+ validity_after = camera.isValid+ require(type(validity_after) is bool, 'camera_readback_mismatch', 'Camera validity readback is not Boolean.')+ return {'available':True, 'readbackVerified':True, 'assignedToViewport':False,+ 'nativeValidity':{'before':validity_before, 'after':validity_after,+ 'knownIssueReference':'FUS-263280', 'renderEligibility':'NOT_ESTABLISHED'},+ 'scope':'independent temporary camera; current scene is unchanged', 'descriptor':result}+ except MechanicalError:+ raise+ except Exception as error:+ raise MechanicalError('camera_unavailable', 'Detached camera construction/readback failed: ' + str(error)[:512])+++def render_scene_inspect(app, args):+ # This guard must precede target() and every native getter, including ownership.+ require(threading.current_thread() is threading.main_thread(), 'wrong_thread', 'Native render reads require the main thread.')+ require(args.get('sessionId') == SESSION_ID, 'session_mismatch', 'Scene inspection requires the current add-in session.')+ wanted = args.get('target')+ require(isinstance(wanted,dict) and isinstance(wanted.get('documentId'),str) and isinstance(wanted.get('revision'),str),+ 'invalid_input', 'Scene inspection requires exact target documentId and revision.')+ budget = args.get('maxEnvironments',16)+ require(type(budget) is int and 0 <= budget <= MAX_ENVIRONMENTS, 'invalid_input', 'maxEnvironments must be an integer from 0 to 32.')+ if 'camera' in args: camera_arguments(args['camera'])+ document, design, value = target(app,args)+ first = scene_observation(app,document,design,max_environments=budget)+ prepared = detached_camera(args['camera']) if 'camera' in args else {'available':None, 'notRequested':True}+ second = scene_observation(app,document,design,max_environments=budget)+ _, _, current = target(app,args)+ require(value == current and first == second, 'scene_changed_during_inspection',+ 'The model target or bounded scene observations changed during inspection; no settings were restored.')+ require(first['documentFlagsUnchanged'], 'scene_changed_during_inspection',+ 'Unchanged saved/modified document flags could not be verified.')+ first.update(target=value, detachedCamera=prepared, observedStateUnchanged=True,+ unchangedScope='available fields only; unreadable fields and omitted scene graphs remain unverified',+ observationPasses=2, maxEnvironments=budget)+ require(len(json.dumps(first,allow_nan=False).encode('utf-8')) <= MAX_OUTPUT_BYTES,+ 'output_too_large', 'Scene inspection exceeds its 128 KiB result budget.')+ return first+++READS = {'render_scene_inspect': render_scene_inspect}
addin/AdomBridge/commands/mechanical/schema.jsonadded+6993@@ -0,0 +1,6993 @@+{+ "inspect": {+ "type": "object",+ "required": [+ "op"+ ],+ "properties": {},+ "description": "Inspect the active Design without mutation. Returns target identity/revision, native component-space bodies and occurrence transforms, parameters and feature health."+ },+ "inspect_entity": {+ "type": "object",+ "required": [+ "op",+ "target",+ "entity"+ ],+ "properties": {+ "entity": {+ "type": "object",+ "description": "Exact reference returned by inspect; resolves uniquely with document/revision/type/occurrence guards."+ }+ }+ },+ "new_design": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "operationId"+ ],+ "properties": {+ "name": {+ "type": "string"+ },+ "box": {+ "type": "object",+ "required": [+ "width",+ "height",+ "depth"+ ],+ "additionalProperties": false,+ "properties": {+ "width": {+ "type": "number",+ "exclusiveMinimum": 0+ },+ "height": {+ "type": "number",+ "exclusiveMinimum": 0+ },+ "depth": {+ "type": "number",+ "exclusiveMinimum": 0+ }+ }+ }+ },+ "description": "Create an unnamed unsaved parametric hybrid Design; name is retained as requestedName metadata to keep failure recovery free of save dialogs. Optional box dimensions in mm create a driving-parameter fixture. Never edits an existing document."+ },+ "parameter_set": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name",+ "expression"+ ],+ "properties": {+ "name": {+ "type": "string"+ },+ "expression": {+ "type": "string"+ }+ },+ "description": "Set an expression, recompute and inspect health. Failure restores old expression and reports a semantic recovery witness; not an atomic transaction."+ },+ "parameter_create": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name",+ "expression",+ "unit"+ ],+ "properties": {+ "name": {+ "type": "string"+ },+ "expression": {+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm",+ "deg",+ "kg",+ ""+ ]+ },+ "comment": {+ "type": "string"+ }+ }+ },+ "health": {+ "type": "object",+ "required": [+ "op",+ "target"+ ],+ "properties": {+ "recompute": {+ "type": "boolean",+ "default": false+ }+ },+ "description": "Read timeline warnings/errors. recompute:true also requires current target revision, sessionId and operationId."+ },+ "export": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "outputPath"+ ],+ "properties": {+ "format": {+ "enum": [+ "step",+ "stl",+ "3mf",+ "f3d",+ "iges",+ "sat",+ "smt",+ "usdz",+ "fbx",+ "skp"+ ],+ "default": "step"+ },+ "outputPath": {+ "type": "string"+ },+ "overwrite": {+ "type": "boolean",+ "default": false+ },+ "refinement": {+ "enum": [+ "low",+ "medium",+ "high"+ ],+ "default": "medium"+ }+ },+ "description": "Export entire design using runtime-checked API. Preflight and unique same-filesystem staging preserve the destination on failure; publish only nonempty verified output. Availability differs by Fusion build. STL numeric coordinates use mm."+ },+ "transaction_probe": {+ "type": "object",+ "properties": {},+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target"+ ],+ "description": "Verify runtime PTransaction abort+commit using temporary user parameters, remove probe data and compare semantic restoration. Required once per running add-in session before feature/sketch/atomic-batch writes. Returns fresh target; inspect again for current references."+ },+ "sketch_create": {+ "type": "object",+ "properties": {+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target"+ ],+ "description": "Create a native component-definition sketch on an origin/construction plane or planar face. Coordinates in subsequent geometry are sketch-space mm."+ },+ "sketch_geometry": {+ "type": "object",+ "properties": {+ "sketch": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "items": {+ "type": "array",+ "items": {+ "oneOf": [+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "point"+ ]+ },+ "position": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ }+ },+ "required": [+ "type",+ "position"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "line"+ ]+ },+ "start": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "end": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "construction": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "start",+ "end"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "rectangle"+ ]+ },+ "start": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "end": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "construction": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "start",+ "end"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "circle"+ ]+ },+ "center": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "radius": {+ "type": "number",+ "exclusiveMinimum": 0+ },+ "construction": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "center",+ "radius"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "arc"+ ]+ },+ "center": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "start": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "sweep": {+ "type": "number"+ },+ "construction": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "center",+ "start",+ "sweep"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "polygon"+ ]+ },+ "center": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "radius": {+ "type": "number",+ "exclusiveMinimum": 0+ },+ "sides": {+ "type": "integer",+ "minimum": 3,+ "maximum": 128+ },+ "angle": {+ "type": "number"+ },+ "construction": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "center",+ "radius",+ "sides"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "slot"+ ]+ },+ "start": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "end": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "width": {+ "type": "number",+ "exclusiveMinimum": 0+ },+ "construction": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "start",+ "end",+ "width"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "spline"+ ]+ },+ "points": {+ "type": "array",+ "items": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "minItems": 2,+ "maxItems": 128+ },+ "closed": {+ "type": "boolean"+ },+ "construction": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "points"+ ],+ "additionalProperties": false+ }+ ]+ },+ "minItems": 1,+ "maxItems": 128+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "items"+ ],+ "description": "Create a transactionally grouped set of sketch primitives. Numeric geometry uses mm and arc sweeps/polygon angles use deg."+ },+ "sketch_constraint": {+ "type": "object",+ "properties": {+ "sketch": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "type": {+ "enum": [+ "horizontal",+ "vertical",+ "coincident",+ "parallel",+ "perpendicular",+ "tangent",+ "equal",+ "concentric",+ "collinear",+ "midpoint",+ "symmetry",+ "fix"+ ]+ },+ "entities": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "type",+ "entities"+ ],+ "description": "Constrain entities in one sketch. midpoint order is point,line; symmetry order entity1,entity2,symmetryLine. fix sets one entity fixed."+ },+ "sketch_dimension": {+ "type": "object",+ "properties": {+ "sketch": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "type": {+ "enum": [+ "distance",+ "horizontal",+ "vertical",+ "radius",+ "diameter",+ "angle"+ ]+ },+ "entities": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "expression": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "position": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "parameterName": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "type",+ "entities",+ "expression",+ "position"+ ],+ "description": "Add driving dimensions. Distance uses two point references; radius/diameter one curve; angle two lines. Numeric expressions use mm or deg."+ },+ "sketch_project": {+ "type": "object",+ "properties": {+ "sketch": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "entities": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "entities"+ ],+ "description": "Project selected topology into a sketch with Fusion associative projection."+ },+ "sketch_edit": {+ "type": "object",+ "properties": {+ "sketch": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "changes": {+ "type": "object",+ "properties": {+ "position": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "start": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "end": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "center": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "radius": {+ "type": "number"+ },+ "construction": {+ "type": "boolean"+ },+ "fixed": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "entity",+ "changes"+ ],+ "description": "Move sketch points/endpoints/centers in sketch-space mm, change radius or construction/fixed flags; solver failure aborts the operation."+ },+ "plane_create": {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "offset",+ "angle"+ ]+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "offset": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type"+ ],+ "description": "Create offset plane (offset in mm) or angled plane around axis (angle in deg).",+ "oneOf": [+ {+ "type": "object",+ "required": [+ "type",+ "offset"+ ],+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ]+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "type": {+ "enum": [+ "offset"+ ]+ },+ "offset": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ }+ },+ "additionalProperties": false+ },+ {+ "type": "object",+ "required": [+ "type",+ "axis",+ "angle"+ ],+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ]+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "type": {+ "enum": [+ "angle"+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ }+ },+ "additionalProperties": false+ }+ ]+ },+ "feature_create": {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "extrude",+ "revolve",+ "fillet",+ "chamfer",+ "shell",+ "hole",+ "loft",+ "sweep",+ "combine",+ "split",+ "move",+ "mirror",+ "rectangular_pattern",+ "circular_pattern",+ "draft",+ "thread"+ ]+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "profiles": {},+ "distance": {},+ "direction": {},+ "taper": {},+ "bodies": {},+ "operation": {},+ "axis": {},+ "angle": {},+ "edges": {},+ "radius": {},+ "tangentChain": {},+ "faces": {},+ "thickness": {},+ "points": {},+ "diameter": {},+ "depth": {},+ "path": {},+ "body": {},+ "tools": {},+ "keepTools": {},+ "plane": {},+ "extend": {},+ "translation": {},+ "origin": {},+ "count": {},+ "spacing": {},+ "axis2": {},+ "count2": {},+ "spacing2": {},+ "internal": {},+ "threadType": {},+ "designation": {},+ "threadClass": {},+ "modeled": {}+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type"+ ],+ "description": "Create a typed native solid feature. All length quantities are mm (or expressions), angles deg. Cut/intersect require explicit participant bodies. Join needs a single existing component body; combine selects multiple bodies explicitly. Feature availability is runtime-checked; missing APIs fail before commit.",+ "oneOf": [+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "extrude"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "profiles": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "distance": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "direction": {+ "enum": [+ "positive",+ "negative",+ "symmetric"+ ]+ },+ "taper": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "distance"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "revolve"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "profiles": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "axis",+ "angle"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "fillet"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "edges": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "radius": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "tangentChain": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "edges",+ "radius"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "chamfer"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "edges": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "distance": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "tangentChain": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "edges",+ "distance"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "shell"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "faces": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "thickness": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "tangentChain": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "faces",+ "thickness"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "hole"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "points": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "diameter": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "depth": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ }+ },+ "required": [+ "type",+ "points",+ "diameter",+ "depth",+ "bodies"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "loft"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "profiles": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ }+ },+ "required": [+ "type",+ "profiles"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "sweep"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "profiles": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "path": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "path"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "combine"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "body": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "tools": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "operation": {+ "enum": [+ "join",+ "cut",+ "intersect"+ ]+ },+ "keepTools": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "body",+ "tools",+ "operation"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "split"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "extend": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "bodies",+ "plane"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "move"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "translation": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "axis": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "angle": {+ "type": "number"+ },+ "origin": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ }+ },+ "required": [+ "type",+ "bodies"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "mirror"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "plane"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "rectangular_pattern"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "count": {+ "type": "integer",+ "minimum": 2,+ "maximum": 1000+ },+ "spacing": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis2": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "count2": {+ "type": "integer",+ "minimum": 1,+ "maximum": 1000+ },+ "spacing2": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "axis",+ "count",+ "spacing"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "circular_pattern"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "bodies": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "count": {+ "type": "integer",+ "minimum": 2,+ "maximum": 1000+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "axis",+ "count"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "draft"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "faces": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ ]+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "tangentChain": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "faces",+ "plane",+ "angle"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "thread"+ ]+ },+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "additionalProperties": false+ },+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "faces": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "internal": {+ "type": "boolean"+ },+ "threadType": {+ "type": "string"+ },+ "designation": {+ "type": "string"+ },+ "threadClass": {+ "type": "string"+ },+ "modeled": {+ "type": "boolean"+ }+ },+ "required": [+ "type",+ "faces",+ "internal",+ "threadType",+ "designation",+ "threadClass"+ ],+ "additionalProperties": false+ }+ ]+ },+ "feature_edit": {+ "type": "object",+ "properties": {+ "feature": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "parameters": {+ "type": "object",+ "additionalProperties": {+ "type": "string"+ }+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "feature"+ ],+ "description": "Rename/suppress a feature or change expressions of model parameters whose createdBy is that exact feature; read health and abort on downstream errors."+ },+ "entity_delete": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring."+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "entity"+ ],+ "description": "Delete sketch geometry or independently deletable timeline features. Model transaction and semantic recovery cover downstream failures; document and external file deletion are excluded."+ },+ "parameter_batch": {+ "type": "object",+ "properties": {+ "changes": {+ "type": "array",+ "items": {+ "oneOf": [+ {+ "type": "object",+ "properties": {+ "action": {+ "enum": [+ "create"+ ]+ },+ "name": {+ "type": "string"+ },+ "expression": {+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm",+ "deg",+ "kg",+ ""+ ]+ },+ "comment": {+ "type": "string"+ }+ },+ "required": [+ "action",+ "name",+ "expression",+ "unit"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "action": {+ "enum": [+ "set"+ ]+ },+ "name": {+ "type": "string"+ },+ "expression": {+ "type": "string"+ }+ },+ "required": [+ "action",+ "name",+ "expression"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "action": {+ "enum": [+ "delete"+ ]+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "action",+ "name"+ ],+ "additionalProperties": false+ }+ ]+ },+ "minItems": 1,+ "maxItems": 128+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "changes"+ ],+ "description": "Atomic sequential create/set/delete parameter edits with one recompute+health gate; delete supports user parameters only. Any failure aborts all edits and reports semantic recovery."+ },+ "component_create": {+ "type": "object",+ "properties": {+ "parent": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "transform": {+ "type": "object",+ "properties": {+ "position": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "axis": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "angle": {+ "type": "number"+ }+ },+ "required": [],+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context."+ },+ "metadata": {+ "type": "object",+ "properties": {+ "name": {+ "type": "string"+ },+ "partNumber": {+ "type": "string"+ },+ "description": {+ "type": "string"+ },+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "asPurchased": {+ "type": "boolean"+ },+ "excludeFromBom": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name"+ ],+ "description": "Create a native internal component and its first occurrence in a Hybrid design. parent is a component definition; edits to it affect every instance. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused."+ },+ "occurrence_insert": {+ "type": "object",+ "properties": {+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "parent": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "transform": {+ "type": "object",+ "properties": {+ "position": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "axis": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "angle": {+ "type": "number"+ }+ },+ "required": [],+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context."+ },+ "independentCopy": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "component"+ ],+ "description": "Insert another occurrence of an existing definition; independentCopy:true uses Paste New to create a separate definition. Nested placement is in parent component coordinates. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused."+ },+ "occurrence_update": {+ "type": "object",+ "properties": {+ "occurrence": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "transform": {+ "type": "object",+ "properties": {+ "position": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "axis": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "angle": {+ "type": "number"+ }+ },+ "required": [],+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context."+ },+ "grounded": {+ "type": "boolean"+ },+ "groundedToParent": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "occurrence"+ ],+ "description": "Set occurrence pose in its assembly context and capture position; explicit grounding/ungrounding. Grounded or constrained transforms fail with recovery."+ },+ "occurrence_delete": {+ "type": "object",+ "properties": {+ "occurrence": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "occurrence"+ ],+ "description": "Delete one selected occurrence through the shared transaction executor."+ },+ "component_metadata": {+ "type": "object",+ "properties": {+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "changes": {+ "type": "object",+ "properties": {+ "name": {+ "type": "string"+ },+ "partNumber": {+ "type": "string"+ },+ "description": {+ "type": "string"+ },+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "asPurchased": {+ "type": "boolean"+ },+ "excludeFromBom": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "changes"+ ],+ "description": "Update component-definition name/part number/description and persistent supplier or BOM attributes. Shared occurrences all see this metadata."+ },+ "joint_create": {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ },+ "occurrenceOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "occurrenceTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "rotationLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "slideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rotationValue": {+ "type": "number"+ },+ "slideValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "primarySlideValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "pitchLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "pitchValue": {+ "type": "number"+ },+ "rollLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rollValue": {+ "type": "number"+ },+ "yawLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "yawValue": {+ "type": "number"+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type",+ "occurrenceOne",+ "occurrenceTwo"+ ],+ "description": "Create an as-built joint without moving occurrences. Non-rigid motion requires explicit joint geometry; x/y/z are joint-coordinate directions. Limits and drive values use mm/deg. Seven native motion types; ball uses joint Z pitch/X yaw; planar uses native primary slide direction reported on readback. Use regular_joint_create for geometry-solving placement.",+ "oneOf": [+ {+ "type": "object",+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "rigid"+ ]+ },+ "occurrenceOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "occurrenceTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ }+ },+ "required": [+ "type"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "revolute"+ ]+ },+ "occurrenceOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "occurrenceTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "rotationLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rotationValue": {+ "type": "number"+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "slider"+ ]+ },+ "occurrenceOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "occurrenceTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "slideValue": {+ "type": "number"+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "cylindrical"+ ]+ },+ "occurrenceOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "occurrenceTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "rotationLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rotationValue": {+ "type": "number"+ },+ "slideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "slideValue": {+ "type": "number"+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "pin_slot"+ ]+ },+ "occurrenceOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "occurrenceTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "rotationLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rotationValue": {+ "type": "number"+ },+ "slideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "slideValue": {+ "type": "number"+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "planar"+ ]+ },+ "occurrenceOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "occurrenceTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "rotationLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rotationValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "primarySlideValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "secondarySlideValue": {+ "type": "number"+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "op": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "ball"+ ]+ },+ "occurrenceOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "occurrenceTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "pitchLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "pitchValue": {+ "type": "number"+ },+ "rollLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rollValue": {+ "type": "number"+ },+ "yawLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "additionalProperties": false+ }+ ]+ },+ "joint_edit": {+ "type": "object",+ "properties": {+ "joint": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "rotationLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "slideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rotationValue": {+ "type": "number"+ },+ "slideValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "primarySlideValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "pitchLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "pitchValue": {+ "type": "number"+ },+ "rollLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rollValue": {+ "type": "number"+ },+ "yawLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "description": "Edit joint name/suppression, enabled limits, rest and drive values; wrong motion type or out-of-limit values fail with recovery. For driven root joints, active motion links must preserve their settings and their partner motion must follow the native ratio; a coupling mismatch aborts."+ },+ "joint_delete": {+ "type": "object",+ "properties": {+ "joint": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "description": "Delete one selected Joint or AsBuiltJoint with recovery verification."+ },+ "interference": {+ "type": "object",+ "properties": {+ "entities": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "minItems": 2,+ "maxItems": 256+ },+ "includeTouching": {+ "type": "boolean"+ },+ "volumeTolerance": {+ "type": "number",+ "minimum": 0+ }+ },+ "required": [+ "op",+ "target",+ "entities"+ ],+ "description": "Analyze root-context occurrences/body proxies; returns overlap volume mm3 or touching. Does not create interference bodies."+ },+ "bom": {+ "type": "object",+ "properties": {+ "explodePurchased": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "description": "Structured component-definition and instance BOM; quantities by actual identity, root bodies plus nested children; purchased metadata collapses subtrees unless exploded. Physical mass includes all unsuppressed bodies exactly once per instance. Includes exact current active configuration provenance; incomplete configuration scope refuses. Visibility does not remove quantity."+ },+ "bom_export": {+ "type": "object",+ "properties": {+ "filename": {+ "type": "string"+ },+ "overwrite": {+ "type": "boolean"+ },+ "explodePurchased": {+ "type": "boolean"+ },+ "mode": {+ "enum": [+ "flat",+ "structured"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "filename"+ ],+ "description": "Export flat grouped purchasing rows or structured parent/path/depth instance rows and supplier provenance to CSV with exclusive/atomic publication. This external effect is separate from model transactions. Both CSV modes include document/revision and current active configuration table/row provenance."+ },+ "cad_import": {+ "type": "object",+ "properties": {+ "filename": {+ "type": "string"+ },+ "sha256": {+ "type": "string"+ },+ "name": {+ "type": "string"+ },+ "parent": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "transform": {+ "type": "object",+ "properties": {+ "position": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "axis": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "angle": {+ "type": "number"+ }+ },+ "required": [],+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context."+ },+ "provenance": {+ "type": "object",+ "properties": {+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "description": {+ "type": "string"+ }+ },+ "required": [+ "supplier",+ "supplierPartNumber",+ "sourceUrl"+ ],+ "additionalProperties": false+ },+ "asPurchased": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "filename",+ "provenance"+ ],+ "description": "Import a pre-downloaded local STEP/SAT into a new wrapper component in Hybrid design. Validate optional expected SHA-256, record actual source/hash/supplier metadata, preserve camera, measure model recovery. Does not fetch URLs or create cloud documents. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused."+ },+ "mcmaster_discover": {+ "type": "object",+ "properties": {},+ "required": [+ "op"+ ],+ "description": "Discover Fusion native Insert McMaster-Carr command and requirements in the active design."+ },+ "mcmaster_open": {+ "type": "object",+ "properties": {},+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target"+ ],+ "description": "Invoke Fusion native McMaster UI in the targeted Hybrid design. Interactive external operation: completion/insert/cancel stays with Fusion; inspect afterward. Idempotency records launch only."+ },+ "material_list": {+ "type": "object",+ "properties": {+ "libraryId": {+ "type": "string"+ },+ "query": {+ "type": "string"+ },+ "limit": {+ "type": "integer",+ "minimum": 1,+ "maximum": 1000+ }+ },+ "required": [+ "op",+ "target"+ ],+ "description": "List loaded material libraries and matching materials by stable library/material IDs. Omit libraryId to search the document's materials and then every loaded library; each row names the libraryId material_assign needs (null for the document's own)."+ },+ "material_assign": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "libraryId": {+ "type": "string"+ },+ "materialId": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "entity",+ "materialId"+ ],+ "description": "Assign a loaded library or document material to an exact native Component/BRepBody; verify material ID readback with model transaction recovery. Query assembly_properties for resulting mass."+ },+ "rigid_group_create": {+ "type": "object",+ "properties": {+ "occurrences": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "minItems": 2,+ "maxItems": 256+ },+ "includeChildren": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "occurrences"+ ],+ "description": "Create a native rigid group from exact occurrence instances; includeChildren defaults true."+ },+ "rigid_group_edit": {+ "type": "object",+ "properties": {+ "group": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "group"+ ],+ "description": "Rename or suppress an exact rigid group with transaction recovery."+ },+ "rigid_group_delete": {+ "type": "object",+ "properties": {+ "group": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "group"+ ],+ "description": "Delete an exact rigid group with transaction recovery."+ },+ "assembly_properties": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "op",+ "target"+ ],+ "description": "High-accuracy physical properties of whole root assembly (default) or an exact Component/Occurrence/BRepBody. Returns mass kg, volume mm3, area mm2, center of mass mm, mean density kg/mm3 and definition/instance coordinate scope."+ },+ "geometry_query": {+ "type": "object",+ "properties": {+ "body": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "kind": {+ "enum": [+ "faces",+ "edges",+ "vertices"+ ]+ },+ "space": {+ "enum": [+ "root",+ "component"+ ]+ },+ "selection": {+ "enum": [+ "one",+ "many"+ ]+ },+ "filters": {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "Plane",+ "Cylinder",+ "Cone",+ "Sphere",+ "Torus",+ "NurbsSurface",+ "Line3D",+ "Arc3D",+ "Circle3D",+ "Ellipse3D",+ "EllipticalArc3D",+ "NurbsCurve3D",+ "Point3D",+ "degenerate"+ ]+ },+ "area": {+ "type": "object",+ "properties": {+ "min": {+ "type": "number",+ "minimum": 0+ },+ "max": {+ "type": "number",+ "minimum": 0+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "length": {+ "type": "object",+ "properties": {+ "min": {+ "type": "number",+ "minimum": 0+ },+ "max": {+ "type": "number",+ "minimum": 0+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "radius": {+ "type": "object",+ "properties": {+ "min": {+ "type": "number",+ "minimum": 0+ },+ "max": {+ "type": "number",+ "minimum": 0+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "near": {+ "type": "object",+ "properties": {+ "point": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "tolerance": {+ "type": "number",+ "minimum": 0+ }+ },+ "required": [+ "point",+ "tolerance"+ ],+ "additionalProperties": false+ },+ "direction": {+ "type": "object",+ "properties": {+ "vector": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 3,+ "maxItems": 3+ },+ "tolerance": {+ "type": "number",+ "minimum": 0,+ "maximum": 90+ },+ "orientation": {+ "enum": [+ "same",+ "parallel"+ ]+ }+ },+ "required": [+ "vector"+ ],+ "additionalProperties": false+ }+ },+ "required": [],+ "additionalProperties": false+ },+ "maxResults": {+ "type": "integer",+ "minimum": 1,+ "maximum": 1024+ }+ },+ "required": [+ "op",+ "target",+ "body",+ "kind",+ "space"+ ],+ "description": "Complete body-scoped topology query, no arbitrary first-match selection. selection one (default) refuses zero/multiple matches; many may return empty. Geometry types are exact native types; size ranges inclusive in mm/mm2, near is minimum distance in the explicit frame; direction is outward planar face normal or linear edge tangent. Returned refs retain instance context even for component-space filters. Exceeding maxResults fails rather than truncating."+ },+ "measure": {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "distance",+ "angle"+ ]+ },+ "space": {+ "enum": [+ "root"+ ]+ },+ "entities": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "minItems": 2,+ "maxItems": 3+ }+ },+ "required": [+ "op",+ "target",+ "type",+ "space",+ "entities"+ ],+ "description": "Native engineering minimum distance or angle in root assembly space. Non-root definition refs require an occurrence context. Distance needs2 entities; angle needs2 lines/planes or3 points (second apex). Returns mm/deg and native witness positions in root mm. witnessValidation identifies native points or native geometry directions used for valid parallel angles with degenerate native points; degenerate point angles and unavailable/inconsistent witnesses fail."+ },+ "sketch_curve_edit": {+ "type": "object",+ "properties": {+ "sketch": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "curve": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "action": {+ "enum": [+ "trim",+ "extend",+ "split",+ "break"+ ]+ },+ "pick": {+ "type": "array",+ "items": {+ "type": "number"+ },+ "minItems": 2,+ "maxItems": 3+ },+ "createConstraints": {+ "type": "boolean"+ },+ "allowDelete": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "sketch",+ "curve",+ "action",+ "pick"+ ],+ "description": "Edit one native sketch curve with sketch-space mm pick. trim removes the nearest segment; extend chooses nearest endpoint; split requires an interior on-curve point; break uses neighboring intersections. Fresh replacement refs are returned; original may be deleted. In-place extend returns the original with a fresh ref only after its geometry change survives recompute. No change or empty break/split fails. Full trim deletion requires allowDelete true. Linked/derived/deferred-compute geometry refused."+ },+ "sketch_offset": {+ "type": "object",+ "properties": {+ "sketch": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "curves": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "minItems": 1,+ "maxItems": 256+ },+ "distance": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "topologyMatched": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "sketch",+ "curves",+ "distance"+ ],+ "description": "Modern createOffsetInput/addOffset2. Curves must be native 2D, unique, in connected flow order (never reordered). Nonzero signed mm number/expression: positive right, negative left; a single circle/ellipse positive outside. Topology matched defaults true. Read back native signed distance, parent/child refs and controlling parameter after recompute. No retired Sketch.offset fallback."+ },+ "construction_axis": {+ "type": "object",+ "properties": {+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "type": {+ "enum": [+ "edge",+ "circular_face",+ "two_planes",+ "two_points"+ ]+ },+ "entities": {+ "type": "array",+ "items": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "minItems": 1,+ "maxItems": 2+ },+ "creationOccurrence": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "type",+ "entities"+ ],+ "description": "Create a parametric axis from native references (edge/circular face/two intersecting planes/two distinct points). Root-space external geometry requires exact occurrence context. For cross-component geometry into a non-root target component, creationOccurrence must identify a root-context instance of that target. Returns component-space native axis geometry/health. No direct-only infinite-line or preview snap APIs."+ },+ "sketch_diagnostics": {+ "type": "object",+ "properties": {+ "sketch": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context."+ }+ },+ "required": [+ "op",+ "target",+ "sketch"+ ],+ "description": "Read native sketch health/message, fully-constrained/fixed/linked/reference flags, geometric constraints and driving dimensions. Reports no solver DOF count or conflict-set explanation; no recompute/mutation."+ },+ "joint_origin_create": {+ "type": "object",+ "properties": {+ "component": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string",+ "minLength": 1,+ "maxLength": 120+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ },+ "edge": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "flipped": {+ "type": "boolean"+ },+ "xAxisEntity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "zAxisEntity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offsetX": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offsetY": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offsetZ": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "geometry"+ ],+ "description": "Create a native component-definition joint origin from guarded native geometry, with mm offsets/deg angle and optional straight axis references. Child occurrence origin references are exposed by inspect_entity(occurrence)."+ },+ "joint_origin_edit": {+ "type": "object",+ "properties": {+ "origin": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string",+ "minLength": 1,+ "maxLength": 120+ },+ "geometry": {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ },+ "edge": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ "flipped": {+ "type": "boolean"+ },+ "xAxisEntity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "zAxisEntity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offsetX": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offsetY": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offsetZ": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "origin"+ ],+ "description": "Edit a native joint origin definition; rolls before its timeline item and restores the original marker in finally. Recomputes and verifies attachment, axes, offsets, angle, name and flip."+ },+ "joint_origin_delete": {+ "type": "object",+ "properties": {+ "origin": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "origin"+ ],+ "description": "Delete a native joint origin; unhealthy downstream joints cause transaction rollback."+ },+ "regular_joint_create": {+ "type": "object",+ "properties": {+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "name": {+ "type": "string"+ },+ "rotationLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "slideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rotationValue": {+ "type": "number"+ },+ "slideValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "primarySlideValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "pitchLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "pitchValue": {+ "type": "number"+ },+ "rollLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "rollValue": {+ "type": "number"+ },+ "yawLimits": {+ "type": "object",+ "properties": {+ "minimum": {+ "type": "number"+ },+ "maximum": {+ "type": "number"+ },+ "rest": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "additionalProperties": false,+ "minProperties": 1+ },+ "yawValue": {+ "type": "number"+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometryOne": {+ "oneOf": [+ {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ },+ "edge": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "origin": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "origin"+ ],+ "additionalProperties": false+ }+ ]+ },+ "geometryTwo": {+ "oneOf": [+ {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ },+ "edge": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "origin": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "origin"+ ],+ "additionalProperties": false+ }+ ]+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offset": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "flipped": {+ "type": "boolean"+ },+ "locked": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type",+ "geometryOne",+ "geometryTwo"+ ],+ "description": "Create a geometry-solving regular joint in the root component using two distinct instance-context attachments or joint origins. All seven native motion families; offsets mm, angle and angular limits deg. Read back freshly resolved attachment graph, native parameters and root-space occurrence placement. Root/non-root sides must be unambiguous; both grounded and ancestor/descendant pairs are refused. Inferred joints are unsupported. Existing pending positions must be captured or reverted first. Before applying drive values, checks native joint-transform anchor offset magnitude/axiality and zero-angle axis collinearity. Native joint matrices have explicit provenance; physical angle/flip sense requires independent occurrence-geometry acceptance."+ },+ "regular_joint_edit": {+ "type": "object",+ "properties": {+ "joint": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "geometryOne": {+ "oneOf": [+ {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ },+ "edge": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "origin": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "origin"+ ],+ "additionalProperties": false+ }+ ]+ },+ "geometryTwo": {+ "oneOf": [+ {+ "type": "object",+ "properties": {+ "entity": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ },+ "edge": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "entity"+ ],+ "additionalProperties": false+ },+ {+ "type": "object",+ "properties": {+ "origin": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "origin"+ ],+ "additionalProperties": false+ }+ ]+ },+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offset": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offsetX": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "offsetY": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "flipped": {+ "type": "boolean"+ },+ "locked": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "description": "Redefine root-owned regular joint geometry, motion family, offsets, angle, flip or lock. Restore timeline in finally and verify after recompute. Use joint_edit for name, suppression, limits and drive values; joint_delete for deletion."+ },+ "motion_link_create": {+ "type": "object",+ "properties": {+ "jointOne": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "jointTwo": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "name": {+ "type": "string",+ "minLength": 1,+ "maxLength": 120+ },+ "motionOne": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "motionTwo": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "valueOne": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "valueTwo": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "reversed": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "jointOne",+ "motionOne",+ "motionTwo",+ "valueOne",+ "valueTwo"+ ],+ "description": "Create a native motion link between two root-owned joints, or two different motions of jointOne when jointTwo is omitted. Positive ratio magnitudes use mm for translation and deg for rotation; reversed selects opposite direction. Requires the November 2025+ API; no script fallback."+ },+ "motion_link_edit": {+ "type": "object",+ "properties": {+ "link": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ },+ "suppressed": {+ "type": "boolean"+ },+ "name": {+ "type": "string",+ "minLength": 1,+ "maxLength": 120+ },+ "motionOne": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "motionTwo": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "valueOne": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "valueTwo": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string",+ "minLength": 1,+ "maxLength": 512+ }+ ],+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression."+ },+ "reversed": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "link"+ ],+ "description": "Edit a root-owned motion link ratio, degree-of-freedom pair, direction, name or suppression. Joint membership is immutable; recreate the link to change members. Verify exact joint identities, ratio, flags and health."+ },+ "motion_link_delete": {+ "type": "object",+ "properties": {+ "link": {+ "type": "object",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context."+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "link"+ ],+ "description": "Delete a root-owned motion link under the same transaction/recovery contract."+ },+ "configuration_inspect": {+ "type": "object",+ "description": "Read the loaded Design configuration graph without activation or recompute: ordinary rows, the separate native reserved test row, all supported typed cells including inactive/test cells, exact controlled-source links and current ordinary row. Unreadable fields and unknown/theme/hierarchical/inserted scopes remain explicit incomplete diagnostics. Configuration lifecycle and configured-design model mutations remain disabled pending native recovery/archive gates.",+ "properties": {},+ "required": [+ "op",+ "target"+ ]+ },+ "render_scene_inspect": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "target"+ ],+ "properties": {+ "sessionId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ }+ },+ "maxEnvironments": {+ "type": "integer",+ "minimum": 0,+ "maximum": 32+ },+ "camera": {+ "oneOf": [+ {+ "type": "object",+ "required": [+ "type",+ "space",+ "unit",+ "eye",+ "target",+ "upVector",+ "extents"+ ],+ "additionalProperties": false,+ "properties": {+ "type": {+ "type": "string",+ "enum": [+ "orthographic"+ ]+ },+ "space": {+ "type": "string",+ "enum": [+ "world"+ ]+ },+ "unit": {+ "type": "string",+ "enum": [+ "mm"+ ]+ },+ "eye": {+ "type": "array",+ "minItems": 3,+ "maxItems": 3,+ "items": {+ "type": "number",+ "minimum": -1000000000.0,+ "maximum": 1000000000.0+ }+ },+ "target": {+ "type": "array",+ "minItems": 3,+ "maxItems": 3,+ "items": {+ "type": "number",+ "minimum": -1000000000.0,+ "maximum": 1000000000.0+ }+ },+ "upVector": {+ "type": "array",+ "minItems": 3,+ "maxItems": 3,+ "items": {+ "type": "number",+ "minimum": -1000000000.0,+ "maximum": 1000000000.0+ }+ },+ "extents": {+ "type": "array",+ "minItems": 2,+ "maxItems": 2,+ "items": {+ "type": "number",+ "minimum": 1e-06,+ "maximum": 1000000000.0+ }+ }+ }+ },+ {+ "type": "object",+ "required": [+ "type",+ "space",+ "unit",+ "eye",+ "target",+ "upVector",+ "perspectiveAngle",+ "angleUnit"+ ],+ "additionalProperties": false,+ "properties": {+ "type": {+ "type": "string",+ "enum": [+ "perspective"+ ]+ },+ "space": {+ "type": "string",+ "enum": [+ "world"+ ]+ },+ "unit": {+ "type": "string",+ "enum": [+ "mm"+ ]+ },+ "eye": {+ "type": "array",+ "minItems": 3,+ "maxItems": 3,+ "items": {+ "type": "number",+ "minimum": -1000000000.0,+ "maximum": 1000000000.0+ }+ },+ "target": {+ "type": "array",+ "minItems": 3,+ "maxItems": 3,+ "items": {+ "type": "number",+ "minimum": -1000000000.0,+ "maximum": 1000000000.0+ }+ },+ "upVector": {+ "type": "array",+ "minItems": 3,+ "maxItems": 3,+ "items": {+ "type": "number",+ "minimum": -1000000000.0,+ "maximum": 1000000000.0+ }+ },+ "perspectiveAngle": {+ "type": "number",+ "exclusiveMinimum": 0,+ "maximum": 179.999999999+ },+ "angleUnit": {+ "type": "string",+ "enum": [+ "deg"+ ]+ }+ }+ }+ ]+ }+ },+ "description": "Guarded main-thread read of current viewport camera, render settings and up to 32 environments. Optional detached world-mm camera is constructed and read back without viewport assignment. Missing fields return PARTIAL; observationComplete covers only bounded available scope. sceneWitnessComplete and renderQualified remain false. No rendering, workspace activation or scene writes."+ },+ "exploded_view_plan": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "target",+ "unit",+ "steps"+ ],+ "properties": {+ "sessionId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ }+ },+ "unit": {+ "type": "string",+ "enum": [+ "mm"+ ]+ },+ "steps": {+ "type": "array",+ "minItems": 1,+ "maxItems": 32,+ "items": {+ "type": "object",+ "required": [+ "durationSeconds",+ "moves"+ ],+ "additionalProperties": false,+ "properties": {+ "title": {+ "type": "string"+ },+ "durationSeconds": {+ "type": "number",+ "exclusiveMinimum": 0,+ "maximum": 3600+ },+ "moves": {+ "type": "array",+ "minItems": 1,+ "maxItems": 128,+ "items": {+ "type": "object",+ "required": [+ "occurrence",+ "direction",+ "distance",+ "frame"+ ],+ "additionalProperties": false,+ "properties": {+ "occurrence": {+ "type": "object",+ "description": "Exact guarded occurrence reference from inspect; every repeated/nested instance is distinct."+ },+ "direction": {+ "type": "array",+ "minItems": 3,+ "maxItems": 3,+ "items": {+ "type": "number",+ "minimum": -1000000000.0,+ "maximum": 1000000000.0+ }+ },+ "distance": {+ "type": "number",+ "exclusiveMinimum": 0,+ "maximum": 1000000.0+ },+ "frame": {+ "type": "string",+ "enum": [+ "world",+ "parent",+ "local"+ ]+ }+ }+ }+ },+ "instruction": {+ "type": "string"+ }+ }+ }+ },+ "sampleTimes": {+ "type": "array",+ "maxItems": 62,+ "items": {+ "type": "number",+ "minimum": 0,+ "maximum": 3600+ }+ },+ "playback": {+ "type": "string",+ "enum": [+ "explode",+ "assemble"+ ]+ }+ },+ "description": "Read-only source-bound exploded assembly plan. Normalized translation directions in immutable home world/parent/local axes; simultaneous moves within ordered steps. Descendants inherit ancestor translations once, plus own moves. Bounded deterministic sample matrices and exact home reverse endpoint. No native moves, graphics, BOM quantity changes, render/video or storyboard creation. Complete configuration identity required."+ },+ "exploded_preview_create": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sourceTarget",+ "creationOperationId",+ "expectedPlanId",+ "planner"+ ],+ "properties": {+ "sessionId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ }+ },+ "sourceTarget": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ }+ },+ "creationOperationId": {+ "type": "string"+ },+ "expectedPlanId": {+ "type": "string"+ },+ "planner": {+ "type": "object",+ "required": [+ "unit",+ "steps"+ ],+ "additionalProperties": false,+ "properties": {+ "unit": {+ "type": "string",+ "enum": [+ "mm"+ ]+ },+ "steps": {+ "type": "array",+ "minItems": 1,+ "maxItems": 32,+ "items": {+ "type": "object",+ "required": [+ "durationSeconds",+ "moves"+ ],+ "additionalProperties": false,+ "properties": {+ "title": {+ "type": "string"+ },+ "durationSeconds": {+ "type": "number",+ "exclusiveMinimum": 0,+ "maximum": 3600+ },+ "moves": {+ "type": "array",+ "minItems": 1,+ "maxItems": 128,+ "items": {+ "type": "object",+ "required": [+ "occurrence",+ "direction",+ "distance",+ "frame"+ ],+ "additionalProperties": false,+ "properties": {+ "occurrence": {+ "type": "object",+ "description": "Exact guarded occurrence reference from inspect; every repeated/nested instance is distinct."+ },+ "direction": {+ "type": "array",+ "minItems": 3,+ "maxItems": 3,+ "items": {+ "type": "number",+ "minimum": -1000000000.0,+ "maximum": 1000000000.0+ }+ },+ "distance": {+ "type": "number",+ "exclusiveMinimum": 0,+ "maximum": 1000000.0+ },+ "frame": {+ "type": "string",+ "enum": [+ "world",+ "parent",+ "local"+ ]+ }+ }+ }+ },+ "instruction": {+ "type": "string"+ }+ }+ }+ },+ "sampleTimes": {+ "type": "array",+ "maxItems": 62,+ "items": {+ "type": "number",+ "minimum": 0,+ "maximum": 3600+ }+ },+ "playback": {+ "type": "string",+ "enum": [+ "explode",+ "assemble"+ ]+ }+ }+ }+ },+ "description": "Create finite copied solid-BRep presentation graphics in the exact active blank unsaved Design proven by a committed current-session new_design result. Regenerates the guarded source plan; leaves source active. Exclusive session lease, no source moves/visibility writes, no model transaction, automatic document close, render or capture. Live capability and native backend qualification are separate from durable operation receipts."+ },+ "exploded_preview_sample": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration",+ "sampleIndex"+ ],+ "properties": {+ "sessionId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ }+ },+ "previewId": {+ "type": "string"+ },+ "expectedGeneration": {+ "type": "integer",+ "minimum": 0,+ "maximum": 1000000000+ },+ "sampleIndex": {+ "type": "integer",+ "minimum": 0,+ "maximum": 63+ }+ },+ "description": "Apply one server-stored absolute sample to exact-owned graphics; preserve active document. Source must remain unchanged. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound."+ },+ "exploded_preview_show": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration"+ ],+ "properties": {+ "sessionId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ }+ },+ "previewId": {+ "type": "string"+ },+ "expectedGeneration": {+ "type": "integer",+ "minimum": 0,+ "maximum": 1000000000+ }+ },+ "description": "Explicitly activate the exact-owned presentation in Fusion; refuse an intervening foreign active document. No OS foreground, camera change or capture. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound."+ },+ "exploded_preview_dispose": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration"+ ],+ "properties": {+ "sessionId": {+ "type": "string"+ },+ "target": {+ "type": "object",+ "required": [+ "documentId",+ "revision"+ ],+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ }+ },+ "previewId": {+ "type": "string"+ },+ "expectedGeneration": {+ "type": "integer",+ "minimum": 0,+ "maximum": 1000000000+ }+ },+ "description": "Delete only the exact retained graphics tree and leave the blank document open for caller. Supports a narrowly proven cleanup after unresolved terminal storage without rewriting that history; no global Undo or close. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound."+ },+ "exploded_preview_inspect": {+ "type": "object",+ "required": [+ "op",+ "sessionId",+ "previewId"+ ],+ "properties": {+ "sessionId": {+ "type": "string"+ },+ "previewId": {+ "type": "string"+ }+ },+ "description": "Main-thread live preview ownership/geometry/history observation. Cached operation receipts do not establish current graphics liveness. Old-session history remains separately readable through mech_operation_status; no native reference adoption or lifecycle repair is provided here."+ }+}
addin/AdomBridge/commands/mechanical/sketch_edits.pyadded+148@@ -0,0 +1,148 @@+"""Sketch-space curve edits and signed, parameter-driven native offsets."""+import json+import math++from .core import items, parameter_data, ref, require, resolve, same, target+from .readback import curve_data, sketch_data+from .sketches import get_sketch, point, quantity+from .transactions import equivalent+++CURVES = ("SketchLine", "SketchArc", "SketchCircle", "SketchEllipse", "SketchEllipticalArc",+ "SketchFittedSpline", "SketchControlPointSpline", "SketchFixedSpline")+++def curve_shapes(sketch_result):+ # Constraint/flag changes or collection reordering alone do not prove that+ # trim/extend/split/break changed curve geometry.+ return sorted(({key: data for key, data in curve.items() if key not in ("fixed", "construction", "ref")}+ for curve in sketch_result["curves"]), key=lambda row: json.dumps(row, sort_keys=True))+++def editable_curve(design, value, reference, sketch):+ curve = resolve(design, value, reference, CURVES)+ require(getattr(curve, "assemblyContext", None) is None and same(curve.parentSketch, sketch),+ "wrong_sketch", "Curve edits require native references in this sketch definition.")+ require(not curve.isLinked and not curve.isReference, "linked_geometry", "Linked/reference geometry must be explicitly unlinked before curve editing.")+ return curve+++def sketch_curve_edit(design, value, args):+ require("allowDelete" not in args or args["action"] == "trim", "invalid_input", "allowDelete applies only to trim.")+ sketch = get_sketch(design, value, args)+ require(not sketch.isDerived and not sketch.isComputeDeferred, "sketch_not_editable", "Derived or deferred-compute sketches cannot be edited.")+ curve = editable_curve(design, value, args["curve"], sketch)+ before = sketch_data(sketch)+ selected_before = curve_shapes({"curves": [curve_data(curve)]})+ pick = point(args["pick"])+ if args["action"] == "split":+ ok, parameter = curve.geometry.evaluator.getParameterAtPoint(pick)+ require(ok, "pick_off_curve", "Split point must lie on the selected curve in sketch coordinates.")+ ok, actual = curve.geometry.evaluator.getPointAtParameter(parameter)+ require(ok and actual.distanceTo(pick) <= 1e-7, "pick_off_curve", "Split point must lie on the curve.")+ if hasattr(curve, "startSketchPoint") and hasattr(curve, "endSketchPoint"):+ require(min(pick.distanceTo(curve.startSketchPoint.geometry), pick.distanceTo(curve.endSketchPoint.geometry)) > 1e-7,+ "invalid_pick", "Split point must be inside the curve, not an endpoint.")+ if args["action"] == "extend" and hasattr(curve, "startSketchPoint") and hasattr(curve, "endSketchPoint"):+ require(abs(pick.distanceTo(curve.startSketchPoint.geometry) - pick.distanceTo(curve.endSketchPoint.geometry)) > 1e-7,+ "ambiguous_pick", "Extension pick is equally near both endpoints.")+ method = "breakCurve" if args["action"] == "break" else args["action"]+ returned = getattr(curve, method)(pick, args.get("createConstraints", True))+ require(returned is not None, "curve_edit_failed", "Fusion returned no curve edit result.")+ replacements = items(returned)+ deleted = not curve.isValid+ # Native extend can mutate the original in place and return an empty+ # collection. The post-compute selected-geometry checks below must still+ # prove the edit; this merely supplies its fresh reference on success.+ if args["action"] == "extend" and not replacements and not deleted:+ replacements = [curve]+ require(replacements or args["action"] == "trim" and deleted and args.get("allowDelete", False),+ "curve_edit_empty", "Edit returned no replacement curves; full trim deletion requires allowDelete:true.")+ def result(fresh):+ require(not equivalent(curve_shapes(before), curve_shapes(sketch_data(sketch))), "curve_edit_unchanged", "Curve edit made no persistent curve geometry change.")+ require((not curve.isValid) == deleted, "curve_edit_failed", "Original curve deletion state changed during recompute.")+ require(deleted or not equivalent(selected_before, curve_shapes({"curves": [curve_data(curve)]})),+ "curve_edit_unchanged", "Selected curve did not change; unrelated sketch changes cannot prove this edit.")+ require(all(item.isValid and same(item.parentSketch, sketch) for item in replacements),+ "curve_edit_failed", "Replacement curve disappeared after recompute.")+ return {"action": args["action"], "space": "sketch", "originalDeleted": deleted,+ "curves": [curve_data(item, fresh) for item in replacements], "sketch": sketch_data(sketch, fresh)}+ return result+++def ordered_chain(curves):+ """Validate supplied order; never reorder and silently change offset sign."""+ require(all(curve.is2D for curve in curves), "unsupported_geometry", "Offset requires curves in the sketch XY plane.")+ for index, curve in enumerate(curves):+ require(not any(same(curve, prior) for prior in curves[:index]), "duplicate_entity", "Offset input repeats a curve.")+ if len(curves) == 1: return+ ends = []+ for curve in curves:+ require(hasattr(curve, "startSketchPoint") and hasattr(curve, "endSketchPoint"),+ "disconnected_chain", "Closed standalone curves cannot be mixed into an offset chain.")+ ends.append([curve.startSketchPoint.geometry, curve.endSketchPoint.geometry])+ close = lambda a, b: a.distanceTo(b) <= 1e-7+ shared = [(i, j) for i in (0, 1) for j in (0, 1) if close(ends[0][i], ends[1][j])]+ require(len(shared) == 1, "ambiguous_chain", "First two curves must share exactly one endpoint.")+ previous = ends[1][1-shared[0][1]]+ for pair in ends[2:]:+ match = [index for index, endpoint in enumerate(pair) if close(previous, endpoint)]+ require(len(match) == 1, "disconnected_chain", "Curves must be supplied in connected flow order.")+ previous = pair[1-match[0]]+++def sketch_offset(design, value, args):+ sketch = get_sketch(design, value, args)+ require(not sketch.isDerived and not sketch.isComputeDeferred, "sketch_not_editable", "Derived or deferred-compute sketches cannot be edited.")+ curves = [editable_curve(design, value, item, sketch) for item in args["curves"]]+ ordered_chain(curves)+ offset = quantity(design, args["distance"])+ expression = offset.stringValue+ expected = design.unitsManager.evaluateExpression(expression, "mm")+ require(abs(expected) > 1e-8, "invalid_quantity", "Offset distance must be nonzero.")+ constraints = sketch.geometricConstraints+ require(hasattr(constraints, "createOffsetInput") and hasattr(constraints, "addOffset2"),+ "unsupported_capability", "This Fusion runtime lacks the modern signed offset API.")+ inp = constraints.createOffsetInput(curves, offset)+ require(inp is not None, "offset_failed", "Fusion rejected the offset inputs.")+ matched = args.get("topologyMatched", True)+ inp.isTopologyMatched = matched+ constraint = constraints.addOffset2(inp)+ require(constraint is not None and constraint.isValid, "offset_failed", "Fusion did not create an offset constraint.")+ def result(fresh):+ require(constraint.isValid and constraint.dimension is not None, "offset_failed", "Offset or its controlling dimension disappeared.")+ children = list(constraint.childCurves)+ require(children and all(child.isValid for child in children), "offset_failed", "Offset has no valid child curves.")+ require(constraint.isTopologyMatched == matched, "offset_failed", "Native offset topology setting differs from the request.")+ require(math.isclose(constraint.distance, expected, abs_tol=1e-8, rel_tol=1e-8),+ "offset_failed", "Native signed offset distance differs from the requested value.")+ parents = list(constraint.parentCurves)+ require(len(parents) == len(curves) and all(any(same(parent, curve) for parent in parents) for curve in curves),+ "offset_failed", "Native offset parents differ from the selected chain.")+ return {"space": "sketch", "offset": {"ref": ref(constraint, fresh), "distance": constraint.distance * 10,+ "unit": "mm", "requestedExpression": expression, "topologyMatched": constraint.isTopologyMatched,+ "parameter": parameter_data(design, constraint.dimension.parameter),+ "parentRefs": [ref(curve, fresh) for curve in curves],+ "curves": [curve_data(child, fresh) for child in children]}, "sketch": sketch_data(sketch, fresh)}+ return result+++def sketch_diagnostics(app, args):+ _, design, value = target(app, args)+ sketch = resolve(design, value, args["sketch"], ("Sketch",))+ entities = items(sketch.sketchCurves) + items(sketch.sketchPoints)+ return {"target": value, "sketch": ref(sketch, value), "fullyConstrained": sketch.isFullyConstrained,+ "healthState": int(sketch.healthState), "message": sketch.errorOrWarningMessage,+ "computeDeferred": sketch.isComputeDeferred,+ "entities": [{"ref": ref(entity, value), "fullyConstrained": entity.isFullyConstrained,+ "fixed": entity.isFixed, "linked": entity.isLinked, "reference": entity.isReference}+ for entity in entities],+ "constraints": [{"ref": ref(item, value), "type": item.objectType, "valid": item.isValid}+ for item in items(sketch.geometricConstraints)],+ "dimensions": [{"name": item.parameter.name, "expression": item.parameter.expression,+ "driving": item.isDriving} for item in items(sketch.sketchDimensions)],+ "diagnosticScope": "Native health and constraint flags only; no solver degrees-of-freedom or conflict-set explanation."}+++OPERATIONS = {"sketch_curve_edit": sketch_curve_edit, "sketch_offset": sketch_offset}+READS = {"sketch_diagnostics": sketch_diagnostics}
addin/AdomBridge/commands/mechanical/sketches.pyadded+234@@ -0,0 +1,234 @@+"""Typed sketches, dimensions, constraints and construction planes (mm/deg)."""+import math++import adsk.core+import adsk.fusion++from .core import items, number, ref, require, resolve, same+from .readback import sketch_data+++def collection(entities):+ result = adsk.core.ObjectCollection.create()+ for entity in entities:+ result.add(entity)+ return result+++def point(value):+ require(isinstance(value, list) and len(value) in (2, 3), "invalid_input", "Point must be [x,y] or [x,y,z] in mm.")+ coords = [number(v, "coordinate") / 10 for v in value]+ return adsk.core.Point3D.create(*(coords + [0] if len(coords) == 2 else coords))+++def quantity(design, value, unit="mm", positive=False):+ if isinstance(value, (int, float)) and not isinstance(value, bool):+ expression = str(number(value, "quantity", positive)) + (" " + unit if unit else "")+ else:+ require(isinstance(value, str) and value.strip(), "invalid_input", "Quantity must be a number or unit-bearing expression.")+ expression = value+ require(design.unitsManager.isValidExpression(expression, unit), "invalid_expression", "Quantity does not match " + (unit or "unitless") + ".")+ evaluated = design.unitsManager.evaluateExpression(expression, unit)+ require(math.isfinite(evaluated) and (not positive or evaluated > 0), "invalid_quantity", "Quantity must be finite" + (" and positive." if positive else "."))+ return adsk.core.ValueInput.createByString(expression)+++def component(design, value, args):+ if args.get("component"):+ return resolve(design, value, args["component"], ("Component",))+ return design.rootComponent+++def plane(design, value, comp, selector):+ if isinstance(selector, str):+ require(selector in ("xy", "xz", "yz"), "invalid_plane", "Plane must be xy, xz, yz or a guarded reference.")+ return getattr(comp, {"xy": "xYConstructionPlane", "xz": "xZConstructionPlane", "yz": "yZConstructionPlane"}[selector])+ entity = resolve(design, value, selector, ("ConstructionPlane", "BRepFace"))+ owner = entity.body.parentComponent if hasattr(entity, "body") else entity.component+ require(getattr(entity, "assemblyContext", None) is None and same(owner, comp), "wrong_component", "Plane reference must belong to this native component definition.")+ return entity+++def axis(design, value, comp, selector):+ if isinstance(selector, str):+ require(selector in ("x", "y", "z"), "invalid_axis", "Axis must be x, y, z or a guarded reference.")+ return getattr(comp, selector + "ConstructionAxis")+ entity = resolve(design, value, selector, ("ConstructionAxis", "SketchLine", "BRepEdge"))+ owner = entity.body.parentComponent if hasattr(entity, "body") else (entity.parentSketch.parentComponent if hasattr(entity, "parentSketch") else entity.component)+ require(getattr(entity, "assemblyContext", None) is None and same(owner, comp), "wrong_component", "Axis reference must belong to this native component definition.")+ return entity+++def named(entity, args):+ require(entity is not None and entity.isValid, "creation_failed", "Fusion did not return a valid entity.")+ if args.get("name"):+ entity.name = args["name"]+ return entity+++def get_sketch(design, value, args):+ sketch = resolve(design, value, args["sketch"], ("Sketch",))+ require(getattr(sketch, "assemblyContext", None) is None, "definition_reference_required", "Sketch edits require a native definition reference.")+ return sketch+++def sketch_create(design, value, args):+ comp = component(design, value, args)+ support = plane(design, value, comp, args.get("plane", "xy"))+ sketch = named(comp.sketches.add(support), args)+ return lambda fresh: {"sketch": sketch_data(sketch, fresh)}+++def sketch_geometry(design, value, args):+ sketch = get_sketch(design, value, args)+ curves = sketch.sketchCurves+ for item in args["items"]:+ kind = item["type"]+ created = []+ if kind == "point":+ created = [sketch.sketchPoints.add(point(item["position"]))]+ elif kind == "line":+ created = [curves.sketchLines.addByTwoPoints(point(item["start"]), point(item["end"]))]+ elif kind == "rectangle":+ created = items(curves.sketchLines.addTwoPointRectangle(point(item["start"]), point(item["end"])))+ elif kind == "circle":+ radius = number(item["radius"], "radius", True) / 10+ created = [curves.sketchCircles.addByCenterRadius(point(item["center"]), radius)]+ elif kind == "arc":+ sweep = math.radians(number(item["sweep"], "sweep"))+ require(0 < abs(sweep) < 2 * math.pi, "invalid_input", "Arc sweep magnitude must be between 0 and 360 deg.")+ created = [curves.sketchArcs.addByCenterStartSweep(point(item["center"]), point(item["start"]), sweep)]+ elif kind == "polygon":+ center = point(item["center"])+ count = item["sides"]+ require(isinstance(count, int) and not isinstance(count, bool) and 3 <= count <= 128, "invalid_input", "Polygon sides must be 3..128.")+ radius = number(item["radius"], "radius", True) / 10+ angle = math.radians(number(item.get("angle", 0), "angle"))+ vertices = [adsk.core.Point3D.create(center.x + radius * math.cos(angle + 2 * math.pi * i / count),+ center.y + radius * math.sin(angle + 2 * math.pi * i / count), center.z) for i in range(count)]+ created = [curves.sketchLines.addByTwoPoints(vertices[i], vertices[(i + 1) % count]) for i in range(count)]+ elif kind == "slot":+ start, end = point(item["start"]), point(item["end"])+ require(abs(start.z - end.z) < 1e-9, "invalid_input", "Slot center points must share their sketch-space Z coordinate.")+ radius = number(item["width"], "width", True) / 20+ dx, dy = end.x - start.x, end.y - start.y+ length = math.hypot(dx, dy)+ require(length > 1e-8, "invalid_input", "Slot center points must differ.")+ ox, oy = -dy * radius / length, dx * radius / length+ p = lambda center, sign: adsk.core.Point3D.create(center.x + sign * ox, center.y + sign * oy, center.z)+ a, b, cc, d = p(start, 1), p(end, 1), p(end, -1), p(start, -1)+ created = [curves.sketchLines.addByTwoPoints(a, b),+ curves.sketchArcs.addByCenterStartSweep(end, b, -math.pi),+ curves.sketchLines.addByTwoPoints(cc, d),+ curves.sketchArcs.addByCenterStartSweep(start, d, -math.pi)]+ elif kind == "spline":+ spline = curves.sketchFittedSplines.add(collection([point(p) for p in item["points"]]))+ if item.get("closed"):+ spline.isClosed = True+ created = [spline]+ else:+ require(False, "unsupported_geometry", "Unsupported sketch primitive.")+ for entity in created:+ require(entity and entity.isValid, "creation_failed", "Sketch primitive creation failed.")+ if "construction" in item:+ require(hasattr(entity, "isConstruction"), "invalid_input", "Construction applies to curves.")+ entity.isConstruction = item["construction"]+ return lambda fresh: {"sketch": sketch_data(sketch, fresh)}+++def sketch_entities(design, value, args, sketch):+ entities = [resolve(design, value, entity) for entity in args["entities"]]+ for entity in entities:+ require(hasattr(entity, "parentSketch") and same(entity.parentSketch, sketch), "wrong_sketch", "Every selected entity must belong to the specified sketch.")+ return entities+++def sketch_constraint(design, value, args):+ sketch = get_sketch(design, value, args)+ entities = sketch_entities(design, value, args, sketch)+ methods = {"horizontal": ("addHorizontal", 1), "vertical": ("addVertical", 1),+ "coincident": ("addCoincident", 2), "parallel": ("addParallel", 2),+ "perpendicular": ("addPerpendicular", 2), "tangent": ("addTangent", 2),+ "equal": ("addEqual", 2), "concentric": ("addConcentric", 2),+ "collinear": ("addCollinear", 2), "midpoint": ("addMidPoint", 2),+ "symmetry": ("addSymmetry", 3)}+ if args["type"] == "fix":+ require(len(entities) == 1, "invalid_input", "fix requires one entity.")+ entities[0].isFixed = True+ else:+ method, count = methods[args["type"]]+ require(len(entities) == count, "invalid_input", "Constraint requires " + str(count) + " entities.")+ result = getattr(sketch.geometricConstraints, method)(*entities)+ require(result is not None, "constraint_failed", "Fusion did not create the constraint.")+ return lambda fresh: {"sketch": sketch_data(sketch, fresh)}+++def sketch_dimension(design, value, args):+ sketch = get_sketch(design, value, args)+ entities = sketch_entities(design, value, args, sketch)+ position, kind = point(args["position"]), args["type"]+ dims = sketch.sketchDimensions+ if kind in ("distance", "horizontal", "vertical"):+ require(len(entities) == 2, "invalid_input", "Distance dimension requires two sketch points.")+ orientation = getattr(adsk.fusion.DimensionOrientations, {"distance": "AlignedDimensionOrientation", "horizontal": "HorizontalDimensionOrientation", "vertical": "VerticalDimensionOrientation"}[kind])+ dimension = dims.addDistanceDimension(entities[0], entities[1], orientation, position)+ elif kind == "angle":+ require(len(entities) == 2, "invalid_input", "Angle dimension requires two lines.")+ dimension = dims.addAngularDimension(entities[0], entities[1], position)+ else:+ require(len(entities) == 1, "invalid_input", "Radius/diameter dimension requires one circle or arc.")+ dimension = getattr(dims, "addRadialDimension" if kind == "radius" else "addDiameterDimension")(entities[0], position)+ require(dimension is not None, "dimension_failed", "Fusion did not create the dimension.")+ expression = args["expression"]+ quantity(design, expression, "deg" if kind == "angle" else "mm")+ dimension.parameter.expression = str(expression) + (" deg" if kind == "angle" else " mm") if isinstance(expression, (int, float)) else expression+ if args.get("parameterName"):+ dimension.parameter.name = args["parameterName"]+ return lambda fresh: {"sketch": sketch_data(sketch, fresh), "parameterName": dimension.parameter.name}+++def sketch_project(design, value, args):+ sketch = get_sketch(design, value, args)+ projected = []+ for reference in args["entities"]:+ entity = resolve(design, value, reference, ("BRepEdge", "BRepFace", "BRepVertex", "SketchPoint", "SketchLine", "SketchCircle", "SketchArc"))+ projected.extend(items(sketch.project(entity)))+ require(projected, "projection_failed", "No sketch entities were projected.")+ return lambda fresh: {"sketch": sketch_data(sketch, fresh), "projected": [ref(e, fresh) for e in projected]}+++def sketch_edit(design, value, args):+ sketch = get_sketch(design, value, args)+ entity = resolve(design, value, args["entity"])+ require(same(entity.parentSketch, sketch), "wrong_sketch", "Entity is not in this sketch.")+ changes = args["changes"]+ for key, val in changes.items():+ if key in ("construction", "fixed"):+ setattr(entity, "isConstruction" if key == "construction" else "isFixed", val)+ elif key == "radius":+ entity.radius = number(val, "radius", True) / 10+ elif key in ("position", "start", "end", "center"):+ handle = entity if key == "position" else getattr(entity, {"start": "startSketchPoint", "end": "endSketchPoint", "center": "centerSketchPoint"}[key])+ wanted, old = point(val), handle.geometry+ require(handle.move(adsk.core.Vector3D.create(wanted.x - old.x, wanted.y - old.y, wanted.z - old.z)), "edit_failed", "Sketch point move was refused.")+ else:+ require(False, "invalid_input", "Unknown sketch edit property.")+ return lambda fresh: {"sketch": sketch_data(sketch, fresh)}+++def plane_create(design, value, args):+ comp = component(design, value, args)+ base = plane(design, value, comp, args.get("plane", "xy"))+ inp = comp.constructionPlanes.createInput()+ if args.get("type", "offset") == "offset":+ require(inp.setByOffset(base, quantity(design, args["offset"])), "plane_failed", "Offset plane input failed.")+ else:+ rotation = axis(design, value, comp, args["axis"])+ require(inp.setByAngle(rotation, quantity(design, args["angle"], "deg"), base), "plane_failed", "Angled plane input failed.")+ created = named(comp.constructionPlanes.add(inp), args)+ return lambda fresh: {"plane": {"ref": ref(created, fresh), "name": created.name}}+++OPERATIONS = {"sketch_create": sketch_create, "sketch_geometry": sketch_geometry,+ "sketch_constraint": sketch_constraint, "sketch_dimension": sketch_dimension,+ "sketch_project": sketch_project, "sketch_edit": sketch_edit, "plane_create": plane_create}
addin/AdomBridge/commands/mechanical/transactions.pyadded+157@@ -0,0 +1,157 @@+"""Verified Fusion PTransaction adapter; excludes documents and external files.++Autodesk's FusionMCPSample uses these text commands. Availability alone is not+proof: this adapter is enabled only after a session probe demonstrates both abort+and commit effects, followed by semantic restoration. Commands fail closed.+"""+import json+import math+import uuid++import adsk.core++from .core import (MechanicalError, active, current_target, recovery_snapshot,+ require, require_healthy, same, target)++_verified = False+_probe_evidence = None+# Called after every probe so the exe's capabilities see the new state (set by __init__).+on_probe = None+++def capability():+ return {"verified": _verified, "backend": "PTransaction", "evidence": _probe_evidence,+ "scope": "one active Fusion Design; no document lifecycle, files or cloud effects"}+++def command(app, text):+ response = str(app.executeTextCommand(text)).strip()+ require(response == "1", "transaction_unavailable", "Fusion did not acknowledge transaction command.",+ command=text.split()[0], response=response)+++def equivalent(left, right):+ if isinstance(left, float) and isinstance(right, (float, int)):+ return math.isclose(left, right, rel_tol=1e-8, abs_tol=1e-8)+ if isinstance(left, dict) and isinstance(right, dict):+ return left.keys() == right.keys() and all(equivalent(left[k], right[k]) for k in left)+ if isinstance(left, list) and isinstance(right, list):+ return len(left) == len(right) and all(equivalent(a, b) for a, b in zip(left, right))+ return left == right+++def probe(app, args):+ try:+ return _probe(app, args)+ finally:+ if on_probe is not None:+ try:+ on_probe()+ except Exception:+ pass+++def _probe(app, args):+ global _verified, _probe_evidence+ doc, design, _ = target(app, args, True)+ require_healthy(design, False)+ before = recovery_snapshot(design)+ name = "AdomProbe" + uuid.uuid4().hex+ started = False+ try:+ command(app, 'PTransaction.Start "Adom recovery capability probe"')+ started = True+ parameter = design.userParameters.add(name, adsk.core.ValueInput.createByString("1 mm"), "mm", "temporary recovery probe")+ require(parameter is not None, "transaction_probe_failed", "Probe parameter creation failed.")+ command(app, "PTransaction.Abort")+ started = False+ require(design.userParameters.itemByName(name) is None, "transaction_probe_failed", "Abort did not remove the probe parameter.")+ command(app, 'PTransaction.Start "Adom commit capability probe"')+ started = True+ design.userParameters.add(name, adsk.core.ValueInput.createByString("2 mm"), "mm", "temporary recovery probe")+ command(app, "PTransaction.Commit")+ started = False+ parameter = design.userParameters.itemByName(name)+ require(parameter is not None and abs(parameter.value - 0.2) < 1e-9, "transaction_probe_failed", "Commit did not preserve the probe parameter.")+ require(bool(parameter.deleteMe()), "transaction_probe_failed", "Could not remove committed probe parameter.")+ require_healthy(design)+ require(equivalent(before, recovery_snapshot(design)), "transaction_probe_failed", "Probe did not restore the semantic witness.")+ _verified = True+ _probe_evidence = {"abortRemovedParameter": True, "commitPreservedParameter": True,+ "semanticRestoration": True, "applicationVersion": str(getattr(app, "version", "unknown"))}+ return {"target": current_target(app), "transaction": capability()}+ except Exception as exc:+ _verified = False+ recovery = {"verified": False, "strategy": "abort_and_remove_probe_parameter"}+ try:+ require(same(app.activeDocument, doc), "wrong_document", "Active document changed during probe.")+ if started:+ command(app, "PTransaction.Abort")+ parameter = design.userParameters.itemByName(name)+ if parameter:+ require(parameter.deleteMe(), "recovery_failed", "Probe parameter deletion failed.")+ require_healthy(design)+ recovery["verified"] = equivalent(before, recovery_snapshot(design))+ except Exception as restore_error:+ recovery["error"] = str(restore_error)+ raise MechanicalError("transaction_probe_failed", str(exc), recovery=recovery)+++def _retarget(value, fresh):+ if isinstance(value, dict):+ if "documentId" in value and "revision" in value:+ value.update(fresh)+ for nested in value.values():+ _retarget(nested, fresh)+ elif isinstance(value, list):+ for nested in value:+ _retarget(nested, fresh)+++def execute(app, args, operation):+ require(_verified, "transaction_not_verified", "Run transaction_probe in this add-in session before typed feature mutations.")+ doc, design, value = target(app, args, True)+ require(design.timeline.markerPosition == design.timeline.count, "timeline_not_at_end", "Finish editing history before a typed model mutation.")+ require_healthy(design, False)+ before = recovery_snapshot(design)+ started, committed = False, False+ try:+ command(app, 'PTransaction.Start "Adom mechanical operation"')+ started = True+ serialize = operation(design, value, args)+ require(same(app.activeDocument, doc), "wrong_document", "Active document changed during the operation.")+ state = require_healthy(design)+ provisional = current_target(app)+ result = serialize(provisional) if callable(serialize) else serialize+ result.update(target=provisional, health=state, transactional=True)+ json.dumps(result, allow_nan=False) # Serialization is validated before commit.+ command(app, "PTransaction.Commit")+ committed = True+ fresh = current_target(app)+ _retarget(result, fresh)+ result["transaction"] = {"committed": True, "backend": "PTransaction"}+ return result+ except Exception as exc:+ if committed:+ # Readback after commit can fail, but the model has already changed.+ return {"transaction": {"committed": True, "backend": "PTransaction"},+ "inspectionError": str(exc), "refreshRequired": True}+ recovery = {"strategy": "PTransaction.Abort", "verified": False,+ "witness": "complete supported configuration graph including inactive cells, parameters, feature state, sketch geometry/constraint graph and offset links, construction axes, body geometry metrics, component metadata/materials, occurrence transforms/grounding, joint attachments/origins/motion/limits, motion links and timeline marker"}+ try:+ require(same(app.activeDocument, doc), "wrong_document", "Refusing to abort another document's transaction.")+ if started:+ command(app, "PTransaction.Abort")+ require_healthy(design)+ recovery["verified"] = equivalent(before, recovery_snapshot(design))+ except Exception as restore_error:+ recovery["error"] = str(restore_error)+ details = {"recovery": recovery, "causeCode": exc.code if isinstance(exc, MechanicalError) else+ ("unsupported_capability" if isinstance(exc, AttributeError) else "kernel_error")}+ if isinstance(exc, MechanicalError) and exc.details:+ details["causeDetails"] = exc.details+ try:+ details["target"] = current_target(app)+ except Exception:+ details["refreshRequired"] = True+ raise MechanicalError("mutation_failed", str(exc), **details)
bridge.json+4−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.12.8",+ "version": "1.13.0-dev.1", "description": "Drive Autodesk Fusion 360 from the cloud: launch Fusion, electronics board layout, design rules, exports (STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers/BOM/CPL), fast APS server-indexed cloud search plus browse/recent/file-info/versions, cloud file download/upload and folder creation, and in-app parametric modeling (fusion_run_modeling_script). Never-charge: APS calls are capped to the free tier.", "homepage": "https://wiki.adom.inc/adom/fusion-bridge", "author": "Adom Inc.",@@ -170,6 +170,9 @@ "fusion_mcp_resources", "fusion_mcp_status", "fusion_mcp_tools",+ "fusion_mech_capabilities",+ "fusion_mech_operation_status",+ "fusion_mechanical", "fusion_move_parts", "fusion_narrate", "fusion_net_classes",
contracts/describe.json+13411@@ -3008,6 +3008,14056 @@ "summary": "Full tool list from Autodesk's Fusion MCP server INCLUDING input schemas, so you can construct a valid fusion_mcp_call. These are Autodesk's tools, not this bridge's.", "timeoutSeconds": 45 },+ {+ "example": {},+ "input": {},+ "longRunning": false,+ "name": "fusion_mech_capabilities",+ "output": {+ "idempotency": "object",+ "operations": {+ "assembly_properties": {+ "description": "High-accuracy physical properties of whole root assembly (default) or an exact Component/Occurrence/BRepBody. Returns mass kg, volume mm3, area mm2, center of mass mm, mean density kg/mm3 and definition/instance coordinate scope.",+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "bom": {+ "description": "Structured component-definition and instance BOM; quantities by actual identity, root bodies plus nested children; purchased metadata collapses subtrees unless exploded. Physical mass includes all unsuppressed bodies exactly once per instance. Includes exact current active configuration provenance; incomplete configuration scope refuses. Visibility does not remove quantity.",+ "properties": {+ "explodePurchased": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "bom_export": {+ "description": "Export flat grouped purchasing rows or structured parent/path/depth instance rows and supplier provenance to CSV with exclusive/atomic publication. This external effect is separate from model transactions. Both CSV modes include document/revision and current active configuration table/row provenance.",+ "properties": {+ "explodePurchased": {+ "type": "boolean"+ },+ "filename": {+ "type": "string"+ },+ "mode": {+ "enum": [+ "flat",+ "structured"+ ]+ },+ "overwrite": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "filename"+ ],+ "type": "object"+ },+ "cad_import": {+ "description": "Import a pre-downloaded local STEP/SAT into a new wrapper component in Hybrid design. Validate optional expected SHA-256, record actual source/hash/supplier metadata, preserve camera, measure model recovery. Does not fetch URLs or create cloud documents. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused.",+ "properties": {+ "asPurchased": {+ "type": "boolean"+ },+ "filename": {+ "type": "string"+ },+ "name": {+ "type": "string"+ },+ "parent": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "provenance": {+ "additionalProperties": false,+ "properties": {+ "description": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ }+ },+ "required": [+ "supplier",+ "supplierPartNumber",+ "sourceUrl"+ ],+ "type": "object"+ },+ "sha256": {+ "type": "string"+ },+ "transform": {+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context.",+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "filename",+ "provenance"+ ],+ "type": "object"+ },+ "component_create": {+ "description": "Create a native internal component and its first occurrence in a Hybrid design. parent is a component definition; edits to it affect every instance. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused.",+ "properties": {+ "metadata": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "asPurchased": {+ "type": "boolean"+ },+ "description": {+ "type": "string"+ },+ "excludeFromBom": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "partNumber": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ }+ },+ "required": [],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "parent": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "transform": {+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context.",+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name"+ ],+ "type": "object"+ },+ "component_metadata": {+ "description": "Update component-definition name/part number/description and persistent supplier or BOM attributes. Shared occurrences all see this metadata.",+ "properties": {+ "changes": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "asPurchased": {+ "type": "boolean"+ },+ "description": {+ "type": "string"+ },+ "excludeFromBom": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "partNumber": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ }+ },+ "required": [],+ "type": "object"+ },+ "component": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "changes"+ ],+ "type": "object"+ },+ "configuration_inspect": {+ "description": "Read the loaded Design configuration graph without activation or recompute: ordinary rows, the separate native reserved test row, all supported typed cells including inactive/test cells, exact controlled-source links and current ordinary row. Unreadable fields and unknown/theme/hierarchical/inserted scopes remain explicit incomplete diagnostics. Configuration lifecycle and configured-design model mutations remain disabled pending native recovery/archive gates.",+ "properties": {},+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "construction_axis": {+ "description": "Create a parametric axis from native references (edge/circular face/two intersecting planes/two distinct points). Root-space external geometry requires exact occurrence context. For cross-component geometry into a non-root target component, creationOccurrence must identify a root-context instance of that target. Returns component-space native axis geometry/health. No direct-only infinite-line or preview snap APIs.",+ "properties": {+ "component": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "creationOccurrence": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entities": {+ "items": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 2,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "type": {+ "enum": [+ "edge",+ "circular_face",+ "two_planes",+ "two_points"+ ]+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "type",+ "entities"+ ],+ "type": "object"+ },+ "entity_delete": {+ "description": "Delete sketch geometry or independently deletable timeline features. Model transaction and semantic recovery cover downstream failures; document and external file deletion are excluded.",+ "properties": {+ "entity": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "entity"+ ],+ "type": "object"+ },+ "exploded_preview_create": {+ "description": "Create finite copied solid-BRep presentation graphics in the exact active blank unsaved Design proven by a committed current-session new_design result. Regenerates the guarded source plan; leaves source active. Exclusive session lease, no source moves/visibility writes, no model transaction, automatic document close, render or capture. Live capability and native backend qualification are separate from durable operation receipts.",+ "properties": {+ "creationOperationId": {+ "type": "string"+ },+ "expectedPlanId": {+ "type": "string"+ },+ "planner": {+ "additionalProperties": false,+ "properties": {+ "playback": {+ "enum": [+ "explode",+ "assemble"+ ],+ "type": "string"+ },+ "sampleTimes": {+ "items": {+ "maximum": 3600,+ "minimum": 0,+ "type": "number"+ },+ "maxItems": 62,+ "type": "array"+ },+ "steps": {+ "items": {+ "additionalProperties": false,+ "properties": {+ "durationSeconds": {+ "exclusiveMinimum": 0,+ "maximum": 3600,+ "type": "number"+ },+ "instruction": {+ "type": "string"+ },+ "moves": {+ "items": {+ "additionalProperties": false,+ "properties": {+ "direction": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "distance": {+ "exclusiveMinimum": 0,+ "maximum": 1000000.0,+ "type": "number"+ },+ "frame": {+ "enum": [+ "world",+ "parent",+ "local"+ ],+ "type": "string"+ },+ "occurrence": {+ "description": "Exact guarded occurrence reference from inspect; every repeated/nested instance is distinct.",+ "type": "object"+ }+ },+ "required": [+ "occurrence",+ "direction",+ "distance",+ "frame"+ ],+ "type": "object"+ },+ "maxItems": 128,+ "minItems": 1,+ "type": "array"+ },+ "title": {+ "type": "string"+ }+ },+ "required": [+ "durationSeconds",+ "moves"+ ],+ "type": "object"+ },+ "maxItems": 32,+ "minItems": 1,+ "type": "array"+ },+ "unit": {+ "enum": [+ "mm"+ ],+ "type": "string"+ }+ },+ "required": [+ "unit",+ "steps"+ ],+ "type": "object"+ },+ "sessionId": {+ "type": "string"+ },+ "sourceTarget": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sourceTarget",+ "creationOperationId",+ "expectedPlanId",+ "planner"+ ],+ "type": "object"+ },+ "exploded_preview_dispose": {+ "description": "Delete only the exact retained graphics tree and leave the blank document open for caller. Supports a narrowly proven cleanup after unresolved terminal storage without rewriting that history; no global Undo or close. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound.",+ "properties": {+ "expectedGeneration": {+ "maximum": 1000000000,+ "minimum": 0,+ "type": "integer"+ },+ "previewId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration"+ ],+ "type": "object"+ },+ "exploded_preview_inspect": {+ "description": "Main-thread live preview ownership/geometry/history observation. Cached operation receipts do not establish current graphics liveness. Old-session history remains separately readable through mech_operation_status; no native reference adoption or lifecycle repair is provided here.",+ "properties": {+ "previewId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "previewId"+ ],+ "type": "object"+ },+ "exploded_preview_sample": {+ "description": "Apply one server-stored absolute sample to exact-owned graphics; preserve active document. Source must remain unchanged. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound.",+ "properties": {+ "expectedGeneration": {+ "maximum": 1000000000,+ "minimum": 0,+ "type": "integer"+ },+ "previewId": {+ "type": "string"+ },+ "sampleIndex": {+ "maximum": 63,+ "minimum": 0,+ "type": "integer"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration",+ "sampleIndex"+ ],+ "type": "object"+ },+ "exploded_preview_show": {+ "description": "Explicitly activate the exact-owned presentation in Fusion; refuse an intervening foreign active document. No OS foreground, camera change or capture. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound.",+ "properties": {+ "expectedGeneration": {+ "maximum": 1000000000,+ "minimum": 0,+ "type": "integer"+ },+ "previewId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration"+ ],+ "type": "object"+ },+ "exploded_view_plan": {+ "description": "Read-only source-bound exploded assembly plan. Normalized translation directions in immutable home world/parent/local axes; simultaneous moves within ordered steps. Descendants inherit ancestor translations once, plus own moves. Bounded deterministic sample matrices and exact home reverse endpoint. No native moves, graphics, BOM quantity changes, render/video or storyboard creation. Complete configuration identity required.",+ "properties": {+ "playback": {+ "enum": [+ "explode",+ "assemble"+ ],+ "type": "string"+ },+ "sampleTimes": {+ "items": {+ "maximum": 3600,+ "minimum": 0,+ "type": "number"+ },+ "maxItems": 62,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "steps": {+ "items": {+ "additionalProperties": false,+ "properties": {+ "durationSeconds": {+ "exclusiveMinimum": 0,+ "maximum": 3600,+ "type": "number"+ },+ "instruction": {+ "type": "string"+ },+ "moves": {+ "items": {+ "additionalProperties": false,+ "properties": {+ "direction": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "distance": {+ "exclusiveMinimum": 0,+ "maximum": 1000000.0,+ "type": "number"+ },+ "frame": {+ "enum": [+ "world",+ "parent",+ "local"+ ],+ "type": "string"+ },+ "occurrence": {+ "description": "Exact guarded occurrence reference from inspect; every repeated/nested instance is distinct.",+ "type": "object"+ }+ },+ "required": [+ "occurrence",+ "direction",+ "distance",+ "frame"+ ],+ "type": "object"+ },+ "maxItems": 128,+ "minItems": 1,+ "type": "array"+ },+ "title": {+ "type": "string"+ }+ },+ "required": [+ "durationSeconds",+ "moves"+ ],+ "type": "object"+ },+ "maxItems": 32,+ "minItems": 1,+ "type": "array"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "unit": {+ "enum": [+ "mm"+ ],+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "target",+ "unit",+ "steps"+ ],+ "type": "object"+ },+ "export": {+ "description": "Export entire design using runtime-checked API. Preflight and unique same-filesystem staging preserve the destination on failure; publish only nonempty verified output. Availability differs by Fusion build. STL numeric coordinates use mm.",+ "properties": {+ "format": {+ "default": "step",+ "enum": [+ "step",+ "stl",+ "3mf",+ "f3d",+ "iges",+ "sat",+ "smt",+ "usdz",+ "fbx",+ "skp"+ ]+ },+ "outputPath": {+ "type": "string"+ },+ "overwrite": {+ "default": false,+ "type": "boolean"+ },+ "refinement": {+ "default": "medium",+ "enum": [+ "low",+ "medium",+ "high"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "outputPath"+ ],+ "type": "object"+ },+ "feature_create": {+ "description": "Create a typed native solid feature. All length quantities are mm (or expressions), angles deg. Cut/intersect require explicit participant bodies. Join needs a single existing component body; combine selects multiple bodies explicitly. Feature availability is runtime-checked; missing APIs fail before commit.",+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "direction": {+ "enum": [+ "positive",+ "negative",+ "symmetric"+ ]+ },+ "distance": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "profiles": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "taper": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "extrude"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "distance"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "profiles": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "revolve"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "axis",+ "angle"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "edges": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "radius": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "tangentChain": {+ "type": "boolean"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "fillet"+ ]+ }+ },+ "required": [+ "type",+ "edges",+ "radius"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "distance": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "edges": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "tangentChain": {+ "type": "boolean"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "chamfer"+ ]+ }+ },+ "required": [+ "type",+ "edges",+ "distance"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "faces": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "tangentChain": {+ "type": "boolean"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "thickness": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "type": {+ "enum": [+ "shell"+ ]+ }+ },+ "required": [+ "type",+ "faces",+ "thickness"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "depth": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "diameter": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "points": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "hole"+ ]+ }+ },+ "required": [+ "type",+ "points",+ "diameter",+ "depth",+ "bodies"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "profiles": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "loft"+ ]+ }+ },+ "required": [+ "type",+ "profiles"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "path": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "profiles": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "sweep"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "path"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "body": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keepTools": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "tools": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "type": {+ "enum": [+ "combine"+ ]+ }+ },+ "required": [+ "type",+ "body",+ "tools",+ "operation"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "extend": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "split"+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "plane"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "origin": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "translation": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "type": {+ "enum": [+ "move"+ ]+ }+ },+ "required": [+ "type",+ "bodies"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "mirror"+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "plane"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "axis2": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "count": {+ "maximum": 1000,+ "minimum": 2,+ "type": "integer"+ },+ "count2": {+ "maximum": 1000,+ "minimum": 1,+ "type": "integer"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "spacing": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "spacing2": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "rectangular_pattern"+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "axis",+ "count",+ "spacing"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "count": {+ "maximum": 1000,+ "minimum": 2,+ "type": "integer"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "circular_pattern"+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "axis",+ "count"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "faces": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "tangentChain": {+ "type": "boolean"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "draft"+ ]+ }+ },+ "required": [+ "type",+ "faces",+ "plane",+ "angle"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "designation": {+ "type": "string"+ },+ "faces": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "internal": {+ "type": "boolean"+ },+ "modeled": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "threadClass": {+ "type": "string"+ },+ "threadType": {+ "type": "string"+ },+ "type": {+ "enum": [+ "thread"+ ]+ }+ },+ "required": [+ "type",+ "faces",+ "internal",+ "threadType",+ "designation",+ "threadClass"+ ],+ "type": "object"+ }+ ],+ "properties": {+ "angle": {},+ "axis": {},+ "axis2": {},+ "bodies": {},+ "body": {},+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "count": {},+ "count2": {},+ "depth": {},+ "designation": {},+ "diameter": {},+ "direction": {},+ "distance": {},+ "edges": {},+ "extend": {},+ "faces": {},+ "internal": {},+ "keepTools": {},+ "modeled": {},+ "name": {+ "type": "string"+ },+ "operation": {},+ "origin": {},+ "path": {},+ "plane": {},+ "points": {},+ "profiles": {},+ "radius": {},+ "spacing": {},+ "spacing2": {},+ "tangentChain": {},+ "taper": {},+ "thickness": {},+ "threadClass": {},+ "threadType": {},+ "tools": {},+ "translation": {},+ "type": {+ "enum": [+ "extrude",+ "revolve",+ "fillet",+ "chamfer",+ "shell",+ "hole",+ "loft",+ "sweep",+ "combine",+ "split",+ "move",+ "mirror",+ "rectangular_pattern",+ "circular_pattern",+ "draft",+ "thread"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type"+ ],+ "type": "object"+ },+ "feature_edit": {+ "description": "Rename/suppress a feature or change expressions of model parameters whose createdBy is that exact feature; read health and abort on downstream errors.",+ "properties": {+ "feature": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "parameters": {+ "additionalProperties": {+ "type": "string"+ },+ "type": "object"+ },+ "suppressed": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "feature"+ ],+ "type": "object"+ },+ "geometry_query": {+ "description": "Complete body-scoped topology query, no arbitrary first-match selection. selection one (default) refuses zero/multiple matches; many may return empty. Geometry types are exact native types; size ranges inclusive in mm/mm2, near is minimum distance in the explicit frame; direction is outward planar face normal or linear edge tangent. Returned refs retain instance context even for component-space filters. Exceeding maxResults fails rather than truncating.",+ "properties": {+ "body": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "filters": {+ "additionalProperties": false,+ "properties": {+ "area": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "max": {+ "minimum": 0,+ "type": "number"+ },+ "min": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [],+ "type": "object"+ },+ "direction": {+ "additionalProperties": false,+ "properties": {+ "orientation": {+ "enum": [+ "same",+ "parallel"+ ]+ },+ "tolerance": {+ "maximum": 90,+ "minimum": 0,+ "type": "number"+ },+ "vector": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [+ "vector"+ ],+ "type": "object"+ },+ "length": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "max": {+ "minimum": 0,+ "type": "number"+ },+ "min": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [],+ "type": "object"+ },+ "near": {+ "additionalProperties": false,+ "properties": {+ "point": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "tolerance": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [+ "point",+ "tolerance"+ ],+ "type": "object"+ },+ "radius": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "max": {+ "minimum": 0,+ "type": "number"+ },+ "min": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [],+ "type": "object"+ },+ "type": {+ "enum": [+ "Plane",+ "Cylinder",+ "Cone",+ "Sphere",+ "Torus",+ "NurbsSurface",+ "Line3D",+ "Arc3D",+ "Circle3D",+ "Ellipse3D",+ "EllipticalArc3D",+ "NurbsCurve3D",+ "Point3D",+ "degenerate"+ ]+ }+ },+ "required": [],+ "type": "object"+ },+ "kind": {+ "enum": [+ "faces",+ "edges",+ "vertices"+ ]+ },+ "maxResults": {+ "maximum": 1024,+ "minimum": 1,+ "type": "integer"+ },+ "selection": {+ "enum": [+ "one",+ "many"+ ]+ },+ "space": {+ "enum": [+ "root",+ "component"+ ]+ }+ },+ "required": [+ "op",+ "target",+ "body",+ "kind",+ "space"+ ],+ "type": "object"+ },+ "health": {+ "description": "Read timeline warnings/errors. recompute:true also requires current target revision, sessionId and operationId.",+ "properties": {+ "recompute": {+ "default": false,+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "inspect": {+ "description": "Inspect the active Design without mutation. Returns target identity/revision, native component-space bodies and occurrence transforms, parameters and feature health.",+ "properties": {},+ "required": [+ "op"+ ],+ "type": "object"+ },+ "inspect_entity": {+ "properties": {+ "entity": {+ "description": "Exact reference returned by inspect; resolves uniquely with document/revision/type/occurrence guards.",+ "type": "object"+ }+ },+ "required": [+ "op",+ "target",+ "entity"+ ],+ "type": "object"+ },+ "interference": {+ "description": "Analyze root-context occurrences/body proxies; returns overlap volume mm3 or touching. Does not create interference bodies.",+ "properties": {+ "entities": {+ "items": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 2,+ "type": "array"+ },+ "includeTouching": {+ "type": "boolean"+ },+ "volumeTolerance": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [+ "op",+ "target",+ "entities"+ ],+ "type": "object"+ },+ "joint_create": {+ "description": "Create an as-built joint without moving occurrences. Non-rigid motion requires explicit joint geometry; x/y/z are joint-coordinate directions. Limits and drive values use mm/deg. Seven native motion types; ball uses joint Z pitch/X yaw; planar uses native primary slide direction reported on readback. Use regular_joint_create for geometry-solving placement.",+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "rigid"+ ]+ }+ },+ "required": [+ "type"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "revolute"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "slider"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "cylindrical"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "pin_slot"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "primarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "primarySlideValue": {+ "type": "number"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "planar"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "pitchLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "pitchValue": {+ "type": "number"+ },+ "rollLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rollValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "ball"+ ]+ },+ "yawLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ }+ ],+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "pitchLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "pitchValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "primarySlideValue": {+ "type": "number"+ },+ "rollLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rollValue": {+ "type": "number"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ },+ "yawLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type",+ "occurrenceOne",+ "occurrenceTwo"+ ],+ "type": "object"+ },+ "joint_delete": {+ "description": "Delete one selected Joint or AsBuiltJoint with recovery verification.",+ "properties": {+ "joint": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "type": "object"+ },+ "joint_edit": {+ "description": "Edit joint name/suppression, enabled limits, rest and drive values; wrong motion type or out-of-limit values fail with recovery. For driven root joints, active motion links must preserve their settings and their partner motion must follow the native ratio; a coupling mismatch aborts.",+ "properties": {+ "joint": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "pitchLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "pitchValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "primarySlideValue": {+ "type": "number"+ },+ "rollLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rollValue": {+ "type": "number"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "yawLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "type": "object"+ },+ "joint_origin_create": {+ "description": "Create a native component-definition joint origin from guarded native geometry, with mm offsets/deg angle and optional straight axis references. Child occurrence origin references are exposed by inspect_entity(occurrence).",+ "properties": {+ "angle": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "component": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "flipped": {+ "type": "boolean"+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "maxLength": 120,+ "minLength": 1,+ "type": "string"+ },+ "offsetX": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetY": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetZ": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "xAxisEntity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "zAxisEntity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "geometry"+ ],+ "type": "object"+ },+ "joint_origin_delete": {+ "description": "Delete a native joint origin; unhealthy downstream joints cause transaction rollback.",+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "origin"+ ],+ "type": "object"+ },+ "joint_origin_edit": {+ "description": "Edit a native joint origin definition; rolls before its timeline item and restores the original marker in finally. Recomputes and verifies attachment, axes, offsets, angle, name and flip.",+ "properties": {+ "angle": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "flipped": {+ "type": "boolean"+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "maxLength": 120,+ "minLength": 1,+ "type": "string"+ },+ "offsetX": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetY": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetZ": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "xAxisEntity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "zAxisEntity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "origin"+ ],+ "type": "object"+ },+ "material_assign": {+ "description": "Assign a loaded library or document material to an exact native Component/BRepBody; verify material ID readback with model transaction recovery. Query assembly_properties for resulting mass.",+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "libraryId": {+ "type": "string"+ },+ "materialId": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "entity",+ "materialId"+ ],+ "type": "object"+ },+ "material_list": {+ "description": "List loaded material libraries and matching materials by stable library/material IDs. Omit libraryId to search the document's materials and then every loaded library; each row names the libraryId material_assign needs (null for the document's own).",+ "properties": {+ "libraryId": {+ "type": "string"+ },+ "limit": {+ "maximum": 1000,+ "minimum": 1,+ "type": "integer"+ },+ "query": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "mcmaster_discover": {+ "description": "Discover Fusion native Insert McMaster-Carr command and requirements in the active design.",+ "properties": {},+ "required": [+ "op"+ ],+ "type": "object"+ },+ "mcmaster_open": {+ "description": "Invoke Fusion native McMaster UI in the targeted Hybrid design. Interactive external operation: completion/insert/cancel stays with Fusion; inspect afterward. Idempotency records launch only.",+ "properties": {},+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target"+ ],+ "type": "object"+ },+ "measure": {+ "description": "Native engineering minimum distance or angle in root assembly space. Non-root definition refs require an occurrence context. Distance needs2 entities; angle needs2 lines/planes or3 points (second apex). Returns mm/deg and native witness positions in root mm. witnessValidation identifies native points or native geometry directions used for valid parallel angles with degenerate native points; degenerate point angles and unavailable/inconsistent witnesses fail.",+ "properties": {+ "entities": {+ "items": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "space": {+ "enum": [+ "root"+ ]+ },+ "type": {+ "enum": [+ "distance",+ "angle"+ ]+ }+ },+ "required": [+ "op",+ "target",+ "type",+ "space",+ "entities"+ ],+ "type": "object"+ },+ "motion_link_create": {+ "description": "Create a native motion link between two root-owned joints, or two different motions of jointOne when jointTwo is omitted. Positive ratio magnitudes use mm for translation and deg for rotation; reversed selects opposite direction. Requires the November 2025+ API; no script fallback.",+ "properties": {+ "jointOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "jointTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "motionOne": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "motionTwo": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "name": {+ "maxLength": 120,+ "minLength": 1,+ "type": "string"+ },+ "reversed": {+ "type": "boolean"+ },+ "valueOne": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "valueTwo": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "jointOne",+ "motionOne",+ "motionTwo",+ "valueOne",+ "valueTwo"+ ],+ "type": "object"+ },+ "motion_link_delete": {+ "description": "Delete a root-owned motion link under the same transaction/recovery contract.",+ "properties": {+ "link": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "link"+ ],+ "type": "object"+ },+ "motion_link_edit": {+ "description": "Edit a root-owned motion link ratio, degree-of-freedom pair, direction, name or suppression. Joint membership is immutable; recreate the link to change members. Verify exact joint identities, ratio, flags and health.",+ "properties": {+ "link": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "motionOne": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "motionTwo": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "name": {+ "maxLength": 120,+ "minLength": 1,+ "type": "string"+ },+ "reversed": {+ "type": "boolean"+ },+ "suppressed": {+ "type": "boolean"+ },+ "valueOne": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "valueTwo": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "link"+ ],+ "type": "object"+ },+ "new_design": {+ "description": "Create an unnamed unsaved parametric hybrid Design; name is retained as requestedName metadata to keep failure recovery free of save dialogs. Optional box dimensions in mm create a driving-parameter fixture. Never edits an existing document.",+ "properties": {+ "box": {+ "additionalProperties": false,+ "properties": {+ "depth": {+ "exclusiveMinimum": 0,+ "type": "number"+ },+ "height": {+ "exclusiveMinimum": 0,+ "type": "number"+ },+ "width": {+ "exclusiveMinimum": 0,+ "type": "number"+ }+ },+ "required": [+ "width",+ "height",+ "depth"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId"+ ],+ "type": "object"+ },+ "occurrence_delete": {+ "description": "Delete one selected occurrence through the shared transaction executor.",+ "properties": {+ "occurrence": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "occurrence"+ ],+ "type": "object"+ },+ "occurrence_insert": {+ "description": "Insert another occurrence of an existing definition; independentCopy:true uses Paste New to create a separate definition. Nested placement is in parent component coordinates. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused.",+ "properties": {+ "component": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "independentCopy": {+ "type": "boolean"+ },+ "parent": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "transform": {+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context.",+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "component"+ ],+ "type": "object"+ },+ "occurrence_update": {+ "description": "Set occurrence pose in its assembly context and capture position; explicit grounding/ungrounding. Grounded or constrained transforms fail with recovery.",+ "properties": {+ "grounded": {+ "type": "boolean"+ },+ "groundedToParent": {+ "type": "boolean"+ },+ "occurrence": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "transform": {+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context.",+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "occurrence"+ ],+ "type": "object"+ },+ "parameter_batch": {+ "description": "Atomic sequential create/set/delete parameter edits with one recompute+health gate; delete supports user parameters only. Any failure aborts all edits and reports semantic recovery.",+ "properties": {+ "changes": {+ "items": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "action": {+ "enum": [+ "create"+ ]+ },+ "comment": {+ "type": "string"+ },+ "expression": {+ "type": "string"+ },+ "name": {+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm",+ "deg",+ "kg",+ ""+ ]+ }+ },+ "required": [+ "action",+ "name",+ "expression",+ "unit"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "action": {+ "enum": [+ "set"+ ]+ },+ "expression": {+ "type": "string"+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "action",+ "name",+ "expression"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "action": {+ "enum": [+ "delete"+ ]+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "action",+ "name"+ ],+ "type": "object"+ }+ ]+ },+ "maxItems": 128,+ "minItems": 1,+ "type": "array"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "changes"+ ],+ "type": "object"+ },+ "parameter_create": {+ "properties": {+ "comment": {+ "type": "string"+ },+ "expression": {+ "type": "string"+ },+ "name": {+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm",+ "deg",+ "kg",+ ""+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name",+ "expression",+ "unit"+ ],+ "type": "object"+ },+ "parameter_set": {+ "description": "Set an expression, recompute and inspect health. Failure restores old expression and reports a semantic recovery witness; not an atomic transaction.",+ "properties": {+ "expression": {+ "type": "string"+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name",+ "expression"+ ],+ "type": "object"+ },+ "plane_create": {+ "description": "Create offset plane (offset in mm) or angled plane around axis (angle in deg).",+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "offset": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "offset"+ ]+ }+ },+ "required": [+ "type",+ "offset"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "angle"+ ]+ }+ },+ "required": [+ "type",+ "axis",+ "angle"+ ],+ "type": "object"+ }+ ],+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "offset": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "type": {+ "enum": [+ "offset",+ "angle"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type"+ ],+ "type": "object"+ },+ "regular_joint_create": {+ "description": "Create a geometry-solving regular joint in the root component using two distinct instance-context attachments or joint origins. All seven native motion families; offsets mm, angle and angular limits deg. Read back freshly resolved attachment graph, native parameters and root-space occurrence placement. Root/non-root sides must be unambiguous; both grounded and ancestor/descendant pairs are refused. Inferred joints are unsupported. Existing pending positions must be captured or reverted first. Before applying drive values, checks native joint-transform anchor offset magnitude/axiality and zero-angle axis collinearity. Native joint matrices have explicit provenance; physical angle/flip sense requires independent occurrence-geometry acceptance.",+ "properties": {+ "angle": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "flipped": {+ "type": "boolean"+ },+ "geometryOne": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "origin"+ ],+ "type": "object"+ }+ ]+ },+ "geometryTwo": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "origin"+ ],+ "type": "object"+ }+ ]+ },+ "locked": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "offset": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "pitchLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "pitchValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "primarySlideValue": {+ "type": "number"+ },+ "rollLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rollValue": {+ "type": "number"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ },+ "yawLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type",+ "geometryOne",+ "geometryTwo"+ ],+ "type": "object"+ },+ "regular_joint_edit": {+ "description": "Redefine root-owned regular joint geometry, motion family, offsets, angle, flip or lock. Restore timeline in finally and verify after recompute. Use joint_edit for name, suppression, limits and drive values; joint_delete for deletion.",+ "properties": {+ "angle": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "flipped": {+ "type": "boolean"+ },+ "geometryOne": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "origin"+ ],+ "type": "object"+ }+ ]+ },+ "geometryTwo": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "origin"+ ],+ "type": "object"+ }+ ]+ },+ "joint": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "locked": {+ "type": "boolean"+ },+ "offset": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetX": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetY": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "type": "object"+ },+ "render_scene_inspect": {+ "description": "Guarded main-thread read of current viewport camera, render settings and up to 32 environments. Optional detached world-mm camera is constructed and read back without viewport assignment. Missing fields return PARTIAL; observationComplete covers only bounded available scope. sceneWitnessComplete and renderQualified remain false. No rendering, workspace activation or scene writes.",+ "properties": {+ "camera": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "extents": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": 1e-06,+ "type": "number"+ },+ "maxItems": 2,+ "minItems": 2,+ "type": "array"+ },+ "eye": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "space": {+ "enum": [+ "world"+ ],+ "type": "string"+ },+ "target": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "type": {+ "enum": [+ "orthographic"+ ],+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm"+ ],+ "type": "string"+ },+ "upVector": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [+ "type",+ "space",+ "unit",+ "eye",+ "target",+ "upVector",+ "extents"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angleUnit": {+ "enum": [+ "deg"+ ],+ "type": "string"+ },+ "eye": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "perspectiveAngle": {+ "exclusiveMinimum": 0,+ "maximum": 179.999999999,+ "type": "number"+ },+ "space": {+ "enum": [+ "world"+ ],+ "type": "string"+ },+ "target": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "type": {+ "enum": [+ "perspective"+ ],+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm"+ ],+ "type": "string"+ },+ "upVector": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [+ "type",+ "space",+ "unit",+ "eye",+ "target",+ "upVector",+ "perspectiveAngle",+ "angleUnit"+ ],+ "type": "object"+ }+ ]+ },+ "maxEnvironments": {+ "maximum": 32,+ "minimum": 0,+ "type": "integer"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "target"+ ],+ "type": "object"+ },+ "rigid_group_create": {+ "description": "Create a native rigid group from exact occurrence instances; includeChildren defaults true.",+ "properties": {+ "includeChildren": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "occurrences": {+ "items": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 2,+ "type": "array"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "occurrences"+ ],+ "type": "object"+ },+ "rigid_group_delete": {+ "description": "Delete an exact rigid group with transaction recovery.",+ "properties": {+ "group": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "group"+ ],+ "type": "object"+ },+ "rigid_group_edit": {+ "description": "Rename or suppress an exact rigid group with transaction recovery.",+ "properties": {+ "group": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "group"+ ],+ "type": "object"+ },+ "sketch_constraint": {+ "description": "Constrain entities in one sketch. midpoint order is point,line; symmetry order entity1,entity2,symmetryLine. fix sets one entity fixed.",+ "properties": {+ "entities": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sketch": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "horizontal",+ "vertical",+ "coincident",+ "parallel",+ "perpendicular",+ "tangent",+ "equal",+ "concentric",+ "collinear",+ "midpoint",+ "symmetry",+ "fix"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "type",+ "entities"+ ],+ "type": "object"+ },+ "sketch_create": {+ "description": "Create a native component-definition sketch on an origin/construction plane or planar face. Coordinates in subsequent geometry are sketch-space mm.",+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target"+ ],+ "type": "object"+ },+ "sketch_curve_edit": {+ "description": "Edit one native sketch curve with sketch-space mm pick. trim removes the nearest segment; extend chooses nearest endpoint; split requires an interior on-curve point; break uses neighboring intersections. Fresh replacement refs are returned; original may be deleted. In-place extend returns the original with a fresh ref only after its geometry change survives recompute. No change or empty break/split fails. Full trim deletion requires allowDelete true. Linked/derived/deferred-compute geometry refused.",+ "properties": {+ "action": {+ "enum": [+ "trim",+ "extend",+ "split",+ "break"+ ]+ },+ "allowDelete": {+ "type": "boolean"+ },+ "createConstraints": {+ "type": "boolean"+ },+ "curve": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "pick": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "sketch": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "sketch",+ "curve",+ "action",+ "pick"+ ],+ "type": "object"+ },+ "sketch_diagnostics": {+ "description": "Read native sketch health/message, fully-constrained/fixed/linked/reference flags, geometric constraints and driving dimensions. Reports no solver DOF count or conflict-set explanation; no recompute/mutation.",+ "properties": {+ "sketch": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "target",+ "sketch"+ ],+ "type": "object"+ },+ "sketch_dimension": {+ "description": "Add driving dimensions. Distance uses two point references; radius/diameter one curve; angle two lines. Numeric expressions use mm or deg.",+ "properties": {+ "entities": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "expression": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "parameterName": {+ "type": "string"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "sketch": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "distance",+ "horizontal",+ "vertical",+ "radius",+ "diameter",+ "angle"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "type",+ "entities",+ "expression",+ "position"+ ],+ "type": "object"+ },+ "sketch_edit": {+ "description": "Move sketch points/endpoints/centers in sketch-space mm, change radius or construction/fixed flags; solver failure aborts the operation.",+ "properties": {+ "changes": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "center": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "construction": {+ "type": "boolean"+ },+ "end": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "fixed": {+ "type": "boolean"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "radius": {+ "type": "number"+ },+ "start": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ },+ "entity": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "sketch": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "entity",+ "changes"+ ],+ "type": "object"+ },+ "sketch_geometry": {+ "description": "Create a transactionally grouped set of sketch primitives. Numeric geometry uses mm and arc sweeps/polygon angles use deg.",+ "properties": {+ "items": {+ "items": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "type": {+ "enum": [+ "point"+ ]+ }+ },+ "required": [+ "type",+ "position"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "construction": {+ "type": "boolean"+ },+ "end": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "start": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "type": {+ "enum": [+ "line"+ ]+ }+ },+ "required": [+ "type",+ "start",+ "end"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "construction": {+ "type": "boolean"+ },+ "end": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "start": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "type": {+ "enum": [+ "rectangle"+ ]+ }+ },+ "required": [+ "type",+ "start",+ "end"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "center": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "construction": {+ "type": "boolean"+ },+ "radius": {+ "exclusiveMinimum": 0,+ "type": "number"+ },+ "type": {+ "enum": [+ "circle"+ ]+ }+ },+ "required": [+ "type",+ "center",+ "radius"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "center": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "construction": {+ "type": "boolean"+ },+ "start": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "sweep": {+ "type": "number"+ },+ "type": {+ "enum": [+ "arc"+ ]+ }+ },+ "required": [+ "type",+ "center",+ "start",+ "sweep"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "type": "number"+ },+ "center": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "construction": {+ "type": "boolean"+ },+ "radius": {+ "exclusiveMinimum": 0,+ "type": "number"+ },+ "sides": {+ "maximum": 128,+ "minimum": 3,+ "type": "integer"+ },+ "type": {+ "enum": [+ "polygon"+ ]+ }+ },+ "required": [+ "type",+ "center",+ "radius",+ "sides"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "construction": {+ "type": "boolean"+ },+ "end": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "start": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "type": {+ "enum": [+ "slot"+ ]+ },+ "width": {+ "exclusiveMinimum": 0,+ "type": "number"+ }+ },+ "required": [+ "type",+ "start",+ "end",+ "width"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "closed": {+ "type": "boolean"+ },+ "construction": {+ "type": "boolean"+ },+ "points": {+ "items": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "maxItems": 128,+ "minItems": 2,+ "type": "array"+ },+ "type": {+ "enum": [+ "spline"+ ]+ }+ },+ "required": [+ "type",+ "points"+ ],+ "type": "object"+ }+ ]+ },+ "maxItems": 128,+ "minItems": 1,+ "type": "array"+ },+ "sketch": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "items"+ ],+ "type": "object"+ },+ "sketch_offset": {+ "description": "Modern createOffsetInput/addOffset2. Curves must be native 2D, unique, in connected flow order (never reordered). Nonzero signed mm number/expression: positive right, negative left; a single circle/ellipse positive outside. Topology matched defaults true. Read back native signed distance, parent/child refs and controlling parameter after recompute. No retired Sketch.offset fallback.",+ "properties": {+ "curves": {+ "items": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "distance": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "sketch": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "topologyMatched": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "sketch",+ "curves",+ "distance"+ ],+ "type": "object"+ },+ "sketch_project": {+ "description": "Project selected topology into a sketch with Fusion associative projection.",+ "properties": {+ "entities": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sketch": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "entities"+ ],+ "type": "object"+ },+ "transaction_probe": {+ "description": "Verify runtime PTransaction abort+commit using temporary user parameters, remove probe data and compare semantic restoration. Required once per running add-in session before feature/sketch/atomic-batch writes. Returns fresh target; inspect again for current references.",+ "properties": {},+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target"+ ],+ "type": "object"+ }+ },+ "sessionId": "str",+ "success": "bool",+ "transactions": "object"+ },+ "statusVerb": null,+ "summary": "Discover typed mechanical operations, exact input schemas, units, recovery limitations and the current add-in session ID (null while no add-in session is live). Served by the bridge exe from the add-in's schema and session file.",+ "timeoutSeconds": 30+ },+ {+ "example": {},+ "input": {+ "operationId": "required str",+ "sessionId": "required str"+ },+ "longRunning": false,+ "name": "fusion_mech_operation_status",+ "output": {+ "currentSessionId": "str",+ "historical": "bool",+ "history": "durable identity/phase/timestamps",+ "observedResult": "bounded unconfirmed current-session result or null",+ "phase": "admitted|dispatched|executing|succeeded|failed|not_executed",+ "reconciliationRequired": "bool",+ "result": "recorded terminal result or null",+ "state": "running|succeeded|failed|interrupted|reconciliation_required",+ "success": "bool"+ },+ "statusVerb": null,+ "summary": "Read durable typed-operation history by recorded sessionId and operationId, including after HTTP timeout or add-in restart. Returns the recorded request digest, source version, target identity, phase/timestamps and terminal result. Historical nonterminal work is interrupted/unknown and never replayed. Admission without a durable dispatch claim or uncertain persistence requires reconciliation. Missing history never proves no execution. The bridge exe reads the journal itself, so this answers while Fusion is busy, hung or closed; it never starts/restarts Fusion, touches windows or dismisses dialogs. SQLite recovery may repair only the app-owned journal.",+ "timeoutSeconds": 30+ },+ {+ "example": {+ "op": "inspect"+ },+ "input": {+ "op": "required enum: inspect|inspect_entity|new_design|parameter_set|parameter_create|health|export|transaction_probe|sketch_create|sketch_geometry|sketch_constraint|sketch_dimension|sketch_project|sketch_edit|plane_create|feature_create|feature_edit|entity_delete|parameter_batch|component_create|occurrence_insert|occurrence_update|occurrence_delete|component_metadata|joint_create|joint_edit|joint_delete|interference|bom|bom_export|cad_import|mcmaster_discover|mcmaster_open|material_list|material_assign|rigid_group_create|rigid_group_edit|rigid_group_delete|assembly_properties|geometry_query|measure|sketch_curve_edit|sketch_offset|construction_axis|sketch_diagnostics|joint_origin_create|joint_origin_edit|joint_origin_delete|regular_joint_create|regular_joint_edit|motion_link_create|motion_link_edit|motion_link_delete|configuration_inspect|render_scene_inspect|exploded_view_plan|exploded_preview_create|exploded_preview_sample|exploded_preview_show|exploded_preview_dispose|exploded_preview_inspect",+ "operationId": "required unique write ID; reuse unchanged for retry",+ "operationSchemas": {+ "assembly_properties": {+ "description": "High-accuracy physical properties of whole root assembly (default) or an exact Component/Occurrence/BRepBody. Returns mass kg, volume mm3, area mm2, center of mass mm, mean density kg/mm3 and definition/instance coordinate scope.",+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "bom": {+ "description": "Structured component-definition and instance BOM; quantities by actual identity, root bodies plus nested children; purchased metadata collapses subtrees unless exploded. Physical mass includes all unsuppressed bodies exactly once per instance. Includes exact current active configuration provenance; incomplete configuration scope refuses. Visibility does not remove quantity.",+ "properties": {+ "explodePurchased": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "bom_export": {+ "description": "Export flat grouped purchasing rows or structured parent/path/depth instance rows and supplier provenance to CSV with exclusive/atomic publication. This external effect is separate from model transactions. Both CSV modes include document/revision and current active configuration table/row provenance.",+ "properties": {+ "explodePurchased": {+ "type": "boolean"+ },+ "filename": {+ "type": "string"+ },+ "mode": {+ "enum": [+ "flat",+ "structured"+ ]+ },+ "overwrite": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "filename"+ ],+ "type": "object"+ },+ "cad_import": {+ "description": "Import a pre-downloaded local STEP/SAT into a new wrapper component in Hybrid design. Validate optional expected SHA-256, record actual source/hash/supplier metadata, preserve camera, measure model recovery. Does not fetch URLs or create cloud documents. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused.",+ "properties": {+ "asPurchased": {+ "type": "boolean"+ },+ "filename": {+ "type": "string"+ },+ "name": {+ "type": "string"+ },+ "parent": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "provenance": {+ "additionalProperties": false,+ "properties": {+ "description": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ }+ },+ "required": [+ "supplier",+ "supplierPartNumber",+ "sourceUrl"+ ],+ "type": "object"+ },+ "sha256": {+ "type": "string"+ },+ "transform": {+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context.",+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "filename",+ "provenance"+ ],+ "type": "object"+ },+ "component_create": {+ "description": "Create a native internal component and its first occurrence in a Hybrid design. parent is a component definition; edits to it affect every instance. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused.",+ "properties": {+ "metadata": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "asPurchased": {+ "type": "boolean"+ },+ "description": {+ "type": "string"+ },+ "excludeFromBom": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "partNumber": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ }+ },+ "required": [],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "parent": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "transform": {+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context.",+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name"+ ],+ "type": "object"+ },+ "component_metadata": {+ "description": "Update component-definition name/part number/description and persistent supplier or BOM attributes. Shared occurrences all see this metadata.",+ "properties": {+ "changes": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "asPurchased": {+ "type": "boolean"+ },+ "description": {+ "type": "string"+ },+ "excludeFromBom": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "partNumber": {+ "type": "string"+ },+ "sourceUrl": {+ "type": "string"+ },+ "supplier": {+ "type": "string"+ },+ "supplierPartNumber": {+ "type": "string"+ }+ },+ "required": [],+ "type": "object"+ },+ "component": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "changes"+ ],+ "type": "object"+ },+ "configuration_inspect": {+ "description": "Read the loaded Design configuration graph without activation or recompute: ordinary rows, the separate native reserved test row, all supported typed cells including inactive/test cells, exact controlled-source links and current ordinary row. Unreadable fields and unknown/theme/hierarchical/inserted scopes remain explicit incomplete diagnostics. Configuration lifecycle and configured-design model mutations remain disabled pending native recovery/archive gates.",+ "properties": {},+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "construction_axis": {+ "description": "Create a parametric axis from native references (edge/circular face/two intersecting planes/two distinct points). Root-space external geometry requires exact occurrence context. For cross-component geometry into a non-root target component, creationOccurrence must identify a root-context instance of that target. Returns component-space native axis geometry/health. No direct-only infinite-line or preview snap APIs.",+ "properties": {+ "component": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "creationOccurrence": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entities": {+ "items": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 2,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "type": {+ "enum": [+ "edge",+ "circular_face",+ "two_planes",+ "two_points"+ ]+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "type",+ "entities"+ ],+ "type": "object"+ },+ "entity_delete": {+ "description": "Delete sketch geometry or independently deletable timeline features. Model transaction and semantic recovery cover downstream failures; document and external file deletion are excluded.",+ "properties": {+ "entity": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "entity"+ ],+ "type": "object"+ },+ "exploded_preview_create": {+ "description": "Create finite copied solid-BRep presentation graphics in the exact active blank unsaved Design proven by a committed current-session new_design result. Regenerates the guarded source plan; leaves source active. Exclusive session lease, no source moves/visibility writes, no model transaction, automatic document close, render or capture. Live capability and native backend qualification are separate from durable operation receipts.",+ "properties": {+ "creationOperationId": {+ "type": "string"+ },+ "expectedPlanId": {+ "type": "string"+ },+ "planner": {+ "additionalProperties": false,+ "properties": {+ "playback": {+ "enum": [+ "explode",+ "assemble"+ ],+ "type": "string"+ },+ "sampleTimes": {+ "items": {+ "maximum": 3600,+ "minimum": 0,+ "type": "number"+ },+ "maxItems": 62,+ "type": "array"+ },+ "steps": {+ "items": {+ "additionalProperties": false,+ "properties": {+ "durationSeconds": {+ "exclusiveMinimum": 0,+ "maximum": 3600,+ "type": "number"+ },+ "instruction": {+ "type": "string"+ },+ "moves": {+ "items": {+ "additionalProperties": false,+ "properties": {+ "direction": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "distance": {+ "exclusiveMinimum": 0,+ "maximum": 1000000.0,+ "type": "number"+ },+ "frame": {+ "enum": [+ "world",+ "parent",+ "local"+ ],+ "type": "string"+ },+ "occurrence": {+ "description": "Exact guarded occurrence reference from inspect; every repeated/nested instance is distinct.",+ "type": "object"+ }+ },+ "required": [+ "occurrence",+ "direction",+ "distance",+ "frame"+ ],+ "type": "object"+ },+ "maxItems": 128,+ "minItems": 1,+ "type": "array"+ },+ "title": {+ "type": "string"+ }+ },+ "required": [+ "durationSeconds",+ "moves"+ ],+ "type": "object"+ },+ "maxItems": 32,+ "minItems": 1,+ "type": "array"+ },+ "unit": {+ "enum": [+ "mm"+ ],+ "type": "string"+ }+ },+ "required": [+ "unit",+ "steps"+ ],+ "type": "object"+ },+ "sessionId": {+ "type": "string"+ },+ "sourceTarget": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sourceTarget",+ "creationOperationId",+ "expectedPlanId",+ "planner"+ ],+ "type": "object"+ },+ "exploded_preview_dispose": {+ "description": "Delete only the exact retained graphics tree and leave the blank document open for caller. Supports a narrowly proven cleanup after unresolved terminal storage without rewriting that history; no global Undo or close. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound.",+ "properties": {+ "expectedGeneration": {+ "maximum": 1000000000,+ "minimum": 0,+ "type": "integer"+ },+ "previewId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration"+ ],+ "type": "object"+ },+ "exploded_preview_inspect": {+ "description": "Main-thread live preview ownership/geometry/history observation. Cached operation receipts do not establish current graphics liveness. Old-session history remains separately readable through mech_operation_status; no native reference adoption or lifecycle repair is provided here.",+ "properties": {+ "previewId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "previewId"+ ],+ "type": "object"+ },+ "exploded_preview_sample": {+ "description": "Apply one server-stored absolute sample to exact-owned graphics; preserve active document. Source must remain unchanged. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound.",+ "properties": {+ "expectedGeneration": {+ "maximum": 1000000000,+ "minimum": 0,+ "type": "integer"+ },+ "previewId": {+ "type": "string"+ },+ "sampleIndex": {+ "maximum": 63,+ "minimum": 0,+ "type": "integer"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration",+ "sampleIndex"+ ],+ "type": "object"+ },+ "exploded_preview_show": {+ "description": "Explicitly activate the exact-owned presentation in Fusion; refuse an intervening foreign active document. No OS foreground, camera change or capture. Requires the current controller capability, presentation target, generation and a durable operation ID. Old-session native resources are never rebound.",+ "properties": {+ "expectedGeneration": {+ "maximum": 1000000000,+ "minimum": 0,+ "type": "integer"+ },+ "previewId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "previewId",+ "expectedGeneration"+ ],+ "type": "object"+ },+ "exploded_view_plan": {+ "description": "Read-only source-bound exploded assembly plan. Normalized translation directions in immutable home world/parent/local axes; simultaneous moves within ordered steps. Descendants inherit ancestor translations once, plus own moves. Bounded deterministic sample matrices and exact home reverse endpoint. No native moves, graphics, BOM quantity changes, render/video or storyboard creation. Complete configuration identity required.",+ "properties": {+ "playback": {+ "enum": [+ "explode",+ "assemble"+ ],+ "type": "string"+ },+ "sampleTimes": {+ "items": {+ "maximum": 3600,+ "minimum": 0,+ "type": "number"+ },+ "maxItems": 62,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "steps": {+ "items": {+ "additionalProperties": false,+ "properties": {+ "durationSeconds": {+ "exclusiveMinimum": 0,+ "maximum": 3600,+ "type": "number"+ },+ "instruction": {+ "type": "string"+ },+ "moves": {+ "items": {+ "additionalProperties": false,+ "properties": {+ "direction": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "distance": {+ "exclusiveMinimum": 0,+ "maximum": 1000000.0,+ "type": "number"+ },+ "frame": {+ "enum": [+ "world",+ "parent",+ "local"+ ],+ "type": "string"+ },+ "occurrence": {+ "description": "Exact guarded occurrence reference from inspect; every repeated/nested instance is distinct.",+ "type": "object"+ }+ },+ "required": [+ "occurrence",+ "direction",+ "distance",+ "frame"+ ],+ "type": "object"+ },+ "maxItems": 128,+ "minItems": 1,+ "type": "array"+ },+ "title": {+ "type": "string"+ }+ },+ "required": [+ "durationSeconds",+ "moves"+ ],+ "type": "object"+ },+ "maxItems": 32,+ "minItems": 1,+ "type": "array"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "unit": {+ "enum": [+ "mm"+ ],+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "target",+ "unit",+ "steps"+ ],+ "type": "object"+ },+ "export": {+ "description": "Export entire design using runtime-checked API. Preflight and unique same-filesystem staging preserve the destination on failure; publish only nonempty verified output. Availability differs by Fusion build. STL numeric coordinates use mm.",+ "properties": {+ "format": {+ "default": "step",+ "enum": [+ "step",+ "stl",+ "3mf",+ "f3d",+ "iges",+ "sat",+ "smt",+ "usdz",+ "fbx",+ "skp"+ ]+ },+ "outputPath": {+ "type": "string"+ },+ "overwrite": {+ "default": false,+ "type": "boolean"+ },+ "refinement": {+ "default": "medium",+ "enum": [+ "low",+ "medium",+ "high"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "outputPath"+ ],+ "type": "object"+ },+ "feature_create": {+ "description": "Create a typed native solid feature. All length quantities are mm (or expressions), angles deg. Cut/intersect require explicit participant bodies. Join needs a single existing component body; combine selects multiple bodies explicitly. Feature availability is runtime-checked; missing APIs fail before commit.",+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "direction": {+ "enum": [+ "positive",+ "negative",+ "symmetric"+ ]+ },+ "distance": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "profiles": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "taper": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "extrude"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "distance"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "profiles": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "revolve"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "axis",+ "angle"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "edges": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "radius": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "tangentChain": {+ "type": "boolean"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "fillet"+ ]+ }+ },+ "required": [+ "type",+ "edges",+ "radius"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "distance": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "edges": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "tangentChain": {+ "type": "boolean"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "chamfer"+ ]+ }+ },+ "required": [+ "type",+ "edges",+ "distance"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "faces": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "tangentChain": {+ "type": "boolean"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "thickness": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "type": {+ "enum": [+ "shell"+ ]+ }+ },+ "required": [+ "type",+ "faces",+ "thickness"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "depth": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "diameter": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "points": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "hole"+ ]+ }+ },+ "required": [+ "type",+ "points",+ "diameter",+ "depth",+ "bodies"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "profiles": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "loft"+ ]+ }+ },+ "required": [+ "type",+ "profiles"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "new_body",+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "path": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "profiles": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "sweep"+ ]+ }+ },+ "required": [+ "type",+ "profiles",+ "path"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "body": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keepTools": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operation": {+ "enum": [+ "join",+ "cut",+ "intersect"+ ]+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "tools": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "type": {+ "enum": [+ "combine"+ ]+ }+ },+ "required": [+ "type",+ "body",+ "tools",+ "operation"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "extend": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "split"+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "plane"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "origin": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "translation": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "type": {+ "enum": [+ "move"+ ]+ }+ },+ "required": [+ "type",+ "bodies"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "mirror"+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "plane"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "axis2": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "count": {+ "maximum": 1000,+ "minimum": 2,+ "type": "integer"+ },+ "count2": {+ "maximum": 1000,+ "minimum": 1,+ "type": "integer"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "spacing": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "spacing2": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "rectangular_pattern"+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "axis",+ "count",+ "spacing"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "bodies": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "count": {+ "maximum": 1000,+ "minimum": 2,+ "type": "integer"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "circular_pattern"+ ]+ }+ },+ "required": [+ "type",+ "bodies",+ "axis",+ "count"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "faces": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "tangentChain": {+ "type": "boolean"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "draft"+ ]+ }+ },+ "required": [+ "type",+ "faces",+ "plane",+ "angle"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "designation": {+ "type": "string"+ },+ "faces": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "internal": {+ "type": "boolean"+ },+ "modeled": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "threadClass": {+ "type": "string"+ },+ "threadType": {+ "type": "string"+ },+ "type": {+ "enum": [+ "thread"+ ]+ }+ },+ "required": [+ "type",+ "faces",+ "internal",+ "threadType",+ "designation",+ "threadClass"+ ],+ "type": "object"+ }+ ],+ "properties": {+ "angle": {},+ "axis": {},+ "axis2": {},+ "bodies": {},+ "body": {},+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "count": {},+ "count2": {},+ "depth": {},+ "designation": {},+ "diameter": {},+ "direction": {},+ "distance": {},+ "edges": {},+ "extend": {},+ "faces": {},+ "internal": {},+ "keepTools": {},+ "modeled": {},+ "name": {+ "type": "string"+ },+ "operation": {},+ "origin": {},+ "path": {},+ "plane": {},+ "points": {},+ "profiles": {},+ "radius": {},+ "spacing": {},+ "spacing2": {},+ "tangentChain": {},+ "taper": {},+ "thickness": {},+ "threadClass": {},+ "threadType": {},+ "tools": {},+ "translation": {},+ "type": {+ "enum": [+ "extrude",+ "revolve",+ "fillet",+ "chamfer",+ "shell",+ "hole",+ "loft",+ "sweep",+ "combine",+ "split",+ "move",+ "mirror",+ "rectangular_pattern",+ "circular_pattern",+ "draft",+ "thread"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type"+ ],+ "type": "object"+ },+ "feature_edit": {+ "description": "Rename/suppress a feature or change expressions of model parameters whose createdBy is that exact feature; read health and abort on downstream errors.",+ "properties": {+ "feature": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "parameters": {+ "additionalProperties": {+ "type": "string"+ },+ "type": "object"+ },+ "suppressed": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "feature"+ ],+ "type": "object"+ },+ "geometry_query": {+ "description": "Complete body-scoped topology query, no arbitrary first-match selection. selection one (default) refuses zero/multiple matches; many may return empty. Geometry types are exact native types; size ranges inclusive in mm/mm2, near is minimum distance in the explicit frame; direction is outward planar face normal or linear edge tangent. Returned refs retain instance context even for component-space filters. Exceeding maxResults fails rather than truncating.",+ "properties": {+ "body": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "filters": {+ "additionalProperties": false,+ "properties": {+ "area": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "max": {+ "minimum": 0,+ "type": "number"+ },+ "min": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [],+ "type": "object"+ },+ "direction": {+ "additionalProperties": false,+ "properties": {+ "orientation": {+ "enum": [+ "same",+ "parallel"+ ]+ },+ "tolerance": {+ "maximum": 90,+ "minimum": 0,+ "type": "number"+ },+ "vector": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [+ "vector"+ ],+ "type": "object"+ },+ "length": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "max": {+ "minimum": 0,+ "type": "number"+ },+ "min": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [],+ "type": "object"+ },+ "near": {+ "additionalProperties": false,+ "properties": {+ "point": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "tolerance": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [+ "point",+ "tolerance"+ ],+ "type": "object"+ },+ "radius": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "max": {+ "minimum": 0,+ "type": "number"+ },+ "min": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [],+ "type": "object"+ },+ "type": {+ "enum": [+ "Plane",+ "Cylinder",+ "Cone",+ "Sphere",+ "Torus",+ "NurbsSurface",+ "Line3D",+ "Arc3D",+ "Circle3D",+ "Ellipse3D",+ "EllipticalArc3D",+ "NurbsCurve3D",+ "Point3D",+ "degenerate"+ ]+ }+ },+ "required": [],+ "type": "object"+ },+ "kind": {+ "enum": [+ "faces",+ "edges",+ "vertices"+ ]+ },+ "maxResults": {+ "maximum": 1024,+ "minimum": 1,+ "type": "integer"+ },+ "selection": {+ "enum": [+ "one",+ "many"+ ]+ },+ "space": {+ "enum": [+ "root",+ "component"+ ]+ }+ },+ "required": [+ "op",+ "target",+ "body",+ "kind",+ "space"+ ],+ "type": "object"+ },+ "health": {+ "description": "Read timeline warnings/errors. recompute:true also requires current target revision, sessionId and operationId.",+ "properties": {+ "recompute": {+ "default": false,+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "inspect": {+ "description": "Inspect the active Design without mutation. Returns target identity/revision, native component-space bodies and occurrence transforms, parameters and feature health.",+ "properties": {},+ "required": [+ "op"+ ],+ "type": "object"+ },+ "inspect_entity": {+ "properties": {+ "entity": {+ "description": "Exact reference returned by inspect; resolves uniquely with document/revision/type/occurrence guards.",+ "type": "object"+ }+ },+ "required": [+ "op",+ "target",+ "entity"+ ],+ "type": "object"+ },+ "interference": {+ "description": "Analyze root-context occurrences/body proxies; returns overlap volume mm3 or touching. Does not create interference bodies.",+ "properties": {+ "entities": {+ "items": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 2,+ "type": "array"+ },+ "includeTouching": {+ "type": "boolean"+ },+ "volumeTolerance": {+ "minimum": 0,+ "type": "number"+ }+ },+ "required": [+ "op",+ "target",+ "entities"+ ],+ "type": "object"+ },+ "joint_create": {+ "description": "Create an as-built joint without moving occurrences. Non-rigid motion requires explicit joint geometry; x/y/z are joint-coordinate directions. Limits and drive values use mm/deg. Seven native motion types; ball uses joint Z pitch/X yaw; planar uses native primary slide direction reported on readback. Use regular_joint_create for geometry-solving placement.",+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "rigid"+ ]+ }+ },+ "required": [+ "type"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "revolute"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "sessionId": {+ "type": "string"+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "slider"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "cylindrical"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "pin_slot"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "primarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "primarySlideValue": {+ "type": "number"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "planar"+ ]+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "pitchLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "pitchValue": {+ "type": "number"+ },+ "rollLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rollValue": {+ "type": "number"+ },+ "sessionId": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ },+ "target": {+ "type": "object"+ },+ "type": {+ "enum": [+ "ball"+ ]+ },+ "yawLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "type",+ "geometry"+ ],+ "type": "object"+ }+ ],+ "properties": {+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "occurrenceOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "occurrenceTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "pitchLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "pitchValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "primarySlideValue": {+ "type": "number"+ },+ "rollLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rollValue": {+ "type": "number"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ },+ "yawLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type",+ "occurrenceOne",+ "occurrenceTwo"+ ],+ "type": "object"+ },+ "joint_delete": {+ "description": "Delete one selected Joint or AsBuiltJoint with recovery verification.",+ "properties": {+ "joint": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "type": "object"+ },+ "joint_edit": {+ "description": "Edit joint name/suppression, enabled limits, rest and drive values; wrong motion type or out-of-limit values fail with recovery. For driven root joints, active motion links must preserve their settings and their partner motion must follow the native ratio; a coupling mismatch aborts.",+ "properties": {+ "joint": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "pitchLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "pitchValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "primarySlideValue": {+ "type": "number"+ },+ "rollLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rollValue": {+ "type": "number"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "suppressed": {+ "type": "boolean"+ },+ "yawLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "type": "object"+ },+ "joint_origin_create": {+ "description": "Create a native component-definition joint origin from guarded native geometry, with mm offsets/deg angle and optional straight axis references. Child occurrence origin references are exposed by inspect_entity(occurrence).",+ "properties": {+ "angle": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "component": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "flipped": {+ "type": "boolean"+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "maxLength": 120,+ "minLength": 1,+ "type": "string"+ },+ "offsetX": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetY": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetZ": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "xAxisEntity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "zAxisEntity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "geometry"+ ],+ "type": "object"+ },+ "joint_origin_delete": {+ "description": "Delete a native joint origin; unhealthy downstream joints cause transaction rollback.",+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "origin"+ ],+ "type": "object"+ },+ "joint_origin_edit": {+ "description": "Edit a native joint origin definition; rolls before its timeline item and restores the original marker in finally. Recomputes and verifies attachment, axes, offsets, angle, name and flip.",+ "properties": {+ "angle": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "flipped": {+ "type": "boolean"+ },+ "geometry": {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ "name": {+ "maxLength": 120,+ "minLength": 1,+ "type": "string"+ },+ "offsetX": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetY": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetZ": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "xAxisEntity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "zAxisEntity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "origin"+ ],+ "type": "object"+ },+ "material_assign": {+ "description": "Assign a loaded library or document material to an exact native Component/BRepBody; verify material ID readback with model transaction recovery. Query assembly_properties for resulting mass.",+ "properties": {+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "libraryId": {+ "type": "string"+ },+ "materialId": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "entity",+ "materialId"+ ],+ "type": "object"+ },+ "material_list": {+ "description": "List loaded material libraries and matching materials by stable library/material IDs. Omit libraryId to search the document's materials and then every loaded library; each row names the libraryId material_assign needs (null for the document's own).",+ "properties": {+ "libraryId": {+ "type": "string"+ },+ "limit": {+ "maximum": 1000,+ "minimum": 1,+ "type": "integer"+ },+ "query": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "target"+ ],+ "type": "object"+ },+ "mcmaster_discover": {+ "description": "Discover Fusion native Insert McMaster-Carr command and requirements in the active design.",+ "properties": {},+ "required": [+ "op"+ ],+ "type": "object"+ },+ "mcmaster_open": {+ "description": "Invoke Fusion native McMaster UI in the targeted Hybrid design. Interactive external operation: completion/insert/cancel stays with Fusion; inspect afterward. Idempotency records launch only.",+ "properties": {},+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target"+ ],+ "type": "object"+ },+ "measure": {+ "description": "Native engineering minimum distance or angle in root assembly space. Non-root definition refs require an occurrence context. Distance needs2 entities; angle needs2 lines/planes or3 points (second apex). Returns mm/deg and native witness positions in root mm. witnessValidation identifies native points or native geometry directions used for valid parallel angles with degenerate native points; degenerate point angles and unavailable/inconsistent witnesses fail.",+ "properties": {+ "entities": {+ "items": {+ "description": "Current guarded entity reference; root-space child geometry requires its exact occurrence context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 3,+ "minItems": 2,+ "type": "array"+ },+ "space": {+ "enum": [+ "root"+ ]+ },+ "type": {+ "enum": [+ "distance",+ "angle"+ ]+ }+ },+ "required": [+ "op",+ "target",+ "type",+ "space",+ "entities"+ ],+ "type": "object"+ },+ "motion_link_create": {+ "description": "Create a native motion link between two root-owned joints, or two different motions of jointOne when jointTwo is omitted. Positive ratio magnitudes use mm for translation and deg for rotation; reversed selects opposite direction. Requires the November 2025+ API; no script fallback.",+ "properties": {+ "jointOne": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "jointTwo": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "motionOne": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "motionTwo": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "name": {+ "maxLength": 120,+ "minLength": 1,+ "type": "string"+ },+ "reversed": {+ "type": "boolean"+ },+ "valueOne": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "valueTwo": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "jointOne",+ "motionOne",+ "motionTwo",+ "valueOne",+ "valueTwo"+ ],+ "type": "object"+ },+ "motion_link_delete": {+ "description": "Delete a root-owned motion link under the same transaction/recovery contract.",+ "properties": {+ "link": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "link"+ ],+ "type": "object"+ },+ "motion_link_edit": {+ "description": "Edit a root-owned motion link ratio, degree-of-freedom pair, direction, name or suppression. Joint membership is immutable; recreate the link to change members. Verify exact joint identities, ratio, flags and health.",+ "properties": {+ "link": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "motionOne": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "motionTwo": {+ "enum": [+ "rotation",+ "slide",+ "primarySlide",+ "secondarySlide",+ "pitch",+ "roll",+ "yaw"+ ]+ },+ "name": {+ "maxLength": 120,+ "minLength": 1,+ "type": "string"+ },+ "reversed": {+ "type": "boolean"+ },+ "suppressed": {+ "type": "boolean"+ },+ "valueOne": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "valueTwo": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "link"+ ],+ "type": "object"+ },+ "new_design": {+ "description": "Create an unnamed unsaved parametric hybrid Design; name is retained as requestedName metadata to keep failure recovery free of save dialogs. Optional box dimensions in mm create a driving-parameter fixture. Never edits an existing document.",+ "properties": {+ "box": {+ "additionalProperties": false,+ "properties": {+ "depth": {+ "exclusiveMinimum": 0,+ "type": "number"+ },+ "height": {+ "exclusiveMinimum": 0,+ "type": "number"+ },+ "width": {+ "exclusiveMinimum": 0,+ "type": "number"+ }+ },+ "required": [+ "width",+ "height",+ "depth"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId"+ ],+ "type": "object"+ },+ "occurrence_delete": {+ "description": "Delete one selected occurrence through the shared transaction executor.",+ "properties": {+ "occurrence": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "occurrence"+ ],+ "type": "object"+ },+ "occurrence_insert": {+ "description": "Insert another occurrence of an existing definition; independentCopy:true uses Paste New to create a separate definition. Nested placement is in parent component coordinates. Returns a representative root-context occurrence and all affectedOccurrences when the parent definition is reused.",+ "properties": {+ "component": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "independentCopy": {+ "type": "boolean"+ },+ "parent": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "transform": {+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context.",+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "component"+ ],+ "type": "object"+ },+ "occurrence_update": {+ "description": "Set occurrence pose in its assembly context and capture position; explicit grounding/ungrounding. Grounded or constrained transforms fail with recovery.",+ "properties": {+ "grounded": {+ "type": "boolean"+ },+ "groundedToParent": {+ "type": "boolean"+ },+ "occurrence": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "transform": {+ "additionalProperties": false,+ "description": "Absolute rotation about the given nonzero axis through origin (deg), then translation (mm). Create/insert use parent component coordinates; updates use the selected occurrence assembly context.",+ "properties": {+ "angle": {+ "type": "number"+ },+ "axis": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "position": {+ "items": {+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "occurrence"+ ],+ "type": "object"+ },+ "parameter_batch": {+ "description": "Atomic sequential create/set/delete parameter edits with one recompute+health gate; delete supports user parameters only. Any failure aborts all edits and reports semantic recovery.",+ "properties": {+ "changes": {+ "items": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "action": {+ "enum": [+ "create"+ ]+ },+ "comment": {+ "type": "string"+ },+ "expression": {+ "type": "string"+ },+ "name": {+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm",+ "deg",+ "kg",+ ""+ ]+ }+ },+ "required": [+ "action",+ "name",+ "expression",+ "unit"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "action": {+ "enum": [+ "set"+ ]+ },+ "expression": {+ "type": "string"+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "action",+ "name",+ "expression"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "action": {+ "enum": [+ "delete"+ ]+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "action",+ "name"+ ],+ "type": "object"+ }+ ]+ },+ "maxItems": 128,+ "minItems": 1,+ "type": "array"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "changes"+ ],+ "type": "object"+ },+ "parameter_create": {+ "properties": {+ "comment": {+ "type": "string"+ },+ "expression": {+ "type": "string"+ },+ "name": {+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm",+ "deg",+ "kg",+ ""+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name",+ "expression",+ "unit"+ ],+ "type": "object"+ },+ "parameter_set": {+ "description": "Set an expression, recompute and inspect health. Failure restores old expression and reports a semantic recovery witness; not an atomic transaction.",+ "properties": {+ "expression": {+ "type": "string"+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "name",+ "expression"+ ],+ "type": "object"+ },+ "plane_create": {+ "description": "Create offset plane (offset in mm) or angled plane around axis (angle in deg).",+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "offset": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "offset"+ ]+ }+ },+ "required": [+ "type",+ "offset"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "op": {+ "type": "string"+ },+ "operationId": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "angle"+ ]+ }+ },+ "required": [+ "type",+ "axis",+ "angle"+ ],+ "type": "object"+ }+ ],+ "properties": {+ "angle": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "axis": {+ "oneOf": [+ {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "offset": {+ "oneOf": [+ {+ "type": "number"+ },+ {+ "type": "string"+ }+ ]+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ },+ "type": {+ "enum": [+ "offset",+ "angle"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type"+ ],+ "type": "object"+ },+ "regular_joint_create": {+ "description": "Create a geometry-solving regular joint in the root component using two distinct instance-context attachments or joint origins. All seven native motion families; offsets mm, angle and angular limits deg. Read back freshly resolved attachment graph, native parameters and root-space occurrence placement. Root/non-root sides must be unambiguous; both grounded and ancestor/descendant pairs are refused. Inferred joints are unsupported. Existing pending positions must be captured or reverted first. Before applying drive values, checks native joint-transform anchor offset magnitude/axiality and zero-angle axis collinearity. Native joint matrices have explicit provenance; physical angle/flip sense requires independent occurrence-geometry acceptance.",+ "properties": {+ "angle": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "flipped": {+ "type": "boolean"+ },+ "geometryOne": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "origin"+ ],+ "type": "object"+ }+ ]+ },+ "geometryTwo": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "origin"+ ],+ "type": "object"+ }+ ]+ },+ "locked": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "offset": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "pitchLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "pitchValue": {+ "type": "number"+ },+ "primarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "primarySlideValue": {+ "type": "number"+ },+ "rollLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rollValue": {+ "type": "number"+ },+ "rotationLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "rotationValue": {+ "type": "number"+ },+ "secondarySlideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "secondarySlideValue": {+ "type": "number"+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "slideLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "slideValue": {+ "type": "number"+ },+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ },+ "yawLimits": {+ "additionalProperties": false,+ "minProperties": 1,+ "properties": {+ "maximum": {+ "type": "number"+ },+ "maximumEnabled": {+ "type": "boolean"+ },+ "minimum": {+ "type": "number"+ },+ "minimumEnabled": {+ "type": "boolean"+ },+ "rest": {+ "type": "number"+ },+ "restEnabled": {+ "type": "boolean"+ }+ },+ "required": [],+ "type": "object"+ },+ "yawValue": {+ "type": "number"+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "type",+ "geometryOne",+ "geometryTwo"+ ],+ "type": "object"+ },+ "regular_joint_edit": {+ "description": "Redefine root-owned regular joint geometry, motion family, offsets, angle, flip or lock. Restore timeline in finally and verify after recompute. Use joint_edit for name, suppression, limits and drive values; joint_delete for deletion.",+ "properties": {+ "angle": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "axis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "flipped": {+ "type": "boolean"+ },+ "geometryOne": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "origin"+ ],+ "type": "object"+ }+ ]+ },+ "geometryTwo": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "edge": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "entity": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "keyPoint": {+ "enum": [+ "center",+ "start",+ "middle",+ "end"+ ]+ }+ },+ "required": [+ "entity"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "origin": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "origin"+ ],+ "type": "object"+ }+ ]+ },+ "joint": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "locked": {+ "type": "boolean"+ },+ "offset": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetX": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "offsetY": {+ "description": "Number in mm (angle in deg), or a dimensionally valid Fusion parameter expression.",+ "oneOf": [+ {+ "type": "number"+ },+ {+ "maxLength": 512,+ "minLength": 1,+ "type": "string"+ }+ ]+ },+ "slideAxis": {+ "enum": [+ "x",+ "y",+ "z"+ ]+ },+ "type": {+ "enum": [+ "rigid",+ "revolute",+ "slider",+ "cylindrical",+ "pin_slot",+ "planar",+ "ball"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "joint"+ ],+ "type": "object"+ },+ "render_scene_inspect": {+ "description": "Guarded main-thread read of current viewport camera, render settings and up to 32 environments. Optional detached world-mm camera is constructed and read back without viewport assignment. Missing fields return PARTIAL; observationComplete covers only bounded available scope. sceneWitnessComplete and renderQualified remain false. No rendering, workspace activation or scene writes.",+ "properties": {+ "camera": {+ "oneOf": [+ {+ "additionalProperties": false,+ "properties": {+ "extents": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": 1e-06,+ "type": "number"+ },+ "maxItems": 2,+ "minItems": 2,+ "type": "array"+ },+ "eye": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "space": {+ "enum": [+ "world"+ ],+ "type": "string"+ },+ "target": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "type": {+ "enum": [+ "orthographic"+ ],+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm"+ ],+ "type": "string"+ },+ "upVector": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [+ "type",+ "space",+ "unit",+ "eye",+ "target",+ "upVector",+ "extents"+ ],+ "type": "object"+ },+ {+ "additionalProperties": false,+ "properties": {+ "angleUnit": {+ "enum": [+ "deg"+ ],+ "type": "string"+ },+ "eye": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "perspectiveAngle": {+ "exclusiveMinimum": 0,+ "maximum": 179.999999999,+ "type": "number"+ },+ "space": {+ "enum": [+ "world"+ ],+ "type": "string"+ },+ "target": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ },+ "type": {+ "enum": [+ "perspective"+ ],+ "type": "string"+ },+ "unit": {+ "enum": [+ "mm"+ ],+ "type": "string"+ },+ "upVector": {+ "items": {+ "maximum": 1000000000.0,+ "minimum": -1000000000.0,+ "type": "number"+ },+ "maxItems": 3,+ "minItems": 3,+ "type": "array"+ }+ },+ "required": [+ "type",+ "space",+ "unit",+ "eye",+ "target",+ "upVector",+ "perspectiveAngle",+ "angleUnit"+ ],+ "type": "object"+ }+ ]+ },+ "maxEnvironments": {+ "maximum": 32,+ "minimum": 0,+ "type": "integer"+ },+ "sessionId": {+ "type": "string"+ },+ "target": {+ "additionalProperties": false,+ "properties": {+ "documentId": {+ "type": "string"+ },+ "revision": {+ "type": "string"+ }+ },+ "required": [+ "documentId",+ "revision"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "sessionId",+ "target"+ ],+ "type": "object"+ },+ "rigid_group_create": {+ "description": "Create a native rigid group from exact occurrence instances; includeChildren defaults true.",+ "properties": {+ "includeChildren": {+ "type": "boolean"+ },+ "name": {+ "type": "string"+ },+ "occurrences": {+ "items": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 2,+ "type": "array"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "occurrences"+ ],+ "type": "object"+ },+ "rigid_group_delete": {+ "description": "Delete an exact rigid group with transaction recovery.",+ "properties": {+ "group": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "group"+ ],+ "type": "object"+ },+ "rigid_group_edit": {+ "description": "Rename or suppress an exact rigid group with transaction recovery.",+ "properties": {+ "group": {+ "description": "Guarded reference from inspect in this document/revision; occurrence references retain instance context.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "suppressed": {+ "type": "boolean"+ }+ },+ "required": [+ "op",+ "target",+ "sessionId",+ "operationId",+ "group"+ ],+ "type": "object"+ },+ "sketch_constraint": {+ "description": "Constrain entities in one sketch. midpoint order is point,line; symmetry order entity1,entity2,symmetryLine. fix sets one entity fixed.",+ "properties": {+ "entities": {+ "items": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "maxItems": 256,+ "minItems": 1,+ "type": "array"+ },+ "sketch": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "type": {+ "enum": [+ "horizontal",+ "vertical",+ "coincident",+ "parallel",+ "perpendicular",+ "tangent",+ "equal",+ "concentric",+ "collinear",+ "midpoint",+ "symmetry",+ "fix"+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target",+ "sketch",+ "type",+ "entities"+ ],+ "type": "object"+ },+ "sketch_create": {+ "description": "Create a native component-definition sketch on an origin/construction plane or planar face. Coordinates in subsequent geometry are sketch-space mm.",+ "properties": {+ "component": {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ },+ "name": {+ "type": "string"+ },+ "plane": {+ "oneOf": [+ {+ "enum": [+ "xy",+ "xz",+ "yz"+ ]+ },+ {+ "description": "Reference returned by inspect/inspect_entity in this document revision; native component-definition references for authoring.",+ "required": [+ "documentId",+ "revision",+ "objectType"+ ],+ "type": "object"+ }+ ]+ }+ },+ "required": [+ "op",+ "sessionId",+ "operationId",+ "target"+ ],+ "type": "object"+ },+ "sketch_curve_edit": {+ "description": "Edit one native sketch curve with sketch-space mm pick. trim removes the nearest segment; extend chooses nearest endpoint; split requires an interior on-curve point; break uses neighboring intersections. Fresh replacement refs are returned; original may be deleted. In-place extend returns the original with a fresh ref only after its geometry change survives recompute. No change or empty break/split fails. Full trim deletion requires allowDelete true. Linked/derived/deferred-compute geometry refused.",+ "properties": {+ "action": {+ "enum": [+ "trim",+ "extend",+ "split",+ "break"+ ]+ },+ "allowDelete": {+ "type": "boolean"+ },+ "createConstraints": {+ "type": "boolean"+ },+ "curve": {