python-cad-tools 0.1.13__tar.gz → 0.1.16__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 (272) hide show
  1. python_cad_tools-0.1.16/.opencode/tools/specrepo-autocommit.py +259 -0
  2. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/CHANGELOG.md +60 -0
  3. {python_cad_tools-0.1.13/src/python_cad_tools.egg-info → python_cad_tools-0.1.16}/PKG-INFO +7 -1
  4. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/README.md +6 -0
  5. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/neutral-model.md +107 -5
  6. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/project-configuration.md +14 -4
  7. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/public-api.md +3 -0
  8. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_project_loader.py +53 -1
  9. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/artifacts.py +20 -0
  10. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/build-info.json +1 -1
  11. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/cli.py +20 -2
  12. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/elements.py +5 -1
  13. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/ifc.py +187 -17
  14. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/manifests.py +23 -0
  15. python_cad_tools-0.1.16/src/python_cad_tools/recipes.py +293 -0
  16. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/validation.py +226 -5
  17. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16/src/python_cad_tools.egg-info}/PKG-INFO +7 -1
  18. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools.egg-info/SOURCES.txt +1 -0
  19. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools.egg-info/scm_file_list.json +2 -0
  20. python_cad_tools-0.1.16/src/python_cad_tools.egg-info/scm_version.json +8 -0
  21. python_cad_tools-0.1.13/.opencode/tools/specrepo-autocommit.py +0 -205
  22. python_cad_tools-0.1.13/src/python_cad_tools.egg-info/scm_version.json +0 -8
  23. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.gitattributes +0 -0
  24. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.githooks/pre-push +0 -0
  25. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.github/workflows/ci.yml +0 -0
  26. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.github/workflows/downstream-compatibility.yml +0 -0
  27. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.github/workflows/release-candidate.yml +0 -0
  28. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.github/workflows/release.yml +0 -0
  29. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.gitignore +0 -0
  30. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.opencode/commands/autocommit.md +0 -0
  31. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/.opencode/tools/specrepo-autocommit.js +0 -0
  32. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/AGENTS.md +0 -0
  33. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/CLAUDE.md +0 -0
  34. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/LICENSE +0 -0
  35. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/MANIFEST.in +0 -0
  36. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/build-semantics.md +0 -0
  37. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/drawing-extensions.md +0 -0
  38. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/manifests.md +0 -0
  39. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/spikes/format-feasibility.md +0 -0
  40. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/toolchain.md +0 -0
  41. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/two-tier-contract.md +0 -0
  42. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/docs/viewer-delivery.md +0 -0
  43. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/pyproject.toml +0 -0
  44. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/setup.cfg +0 -0
  45. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/__init__.py +0 -0
  46. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/__main__.py +0 -0
  47. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_locking.py +0 -0
  48. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_project_state.py +0 -0
  49. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_scaffold.py +0 -0
  50. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_transaction.py +0 -0
  51. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/app.webmanifest +0 -0
  52. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/asset-manifest.json +0 -0
  53. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/animationGroup-d5_0GqzW.js +0 -0
  54. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/bakedVertexAnimation-BTnhNnCG.js +0 -0
  55. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/basisTextureLoader-B1WedbgE.js +0 -0
  56. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/brdfTextureTools-DIdTXZSR.js +0 -0
  57. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/bumpFragment-C_S7LReC.js +0 -0
  58. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/bumpVertex-9p8w1w7z.js +0 -0
  59. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/bumpVertexDeclaration-B8rpdm3U.js +0 -0
  60. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/clipPlaneFragment-A-1cS0Wr.js +0 -0
  61. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/clipPlaneVertex-B34pEy_z.js +0 -0
  62. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/cubemapToSphericalPolynomial-BAw8ctMB.js +0 -0
  63. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/dds-BDxArwXs.js +0 -0
  64. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/dds.pure-D_Zw5OEt.js +0 -0
  65. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/ddsTextureLoader-DeQLZozV.js +0 -0
  66. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/default.fragment-BI3GAR2C.js +0 -0
  67. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/default.vertex-CQ4MILUZ.js +0 -0
  68. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/defaultUboDeclaration-DOBRS3iS.js +0 -0
  69. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/dumpTools-DCMMRWtL.js +0 -0
  70. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/envTextureLoader-C-7LpDe_.js +0 -0
  71. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/exrTextureLoader-oNqXrLmH.js +0 -0
  72. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphApplyForceBlock-DUMH2798.js +0 -0
  73. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphApplyImpulseBlock-DUvs7iqs.js +0 -0
  74. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphArrayIndexBlock-B-qeUHG6.js +0 -0
  75. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphBezierCurveEasingBlock-Cjm4_L1J.js +0 -0
  76. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphBinaryOperationBlock-CXnNy9QR.js +0 -0
  77. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphBranchBlock-DLUVB1dZ.js +0 -0
  78. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphCachedOperationBlock-BkfdjQtr.js +0 -0
  79. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphCancelDelayBlock-DwzbjNTQ.js +0 -0
  80. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphCodeExecutionBlock-CzFaSgik.js +0 -0
  81. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphConditionalDataBlock-DsMUDoGH.js +0 -0
  82. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphConsoleLogBlock-CVWywmBY.js +0 -0
  83. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphConstantBlock-BNIgu4QC.js +0 -0
  84. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphContextBlock-DuQCYIgK.js +0 -0
  85. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphCounterBlock-DxCqjQc8.js +0 -0
  86. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphDataSwitchBlock-BOncXvGP.js +0 -0
  87. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphDebounceBlock-43eNp_3W.js +0 -0
  88. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphDebugBlock-CYRuaSbJ.js +0 -0
  89. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphDoNBlock-Du-7uIwS.js +0 -0
  90. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphEasingBlock-C6lQQb9c.js +0 -0
  91. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphFlipFlopBlock-Ba-WebLx.js +0 -0
  92. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphForLoopBlock-CC4ZykmE.js +0 -0
  93. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphFunctionReferenceBlock-Cc8odCYm.js +0 -0
  94. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphGLTFDataProvider-Bs8eI-tc.js +0 -0
  95. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphGetAngularVelocityBlock-BR2ioMLJ.js +0 -0
  96. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphGetAssetBlock-DD4Eo4N8.js +0 -0
  97. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphGetLinearVelocityBlock-CiXY_22u.js +0 -0
  98. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphGetPhysicsMassPropertiesBlock-BFuYrykd.js +0 -0
  99. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphGetPropertyBlock-yylHk0Tj.js +0 -0
  100. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphGetSoundVolumeBlock-Yoy3NMR4.js +0 -0
  101. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphGetVariableBlock-DP0lG6uk.js +0 -0
  102. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphIndexOfBlock-Dp46lAXL.js +0 -0
  103. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphInterpolationBlock-DAAojSot.js +0 -0
  104. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphIsKeyPressedBlock-BzyaFJtY.js +0 -0
  105. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphIsSoundPlayingBlock-4qIsPViO.js +0 -0
  106. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphJsonPointerParserBlock-BZG5ehwx.js +0 -0
  107. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphKeyDownEventBlock-rpKm7KQS.js +0 -0
  108. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphKeyUpEventBlock-DZ0cYho3.js +0 -0
  109. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphKeyboardEventBlock-CiAmYMbH.js +0 -0
  110. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphMathBlocks-CbJDlFUY.js +0 -0
  111. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphMathCombineExtractBlocks-DICrYBP4.js +0 -0
  112. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphMatrixMathBlocks-ftOIDo5d.js +0 -0
  113. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphMeshPickEventBlock-DTChczhf.js +0 -0
  114. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphMultiGateBlock-DdupWV9J.js +0 -0
  115. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPauseAnimationBlock-DBsgO-6v.js +0 -0
  116. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPauseSoundBlock-DWcr1ULl.js +0 -0
  117. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPhysicsCollisionEventBlock-CVKDMyoo.js +0 -0
  118. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPlayAnimationBlock-BXVH0aEG.js +0 -0
  119. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPlaySoundBlock-LNU38ZaS.js +0 -0
  120. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerDownEventBlock-L9Wzp4sm.js +0 -0
  121. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerMoveEventBlock-Cgf9rHw2.js +0 -0
  122. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerOutEventBlock-0bOFbyCB.js +0 -0
  123. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerOverEventBlock-CJQEfL96.js +0 -0
  124. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerUpEventBlock-Dr4yXdtL.js +0 -0
  125. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphReceiveCustomEventBlock-DpKjQPwo.js +0 -0
  126. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSceneReadyEventBlock-CTK4Ndci.js +0 -0
  127. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSceneTickEventBlock-CzJbobbg.js +0 -0
  128. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSendCustomEventBlock-DBQ1n5eJ.js +0 -0
  129. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSequenceBlock-oBhLC7Vj.js +0 -0
  130. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSetAngularVelocityBlock-DXlS3umF.js +0 -0
  131. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSetDelayBlock-DhrCI14Z.js +0 -0
  132. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSetLinearVelocityBlock-DdNE9BcN.js +0 -0
  133. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSetPhysicsMotionTypeBlock-Be7qTZ0l.js +0 -0
  134. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSetPropertyBlock-Bx8fCC87.js +0 -0
  135. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSetSoundVolumeBlock-Bmh5show.js +0 -0
  136. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSetVariableBlock-BP87cysh.js +0 -0
  137. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSoundEndedEventBlock-DPK4HFJ5.js +0 -0
  138. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphStopAnimationBlock-9n5l3RYF.js +0 -0
  139. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphStopSoundBlock-BkqUQ3IX.js +0 -0
  140. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphSwitchBlock-CzYmr0Rg.js +0 -0
  141. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphThrottleBlock-C72DA8Ot.js +0 -0
  142. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphTransformCoordinatesSystemBlock-B57atF6V.js +0 -0
  143. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphTypeToTypeBlocks-BwO8ctcl.js +0 -0
  144. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphUnaryOperationBlock-B5xQ7raL.js +0 -0
  145. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphVectorMathBlocks-BSQ0Jp6s.js +0 -0
  146. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphWaitAllBlock-DkjMKncY.js +0 -0
  147. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/flowGraphWhileLoopBlock-DiDLbXDS.js +0 -0
  148. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/fogFragment-QU9YOYuH.js +0 -0
  149. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting-CBvJqSQF.js +0 -0
  150. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting-EGwqbpHv.js +0 -0
  151. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting.fragment-CG3O4l2t.js +0 -0
  152. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting.fragment-CYV0OcvY.js +0 -0
  153. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting.vertex-DU77eH5y.js +0 -0
  154. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting.vertex-s8IsAoDR.js +0 -0
  155. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.fragment-Bt1AWZNn.js +0 -0
  156. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.fragment-luoOzTyP.js +0 -0
  157. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.vertex-m9aQdgcg.js +0 -0
  158. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.vertex-xDMDxXMl.js +0 -0
  159. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/geometry.fragment-C_-6JJr4.js +0 -0
  160. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/geometry.fragment-oisMFtj8.js +0 -0
  161. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/geometry.vertex-CBkXTBVF.js +0 -0
  162. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/geometry.vertex-DQDqvDk5.js +0 -0
  163. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/glowBlurPostProcess.fragment-QolNMgw6.js +0 -0
  164. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/glowMapGeneration.fragment-DRwn6uC2.js +0 -0
  165. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/glowMapGeneration.vertex-BPPwu0E9.js +0 -0
  166. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/glowMapMerge.fragment-ODjisfEL.js +0 -0
  167. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/glowMapMerge.vertex-DB7eIwXj.js +0 -0
  168. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/harmonicsFunctions-B2jlXPLE.js +0 -0
  169. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/hdrTextureLoader-BGQvgh0h.js +0 -0
  170. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/helperFunctions-B6Z1LJrO.js +0 -0
  171. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/iesTextureLoader-8wFKF-ak.js +0 -0
  172. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/index-BnAaL7KS.js +0 -0
  173. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/index-CnrKUEff.css +0 -0
  174. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/kernelBlur.fragment-C0PAazdd.js +0 -0
  175. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/kernelBlur.fragment-DmepFK-C.js +0 -0
  176. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/kernelBlur.vertex-CQTyg7d5.js +0 -0
  177. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/kernelBlur.vertex-C_mnrqaY.js +0 -0
  178. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/kernelBlurVaryingDeclaration-DCLBcYIA.js +0 -0
  179. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/kernelBlurVaryingDeclaration-DuoJgAZp.js +0 -0
  180. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/ktxTextureLoader-CIypOERT.js +0 -0
  181. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/lightFragment-BinX63GB.js +0 -0
  182. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/logDepthDeclaration-D8jcsZiW.js +0 -0
  183. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/logDepthVertex-CAoFmcco.js +0 -0
  184. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/mainUVVaryingDeclaration-oF31v9Zp.js +0 -0
  185. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/mesh.vertexData.functions-ayNlGC5K.js +0 -0
  186. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/meshUboDeclaration-BXrkHWWW.js +0 -0
  187. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/oitFragment-2x3JPdq6.js +0 -0
  188. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbr.fragment-B5ZDpeFy.js +0 -0
  189. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbr.fragment-CpT08ThE.js +0 -0
  190. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbr.vertex-BaGhkjmV.js +0 -0
  191. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbr.vertex-DAeXubyL.js +0 -0
  192. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbrMaterial.pure-BXY_lClt.js +0 -0
  193. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbrMaterialLoadingAdapter-MpirTHMd.js +0 -0
  194. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbrTransmissionLayerData-CHHhh58w.js +0 -0
  195. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbrTransmissionLayerData-DthxrR9-.js +0 -0
  196. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbrUboDeclaration--Zhq0q7d.js +0 -0
  197. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/openpbrUboDeclaration-CXcZlREH.js +0 -0
  198. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/packingFunctions-Bo_ikKLB.js +0 -0
  199. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/packingFunctions-u3uWRkpF.js +0 -0
  200. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pass.fragment-slK02MnO.js +0 -0
  201. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/passCube.fragment-C13WHRV7.js +0 -0
  202. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/passCube.fragment-DnuA3v4l.js +0 -0
  203. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pbr.fragment-8v2GDfO1.js +0 -0
  204. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pbr.vertex-BuCPFkDX.js +0 -0
  205. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pbrBRDFFunctions-5nHUuKRr.js +0 -0
  206. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pbrDebug-C8fLVEpD.js +0 -0
  207. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pbrIBLFunctions-BM49sbQB.js +0 -0
  208. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pbrMaterial.pure-B6n6Ic80.js +0 -0
  209. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pbrMaterialLoadingAdapter-C0d04FRz.js +0 -0
  210. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/pbrUboDeclaration-CL7R5LTR.js +0 -0
  211. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/postprocess.vertex-BhCyZbyQ.js +0 -0
  212. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/procedural.vertex-Cb5DsSFx.js +0 -0
  213. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/procedural.vertex-W-IcNTXE.js +0 -0
  214. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/reflectionFunction-DYrae64q.js +0 -0
  215. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/rgbdDecode.fragment-CFpIpdxk.js +0 -0
  216. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/rgbdEncode.fragment-BIBfrJET.js +0 -0
  217. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/rgbdEncode.fragment-yPbrkGKz.js +0 -0
  218. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/sceneUboDeclaration-D5YHSknt.js +0 -0
  219. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/textureProcessor.fragment-GSzJSZKX.js +0 -0
  220. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/textureProcessor.fragment-NwUMt_sy.js +0 -0
  221. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/tgaTextureLoader-Z9HWdnv1.js +0 -0
  222. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/assets/vertexColorMixing-DkZl_JUT.js +0 -0
  223. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/favicon.svg +0 -0
  224. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/index.html +0 -0
  225. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/_viewer_static/sw.js +0 -0
  226. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/build.py +0 -0
  227. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/context.py +0 -0
  228. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/determinism.py +0 -0
  229. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/drawings/__init__.py +0 -0
  230. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/drawings/_catalog.py +0 -0
  231. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/drawings/_validation.py +0 -0
  232. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exceptions.py +0 -0
  233. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/__init__.py +0 -0
  234. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/base.py +0 -0
  235. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/drawings/__init__.py +0 -0
  236. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/drawings/annotations.py +0 -0
  237. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/drawings/dxf.py +0 -0
  238. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/drawings/model.py +0 -0
  239. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/drawings/pdf.py +0 -0
  240. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/drawings/primitives.py +0 -0
  241. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/drawings/projection.py +0 -0
  242. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/drawings/svg.py +0 -0
  243. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/glb.py +0 -0
  244. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/quantities.py +0 -0
  245. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/exporters/step.py +0 -0
  246. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/geometry.py +0 -0
  247. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/project_config.py +0 -0
  248. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/py.typed +0 -0
  249. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/runtime_sync.py +0 -0
  250. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/build-manifest-1.json +0 -0
  251. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/design-manifest-1.json +0 -0
  252. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/download-manifest-2.json +0 -0
  253. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/drawing-annotations-1.json +0 -0
  254. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/glb-manifest-1.json +0 -0
  255. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/ifc-validation-1.json +0 -0
  256. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/quantities-1.json +0 -0
  257. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/run-metadata-1.json +0 -0
  258. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/site-metadata-1.json +0 -0
  259. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/schemas/step-validation-1.json +0 -0
  260. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/site.py +0 -0
  261. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/templates/project/.gitignore +0 -0
  262. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/templates/project/README.md +0 -0
  263. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/templates/project/config.py +0 -0
  264. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/templates/project/drawing_annotations.py +0 -0
  265. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/templates/project/generated/.gitkeep +0 -0
  266. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/templates/project/model.py +0 -0
  267. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/templates/project/pyproject.toml +0 -0
  268. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools/units.py +0 -0
  269. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools.egg-info/dependency_links.txt +0 -0
  270. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools.egg-info/entry_points.txt +0 -0
  271. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools.egg-info/requires.txt +0 -0
  272. {python_cad_tools-0.1.13 → python_cad_tools-0.1.16}/src/python_cad_tools.egg-info/top_level.txt +0 -0
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/env python3
2
+ """Run the LangChain AutoCommit CLI for a verified SpecRepo change."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import os
7
+ import shutil
8
+ import site
9
+ import subprocess
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import NoReturn
13
+
14
+ AUTOCOMMIT_REPOSITORY = "git+https://github.com/brandon-benge/langchain_autocommit.git"
15
+ CONFIGURATION_GUIDE = "https://github.com/brandon-benge/langchain_autocommit/blob/main/README.md#config-overrides"
16
+ EX_USAGE = 64
17
+
18
+
19
+ def print_usage() -> None:
20
+ print(
21
+ 'Usage: specrepo-autocommit "<summary of what changed>" [--no-push] '
22
+ "[--config-file <path>] [--env-var-name <NAME>] [--base-url <URL>] "
23
+ "[--model <MODEL>]\n\n"
24
+ "Runs autocommit on the current git branch.\n"
25
+ "--config-file points to the YAML configuration to use; defaults to "
26
+ "AUTOCOMMIT_PARAMS, then to .autoconfig.yaml in this repository.\n"
27
+ "--no-push commits locally without pushing to the remote.\n"
28
+ "--env-var-name, --base-url, and --model override the LLM API key "
29
+ "environment variable, base URL, and model that .autoconfig.yaml "
30
+ "otherwise supplies, e.g. to point at the AI Settings selected in "
31
+ "the Mac app.",
32
+ file=sys.stderr,
33
+ )
34
+
35
+
36
+ def fail(message: str, *, config_path: Path | None = None, code: int = 1) -> NoReturn:
37
+ print(message, file=sys.stderr)
38
+ if config_path is not None:
39
+ print(f"AUTOCOMMIT_PARAMS location: {config_path}", file=sys.stderr)
40
+ print(
41
+ f"Update that file as needed. Configuration guide: {CONFIGURATION_GUIDE}",
42
+ file=sys.stderr,
43
+ )
44
+ raise SystemExit(code)
45
+
46
+
47
+ def get_config_path(explicit_path: str | None = None) -> Path:
48
+ if explicit_path:
49
+ config_path = Path(os.path.expandvars(explicit_path)).expanduser().resolve()
50
+ print(f"--config-file is set to: {config_path}", flush=True)
51
+ else:
52
+ raw_path = os.environ.get("AUTOCOMMIT_PARAMS", "").strip()
53
+ if raw_path:
54
+ config_path = Path(os.path.expandvars(raw_path)).expanduser().resolve()
55
+ print(f"AUTOCOMMIT_PARAMS is set to: {config_path}", flush=True)
56
+ else:
57
+ config_path = (
58
+ Path(__file__).resolve().parent.parent.parent / ".autoconfig.yaml"
59
+ )
60
+ print(f"AUTOCOMMIT_PARAMS not set, using local fallback: {config_path}", flush=True)
61
+
62
+ if not config_path.is_file():
63
+ fail(
64
+ "Autocommit blocked: the configuration file does not exist or is not a regular file.",
65
+ config_path=config_path,
66
+ )
67
+
68
+ return config_path
69
+
70
+
71
+ def get_current_branch(config_path: Path) -> str:
72
+ try:
73
+ symbolic_ref = subprocess.run(
74
+ ["git", "symbolic-ref", "--quiet", "--short", "HEAD"],
75
+ check=False,
76
+ capture_output=True,
77
+ text=True,
78
+ )
79
+ branch = symbolic_ref.stdout.strip()
80
+
81
+ if not branch:
82
+ revision = subprocess.run(
83
+ ["git", "rev-parse", "--short", "HEAD"],
84
+ check=False,
85
+ capture_output=True,
86
+ text=True,
87
+ )
88
+ branch = revision.stdout.strip()
89
+ except OSError as error:
90
+ fail(
91
+ f"Autocommit blocked: could not run git: {error}",
92
+ config_path=config_path,
93
+ )
94
+
95
+ if not branch:
96
+ fail(
97
+ "Autocommit blocked: could not determine the current git branch.",
98
+ config_path=config_path,
99
+ )
100
+
101
+ return branch
102
+
103
+
104
+ def find_autocommit() -> str | None:
105
+ executable = shutil.which("autocommit")
106
+ if executable:
107
+ return executable
108
+
109
+ scripts_directory = Path(site.getuserbase()) / ("Scripts" if os.name == "nt" else "bin")
110
+ candidate = scripts_directory / ("autocommit.exe" if os.name == "nt" else "autocommit")
111
+ return str(candidate) if candidate.is_file() else None
112
+
113
+
114
+ def ensure_autocommit_installed(config_path: Path) -> str:
115
+ executable = find_autocommit()
116
+ if executable:
117
+ return executable
118
+
119
+ print("autocommit CLI not found. Installing it with the current Python...")
120
+ try:
121
+ installation = subprocess.run(
122
+ [
123
+ sys.executable,
124
+ "-m",
125
+ "pip",
126
+ "install",
127
+ "--user",
128
+ AUTOCOMMIT_REPOSITORY,
129
+ ],
130
+ check=False,
131
+ )
132
+ except OSError as error:
133
+ fail(
134
+ f"Autocommit installation failed: {error}",
135
+ config_path=config_path,
136
+ )
137
+
138
+ if installation.returncode != 0:
139
+ fail(
140
+ f"Autocommit installation failed with exit code {installation.returncode}.",
141
+ config_path=config_path,
142
+ code=installation.returncode,
143
+ )
144
+
145
+ executable = find_autocommit()
146
+ if not executable:
147
+ fail(
148
+ "Autocommit was installed but its executable could not be found.",
149
+ config_path=config_path,
150
+ )
151
+
152
+ print("autocommit installed successfully.")
153
+ return executable
154
+
155
+
156
+ def run_autocommit(
157
+ executable: str,
158
+ config_path: Path,
159
+ summary: str,
160
+ branch: str,
161
+ no_push: bool = False,
162
+ env_var_name: str | None = None,
163
+ base_url: str | None = None,
164
+ model: str | None = None,
165
+ ) -> None:
166
+ print(f"Running autocommit on branch {branch}.")
167
+ command = [
168
+ executable,
169
+ "--yes",
170
+ "--config-file",
171
+ str(config_path),
172
+ "-c",
173
+ summary,
174
+ ]
175
+ if no_push:
176
+ command.append("--no-push")
177
+ if env_var_name:
178
+ command += ["--env-var", "--env-var-name", env_var_name]
179
+ if base_url:
180
+ command += ["--base-url", base_url]
181
+ if model:
182
+ command += ["--model", model]
183
+ try:
184
+ result = subprocess.run(command, check=False)
185
+ except OSError as error:
186
+ fail(
187
+ f"Autocommit failed to start: {error}",
188
+ config_path=config_path,
189
+ )
190
+
191
+ if result.returncode != 0:
192
+ fail(
193
+ f"Autocommit failed with exit code {result.returncode}.",
194
+ config_path=config_path,
195
+ code=result.returncode if result.returncode > 0 else 1,
196
+ )
197
+
198
+
199
+ def main(argv: list[str]) -> int:
200
+ # Extract --no-push, --config-file <path>, and the LLM override flags
201
+ # before joining the remainder into the summary.
202
+ no_push = False
203
+ config_file: str | None = None
204
+ env_var_name: str | None = None
205
+ base_url: str | None = None
206
+ model: str | None = None
207
+ positional: list[str] = []
208
+ args_iter = iter(argv[1:])
209
+ for arg in args_iter:
210
+ if arg == "--no-push":
211
+ no_push = True
212
+ elif arg in ("--config-file", "--env-var-name", "--base-url", "--model"):
213
+ try:
214
+ value = next(args_iter)
215
+ except StopIteration:
216
+ print_usage()
217
+ fail(
218
+ f"Autocommit blocked: {arg} requires a value argument.",
219
+ code=EX_USAGE,
220
+ )
221
+ if arg == "--config-file":
222
+ config_file = value
223
+ elif arg == "--env-var-name":
224
+ env_var_name = value
225
+ elif arg == "--base-url":
226
+ base_url = value
227
+ else:
228
+ model = value
229
+ else:
230
+ positional.append(arg)
231
+
232
+ config_path = get_config_path(config_file)
233
+
234
+ if not positional or not " ".join(positional).strip():
235
+ print_usage()
236
+ fail(
237
+ "Autocommit blocked: a summary of what changed is required.",
238
+ config_path=config_path,
239
+ code=EX_USAGE,
240
+ )
241
+
242
+ summary = " ".join(positional).strip()
243
+ branch = get_current_branch(config_path)
244
+ executable = ensure_autocommit_installed(config_path)
245
+ run_autocommit(
246
+ executable,
247
+ config_path,
248
+ summary,
249
+ branch,
250
+ no_push=no_push,
251
+ env_var_name=env_var_name,
252
+ base_url=base_url,
253
+ model=model,
254
+ )
255
+ return 0
256
+
257
+
258
+ if __name__ == "__main__":
259
+ raise SystemExit(main(sys.argv))
@@ -7,6 +7,66 @@ stable release is published.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.16] - 2026-09-22
11
+
12
+ ### Fixed
13
+
14
+ - `python-cad --version` (and every other CLI invocation) no longer requires
15
+ `build123d`/the rest of the CAD toolchain to be importable: `cli.py`'s
16
+ `ValidationIssue` import — annotation-only, never evaluated at runtime
17
+ under this module's `from __future__ import annotations` — moved behind
18
+ `TYPE_CHECKING`. This was silently blocking every release since 0.1.14:
19
+ `build-publish` requires `tests/packaging` to pass, and its
20
+ clean-install-then-`--version` check installs the wheel with `--no-deps`,
21
+ which cannot satisfy the transitive `build123d` import `--version` didn't
22
+ actually need. 0.1.14 and 0.1.15 were tagged but never reached PyPI as a
23
+ result; 0.1.13 remained the latest published release throughout.
24
+
25
+ ## [0.1.15] - 2026-09-22
26
+
27
+ ### Changed
28
+
29
+ - `models/**/*.py` no longer needs an explicit `source-inputs` entry, at any
30
+ package depth and whether or not it has an `__init__.py`. `models/` is the
31
+ one directory a project is expected to keep adding files to over its
32
+ lifetime — one new module per new design object — so requiring a
33
+ `pyproject.toml` edit for each new file added no safety benefit: the build
34
+ manifest's `source_set_hash` already covers every file actually imported,
35
+ declared or not. `config.py`, `model.py`, `drawing_annotations.py`, and
36
+ `pyproject.toml` itself are unaffected and still require explicit
37
+ declaration, as does any other project-root Python file or data file read
38
+ through the public context. See `docs/project-configuration.md`.
39
+
40
+ ## [0.1.14] - 2026-08-13
41
+
42
+ ### Added
43
+
44
+ - New public `python_cad_tools.recipes` module: an opt-in, declarative
45
+ construction-recipe vocabulary (`RectangleProfile`, `CircleProfile`,
46
+ `ExtrudedProfile`, `DifferenceRecipe`, `evaluate`) that lets Tier 1
47
+ authoring produce parametric BREP IFC geometry
48
+ (`IfcExtrudedAreaSolid`/`SweptSolid`, `IfcBooleanResult`/`CSG`) instead of
49
+ only tessellated meshes. New `DesignElement.recipe` field, appended last;
50
+ `geometry` may now be `None` when a recipe is present. Recipes are
51
+ strictly opt-in per element — a model with no recipes produces IFC output
52
+ byte-identical to before this change, since neither recipe type adds a
53
+ unit-assignment entry. See `docs/neutral-model.md#construction-recipes`.
54
+ - The IFC validation sidecar gains additive `element_representation_types`
55
+ and `element_topology` keys; the design manifest gains additive
56
+ `recipe_kind` and `topology` per-element keys. `schema_version` is
57
+ unchanged on both.
58
+
59
+ ### Changed
60
+
61
+ - The IFC sidecar's `bounds_mm` is now the union of tessellation-derived
62
+ bounds and evaluated-solid bounds, so BREP-only and mixed models report
63
+ correct bounds (a BREP element emits no `IfcCartesianPointList3D`, which
64
+ the previous derivation depended on exclusively).
65
+ - `python-cad validate`'s CLI error message now includes each failing
66
+ issue's `element_id` (formatted as `code[element_id]: message`) when the
67
+ issue has one, instead of only `code: message`. Issues without an
68
+ element, such as `model-id`, are unaffected.
69
+
10
70
  ## [0.1.11] - 2026-08-11
11
71
 
12
72
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cad-tools
3
- Version: 0.1.13
3
+ Version: 0.1.16
4
4
  Summary: Typed, headless Python tooling for CAD/BIM models and artifacts
5
5
  Author: Brandon Benge
6
6
  License: AGPL-3.0-only
@@ -187,6 +187,12 @@ Project code should preserve stable semantic IDs, keep geometry parametric, and
187
187
  create geometry and metadata together. Generated artifacts are outputs, never
188
188
  design inputs.
189
189
 
190
+ `python_cad_tools.recipes` lets an element opt into parametric BREP IFC
191
+ geometry (`IfcExtrudedAreaSolid`/`SweptSolid`, `IfcBooleanResult`/`CSG`)
192
+ instead of the default tessellated mesh, verified by a topology round-trip
193
+ after every build. See
194
+ [`docs/neutral-model.md#construction-recipes`](docs/neutral-model.md#construction-recipes).
195
+
190
196
  Project-owned drawing annotations use the typed, renderer-independent boundary
191
197
  documented in [`docs/drawing-extensions.md`](docs/drawing-extensions.md). One
192
198
  annotation set is validated and rendered consistently to SVG, DXF, and PDF
@@ -131,6 +131,12 @@ Project code should preserve stable semantic IDs, keep geometry parametric, and
131
131
  create geometry and metadata together. Generated artifacts are outputs, never
132
132
  design inputs.
133
133
 
134
+ `python_cad_tools.recipes` lets an element opt into parametric BREP IFC
135
+ geometry (`IfcExtrudedAreaSolid`/`SweptSolid`, `IfcBooleanResult`/`CSG`)
136
+ instead of the default tessellated mesh, verified by a topology round-trip
137
+ after every build. See
138
+ [`docs/neutral-model.md#construction-recipes`](docs/neutral-model.md#construction-recipes).
139
+
134
140
  Project-owned drawing annotations use the typed, renderer-independent boundary
135
141
  documented in [`docs/drawing-extensions.md`](docs/drawing-extensions.md). One
136
142
  annotation set is validated and rendered consistently to SVG, DXF, and PDF
@@ -14,7 +14,7 @@ dimensions), and the validator invocation model. See
14
14
  | `id` | `str` | Stable, unique identifier. Used to derive every downstream IFC `GlobalId` and to key manifests. |
15
15
  | `name` | `str` | Human-readable display name. |
16
16
  | `category` | `str` | Free-text classification, exported as `ObjectType` in IFC. |
17
- | `geometry` | `Any` | A build123d-compatible solid, shell, wire, or edge. Required and opaque — the framework does not restrict how it was built. |
17
+ | `geometry` | `Any` | A build123d-compatible solid, shell, wire, or edge. Required and opaque — the framework does not restrict how it was built. May be `None` when `recipe` is present; the framework materializes it. |
18
18
  | `geometry_kind` | `"solid" \| "shell" \| "wire" \| "edge" \| "reference"` | Declares the geometry's topological kind. Physical elements must be `"solid"`. |
19
19
  | `placement` | `Placement` | Translation (mm) and rotation (degrees, XYZ) applied on top of `geometry`'s own coordinates. |
20
20
  | `dimensions` | `Dimensions` | Declared, author-stated extents. Independent of the geometry's actual bounds; exported into `Pset_PythonCadDesign`. |
@@ -31,6 +31,7 @@ dimensions), and the validator invocation model. See
31
31
  | `physical` | `bool` | Physical elements must be `"solid"` and require a material; non-physical (reference) elements are excluded from IFC, quantities, and mass. |
32
32
  | `parent_id` | `str \| None` | Parent element ID, exported as an `IfcRelAggregates` relationship when both ends are IFC-exported. |
33
33
  | `openings` | `list[Opening]` | Semantic voids in this host element. See "Openings" below. |
34
+ | `recipe` | `ConstructionRecipe \| None` | Opt-in declarative geometry. See "Construction recipes" below. |
34
35
 
35
36
  ## `IfcMapping`
36
37
 
@@ -131,8 +132,109 @@ returning a `list[ValidationIssue]`. `fatal_issues(...)` filters to
131
132
  `severity == "error"`; `python-cad build`/`validate` fail on any fatal issue
132
133
  before export begins.
133
134
 
134
- ## Construction recipes (Phase 2)
135
+ ## Construction recipes
135
136
 
136
- Parametric BREP/SweptSolid IFC representation via a construction-recipe model
137
- (`python_cad_tools.recipes`) is scoped as a separate SpecRepo proposal and is
138
- not yet implemented. This section will be extended when that proposal ships.
137
+ `python_cad_tools.recipes` lets Tier 1 authoring produce **parametric BREP**
138
+ IFC geometry instead of only tessellated meshes, for the two shapes
139
+ evidenced as needed by real downstream models: an extruded 2D profile, and
140
+ one extruded profile with a second subtracted from it. Recipes are strictly
141
+ **opt-in per element** — omitting `DesignElement.recipe` preserves today's
142
+ tessellated output byte-for-byte.
143
+
144
+ ### Type vocabulary
145
+
146
+ ```python
147
+ RectangleProfile(x_dim_mm: float, y_dim_mm: float, position: Placement = Placement()) # -> IfcRectangleProfileDef
148
+ CircleProfile(radius_mm: float, position: Placement = Placement()) # -> IfcCircleProfileDef
149
+ Profile = RectangleProfile | CircleProfile
150
+
151
+ ExtrudedProfile(profile: Profile, direction: tuple[float, float, float], depth_mm: float) # -> IfcExtrudedAreaSolid
152
+ DifferenceRecipe(base: ExtrudedProfile, tool: ExtrudedProfile) # -> IfcBooleanResult(Operator=DIFFERENCE)
153
+ ConstructionRecipe = ExtrudedProfile | DifferenceRecipe
154
+ ```
155
+
156
+ Both profile types are centered at the origin, matching IFC's own
157
+ `IfcRectangleProfileDef`/`IfcCircleProfileDef` semantics. `direction` and
158
+ `depth_mm` are independent fields, mirroring `IfcExtrudedAreaSolid`'s
159
+ `ExtrudedDirection`/`Depth` split — `direction`'s magnitude is ignored, only
160
+ its direction matters. `DifferenceRecipe.base`/`.tool` are typed as
161
+ `ExtrudedProfile`, not `ConstructionRecipe`: nesting is structurally
162
+ impossible, not merely unvalidated. There is no `RevolvedProfile`,
163
+ `PolygonProfile`, or general recursive boolean type — each would be a new
164
+ SpecRepo request; see `specrepo/rejected/2026-08-12-...-brep-recipes/` for
165
+ why the original, broader vocabulary was withdrawn in favor of this one.
166
+
167
+ ### Three authoring shapes
168
+
169
+ - **Recipe only** (`recipe` set, `geometry=None`) — the framework evaluates
170
+ the recipe through build123d and assigns the result to `geometry`.
171
+ Recommended: the same solid then feeds STEP, GLB, drawings, quantities,
172
+ bounds, and the tessellated fallback, while IFC additionally gets the
173
+ parametric BREP.
174
+ - **Recipe and geometry** (both set) — the framework evaluates the recipe
175
+ and verifies it against the supplied `geometry` (volume relative
176
+ tolerance `1e-6`, bounds absolute tolerance `1e-6` mm, equal face and
177
+ edge counts). A mismatch is an error-severity
178
+ `ValidationIssue("recipe-geometry-mismatch", ...)`. The supplied
179
+ `geometry` object is kept — only its equivalence is asserted.
180
+ Evaluation for this comparison happens in `validate_model`, not the
181
+ loader, and does not overwrite `geometry`.
182
+ - **No recipe** — today's behavior, unchanged in every respect.
183
+
184
+ ### Recipe → IFC entity map
185
+
186
+ | Recipe | IFC entity | `RepresentationType` |
187
+ | --- | --- | --- |
188
+ | `ExtrudedProfile` | `IfcExtrudedAreaSolid` | `"SweptSolid"` |
189
+ | `DifferenceRecipe` | `IfcBooleanResult` (`Operator=DIFFERENCE`) over two `IfcExtrudedAreaSolid` operands | `"CSG"` |
190
+ | *(no recipe)* | `IfcTriangulatedFaceSet` | `"Tessellation"` |
191
+
192
+ Neither recipe type carries an angle measure, so the IFC unit assignment is
193
+ unchanged — no `PLANEANGLEUNIT` entry, and no existing project's IFC hash
194
+ changes, including for projects that adopt a recipe.
195
+
196
+ ### Topology round-trip verification
197
+
198
+ After writing the file, for each element with a recipe the exporter
199
+ reloads it, parses the emitted representation item back into a recipe,
200
+ asserts it is structurally equal to the authored recipe (floating-point
201
+ precision, relative/absolute tolerance `1e-9`), re-evaluates the parsed
202
+ recipe, and compares the result against the authored solid
203
+ (`element.geometry`) on face count, edge count, and volume (relative
204
+ tolerance `1e-6`), plus bounds (absolute tolerance `1e-6` mm) as a sanity
205
+ check. Any mismatch raises `BuildExportError` naming the element and the
206
+ differing measure. This parses-and-re-evaluates rather than reading
207
+ topology out of the reloaded BREP directly, because the framework pins
208
+ `cadquery-ocp-novtk` (OCP) rather than pythonocc (OCC); `ifcopenshell.geom`'s
209
+ OCC integration is unavailable and its default iterator path triangulates,
210
+ destroying the topology the check is meant to compare.
211
+
212
+ The IFC validation sidecar records `element_representation_types` (element
213
+ id → `"Tessellation"` / `"SweptSolid"` / `"CSG"`) and `element_topology`
214
+ (element id → face count, edge count, volume, for recipe elements only).
215
+ The design manifest records the same information per element as
216
+ `recipe_kind` (`null` / `"extruded_profile"` / `"difference"`) and, for
217
+ recipe elements, `topology`. `bounds_mm` in the sidecar is the union of
218
+ tessellation-derived bounds and evaluated-solid bounds, since a BREP-only
219
+ element emits no `IfcCartesianPointList3D` for the older derivation to read.
220
+
221
+ ### Relationship to `openings`
222
+
223
+ `DesignElement.openings` (see "Openings" above) never subtracts material
224
+ from the host's own geometry — it only declares an `IfcOpeningElement`/
225
+ `IfcRelVoidsElement` semantic relationship on top of whatever geometry the
226
+ host already has. `DifferenceRecipe` is what makes the host's actual solid
227
+ have material missing where a door or window belongs — the two mechanisms
228
+ are complementary, not alternatives. A fully-converted host wall uses both:
229
+ `DifferenceRecipe` for the real notch, `openings` for the semantic
230
+ annotation. A project that only wants the semantic annotation keeps that
231
+ option without adopting a recipe at all.
232
+
233
+ ### Display trade-off
234
+
235
+ A single `DifferenceRecipe` is a common, well-supported IFC authoring
236
+ pattern, but any `IfcBooleanResult`/`CSG` representation may still render
237
+ less cleanly in some BIM viewers than a plain `SweptSolid`. Prefer a bare
238
+ `ExtrudedProfile` over `DifferenceRecipe` wherever the notch isn't actually
239
+ needed as real geometry — falling back to `openings` alone for the semantic
240
+ relationship.
@@ -24,7 +24,15 @@ duplicate inputs fail before project Python runs.
24
24
  absolute paths, backslashes, traversal components, missing files, and
25
25
  directories are rejected. It must include `pyproject.toml`. Every project-root
26
26
  Python file imported during an operation and every data file read through the
27
- public context must be listed. There is no recursive source guessing.
27
+ public context must be listed, with one exception: any `models/**/*.py` file
28
+ is always authoritative and never needs a `source-inputs` entry, at any
29
+ package depth and whether or not it has an `__init__.py`. `models/` is the
30
+ one directory a project is expected to keep adding files to over its
31
+ lifetime -- one new module per new design object -- so it is exempt from
32
+ declaration; everything else still has no recursive source guessing. The
33
+ build manifest's `source_set_hash` still covers every file actually
34
+ imported, declared or not, so this exemption changes only what must be
35
+ declared up front, not what a build's determinism claim is based on.
28
36
 
29
37
  ## Entry points
30
38
 
@@ -64,9 +72,11 @@ with context.open_source("data/materials.csv") as stream:
64
72
  materials = stream.read()
65
73
  ```
66
74
 
67
- These paths must also appear in `source-inputs`. `BuildContext` contains only
68
- `project_root`, `config`, `source_revision`, and `source_dirty`; it has no output
69
- or staging path.
75
+ These paths must also appear in `source-inputs` -- the `models/**/*.py`
76
+ exemption above covers imported Python modules only, not data files read
77
+ through this API, even when the path happens to fall under `models/`.
78
+ `BuildContext` contains only `project_root`, `config`, `source_revision`, and
79
+ `source_dirty`; it has no output or staging path.
70
80
 
71
81
  ## Isolation and environment
72
82
 
@@ -22,6 +22,9 @@ of optional runtime imports.
22
22
  `ElevationMarker`, `TableRow`, `Table`, `SheetAnnotations`, and
23
23
  `DrawingAnnotationSet`.
24
24
  - `python_cad_tools.validation`: `ValidationIssue`.
25
+ - `python_cad_tools.recipes`: `RectangleProfile`, `CircleProfile`, `Profile`,
26
+ `ExtrudedProfile`, `DifferenceRecipe`, `ConstructionRecipe`, and
27
+ `evaluate`. See [Construction recipes](neutral-model.md#construction-recipes).
25
28
  - `python_cad_tools.build`: `FormatName`, `BuildOptions`, `ValidationOptions`,
26
29
  `BuildResult`, `ValidationReport`, `build_project`, `validate_project`, and
27
30
  `clean_project`.
@@ -18,6 +18,8 @@ from pathlib import Path
18
18
  from types import ModuleType
19
19
  from typing import Any, cast
20
20
 
21
+ from build123d import Standard_ConstructionError, Standard_Failure, StdFail_NotDone
22
+
21
23
  from ._project_state import authoritative_path, begin_source_tracking, end_source_tracking, record_source
22
24
  from .context import BuildContext, DrawingContext
23
25
  from .drawings import DrawingAnnotationSet, DrawingSheet
@@ -26,8 +28,20 @@ from .drawings._validation import validate_annotation_set
26
28
  from .elements import DesignModel
27
29
  from .exceptions import BuildLockedError, CadToolsError, ModelValidationError, ProjectConfigError
28
30
  from .project_config import ProjectConfig, load_project_config, split_callable_target
31
+ from .recipes import evaluate as evaluate_recipe
29
32
  from .validation import ValidationIssue, validate_model
30
33
 
34
+ # Recipe evaluation failures are intentionally swallowed here (geometry is
35
+ # left None) rather than raised: validate_model's own recipe rule
36
+ # re-evaluates and reports a proper ValidationIssue. This keeps failure
37
+ # reporting on one path instead of having the loader partially duplicate it.
38
+ _RECIPE_EVALUATION_EXCEPTIONS = (
39
+ ModelValidationError,
40
+ Standard_Failure,
41
+ Standard_ConstructionError,
42
+ StdFail_NotDone,
43
+ )
44
+
31
45
  _PROJECT_EXECUTION_LOCK = threading.Lock()
32
46
  _PROJECT_EXECUTION_LOCAL = threading.local()
33
47
  _NAMESPACE_COUNTER = itertools.count()
@@ -144,6 +158,7 @@ def _load_project_model_locked(project_root: Path, *, include_drawings: bool) ->
144
158
  raise ModelValidationError(
145
159
  f"Model entry point {project_config.model_target!r} returned {type(model).__name__}; expected DesignModel"
146
160
  )
161
+ _evaluate_recipes(model)
147
162
  issues = tuple(validate_model(model))
148
163
  drawing_sheets: tuple[DrawingSheet, ...] = ()
149
164
  annotations: DrawingAnnotationSet | None = None
@@ -169,7 +184,11 @@ def _load_project_model_locked(project_root: Path, *, include_drawings: bool) ->
169
184
  accessed.update(_project_module_sources(project_root, snapshot.modules))
170
185
  accessed.update(end_source_tracking(token))
171
186
  tracking_active = False
172
- undeclared = sorted(accessed - project_config.declared_sources)
187
+ undeclared = sorted(
188
+ path
189
+ for path in accessed - project_config.declared_sources
190
+ if not _is_always_authoritative(path)
191
+ )
173
192
  if undeclared:
174
193
  raise ProjectConfigError(
175
194
  "Loaded authoritative project input(s) missing from source-inputs: " + ", ".join(undeclared)
@@ -192,6 +211,39 @@ def _load_project_model_locked(project_root: Path, *, include_drawings: bool) ->
192
211
  snapshot.restore()
193
212
 
194
213
 
214
+ def _is_always_authoritative(relative_path: str) -> bool:
215
+ """``models/**/*.py`` never needs an explicit ``source-inputs`` entry.
216
+
217
+ Every other authoritative input -- ``pyproject.toml``, ``config.py``, ``model.py``,
218
+ ``drawing-annotations`` -- is a single, fixed entry point that a project declares once
219
+ and rarely changes; requiring it in ``source-inputs`` catches an accidental undeclared
220
+ import. ``models/`` is different by design (see docs/project-configuration.md): it is
221
+ the one directory a project is expected to keep adding files to over its lifetime, one
222
+ new module per new design object, with no fixed membership to declare up front. Making
223
+ each new file a `source-inputs` edit before it can be imported would turn an expected,
224
+ routine addition into a two-file change for no safety benefit -- the manifest hash
225
+ already covers every accessed path regardless of declaration (`accessed`, not
226
+ `declared_sources`, feeds `source_inputs` on `LoadedProject` below), so this exemption
227
+ narrows only the *validation gate*, not what a build's determinism claim is based on.
228
+ """
229
+ return relative_path.startswith("models/") and relative_path.endswith(".py")
230
+
231
+
232
+ def _evaluate_recipes(model: DesignModel) -> None:
233
+ """Materialize ``element.geometry`` from ``element.recipe`` before
234
+ validation, but only when ``geometry`` is not already supplied — the
235
+ recipe+geometry authoring shape keeps the author's object identity, and
236
+ its equivalence check happens independently inside ``validate_model``.
237
+ """
238
+ for element in model.walk():
239
+ if element.recipe is None or element.geometry is not None:
240
+ continue
241
+ try:
242
+ element.geometry = evaluate_recipe(element.recipe)
243
+ except _RECIPE_EVALUATION_EXCEPTIONS:
244
+ continue
245
+
246
+
195
247
  def _install_namespace(namespace: str, project_root: Path) -> None:
196
248
  package = ModuleType(namespace)
197
249
  package.__file__ = None
@@ -20,6 +20,7 @@ from .validation import ValidationIssue
20
20
  BUILD_MANIFEST_RELATIVE = "manifests/build-manifest.json"
21
21
  _HEX = re.compile(r"[0-9a-f]{64}\Z")
22
22
  _ARTIFACT_FIELDS = {"path", "size", "sha256", "media_type", "producer", "schema_id", "schema_version", "volatile"}
23
+ _EXPECTED_REPRESENTATION_TYPE = {None: "Tessellation", "extruded_profile": "SweptSolid", "difference": "CSG"}
23
24
 
24
25
 
25
26
  @dataclass(frozen=True, slots=True)
@@ -432,6 +433,25 @@ def _reconcile_ifc_extensions(
432
433
  }
433
434
  if actual_openings != expected_openings:
434
435
  raise ArtifactIntegrityError("IFC openings do not match the design manifest")
436
+ expected_representation_types = {
437
+ item["id"]: _EXPECTED_REPRESENTATION_TYPE[item["recipe_kind"]] for item in expected_elements
438
+ }
439
+ actual_representation_types = report.get("element_representation_types", {})
440
+ if actual_representation_types != expected_representation_types:
441
+ raise ArtifactIntegrityError("IFC representation types do not match the design manifest")
442
+ expected_topology = {
443
+ item["id"]: item["topology"] for item in expected_elements if item.get("recipe_kind") is not None
444
+ }
445
+ actual_topology = report.get("element_topology", {})
446
+ if set(actual_topology) != set(expected_topology):
447
+ raise ArtifactIntegrityError("IFC topology does not match the design manifest")
448
+ for element_id, expected_values in expected_topology.items():
449
+ actual_values = actual_topology[element_id]
450
+ for key in ("face_count", "edge_count"):
451
+ if expected_values[key] != actual_values[key]:
452
+ raise ArtifactIntegrityError(f"IFC topology {key} mismatch for {element_id}")
453
+ if abs(expected_values["volume_mm3"] - actual_values["volume_mm3"]) > 1e-6:
454
+ raise ArtifactIntegrityError(f"IFC topology volume_mm3 mismatch for {element_id}")
435
455
  if "quantities" in formats:
436
456
  quantities = load_json_document(root / "quantities/quantities.json")
437
457
  records_by_id = {record["element_id"]: record for record in quantities["records"]}
@@ -1 +1 @@
1
- {"distribution":"python-cad-tools","schema_id":"urn:python-cad-tools:schema:package-build-info:1","schema_version":1,"source_commit":"cc89617eb61cffaf765a1b3e89ab4be4913e5dc7","source_dirty":true,"version":"0.1.11.dev1","viewer_shell_sha256":"2dac2bfef7884c40edb8abab348d7a574cda2037b5a3bbe8808acc7662e27d54"}
1
+ {"distribution":"python-cad-tools","schema_id":"urn:python-cad-tools:schema:package-build-info:1","schema_version":1,"source_commit":"88544bff4c35b1b804776231bf4db8aac1b5c5f1","source_dirty":true,"version":"0.1.14.dev1","viewer_shell_sha256":"2dac2bfef7884c40edb8abab348d7a574cda2037b5a3bbe8808acc7662e27d54"}