devagent-physical-engine 1.8.2__tar.gz → 1.8.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. {devagent_physical_engine-1.8.2/src/devagent_physical_engine.egg-info → devagent_physical_engine-1.8.5}/PKG-INFO +40 -6
  2. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/README.md +39 -5
  3. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/pyproject.toml +2 -2
  4. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/__init__.py +1 -1
  5. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/workpiece_scene_probe.py +137 -31
  6. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5/src/devagent_physical_engine.egg-info}/PKG-INFO +40 -6
  7. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine.egg-info/SOURCES.txt +4 -1
  8. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_industrial_motion_v170.py +2 -2
  9. devagent_physical_engine-1.8.5/tests/test_package_metadata_v184.py +23 -0
  10. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_transfer_place_diagnostic_v172.py +5 -3
  11. devagent_physical_engine-1.8.5/tests/test_workpiece_attach_semantics_v183.py +38 -0
  12. devagent_physical_engine-1.8.5/tests/test_workpiece_scene_probe_v185.py +52 -0
  13. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/NOTICE +0 -0
  14. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/setup.cfg +0 -0
  15. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/__init__.py +0 -0
  16. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/compiler.py +0 -0
  17. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/contracts.py +0 -0
  18. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/coordinator.py +0 -0
  19. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/critic.py +0 -0
  20. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/evidence.py +0 -0
  21. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/interpreter.py +0 -0
  22. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/planner.py +0 -0
  23. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/recovery.py +0 -0
  24. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/routing.py +0 -0
  25. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/runtime.py +0 -0
  26. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/semantic.py +0 -0
  27. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/agent/structured.py +0 -0
  28. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ai_cli.py +0 -0
  29. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/cli.py +0 -0
  30. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/commercial.py +0 -0
  31. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/commercial_campaign.py +0 -0
  32. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/commercial_cli.py +0 -0
  33. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/commercial_importer.py +0 -0
  34. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/commercial_intelligence.py +0 -0
  35. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/commercial_readiness.py +0 -0
  36. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/commercial_repository.py +0 -0
  37. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/continuous_clearance.py +0 -0
  38. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/controller_runtime_hotfix_v181.py +0 -0
  39. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/doctor.py +0 -0
  40. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/engineering_agent.py +0 -0
  41. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/engineering_request.py +0 -0
  42. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/evidence_replay.py +0 -0
  43. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/evidence_trust.py +0 -0
  44. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/execution.py +0 -0
  45. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/industrial_motion_hotfix_v170.py +0 -0
  46. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/industrial_motion_v170.py +0 -0
  47. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/industrial_motion_v182.py +0 -0
  48. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/models.py +0 -0
  49. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/one_command.py +0 -0
  50. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/one_command_runtime.py +0 -0
  51. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/one_command_v15.py +0 -0
  52. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/one_command_v16.py +0 -0
  53. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/operating_envelope.py +0 -0
  54. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/__init__.py +0 -0
  55. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/benchmark.py +0 -0
  56. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/candidates.py +0 -0
  57. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/contracts.py +0 -0
  58. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/evaluator.py +0 -0
  59. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/evidence.py +0 -0
  60. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/experience.py +0 -0
  61. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/measured.py +0 -0
  62. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/optimizer.py +0 -0
  63. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/optimization/orchestrator.py +0 -0
  64. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/physical_campaign.py +0 -0
  65. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/physical_evidence.py +0 -0
  66. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/physical_motion.py +0 -0
  67. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/planning.py +0 -0
  68. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/pose_math.py +0 -0
  69. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/preexecution_contract.py +0 -0
  70. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/production_authority.py +0 -0
  71. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/production_grade.py +0 -0
  72. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/project.py +0 -0
  73. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/provider_adapters/__init__.py +0 -0
  74. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/provider_adapters/anthropic.py +0 -0
  75. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/provider_adapters/common.py +0 -0
  76. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/provider_adapters/factory.py +0 -0
  77. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/provider_adapters/gemini.py +0 -0
  78. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/provider_adapters/openai.py +0 -0
  79. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/provider_criterion.py +0 -0
  80. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/provider_qualification.py +0 -0
  81. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/providers.py +0 -0
  82. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/qualification.py +0 -0
  83. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/qualification_cli.py +0 -0
  84. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/qualification_harness.py +0 -0
  85. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/reference_workcell.py +0 -0
  86. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/report_test_execution.py +0 -0
  87. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/report_test_execution_strict.py +0 -0
  88. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/robot_platform.py +0 -0
  89. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/robot_support_v18.py +0 -0
  90. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/robots.py +0 -0
  91. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/__init__.py +0 -0
  92. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/abb_irb1200.py +0 -0
  93. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/abb_irb1200_preview.py +0 -0
  94. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/acceptance.py +0 -0
  95. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/commands.py +0 -0
  96. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/continuous_clearance_probe.py +0 -0
  97. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/controller_action_probe.py +0 -0
  98. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/destination_support_contact_v182.py +0 -0
  99. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/doctor.py +0 -0
  100. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/fk_pose.py +0 -0
  101. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/fk_probe.py +0 -0
  102. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/frame_alignment.py +0 -0
  103. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/gazebo_cli.py +0 -0
  104. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/gazebo_pose.py +0 -0
  105. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/gazebo_world.py +0 -0
  106. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/industrial_sequence_planner_v170.py +0 -0
  107. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/industrial_sequence_planner_v171.py +0 -0
  108. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/industrial_sequence_planner_v172.py +0 -0
  109. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/industrial_sequence_planner_v182.py +0 -0
  110. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/joint_state_probe.py +0 -0
  111. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/joint_state_recorder.py +0 -0
  112. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/measured_motion.py +0 -0
  113. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/moveit_joint_plan_probe.py +0 -0
  114. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/moveit_joint_plan_v161_probe.py +0 -0
  115. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/moveit_scene.py +0 -0
  116. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/moveit_task_planner.py +0 -0
  117. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/pilz_sequence_plan_probe.py +0 -0
  118. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/pilz_sequence_plan_probe_v182.py +0 -0
  119. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/preexecution.py +0 -0
  120. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/qualification.py +0 -0
  121. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/qualification_v10.py +0 -0
  122. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/qualification_v11.py +0 -0
  123. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/qualification_v12.py +0 -0
  124. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/report_test_visualization.py +0 -0
  125. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/report_test_visualization_v170.py +0 -0
  126. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/scene_probe.py +0 -0
  127. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/state_validity_probe.py +0 -0
  128. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/support_contact_acm_probe_v170.py +0 -0
  129. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/support_contact_lease_v170.py +0 -0
  130. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/support_contact_runner_v161.py +0 -0
  131. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/support_contact_v161.py +0 -0
  132. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/task_adapter.py +0 -0
  133. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/task_visualization.py +0 -0
  134. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/tf_probe.py +0 -0
  135. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/tool_scene.py +0 -0
  136. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/tool_scene_probe.py +0 -0
  137. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/trajectory.py +0 -0
  138. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/trajectory_action_probe.py +0 -0
  139. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/trajectory_tracking.py +0 -0
  140. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/ur5e.py +0 -0
  141. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/ur5e_adapter.py +0 -0
  142. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/ur5e_v10_adapter.py +0 -0
  143. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/workpiece_follow.py +0 -0
  144. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/workpiece_gazebo_follower.py +0 -0
  145. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/ros2/workpiece_scene.py +0 -0
  146. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/selective_verification.py +0 -0
  147. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/selective_verification_v16.py +0 -0
  148. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/setup_profile.py +0 -0
  149. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/sim_real_qualification.py +0 -0
  150. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/simulation.py +0 -0
  151. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/simulation_platform.py +0 -0
  152. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/smooth_motion_hotfix_v162.py +0 -0
  153. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/support_contact_hotfix_v161.py +0 -0
  154. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/tool_collision.py +0 -0
  155. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/trajectory_qualification.py +0 -0
  156. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/twin.py +0 -0
  157. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/twin_builder.py +0 -0
  158. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/twin_codec.py +0 -0
  159. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/twin_materialization.py +0 -0
  160. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/user_cli.py +0 -0
  161. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/user_cli_v15.py +0 -0
  162. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/user_cli_v16.py +0 -0
  163. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/user_cli_v18.py +0 -0
  164. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/verification.py +0 -0
  165. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine/workpiece_lifecycle.py +0 -0
  166. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine.egg-info/dependency_links.txt +0 -0
  167. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine.egg-info/entry_points.txt +0 -0
  168. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine.egg-info/requires.txt +0 -0
  169. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/src/devagent_physical_engine.egg-info/top_level.txt +0 -0
  170. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_abb_irb1200_visual_v18.py +0 -0
  171. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_agent_core.py +0 -0
  172. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_agent_semantic_hardening.py +0 -0
  173. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_ai_cli.py +0 -0
  174. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_ai_cli_v07.py +0 -0
  175. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_commercial_importer_v1.py +0 -0
  176. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_commercial_intelligence_v15.py +0 -0
  177. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_commercial_readiness_v1.py +0 -0
  178. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_commercial_v1.py +0 -0
  179. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_continuous_clearance_v011.py +0 -0
  180. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_controller_runtime_hotfix_v181.py +0 -0
  181. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_core.py +0 -0
  182. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_destination_support_contact_v182.py +0 -0
  183. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_engineering_request_v07.py +0 -0
  184. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_evidence_replay_v12.py +0 -0
  185. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_evidence_trust_coverage_v12.py +0 -0
  186. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_evidence_trust_transport_v12.py +0 -0
  187. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_evidence_trust_v12.py +0 -0
  188. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_evidence_viewer_palette_v130.py +0 -0
  189. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_gazebo_cli_v0122.py +0 -0
  190. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_golden_abb_runner_v171.py +0 -0
  191. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_golden_abb_showcase.py +0 -0
  192. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_interpreter_profiles_v08.py +0 -0
  193. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_joint_state_probe_v0125.py +0 -0
  194. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_live_provider_qualification.py +0 -0
  195. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_measured_motion_v10.py +0 -0
  196. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_moveit_planning_diagnostics_v0125.py +0 -0
  197. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_moveit_scene_v0123.py +0 -0
  198. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_natural_language_agent_v07.py +0 -0
  199. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_one_command_hardening_v13.py +0 -0
  200. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_one_command_pose_hardening_v15.py +0 -0
  201. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_one_command_regression_v141.py +0 -0
  202. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_one_command_v13.py +0 -0
  203. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_openai_strict_schema_v0113.py +0 -0
  204. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_optimization_v05.py +0 -0
  205. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_physical_campaign_v09.py +0 -0
  206. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_physical_evidence_v09.py +0 -0
  207. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_physical_motion_v09.py +0 -0
  208. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_planning_fallback_v012.py +0 -0
  209. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_pose_math_v0125.py +0 -0
  210. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_preexecution_contract_v10.py +0 -0
  211. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_production_authority_v11.py +0 -0
  212. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_production_grade_coverage_v11.py +0 -0
  213. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_production_grade_v11.py +0 -0
  214. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_project_spine_v013.py +0 -0
  215. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_provider_and_qualification.py +0 -0
  216. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_provider_criterion_v15.py +0 -0
  217. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_qualification_cli_lifecycle_v06.py +0 -0
  218. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_qualification_harness_v06.py +0 -0
  219. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_readme_pypi.py +0 -0
  220. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_real_provider_adapters.py +0 -0
  221. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_reference_workcell_ros_frames_v0125.py +0 -0
  222. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_reference_workcell_v012.py +0 -0
  223. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_report_test_contract_coverage_v16.py +0 -0
  224. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_report_test_lineage_v16.py +0 -0
  225. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_report_test_visual_execution_v16.py +0 -0
  226. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_robot_platform_v08.py +0 -0
  227. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_ros2_production_receipts_v011.py +0 -0
  228. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_ros2_simulation.py +0 -0
  229. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_ros_trajectory_v09.py +0 -0
  230. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_selective_verification_v14.py +0 -0
  231. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_setup_profile.py +0 -0
  232. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_sim_real_qualification_v011.py +0 -0
  233. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_simulation_platform_v08.py +0 -0
  234. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_smooth_motion_hotfix_v162.py +0 -0
  235. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_support_contact_hotfix_v161.py +0 -0
  236. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_tool_collision_v011.py +0 -0
  237. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_tool_scene_v0124.py +0 -0
  238. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_trajectory_action_probe_v0125.py +0 -0
  239. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_trajectory_qualification_v09.py +0 -0
  240. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_trajectory_tracking_v0125.py +0 -0
  241. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_twin_materialization_v10.py +0 -0
  242. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_twin_v08.py +0 -0
  243. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_ur5e_adapter_scopes_v09.py +0 -0
  244. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_ur5e_v10_adapter_scopes.py +0 -0
  245. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_user_cli_interactive_v131.py +0 -0
  246. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_user_cli_progress_v131.py +0 -0
  247. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_user_cli_report_visual_v16.py +0 -0
  248. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_user_cli_selective_v14.py +0 -0
  249. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_user_cli_v15.py +0 -0
  250. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_user_cli_v15_hotfix_inheritance.py +0 -0
  251. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_v052_stabilization.py +0 -0
  252. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_v08_catalog_mapping_regression.py +0 -0
  253. {devagent_physical_engine-1.8.2 → devagent_physical_engine-1.8.5}/tests/test_workpiece_lifecycle_v011.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devagent-physical-engine
3
- Version: 1.8.2
4
- Summary: Production-grade verification-first robotics evidence runtime with one-command customer intake, truthful robot support tiers, qualified UR5e report-test Gazebo execution, direct ROS controller/action readiness, scoped destination-support place contact, experimental ABB IRB1200 Gazebo/MoveIt/RViz visualization, transfer/place diagnostics, requirement-driven test generation, selective verification, deterministic Twin lineage, measured physical verification, replay inspection, regression, and FAT evidence.
3
+ Version: 1.8.5
4
+ Summary: Verification-first robotics commissioning runtime with exact-Twin evidence, qualified UR5e Gazebo/MoveIt report execution, truthful robot support tiers, selective verification, replay, regression, and FAT reporting.
5
5
  Author: Tom Ha
6
6
  License: All Rights Reserved
7
7
  Project-URL: Homepage, https://github.com/tomha85/devagent-physical-engine
@@ -76,7 +76,7 @@ regression + FAT report + Evidence Viewer
76
76
  Python 3.11+ is required.
77
77
 
78
78
  ```bash
79
- python -m pip install --upgrade devagent-physical-engine==1.8.2
79
+ python -m pip install --upgrade devagent-physical-engine==1.8.5
80
80
  ```
81
81
 
82
82
  Verify the installed package:
@@ -88,7 +88,7 @@ python -c "import devagent_physical_engine as d; print(d.__version__)"
88
88
  Expected:
89
89
 
90
90
  ```text
91
- 1.8.2
91
+ 1.8.5
92
92
  ```
93
93
 
94
94
  Run the normal customer workflow:
@@ -394,6 +394,38 @@ The v1.8.2 place receipt records the exact destination support id, workpiece id,
394
394
 
395
395
  The release does **not** change real-execution authority, site qualification, target poses, conservative motion scales, or the rule that every motion group must preflight successfully before execution begins.
396
396
 
397
+ ## v1.8.3 synchronous verified workpiece attach
398
+
399
+ A clean v1.8.2 target-stack run proved all five industrial preflight groups and actual `approach_pick` execution, then failed closed at GRASP with `workpiece_lifecycle_failed:apply_planning_scene_rejected`.
400
+
401
+ The attach request previously combined an `AttachedCollisionObject.ADD` with a redundant same-ID world `CollisionObject.REMOVE`. MoveIt applies attached-object state first, which already transitions the same-ID object out of the world. Processing the later redundant world REMOVE can therefore make synchronous `/apply_planning_scene` report failure even though the attachment transition was already attempted.
402
+
403
+ v1.8.3 makes the attach transition service-safe and deterministic: the request contains only the authoritative attached-object ADD. DevAgent then synchronously reads the PlanningScene back and requires all of the following before motion may continue:
404
+
405
+ ```text
406
+ workpiece absent from world
407
+ exactly one attached object with the expected id
408
+ attached link == verified tool mount link
409
+ attached primitive geometry is valid
410
+ attachment pose/geometry hash matches the expected attachment hash
411
+ ```
412
+
413
+ Detach semantics remain explicit attached-object REMOVE plus world-object ADD at the verified placement pose. No collision rule, tool/workpiece geometry, TCP, touch link, target pose, motion speed, planner, site-qualification boundary, or real-execution authority is relaxed.
414
+
415
+ ## v1.8.4 PyPI metadata compliance hotfix
416
+
417
+ v1.8.4 carries the exact v1.8.3 runtime semantics and shortens only the package Core Metadata `Summary` so it satisfies PyPI's 512-character limit. A regression test now parses `pyproject.toml` and rejects an empty or over-limit project description before release.
418
+
419
+ This metadata hotfix does not change the UR5e attach/detach lifecycle, collision policy, motion planning, Twin lineage, site-qualification boundary, or real-execution authority.
420
+
421
+ ## v1.8.5 quaternion-invariant verified workpiece attach
422
+
423
+ A clean v1.8.4 UR5e target-stack run proved that synchronous `/apply_planning_scene` attachment now succeeds, world/attached state reaches strict read-back, and the remaining GRASP blocker is `workpiece_attachment_hash_mismatch`.
424
+
425
+ MoveIt round-trips attached-object rotations through Eigen. Unit quaternions `q` and `-q` represent the same physical rotation, but a raw component hash treats those two equivalent representations as different. v1.8.5 canonicalizes equivalent quaternion signs before workpiece attachment hashing using the same deterministic positive-`w` / first-nonzero-component rule already used by the qualified tool-scene path.
426
+
427
+ Verification remains strict for attachment identity, link, geometry contract, position, and physical rotation. This is not a tolerance-based PASS and does not change target geometry, TCP, collision policy, planner types, motion speeds, controller binding, Twin lineage, site qualification, or real-execution authority. If a hash mismatch remains, DevAgent records expected and observed hashes plus canonical expected/object/primitive/effective attachment poses for target diagnosis.
428
+
397
429
  ## Qualified visual-execution scope
398
430
 
399
431
  Measured report-test execution remains intentionally narrow:
@@ -594,9 +626,9 @@ devagent-physical-ai optional provider-backed engineering front end
594
626
 
595
627
  # Project status
596
628
 
597
- **v1.8.2 — Production/Stable software workflow**
629
+ **v1.8.5 — Production/Stable software workflow**
598
630
 
599
- v1.8.2 retains the v1.8.1 direct UR5e controller/action binding, v1.8 truthful robot support tiers, and ABB IRB1200 experimental visual runtime. It additionally splits UR5e transfer and placement at a hard motion boundary and scopes destination-support contact to the single zero-blend LIN place planning request, using a request-local full-ACM diff for exactly the attached-workpiece ↔ destination-support pair.
631
+ v1.8.5 retains v1.8.4 PyPI metadata compliance, v1.8.3 synchronous service-safe workpiece attachment, v1.8.2 scoped destination-support place contact, v1.8.1 direct UR5e controller/action binding, truthful robot support tiers, and the ABB IRB1200 experimental visual runtime. It makes strict workpiece attachment verification invariant to the mathematically equivalent quaternion representations `q` and `-q` and persists exact pose/hash diagnostics for any remaining mismatch.
600
632
 
601
633
  Production-oriented software capabilities include:
602
634
 
@@ -611,6 +643,8 @@ exact Requirement → Mapping → Verification Plan → Case provenance
611
643
  truthful robot support tiers
612
644
  qualified UR5e report-test visual execution contract
613
645
  direct UR5e controller-manager/action readiness binding
646
+ synchronous verified workpiece attach/detach lifecycle
647
+ quaternion-invariant strict attachment hashing
614
648
  scoped destination-support place contact
615
649
  experimental exact ABB IRB1200 Gazebo/MoveIt/RViz preview
616
650
  industrial Pilz PTP / LIN planning
@@ -35,7 +35,7 @@ regression + FAT report + Evidence Viewer
35
35
  Python 3.11+ is required.
36
36
 
37
37
  ```bash
38
- python -m pip install --upgrade devagent-physical-engine==1.8.2
38
+ python -m pip install --upgrade devagent-physical-engine==1.8.5
39
39
  ```
40
40
 
41
41
  Verify the installed package:
@@ -47,7 +47,7 @@ python -c "import devagent_physical_engine as d; print(d.__version__)"
47
47
  Expected:
48
48
 
49
49
  ```text
50
- 1.8.2
50
+ 1.8.5
51
51
  ```
52
52
 
53
53
  Run the normal customer workflow:
@@ -353,6 +353,38 @@ The v1.8.2 place receipt records the exact destination support id, workpiece id,
353
353
 
354
354
  The release does **not** change real-execution authority, site qualification, target poses, conservative motion scales, or the rule that every motion group must preflight successfully before execution begins.
355
355
 
356
+ ## v1.8.3 synchronous verified workpiece attach
357
+
358
+ A clean v1.8.2 target-stack run proved all five industrial preflight groups and actual `approach_pick` execution, then failed closed at GRASP with `workpiece_lifecycle_failed:apply_planning_scene_rejected`.
359
+
360
+ The attach request previously combined an `AttachedCollisionObject.ADD` with a redundant same-ID world `CollisionObject.REMOVE`. MoveIt applies attached-object state first, which already transitions the same-ID object out of the world. Processing the later redundant world REMOVE can therefore make synchronous `/apply_planning_scene` report failure even though the attachment transition was already attempted.
361
+
362
+ v1.8.3 makes the attach transition service-safe and deterministic: the request contains only the authoritative attached-object ADD. DevAgent then synchronously reads the PlanningScene back and requires all of the following before motion may continue:
363
+
364
+ ```text
365
+ workpiece absent from world
366
+ exactly one attached object with the expected id
367
+ attached link == verified tool mount link
368
+ attached primitive geometry is valid
369
+ attachment pose/geometry hash matches the expected attachment hash
370
+ ```
371
+
372
+ Detach semantics remain explicit attached-object REMOVE plus world-object ADD at the verified placement pose. No collision rule, tool/workpiece geometry, TCP, touch link, target pose, motion speed, planner, site-qualification boundary, or real-execution authority is relaxed.
373
+
374
+ ## v1.8.4 PyPI metadata compliance hotfix
375
+
376
+ v1.8.4 carries the exact v1.8.3 runtime semantics and shortens only the package Core Metadata `Summary` so it satisfies PyPI's 512-character limit. A regression test now parses `pyproject.toml` and rejects an empty or over-limit project description before release.
377
+
378
+ This metadata hotfix does not change the UR5e attach/detach lifecycle, collision policy, motion planning, Twin lineage, site-qualification boundary, or real-execution authority.
379
+
380
+ ## v1.8.5 quaternion-invariant verified workpiece attach
381
+
382
+ A clean v1.8.4 UR5e target-stack run proved that synchronous `/apply_planning_scene` attachment now succeeds, world/attached state reaches strict read-back, and the remaining GRASP blocker is `workpiece_attachment_hash_mismatch`.
383
+
384
+ MoveIt round-trips attached-object rotations through Eigen. Unit quaternions `q` and `-q` represent the same physical rotation, but a raw component hash treats those two equivalent representations as different. v1.8.5 canonicalizes equivalent quaternion signs before workpiece attachment hashing using the same deterministic positive-`w` / first-nonzero-component rule already used by the qualified tool-scene path.
385
+
386
+ Verification remains strict for attachment identity, link, geometry contract, position, and physical rotation. This is not a tolerance-based PASS and does not change target geometry, TCP, collision policy, planner types, motion speeds, controller binding, Twin lineage, site qualification, or real-execution authority. If a hash mismatch remains, DevAgent records expected and observed hashes plus canonical expected/object/primitive/effective attachment poses for target diagnosis.
387
+
356
388
  ## Qualified visual-execution scope
357
389
 
358
390
  Measured report-test execution remains intentionally narrow:
@@ -553,9 +585,9 @@ devagent-physical-ai optional provider-backed engineering front end
553
585
 
554
586
  # Project status
555
587
 
556
- **v1.8.2 — Production/Stable software workflow**
588
+ **v1.8.5 — Production/Stable software workflow**
557
589
 
558
- v1.8.2 retains the v1.8.1 direct UR5e controller/action binding, v1.8 truthful robot support tiers, and ABB IRB1200 experimental visual runtime. It additionally splits UR5e transfer and placement at a hard motion boundary and scopes destination-support contact to the single zero-blend LIN place planning request, using a request-local full-ACM diff for exactly the attached-workpiece ↔ destination-support pair.
590
+ v1.8.5 retains v1.8.4 PyPI metadata compliance, v1.8.3 synchronous service-safe workpiece attachment, v1.8.2 scoped destination-support place contact, v1.8.1 direct UR5e controller/action binding, truthful robot support tiers, and the ABB IRB1200 experimental visual runtime. It makes strict workpiece attachment verification invariant to the mathematically equivalent quaternion representations `q` and `-q` and persists exact pose/hash diagnostics for any remaining mismatch.
559
591
 
560
592
  Production-oriented software capabilities include:
561
593
 
@@ -570,6 +602,8 @@ exact Requirement → Mapping → Verification Plan → Case provenance
570
602
  truthful robot support tiers
571
603
  qualified UR5e report-test visual execution contract
572
604
  direct UR5e controller-manager/action readiness binding
605
+ synchronous verified workpiece attach/detach lifecycle
606
+ quaternion-invariant strict attachment hashing
573
607
  scoped destination-support place contact
574
608
  experimental exact ABB IRB1200 Gazebo/MoveIt/RViz preview
575
609
  industrial Pilz PTP / LIN planning
@@ -623,4 +657,4 @@ Original creator: Tom Ha
623
657
  Original project: https://github.com/tomha85/devagent-physical-engine
624
658
  All rights reserved.
625
659
 
626
- See repository `LICENSE`, `NOTICE`, and `COPYRIGHT` for complete ownership and usage terms.
660
+ See repository `LICENSE`, `NOTICE`, and `COPYRIGHT` for complete ownership and usage terms.
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devagent-physical-engine"
7
- version = "1.8.2"
8
- description = "Production-grade verification-first robotics evidence runtime with one-command customer intake, truthful robot support tiers, qualified UR5e report-test Gazebo execution, direct ROS controller/action readiness, scoped destination-support place contact, experimental ABB IRB1200 Gazebo/MoveIt/RViz visualization, transfer/place diagnostics, requirement-driven test generation, selective verification, deterministic Twin lineage, measured physical verification, replay inspection, regression, and FAT evidence."
7
+ version = "1.8.5"
8
+ description = "Verification-first robotics commissioning runtime with exact-Twin evidence, qualified UR5e Gazebo/MoveIt report execution, truthful robot support tiers, selective verification, replay, regression, and FAT reporting."
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.11"
11
11
  dependencies = ["PyYAML>=6.0,<7"]
@@ -219,4 +219,4 @@ install_industrial_motion_v170()
219
219
  install_controller_runtime_hotfix_v181()
220
220
  install_one_command_runtime()
221
221
 
222
- __version__ = "1.8.2"
222
+ __version__ = "1.8.5"
@@ -26,6 +26,24 @@ def _qnorm(q):
26
26
  return tuple(float(value) / norm for value in q)
27
27
 
28
28
 
29
+ def _qcanonical(q):
30
+ """Canonicalize equivalent q/-q rotations to one deterministic form."""
31
+
32
+ quaternion = _qnorm(q)
33
+ # A quaternion and its negation represent the same physical rotation. MoveIt
34
+ # round-trips poses through Eigen, which may legitimately return the opposite
35
+ # quaternion sign. Hash the rotation, not an arbitrary representation of it.
36
+ # Prefer positive w; for exact 180-degree rotations use the first non-zero xyz
37
+ # component as the deterministic sign discriminator.
38
+ for component in (quaternion[3], quaternion[0], quaternion[1], quaternion[2]):
39
+ if abs(component) <= 1e-15:
40
+ continue
41
+ if component < 0.0:
42
+ quaternion = tuple(-value for value in quaternion)
43
+ break
44
+ return tuple(_canon_float(value) for value in quaternion)
45
+
46
+
29
47
  def _qmul(first, second):
30
48
  ax, ay, az, aw = first
31
49
  bx, by, bz, bw = second
@@ -60,10 +78,22 @@ def _compose(first_position, first_q, second_position, second_q):
60
78
  def _pose_from_msg(pose):
61
79
  return (
62
80
  (float(pose.position.x), float(pose.position.y), float(pose.position.z)),
63
- (float(pose.orientation.x), float(pose.orientation.y), float(pose.orientation.z), float(pose.orientation.w)),
81
+ (
82
+ float(pose.orientation.x),
83
+ float(pose.orientation.y),
84
+ float(pose.orientation.z),
85
+ float(pose.orientation.w),
86
+ ),
64
87
  )
65
88
 
66
89
 
90
+ def _pose_payload(position, quaternion) -> dict[str, list[float]]:
91
+ return {
92
+ "position_m": [_canon_float(value) for value in position],
93
+ "quaternion_xyzw": list(_qcanonical(quaternion)),
94
+ }
95
+
96
+
67
97
  def _pose_to_msg(position, quaternion, Pose):
68
98
  pose = Pose()
69
99
  pose.position.x, pose.position.y, pose.position.z = (float(value) for value in position)
@@ -73,14 +103,19 @@ def _pose_to_msg(position, quaternion, Pose):
73
103
  return pose
74
104
 
75
105
 
76
- def _canonical_attachment(link_name: str, item: dict[str, Any], position, quaternion) -> dict[str, Any]:
106
+ def _canonical_attachment(
107
+ link_name: str,
108
+ item: dict[str, Any],
109
+ position,
110
+ quaternion,
111
+ ) -> dict[str, Any]:
77
112
  return {
78
113
  "link_name": link_name,
79
114
  "id": str(item["id"]),
80
115
  "geometry_kind": str(item["geometry_kind"]),
81
116
  "dimensions_m": [_canon_float(value) for value in item["dimensions_m"]],
82
117
  "position_m": [_canon_float(value) for value in position],
83
- "quaternion_xyzw": [_canon_float(value) for value in _qnorm(quaternion)],
118
+ "quaternion_xyzw": list(_qcanonical(quaternion)),
84
119
  }
85
120
 
86
121
 
@@ -99,7 +134,17 @@ def _primitive(item, SolidPrimitive):
99
134
  return primitive
100
135
 
101
136
 
102
- def _collision_object(item, frame_id: str, position, quaternion, *, operation: int, CollisionObject, Pose, SolidPrimitive):
137
+ def _collision_object(
138
+ item,
139
+ frame_id: str,
140
+ position,
141
+ quaternion,
142
+ *,
143
+ operation: int,
144
+ CollisionObject,
145
+ Pose,
146
+ SolidPrimitive,
147
+ ):
103
148
  collision = CollisionObject()
104
149
  collision.header.frame_id = frame_id
105
150
  collision.id = str(item["id"])
@@ -116,11 +161,19 @@ def _collision_object(item, frame_id: str, position, quaternion, *, operation: i
116
161
 
117
162
  def _wait_future(node, future, timeout_s: float) -> bool:
118
163
  import rclpy
164
+
119
165
  rclpy.spin_until_future_complete(node, future, timeout_sec=timeout_s)
120
166
  return future.done()
121
167
 
122
168
 
123
- def _lookup_transform(node, buffer, *, target_frame: str, source_frame: str, timeout_s: float):
169
+ def _lookup_transform(
170
+ node,
171
+ buffer,
172
+ *,
173
+ target_frame: str,
174
+ source_frame: str,
175
+ timeout_s: float,
176
+ ):
124
177
  import rclpy
125
178
  from rclpy.time import Time
126
179
 
@@ -148,9 +201,12 @@ def _read_state(get_client, node, timeout_s: float, workpiece_id: str):
148
201
  response = future.result()
149
202
  if response is None:
150
203
  raise RuntimeError("get_planning_scene_empty")
151
- world_present = any(item.id == workpiece_id for item in response.scene.world.collision_objects)
204
+ world_present = any(
205
+ item.id == workpiece_id for item in response.scene.world.collision_objects
206
+ )
152
207
  attached = [
153
- item for item in response.scene.robot_state.attached_collision_objects
208
+ item
209
+ for item in response.scene.robot_state.attached_collision_objects
154
210
  if item.object.id == workpiece_id
155
211
  ]
156
212
  return world_present, attached
@@ -186,11 +242,14 @@ def transition(path: Path, *, operation: str, timeout_s: float) -> dict[str, Any
186
242
 
187
243
  pose_data = item["pose"]
188
244
  world_position = tuple(float(value) for value in pose_data["position_m"])
189
- world_quaternion = tuple(float(value) for value in pose_data["quaternion_xyzw"])
245
+ world_quaternion = tuple(
246
+ float(value) for value in pose_data["quaternion_xyzw"]
247
+ )
190
248
  scene = PlanningScene()
191
249
  scene.is_diff = True
192
250
  scene.robot_state.is_diff = True
193
251
  attachment_hash = None
252
+ expected_attachment_pose = None
194
253
 
195
254
  if operation == "attach":
196
255
  transform = _lookup_transform(
@@ -217,18 +276,14 @@ def transition(path: Path, *, operation: str, timeout_s: float) -> dict[str, Any
217
276
  world_position,
218
277
  world_quaternion,
219
278
  )
220
- scene.world.collision_objects.append(
221
- _collision_object(
222
- item,
223
- planning_frame,
224
- world_position,
225
- world_quaternion,
226
- operation=CollisionObject.REMOVE,
227
- CollisionObject=CollisionObject,
228
- Pose=Pose,
229
- SolidPrimitive=SolidPrimitive,
230
- )
231
- )
279
+
280
+ # MoveIt applies robot_state.attached_collision_objects before processing
281
+ # world.collision_objects in a PlanningScene diff. Adding the attached body
282
+ # automatically removes an existing same-ID world object. Sending a second
283
+ # explicit world REMOVE therefore makes synchronous ApplyPlanningScene
284
+ # report failure because that later REMOVE targets an object that no longer
285
+ # exists. Keep ATTACH as one authoritative attached-object ADD and verify the
286
+ # resulting world/attached state by synchronous read-back below.
232
287
  attached = AttachedCollisionObject()
233
288
  attached.link_name = tool_link
234
289
  attached.touch_links = touch_links
@@ -244,9 +299,16 @@ def transition(path: Path, *, operation: str, timeout_s: float) -> dict[str, Any
244
299
  )
245
300
  scene.robot_state.attached_collision_objects.append(attached)
246
301
  expected_attachment = _canonical_attachment(
247
- tool_link, item, relative_position, relative_quaternion
302
+ tool_link,
303
+ item,
304
+ relative_position,
305
+ relative_quaternion,
248
306
  )
249
307
  attachment_hash = _hash(expected_attachment)
308
+ expected_attachment_pose = _pose_payload(
309
+ relative_position,
310
+ relative_quaternion,
311
+ )
250
312
  elif operation == "detach":
251
313
  attached = AttachedCollisionObject()
252
314
  attached.link_name = tool_link
@@ -278,12 +340,21 @@ def transition(path: Path, *, operation: str, timeout_s: float) -> dict[str, Any
278
340
  return {"success": False, "code": "apply_planning_scene_rejected"}
279
341
 
280
342
  world_present, attached_items = _read_state(
281
- get_client, node, timeout_s, str(item["id"])
343
+ get_client,
344
+ node,
345
+ timeout_s,
346
+ str(item["id"]),
347
+ )
348
+ observed_attached_link = (
349
+ attached_items[0].link_name if len(attached_items) == 1 else None
282
350
  )
283
- observed_attached_link = attached_items[0].link_name if len(attached_items) == 1 else None
284
351
 
285
352
  if operation == "attach":
286
- if world_present or len(attached_items) != 1 or observed_attached_link != tool_link:
353
+ if (
354
+ world_present
355
+ or len(attached_items) != 1
356
+ or observed_attached_link != tool_link
357
+ ):
287
358
  return {
288
359
  "success": False,
289
360
  "code": "workpiece_attach_readback_mismatch",
@@ -291,15 +362,28 @@ def transition(path: Path, *, operation: str, timeout_s: float) -> dict[str, Any
291
362
  "observed_attached_link": observed_attached_link,
292
363
  }
293
364
  observed_object = attached_items[0].object
294
- if len(observed_object.primitives) != 1 or len(observed_object.primitive_poses) != 1:
365
+ if (
366
+ len(observed_object.primitives) != 1
367
+ or len(observed_object.primitive_poses) != 1
368
+ ):
295
369
  return {"success": False, "code": "workpiece_attached_shape_invalid"}
296
370
  object_position, object_q = _pose_from_msg(observed_object.pose)
297
- primitive_position, primitive_q = _pose_from_msg(observed_object.primitive_poses[0])
371
+ primitive_position, primitive_q = _pose_from_msg(
372
+ observed_object.primitive_poses[0]
373
+ )
298
374
  observed_position, observed_q = _compose(
299
- object_position, object_q, primitive_position, primitive_q
375
+ object_position,
376
+ object_q,
377
+ primitive_position,
378
+ primitive_q,
300
379
  )
301
380
  observed_attachment_hash = _hash(
302
- _canonical_attachment(tool_link, item, observed_position, observed_q)
381
+ _canonical_attachment(
382
+ tool_link,
383
+ item,
384
+ observed_position,
385
+ observed_q,
386
+ )
303
387
  )
304
388
  if observed_attachment_hash != attachment_hash:
305
389
  return {
@@ -307,6 +391,18 @@ def transition(path: Path, *, operation: str, timeout_s: float) -> dict[str, Any
307
391
  "code": "workpiece_attachment_hash_mismatch",
308
392
  "observed_world_present": world_present,
309
393
  "observed_attached_link": observed_attached_link,
394
+ "expected_attachment_hash": attachment_hash,
395
+ "observed_attachment_hash": observed_attachment_hash,
396
+ "expected_attachment_pose": expected_attachment_pose,
397
+ "observed_object_pose": _pose_payload(object_position, object_q),
398
+ "observed_primitive_pose": _pose_payload(
399
+ primitive_position,
400
+ primitive_q,
401
+ ),
402
+ "observed_attachment_pose": _pose_payload(
403
+ observed_position,
404
+ observed_q,
405
+ ),
310
406
  }
311
407
  return {
312
408
  "success": True,
@@ -316,16 +412,23 @@ def transition(path: Path, *, operation: str, timeout_s: float) -> dict[str, Any
316
412
  "observed_world_present": False,
317
413
  "observed_attached_link": tool_link,
318
414
  "attachment_hash": attachment_hash,
415
+ "attachment_pose": expected_attachment_pose,
319
416
  }
320
417
 
321
418
  success = world_present and not attached_items
322
419
  return {
323
420
  "success": success,
324
- "code": "workpiece_detach_verified" if success else "workpiece_detach_readback_mismatch",
421
+ "code": (
422
+ "workpiece_detach_verified"
423
+ if success
424
+ else "workpiece_detach_readback_mismatch"
425
+ ),
325
426
  "materialization_hash": materialization_hash,
326
427
  "twin_hash": twin_hash,
327
428
  "observed_world_present": world_present,
328
- "observed_attached_link": None if not attached_items else observed_attached_link,
429
+ "observed_attached_link": (
430
+ None if not attached_items else observed_attached_link
431
+ ),
329
432
  }
330
433
  finally:
331
434
  del listener
@@ -346,7 +449,10 @@ def main() -> int:
346
449
  timeout_s=float(args.timeout),
347
450
  )
348
451
  except Exception as exc:
349
- result = {"success": False, "code": f"workpiece_scene_probe_error:{type(exc).__name__}"}
452
+ result = {
453
+ "success": False,
454
+ "code": f"workpiece_scene_probe_error:{type(exc).__name__}",
455
+ }
350
456
  print(json.dumps(result, sort_keys=True))
351
457
  return 0 if result.get("success") else 2
352
458
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devagent-physical-engine
3
- Version: 1.8.2
4
- Summary: Production-grade verification-first robotics evidence runtime with one-command customer intake, truthful robot support tiers, qualified UR5e report-test Gazebo execution, direct ROS controller/action readiness, scoped destination-support place contact, experimental ABB IRB1200 Gazebo/MoveIt/RViz visualization, transfer/place diagnostics, requirement-driven test generation, selective verification, deterministic Twin lineage, measured physical verification, replay inspection, regression, and FAT evidence.
3
+ Version: 1.8.5
4
+ Summary: Verification-first robotics commissioning runtime with exact-Twin evidence, qualified UR5e Gazebo/MoveIt report execution, truthful robot support tiers, selective verification, replay, regression, and FAT reporting.
5
5
  Author: Tom Ha
6
6
  License: All Rights Reserved
7
7
  Project-URL: Homepage, https://github.com/tomha85/devagent-physical-engine
@@ -76,7 +76,7 @@ regression + FAT report + Evidence Viewer
76
76
  Python 3.11+ is required.
77
77
 
78
78
  ```bash
79
- python -m pip install --upgrade devagent-physical-engine==1.8.2
79
+ python -m pip install --upgrade devagent-physical-engine==1.8.5
80
80
  ```
81
81
 
82
82
  Verify the installed package:
@@ -88,7 +88,7 @@ python -c "import devagent_physical_engine as d; print(d.__version__)"
88
88
  Expected:
89
89
 
90
90
  ```text
91
- 1.8.2
91
+ 1.8.5
92
92
  ```
93
93
 
94
94
  Run the normal customer workflow:
@@ -394,6 +394,38 @@ The v1.8.2 place receipt records the exact destination support id, workpiece id,
394
394
 
395
395
  The release does **not** change real-execution authority, site qualification, target poses, conservative motion scales, or the rule that every motion group must preflight successfully before execution begins.
396
396
 
397
+ ## v1.8.3 synchronous verified workpiece attach
398
+
399
+ A clean v1.8.2 target-stack run proved all five industrial preflight groups and actual `approach_pick` execution, then failed closed at GRASP with `workpiece_lifecycle_failed:apply_planning_scene_rejected`.
400
+
401
+ The attach request previously combined an `AttachedCollisionObject.ADD` with a redundant same-ID world `CollisionObject.REMOVE`. MoveIt applies attached-object state first, which already transitions the same-ID object out of the world. Processing the later redundant world REMOVE can therefore make synchronous `/apply_planning_scene` report failure even though the attachment transition was already attempted.
402
+
403
+ v1.8.3 makes the attach transition service-safe and deterministic: the request contains only the authoritative attached-object ADD. DevAgent then synchronously reads the PlanningScene back and requires all of the following before motion may continue:
404
+
405
+ ```text
406
+ workpiece absent from world
407
+ exactly one attached object with the expected id
408
+ attached link == verified tool mount link
409
+ attached primitive geometry is valid
410
+ attachment pose/geometry hash matches the expected attachment hash
411
+ ```
412
+
413
+ Detach semantics remain explicit attached-object REMOVE plus world-object ADD at the verified placement pose. No collision rule, tool/workpiece geometry, TCP, touch link, target pose, motion speed, planner, site-qualification boundary, or real-execution authority is relaxed.
414
+
415
+ ## v1.8.4 PyPI metadata compliance hotfix
416
+
417
+ v1.8.4 carries the exact v1.8.3 runtime semantics and shortens only the package Core Metadata `Summary` so it satisfies PyPI's 512-character limit. A regression test now parses `pyproject.toml` and rejects an empty or over-limit project description before release.
418
+
419
+ This metadata hotfix does not change the UR5e attach/detach lifecycle, collision policy, motion planning, Twin lineage, site-qualification boundary, or real-execution authority.
420
+
421
+ ## v1.8.5 quaternion-invariant verified workpiece attach
422
+
423
+ A clean v1.8.4 UR5e target-stack run proved that synchronous `/apply_planning_scene` attachment now succeeds, world/attached state reaches strict read-back, and the remaining GRASP blocker is `workpiece_attachment_hash_mismatch`.
424
+
425
+ MoveIt round-trips attached-object rotations through Eigen. Unit quaternions `q` and `-q` represent the same physical rotation, but a raw component hash treats those two equivalent representations as different. v1.8.5 canonicalizes equivalent quaternion signs before workpiece attachment hashing using the same deterministic positive-`w` / first-nonzero-component rule already used by the qualified tool-scene path.
426
+
427
+ Verification remains strict for attachment identity, link, geometry contract, position, and physical rotation. This is not a tolerance-based PASS and does not change target geometry, TCP, collision policy, planner types, motion speeds, controller binding, Twin lineage, site qualification, or real-execution authority. If a hash mismatch remains, DevAgent records expected and observed hashes plus canonical expected/object/primitive/effective attachment poses for target diagnosis.
428
+
397
429
  ## Qualified visual-execution scope
398
430
 
399
431
  Measured report-test execution remains intentionally narrow:
@@ -594,9 +626,9 @@ devagent-physical-ai optional provider-backed engineering front end
594
626
 
595
627
  # Project status
596
628
 
597
- **v1.8.2 — Production/Stable software workflow**
629
+ **v1.8.5 — Production/Stable software workflow**
598
630
 
599
- v1.8.2 retains the v1.8.1 direct UR5e controller/action binding, v1.8 truthful robot support tiers, and ABB IRB1200 experimental visual runtime. It additionally splits UR5e transfer and placement at a hard motion boundary and scopes destination-support contact to the single zero-blend LIN place planning request, using a request-local full-ACM diff for exactly the attached-workpiece ↔ destination-support pair.
631
+ v1.8.5 retains v1.8.4 PyPI metadata compliance, v1.8.3 synchronous service-safe workpiece attachment, v1.8.2 scoped destination-support place contact, v1.8.1 direct UR5e controller/action binding, truthful robot support tiers, and the ABB IRB1200 experimental visual runtime. It makes strict workpiece attachment verification invariant to the mathematically equivalent quaternion representations `q` and `-q` and persists exact pose/hash diagnostics for any remaining mismatch.
600
632
 
601
633
  Production-oriented software capabilities include:
602
634
 
@@ -611,6 +643,8 @@ exact Requirement → Mapping → Verification Plan → Case provenance
611
643
  truthful robot support tiers
612
644
  qualified UR5e report-test visual execution contract
613
645
  direct UR5e controller-manager/action readiness binding
646
+ synchronous verified workpiece attach/detach lifecycle
647
+ quaternion-invariant strict attachment hashing
614
648
  scoped destination-support place contact
615
649
  experimental exact ABB IRB1200 Gazebo/MoveIt/RViz preview
616
650
  industrial Pilz PTP / LIN planning
@@ -196,6 +196,7 @@ tests/test_one_command_regression_v141.py
196
196
  tests/test_one_command_v13.py
197
197
  tests/test_openai_strict_schema_v0113.py
198
198
  tests/test_optimization_v05.py
199
+ tests/test_package_metadata_v184.py
199
200
  tests/test_physical_campaign_v09.py
200
201
  tests/test_physical_evidence_v09.py
201
202
  tests/test_physical_motion_v09.py
@@ -245,4 +246,6 @@ tests/test_user_cli_v15.py
245
246
  tests/test_user_cli_v15_hotfix_inheritance.py
246
247
  tests/test_v052_stabilization.py
247
248
  tests/test_v08_catalog_mapping_regression.py
248
- tests/test_workpiece_lifecycle_v011.py
249
+ tests/test_workpiece_attach_semantics_v183.py
250
+ tests/test_workpiece_lifecycle_v011.py
251
+ tests/test_workpiece_scene_probe_v185.py
@@ -34,8 +34,8 @@ from devagent_physical_engine.ros2.report_test_visualization_v170 import (
34
34
 
35
35
 
36
36
  class IndustrialMotionV170Tests(unittest.TestCase):
37
- def test_release_version_is_v182(self) -> None:
38
- self.assertEqual(package.__version__, "1.8.2")
37
+ def test_release_version_is_v185(self) -> None:
38
+ self.assertEqual(package.__version__, "1.8.5")
39
39
 
40
40
  def test_reference_groups_cover_every_task_phase_once_in_order(self) -> None:
41
41
  groups = reference_industrial_groups()
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ import tomllib
5
+ import unittest
6
+
7
+
8
+ class PackageMetadataV184Tests(unittest.TestCase):
9
+ def test_pypi_summary_respects_core_metadata_limit(self) -> None:
10
+ root = Path(__file__).resolve().parents[1]
11
+ metadata = tomllib.loads((root / "pyproject.toml").read_text(encoding="utf-8"))
12
+ summary = str(metadata["project"]["description"])
13
+ self.assertTrue(summary.strip())
14
+ self.assertLessEqual(len(summary), 512)
15
+
16
+ def test_package_version_is_v185(self) -> None:
17
+ root = Path(__file__).resolve().parents[1]
18
+ metadata = tomllib.loads((root / "pyproject.toml").read_text(encoding="utf-8"))
19
+ self.assertEqual(metadata["project"]["version"], "1.8.5")
20
+
21
+
22
+ if __name__ == "__main__":
23
+ unittest.main()
@@ -153,9 +153,11 @@ class TransferPlaceDiagnosticV172Tests(unittest.TestCase):
153
153
  self.assertTrue(payload["plan_only"])
154
154
  self.assertEqual(payload["result_code"], "pilz_place_segment_planning_failed")
155
155
 
156
- def test_release_preserves_v172_diagnostics_under_v182_runtime(self) -> None:
157
- self.assertEqual(package.__version__, "1.8.2")
158
- self.assertTrue(issubclass(PilzIndustrialSequencePlannerV182, PilzIndustrialSequencePlannerV172))
156
+ def test_v185_release_preserves_v172_diagnostics_and_v182_planner(self) -> None:
157
+ self.assertEqual(package.__version__, "1.8.5")
158
+ self.assertTrue(
159
+ issubclass(PilzIndustrialSequencePlannerV182, PilzIndustrialSequencePlannerV172)
160
+ )
159
161
  self.assertIs(
160
162
  visual_v170.PilzIndustrialSequencePlannerV170,
161
163
  PilzIndustrialSequencePlannerV182,