draftwright 0.4.20__tar.gz → 0.4.22__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 (550) hide show
  1. {draftwright-0.4.20 → draftwright-0.4.22}/CHANGELOG.md +3 -0
  2. {draftwright-0.4.20 → draftwright-0.4.22}/PKG-INFO +2 -2
  3. {draftwright-0.4.20 → draftwright-0.4.22}/docs/reference/recogniser-capabilities.md +26 -19
  4. {draftwright-0.4.20 → draftwright-0.4.22}/docs/reference/sheet.md +95 -0
  5. {draftwright-0.4.20 → draftwright-0.4.22}/pyproject.toml +11 -6
  6. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/_geometry.py +22 -1
  7. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/_common.py +26 -3
  8. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/from_model.py +88 -40
  9. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/holes.py +34 -18
  10. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/audit.py +210 -69
  11. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/compose.py +14 -0
  12. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/drawing.py +129 -15
  13. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/inspection_contract.py +1 -1
  14. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/blend_coverage.py +6 -22
  15. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/coverage.py +36 -5
  16. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/evidence.py +2 -6
  17. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/hole_coverage.py +163 -9
  18. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/pocket_coverage.py +18 -3
  19. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/pocket_pattern_coverage.py +24 -2
  20. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/profiled_bore_coverage.py +55 -8
  21. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/quality.py +2 -0
  22. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/structural.py +41 -28
  23. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/suggest.py +8 -0
  24. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/model/compiled.py +115 -53
  25. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/model/declare.py +20 -1
  26. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/model/detect.py +2 -1
  27. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/model/ir.py +73 -8
  28. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/model/planner.py +209 -64
  29. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/oriented_slot_contract.py +4 -38
  30. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/recogniser_contract.py +15 -4
  31. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/recogniser_schema.py +11 -5
  32. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/repair.py +86 -39
  33. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/section_recess_contract.py +214 -18
  34. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/sheet.py +60 -13
  35. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/sheet_emit.py +58 -49
  36. draftwright-0.4.22/tests/_evidence_contract.py +61 -0
  37. draftwright-0.4.22/tests/_mutation_corpus.py +127 -0
  38. {draftwright-0.4.20 → draftwright-0.4.22}/tests/_section_recess_cases.py +11 -0
  39. {draftwright-0.4.20 → draftwright-0.4.22}/tests/conftest.py +2 -0
  40. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_adr0018_view_selection.py +13 -9
  41. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_audit_differential.py +2 -2
  42. draftwright-0.4.22/tests/test_clone_budget.py +149 -0
  43. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_compiled_plan_boundary.py +5 -1
  44. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_e2e_standards.py +31 -3
  45. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_inspection_contract.py +1 -1
  46. draftwright-0.4.22/tests/test_issue_1006_measurement_comparison.py +269 -0
  47. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1058_wheel_profile.py +19 -21
  48. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1143_hole_completeness.py +64 -10
  49. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1146_scale_completeness.py +1 -1
  50. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1155_grm04_sheet_use.py +5 -1
  51. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1176_quality_fidelity.py +4 -1
  52. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1262_automatic_turned_views.py +2 -1
  53. draftwright-0.4.22/tests/test_issue_1333_bounded_ink_repair.py +287 -0
  54. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1352_searchable_pdf_text.py +5 -5
  55. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1357_framed_activation.py +14 -3
  56. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1369_hole_completeness_evidence.py +9 -5
  57. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1370_countersink_completeness_evidence.py +30 -20
  58. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1370_double_d_completeness_evidence.py +21 -17
  59. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1370_hole_pattern_completeness_evidence.py +21 -15
  60. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1371_flat_completeness_evidence.py +31 -33
  61. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1371_polygonal_stock_completeness_evidence.py +23 -27
  62. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1372_groove_completeness_evidence.py +43 -37
  63. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1372_pad_completeness_evidence.py +31 -36
  64. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1372_pocket_completeness_evidence.py +31 -33
  65. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1372_pocket_pattern_completeness_evidence.py +23 -29
  66. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1372_polygonal_boss_completeness_evidence.py +28 -31
  67. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1373_plate_completeness_evidence.py +37 -40
  68. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1374_chamfer_completeness_evidence.py +32 -33
  69. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1374_fillet_completeness_evidence.py +30 -33
  70. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1374_turned_step_completeness_evidence.py +32 -33
  71. draftwright-0.4.22/tests/test_issue_1378_diameter_leader_targets.py +205 -0
  72. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1382_framed_step_family_evidence.py +3 -1
  73. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1382_through_step_semantics.py +7 -3
  74. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1460_step_inspection.py +1 -0
  75. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1466_dimension_options.py +6 -2
  76. draftwright-0.4.22/tests/test_issue_1466_semantic_sides.py +317 -0
  77. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1471_boundary_failures.py +24 -30
  78. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1471_section_recess_contract.py +13 -5
  79. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1471_section_recess_pockets.py +35 -24
  80. draftwright-0.4.22/tests/test_issue_1485_curved_pockets.py +320 -0
  81. draftwright-0.4.22/tests/test_issue_1485_quiddity_024_boundary.py +71 -0
  82. draftwright-0.4.22/tests/test_issue_1485_rounded_pockets.py +286 -0
  83. draftwright-0.4.22/tests/test_issue_827_real_part_canary.py +143 -0
  84. draftwright-0.4.22/tests/test_issue_883_location_identity.py +575 -0
  85. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_916_pocket_leader.py +4 -28
  86. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_917_open_channel.py +0 -13
  87. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_make_drawing.py +18 -23
  88. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_pitch_dim_footprint.py +10 -3
  89. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_pocket_pattern_recognition.py +1 -9
  90. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_quality_components.py +1 -1
  91. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_recogniser_capabilities.py +45 -5
  92. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_recogniser_contract.py +3 -3
  93. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_score.py +0 -5
  94. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_sheet_emit.py +7 -2
  95. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_step_analysis_evaluation.py +13 -0
  96. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_suppression_ledger.py +4 -1
  97. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_tolerances.py +11 -18
  98. draftwright-0.4.22/tests/test_version_bump_ci.py +250 -0
  99. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_workflows.py +216 -17
  100. draftwright-0.4.20/tests/test_recognition.py +0 -861
  101. {draftwright-0.4.20 → draftwright-0.4.22}/.gitignore +0 -0
  102. {draftwright-0.4.20 → draftwright-0.4.22}/LICENSE +0 -0
  103. {draftwright-0.4.20 → draftwright-0.4.22}/README.md +0 -0
  104. {draftwright-0.4.20 → draftwright-0.4.22}/docs/adr/README.md +0 -0
  105. {draftwright-0.4.20 → draftwright-0.4.22}/docs/research/1062-repeating-radial-profile-evidence.md +0 -0
  106. {draftwright-0.4.20 → draftwright-0.4.22}/skills/SKILL.md +0 -0
  107. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/__init__.py +0 -0
  108. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/_build_profile.py +0 -0
  109. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/_core.py +0 -0
  110. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/_pmi_part21.py +0 -0
  111. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/_warnings.py +0 -0
  112. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/analysis.py +0 -0
  113. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotate.py +0 -0
  114. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/__init__.py +0 -0
  115. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/balloons.py +0 -0
  116. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/gears.py +0 -0
  117. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/leaders.py +0 -0
  118. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/orchestrator.py +0 -0
  119. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/annotations/sections.py +0 -0
  120. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/blend_contract.py +0 -0
  121. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/builder.py +0 -0
  122. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/cli.py +0 -0
  123. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/evaluation/__init__.py +0 -0
  124. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/evaluation/step_analysis.py +0 -0
  125. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/export.py +0 -0
  126. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/feature_identity.py +0 -0
  127. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/fits.py +0 -0
  128. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
  129. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
  130. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
  131. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
  132. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/fonts/__init__.py +0 -0
  133. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/inspection.py +0 -0
  134. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/intents.py +0 -0
  135. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/layout.py +0 -0
  136. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/__init__.py +0 -0
  137. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/_registry.py +0 -0
  138. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/angled_step_coverage.py +0 -0
  139. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/chamfer_coverage.py +0 -0
  140. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/channel_coverage.py +0 -0
  141. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/circular_blind_step_coverage.py +0 -0
  142. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/fillet_coverage.py +0 -0
  143. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/flat_coverage.py +0 -0
  144. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/gear_coverage.py +0 -0
  145. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/groove_coverage.py +0 -0
  146. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/ink_overlap.py +0 -0
  147. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/issues.py +0 -0
  148. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/oriented_slot_coverage.py +0 -0
  149. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/pad_coverage.py +0 -0
  150. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/paired_ramp_step_coverage.py +0 -0
  151. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/passage_coverage.py +0 -0
  152. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/plate_coverage.py +0 -0
  153. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/pmi_coverage.py +0 -0
  154. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/polygonal_boss_coverage.py +0 -0
  155. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/polygonal_stock_coverage.py +0 -0
  156. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/prismatic_pocket_coverage.py +0 -0
  157. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/rectangular_blind_slot_coverage.py +0 -0
  158. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/requirements.py +0 -0
  159. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/round_bottom_blind_slot_coverage.py +0 -0
  160. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/section_recess_coverage.py +0 -0
  161. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/slot_coverage.py +0 -0
  162. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/through_step_coverage.py +0 -0
  163. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/linting/turned_step_coverage.py +0 -0
  164. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/make_drawing.py +0 -0
  165. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/model/__init__.py +0 -0
  166. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/model/callout.py +0 -0
  167. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/model/pmi_lowering.py +0 -0
  168. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/plate_correspondence.py +0 -0
  169. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/pmi.py +0 -0
  170. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/projection.py +0 -0
  171. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/py.typed +0 -0
  172. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/recogniser_policy.py +0 -0
  173. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/recognition/__init__.py +0 -0
  174. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/recognition_cache.py +0 -0
  175. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/recognition_frame.py +0 -0
  176. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/recognition_ownership.py +0 -0
  177. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/registry.py +0 -0
  178. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/reporting.py +0 -0
  179. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/score.py +0 -0
  180. {draftwright-0.4.20 → draftwright-0.4.22}/src/draftwright/view_plan.py +0 -0
  181. {draftwright-0.4.20 → draftwright-0.4.22}/tests/_kernel.py +0 -0
  182. {draftwright-0.4.20 → draftwright-0.4.22}/tests/_layout_sig.py +0 -0
  183. {draftwright-0.4.20 → draftwright-0.4.22}/tests/_recogniser_public_contract.py +0 -0
  184. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/ap242_single_cylinder_diameter.step +0 -0
  185. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/README.md +0 -0
  186. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/ambiguous-open-semicircle.step +0 -0
  187. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/blind-hole.step +0 -0
  188. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-asymmetric.step +0 -0
  189. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-compound.step +0 -0
  190. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-overlap.step +0 -0
  191. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-plain.step +0 -0
  192. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-planar-x.step +0 -0
  193. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-planar-y.step +0 -0
  194. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-planar-z.step +0 -0
  195. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-topology-a.step +0 -0
  196. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-topology-b.step +0 -0
  197. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-turned.step +0 -0
  198. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-chamfers-v1.json +0 -0
  199. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-countersinks-v1.json +0 -0
  200. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-double-d-bores-v1.json +0 -0
  201. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-fillets-v1.json +0 -0
  202. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-flats-v1.json +0 -0
  203. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-grooves-v1.json +0 -0
  204. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-hole-patterns-v1.json +0 -0
  205. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-plates-v1.json +0 -0
  206. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-pocket-patterns-v1.json +0 -0
  207. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-pockets-v1.json +0 -0
  208. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-polygonal-bosses-v1.json +0 -0
  209. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-polygonal-stock-v1.json +0 -0
  210. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-rectangular-pads-v1.json +0 -0
  211. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-turned-steps-v1.json +0 -0
  212. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-v1.json +0 -0
  213. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-deburr.step +0 -0
  214. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-external-cone.step +0 -0
  215. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-mixed-pair.step +0 -0
  216. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-single.step +0 -0
  217. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-topology-a.step +0 -0
  218. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-topology-b.step +0 -0
  219. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-axis-x.step +0 -0
  220. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-axis-y.step +0 -0
  221. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-blind.step +0 -0
  222. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-coaxial-compound.step +0 -0
  223. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-opposed-blind.step +0 -0
  224. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-roll-30.step +0 -0
  225. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-round-bore.step +0 -0
  226. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-single-z.step +0 -0
  227. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-topology-a.step +0 -0
  228. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-topology-b.step +0 -0
  229. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-compound.step +0 -0
  230. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-overlap.step +0 -0
  231. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-plain.step +0 -0
  232. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-planar-x.step +0 -0
  233. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-planar-y.step +0 -0
  234. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-planar-z.step +0 -0
  235. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-repeated.step +0 -0
  236. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-topology-a.step +0 -0
  237. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-topology-b.step +0 -0
  238. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-turned.step +0 -0
  239. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-coaxial.step +0 -0
  240. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-double-d.step +0 -0
  241. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-lone-d.step +0 -0
  242. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-nonround.step +0 -0
  243. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-slanted-double-d.step +0 -0
  244. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-topology-a.step +0 -0
  245. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-topology-b.step +0 -0
  246. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-compound.step +0 -0
  247. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-lone-x.step +0 -0
  248. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-lone-y.step +0 -0
  249. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-lone-z.step +0 -0
  250. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-monotonic-negative.step +0 -0
  251. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-narrow.step +0 -0
  252. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-topology-a.step +0 -0
  253. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-topology-b.step +0 -0
  254. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-compound-equal.step +0 -0
  255. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-detached-body-negative.step +0 -0
  256. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-full-span-ledge-negative.step +0 -0
  257. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-nested-staircase-negative.step +0 -0
  258. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-topology-a.step +0 -0
  259. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-topology-b.step +0 -0
  260. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-x-negative.step +0 -0
  261. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-x-positive.step +0 -0
  262. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-y-negative.step +0 -0
  263. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-y-positive.step +0 -0
  264. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-z-negative.step +0 -0
  265. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-z-positive.step +0 -0
  266. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pattern-ambiguous.step +0 -0
  267. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pattern-grid.step +0 -0
  268. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pattern-topology-a.step +0 -0
  269. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pattern-topology-b.step +0 -0
  270. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plain-block.step +0 -0
  271. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-compound-equal.step +0 -0
  272. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-cross-topology-a.step +0 -0
  273. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-cross-topology-b.step +0 -0
  274. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-detached-negative.step +0 -0
  275. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-rotational-negative.step +0 -0
  276. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-single-negative.step +0 -0
  277. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-t-xz.step +0 -0
  278. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-t-yz.step +0 -0
  279. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-thick-negative.step +0 -0
  280. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-u-additive.step +0 -0
  281. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-u-cut.step +0 -0
  282. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-compound.step +0 -0
  283. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-edge-anchored.step +0 -0
  284. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-lone.step +0 -0
  285. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-opposed.step +0 -0
  286. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-ambiguous.step +0 -0
  287. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-grid.step +0 -0
  288. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-linear.step +0 -0
  289. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-pair.step +0 -0
  290. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-topology-a.step +0 -0
  291. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-topology-b.step +0 -0
  292. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-prismatic-negative.step +0 -0
  293. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-side.step +0 -0
  294. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-through-negative.step +0 -0
  295. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-topology-a.step +0 -0
  296. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-topology-b.step +0 -0
  297. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-two-equal.step +0 -0
  298. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-compound-equal.step +0 -0
  299. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-detached-negative.step +0 -0
  300. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-in-plane-rotated.step +0 -0
  301. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-other-protrusions-negative.step +0 -0
  302. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-recess-negative.step +0 -0
  303. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-stock-negative.step +0 -0
  304. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-topology-a.step +0 -0
  305. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-topology-b.step +0 -0
  306. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-x.step +0 -0
  307. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-y.step +0 -0
  308. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-z.step +0 -0
  309. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-compound-negative.step +0 -0
  310. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-cylinder-negative.step +0 -0
  311. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-irregular-negative.step +0 -0
  312. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-octagon-negative.step +0 -0
  313. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-recess-negative.step +0 -0
  314. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-roll17.step +0 -0
  315. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-topology-a.step +0 -0
  316. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-topology-b.step +0 -0
  317. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-translated.step +0 -0
  318. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-x.step +0 -0
  319. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-y.step +0 -0
  320. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/topology-a.step +0 -0
  321. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/topology-b.step +0 -0
  322. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-axis-x.step +0 -0
  323. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-axis-y.step +0 -0
  324. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-axis-z.step +0 -0
  325. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-compound.step +0 -0
  326. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-repeated-lengths.step +0 -0
  327. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-through-bore.step +0 -0
  328. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-topology-a.step +0 -0
  329. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-topology-b.step +0 -0
  330. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-translated-blind-bore.step +0 -0
  331. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
  332. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/grm03_thumbwheel_drive_screw_ap242_pmi.step +0 -0
  333. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/grm04_drive_plate.step +0 -0
  334. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
  335. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
  336. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/issue_1247_angled_blind_step.step +0 -0
  337. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
  338. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/issue_915_case_study_2.step +0 -0
  339. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
  340. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
  341. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
  342. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
  343. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
  344. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
  345. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
  346. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
  347. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
  348. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
  349. {draftwright-0.4.20 → draftwright-0.4.22}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
  350. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/bracket_section.json +0 -0
  351. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/centered_rebate.json +0 -0
  352. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/chamfered.json +0 -0
  353. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/filleted.json +0 -0
  354. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/flange_dense.json +0 -0
  355. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/grid_plate.json +0 -0
  356. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/grooved_shaft.json +0 -0
  357. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/hex_bar.json +0 -0
  358. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/holed_slot.json +0 -0
  359. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/pocketed.json +0 -0
  360. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/prismatic_ladder.json +0 -0
  361. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/scattered_plate.json +0 -0
  362. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/side_drilled.json +0 -0
  363. {draftwright-0.4.20 → draftwright-0.4.22}/tests/refactor_golden/turned_stepped.json +0 -0
  364. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_add_dimension.py +0 -0
  365. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_adr0018_arrangement_gate.py +0 -0
  366. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_adr0018_view_routing.py +0 -0
  367. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_adr_corpus.py +0 -0
  368. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_agents_guide.py +0 -0
  369. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_api_docs.py +0 -0
  370. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_architecture_docs.py +0 -0
  371. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_balloon_ring_standoff.py +0 -0
  372. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_build_profile_harness.py +0 -0
  373. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_carve_free_position_callers.py +0 -0
  374. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_counting_calls.py +0 -0
  375. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_declare.py +0 -0
  376. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_declared_recognition_gate.py +0 -0
  377. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_dense_sheet_canary.py +0 -0
  378. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_deprecation_dates.py +0 -0
  379. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_detect_once.py +0 -0
  380. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_detect_registry.py +0 -0
  381. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_dimension_role_vocabulary.py +0 -0
  382. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_drawing_encapsulation.py +0 -0
  383. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_e2e_slice.py +0 -0
  384. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_export_dxf_curves.py +0 -0
  385. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_export_dxf_zoom.py +0 -0
  386. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_export_reproducible.py +0 -0
  387. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_external_recognition_boundary.py +0 -0
  388. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_fillets_adjacency.py +0 -0
  389. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_fits.py +0 -0
  390. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_flat_completeness.py +0 -0
  391. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_flat_stock_identity.py +0 -0
  392. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_flat_stock_opposition.py +0 -0
  393. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_gdt_placement.py +0 -0
  394. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_geometry_graph_spike.py +0 -0
  395. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_grid_lattice_convention.py +0 -0
  396. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_import_boundaries.py +0 -0
  397. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_iso_nts_caption_placement.py +0 -0
  398. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1000_envelope_reuse.py +0 -0
  399. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1073_cross_body_patterns.py +0 -0
  400. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1082_polygonal_stock.py +0 -0
  401. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1086_declared_gears.py +0 -0
  402. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1116_ap242_hole_tolerance_lowering.py +0 -0
  403. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1130_view_planning_evidence.py +0 -0
  404. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1142_hole_leader_labels.py +0 -0
  405. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1144_transactional_hole_table.py +0 -0
  406. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1147_legibility_pair_aggregation.py +0 -0
  407. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1153_contradictory_dimensions.py +0 -0
  408. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1154_one_owner_per_measurement.py +0 -0
  409. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1166_cross_pass_feature_leaders.py +0 -0
  410. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1177_angular_dimensions.py +0 -0
  411. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1187_unroutable_leaders.py +0 -0
  412. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1188_per_view_assignment.py +0 -0
  413. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1190_section_decision.py +0 -0
  414. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1196_deterministic_view_names.py +0 -0
  415. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1202_observed_drawing_consumer.py +0 -0
  416. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1204_multiscale_view_issues.py +0 -0
  417. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1209_linear_pmi_witnesses.py +0 -0
  418. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1215_envelope_tolerance.py +0 -0
  419. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1215_no_approved_tolerance_is_dropped.py +0 -0
  420. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1216_callout_reservation_measures_ink.py +0 -0
  421. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1216_pairwise_across_scale_groups.py +0 -0
  422. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1217_claimed_representations.py +0 -0
  423. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1217_the_facility_is_shared.py +0 -0
  424. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1219_location_claims_its_own_axis.py +0 -0
  425. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1229_ledger_member_keying.py +0 -0
  426. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1240_iso_above_strip_visibility.py +0 -0
  427. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1245_passage_disposition.py +0 -0
  428. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1246_prismatic_pocket_disposition.py +0 -0
  429. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1247_angled_step_disposition.py +0 -0
  430. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1254_turned_chamfers.py +0 -0
  431. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1260_view_constraints.py +0 -0
  432. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1276_turned_leader_targets.py +0 -0
  433. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1281_turned_fillets.py +0 -0
  434. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1296_cylindrical_diameter_pmi.py +0 -0
  435. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1298_manufacturing_requirements.py +0 -0
  436. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1299_page_escalation.py +0 -0
  437. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1308_machined_leader_analytics.py +0 -0
  438. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1312_engine_costs.py +0 -0
  439. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1325_nominal_agreement.py +0 -0
  440. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1332_overlap_remedy.py +0 -0
  441. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1332_zone_labels.py +0 -0
  442. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1334_prevent_ink.py +0 -0
  443. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1336_grm03_ap242_pmi_fixture.py +0 -0
  444. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1338_scale_before_page_escalation.py +0 -0
  445. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1349_precision_display.py +0 -0
  446. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1350_detected_takeover.py +0 -0
  447. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1351_structured_note_coverage.py +0 -0
  448. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1353_cnc_authoritative_workflow.py +0 -0
  449. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1357_framed_boundary.py +0 -0
  450. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1357_plural_turned_profiles.py +0 -0
  451. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1357_pmi_frame.py +0 -0
  452. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1360_thread_depth.py +0 -0
  453. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1382_046_step_inventory.py +0 -0
  454. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1382_circular_blind_step_semantics.py +0 -0
  455. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1382_paired_ramp_semantics.py +0 -0
  456. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1390_sheet_registration.py +0 -0
  457. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1392_recognisers_048.py +0 -0
  458. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1395_degenerate_iso_region.py +0 -0
  459. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1396_layout_advisories.py +0 -0
  460. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1397_unverifiable_requirement_message.py +0 -0
  461. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1421_rectangular_blind_slot_completeness.py +0 -0
  462. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1421_rectangular_blind_slot_semantics.py +0 -0
  463. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1421_round_bottom_blind_slot_completeness.py +0 -0
  464. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1421_round_bottom_blind_slot_semantics.py +0 -0
  465. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1432_adoption.py +0 -0
  466. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1432_boundary_failures.py +0 -0
  467. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1432_oriented_slot_semantics.py +0 -0
  468. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1433_blend_semantics.py +0 -0
  469. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_boss_ownership.py +0 -0
  470. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_channel_ownership.py +0 -0
  471. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_cli_report_sidecar.py +0 -0
  472. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_generation_snapshot.py +0 -0
  473. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_nested_ownership.py +0 -0
  474. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_occurrence_ownership.py +0 -0
  475. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_pattern_ownership.py +0 -0
  476. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_plate_ownership.py +0 -0
  477. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_policy_dispositions.py +0 -0
  478. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_report_projection.py +0 -0
  479. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_report_writer.py +0 -0
  480. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_through_step_ownership.py +0 -0
  481. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1438_turned_step_ownership.py +0 -0
  482. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1450_boss_blend_ownership.py +0 -0
  483. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1471_evidence_schema.py +0 -0
  484. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1471_groove_profile.py +0 -0
  485. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_1479_radius_leader_targets.py +0 -0
  486. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_367_leader_ink.py +0 -0
  487. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_443_grm03_axial_coverage.py +0 -0
  488. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_563_placement_intent.py +0 -0
  489. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_676_polygonal_boss.py +0 -0
  490. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
  491. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_740_leader_assignment.py +0 -0
  492. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_798_floor_cardinality.py +0 -0
  493. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_798_material_field.py +0 -0
  494. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_798_silhouette_lint.py +0 -0
  495. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_885_prismatic_coverage.py +0 -0
  496. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_909_sloped_profile.py +0 -0
  497. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_915_dense_case.py +0 -0
  498. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_924_zero_length_shoulders.py +0 -0
  499. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_955_height_contingency.py +0 -0
  500. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_issue_958_cross_solid_recognition.py +0 -0
  501. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_label_provenance.py +0 -0
  502. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_layout.py +0 -0
  503. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_layout_cleanliness.py +0 -0
  504. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_layout_hypothesis.py +0 -0
  505. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_layout_property.py +0 -0
  506. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_leader_footprint.py +0 -0
  507. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_lint_box_cache.py +0 -0
  508. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_lint_ink_overlap.py +0 -0
  509. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_lint_reconciliation.py +0 -0
  510. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_lint_structural.py +0 -0
  511. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_linting.py +0 -0
  512. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_location_vocabulary.py +0 -0
  513. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_note_verb.py +0 -0
  514. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_object_aspects.py +0 -0
  515. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_occupancy_boxes.py +0 -0
  516. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_parameter_id.py +0 -0
  517. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_part_model.py +0 -0
  518. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_pmi.py +0 -0
  519. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_pmi_datum_lowering.py +0 -0
  520. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_pmi_gtol_lowering.py +0 -0
  521. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_pmi_part21.py +0 -0
  522. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_pocket_pattern.py +0 -0
  523. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_principal_profile_classifier.py +0 -0
  524. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_private_test_attr_reads.py +0 -0
  525. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_private_test_imports.py +0 -0
  526. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_recogniser_adoption.py +0 -0
  527. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_recognition_result.py +0 -0
  528. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_refactor_golden.py +0 -0
  529. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_registry.py +0 -0
  530. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_render_seam.py +0 -0
  531. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_repack_geometry_seam.py +0 -0
  532. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_script_detail_parity.py +0 -0
  533. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_shared_box_memo.py +0 -0
  534. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_sheet_gdt.py +0 -0
  535. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_sheet_identity_invariant.py +0 -0
  536. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_sheet_notes.py +0 -0
  537. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_sheet_of.py +0 -0
  538. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_sheet_section.py +0 -0
  539. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_sheet_tables.py +0 -0
  540. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_slanted_blind_step.py +0 -0
  541. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_slot_completeness.py +0 -0
  542. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_slot_pattern.py +0 -0
  543. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_slot_pattern_recognition.py +0 -0
  544. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_soft_deprecation.py +0 -0
  545. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_solve_trace.py +0 -0
  546. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_strip_layout.py +0 -0
  547. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_suppression_marks.py +0 -0
  548. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_turned_steps.py +0 -0
  549. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_view_plan.py +0 -0
  550. {draftwright-0.4.20 → draftwright-0.4.22}/tests/test_witness_label_reconciliation.py +0 -0
@@ -154,6 +154,9 @@
154
154
 
155
155
  ### Changed
156
156
 
157
+ - Updated the exact Quiddity runtime and declared-inspection contract to 0.2.5 for the
158
+ provider's recognition performance improvements.
159
+
157
160
  - Production recognition now consumes published `quiddity==0.2.2`. Unified section-recess
158
161
  occurrences and their same-run patterns feed the existing pocket, channel and blind-slot
159
162
  drawing grammar. Original occurrence ownership, independent completeness and explicit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: draftwright
3
- Version: 0.4.20
3
+ Version: 0.4.22
4
4
  Summary: Automated technical-drawing generation for build123d
5
5
  Project-URL: Homepage, https://github.com/pzfreo/draftwright
6
6
  Project-URL: Repository, https://github.com/pzfreo/draftwright
@@ -690,7 +690,7 @@ Requires-Dist: build123d<0.12,>=0.11; python_full_version >= '3.13'
690
690
  Requires-Dist: numpy>=1.24
691
691
  Requires-Dist: pillow>=10
692
692
  Requires-Dist: pypdfium2>=4
693
- Requires-Dist: quiddity==0.2.2
693
+ Requires-Dist: quiddity==0.2.5
694
694
  Requires-Dist: reportlab>=4.0
695
695
  Requires-Dist: steputils==0.1
696
696
  Requires-Dist: svglib>=1.5
@@ -70,10 +70,11 @@ issue. The contract test derives package record outputs, converter registries, l
70
70
  and emitter branches independently, so copying a new name into the declaration alone cannot make CI
71
71
  pass.
72
72
 
73
- ### Quiddity 0.2.2 runtime contract
73
+ ### Quiddity 0.2.5 runtime contract
74
74
 
75
- [#1471](https://github.com/pzfreo/draftwright/issues/1471) moves production recognition to the
76
- published `quiddity==0.2.2` package. Its public `quiddity`, `quiddity.evidence` and
75
+ The runtime pins the published `quiddity==0.2.5` package, following the 0.2.4 adoption in
76
+ [#1486](https://github.com/pzfreo/draftwright/pull/1486) and the migration in
77
+ [#1471](https://github.com/pzfreo/draftwright/issues/1471). Its public `quiddity`, `quiddity.evidence` and
77
78
  `quiddity.inspection` surfaces supply recognition, exact occurrence evidence and declared geometry
78
79
  reads. There is one recognition aggregate per build; consumers project that result rather than
79
80
  calling the document builder to obtain another run.
@@ -87,8 +88,10 @@ builder is a serialization front door, not another feature family or a productio
87
88
 
88
89
  The shared adapter reads the published frame, profile, run interval and ends and lowers supported
89
90
  principal-axis geometry to the existing pocket, channel and blind-slot IR. It preserves the opening
90
- direction and edge anchoring and rejects malformed records. Unsupported profiles and sloped ends
91
- remain explicit unsupported outcomes; they receive no invented dimensions. Pattern members resolve
91
+ direction and edge anchoring and rejects malformed records. Supported rounded rectangular pockets
92
+ retain their corner radius, and supported cylindrical-mouth pockets carry maximum-depth intent
93
+ rather than an invented uniform depth. Valid profiles and end shapes outside the drafting grammar
94
+ remain explicit unsupported outcomes, including the two-plane passage cases. Pattern members resolve
92
95
  to the original records in the same aggregate before they share a drawing owner. Missing members,
93
96
  duplicate indices, cross-body grouping and inconsistent lattice geometry fail closed.
94
97
 
@@ -98,20 +101,24 @@ Recognition refusals retain source provenance and contribute an unsupported outc
98
101
  fabricated position. Report and inspection schema v2 name `producer.quiddity`; their v1 schemas
99
102
  remain available for existing documents.
100
103
 
101
- The 0.4.20 release includes these known Quiddity 0.2.2 limitations:
102
-
103
- - Some mixed line/arc and nested pockets, including the tuner-jig and dense #915 STEP fixtures,
104
- are refused: [Quiddity #536](https://github.com/pzfreo/quiddity/issues/536).
105
- - Bores intersecting channel walls, floors or an edge-open recess can prevent recognition:
106
- [Quiddity #538](https://github.com/pzfreo/quiddity/issues/538).
107
- - A pocket cut into cylindrical stock can be refused:
108
- [Quiddity #541](https://github.com/pzfreo/quiddity/issues/541).
109
-
110
- These cases can lose automatic annotations that the previous provider emitted. Check recognition
111
- refusals and lint when using affected geometry. An explicit refusal makes the limitation visible;
112
- it does not establish drawing completeness. The regression cases remain in the suite as strict
113
- expected failures linked to the upstream issues. Other recognition, declaration, placement,
114
- reporting and coverage checks remain release gates.
104
+ The common-part restoration checks the five rounded tuner-jig pockets and their grouped callout,
105
+ corner radii, pitch and absolute locations; pockets cut into cylindrical stock; and the pierced
106
+ U-channel's supported channel measurements. Automatic recognition, declared input and executed
107
+ Sheet scripts retain the supported recess facts. These checks do not turn a successfully rendered
108
+ part into a completeness guarantee.
109
+
110
+ The preceding Draftwright 0.4.20 / Quiddity 0.2.2 release documented annotation losses for mixed
111
+ and nested pockets ([Quiddity #536](https://github.com/pzfreo/quiddity/issues/536)), bore-intersected
112
+ or edge-open recesses ([#538](https://github.com/pzfreo/quiddity/issues/538)), and pockets in
113
+ cylindrical stock ([#541](https://github.com/pzfreo/quiddity/issues/541)). Those historical limitations
114
+ must not be used to excuse the common cases restored in #1486. Conversely, recognising more source
115
+ profiles does not imply that every profile now has a supported drafting grammar. General complex
116
+ pockets and nonuniform passages can still produce explicit unsupported requirements.
117
+
118
+ Check the source occurrence outcomes, placed requirements and lint for affected geometry.
119
+ Regression expectations are reassessed per fixture: a restored supported case must pass, while an
120
+ unsupported case must retain its truthful diagnostic rather than an approximate old annotation.
121
+ Recognition, declaration, placement, reporting and coverage checks remain release gates.
115
122
 
116
123
  `bosses` remains a fully consumed reference family. `repeating-radial-profiles` remains geometry-only
117
124
  critique evidence for a separately authored gear declaration, with no inferred gear feature.
@@ -37,6 +37,26 @@ or side override. Invalid references, unsupported controls and unsupported place
37
37
  separate structured refusal codes in `validate_dimension()`; `dimension_options()` raises on an
38
38
  invalid reference.
39
39
 
40
+ Hole-size callouts in the plan and side views accept `left` or `right`. The overall
41
+ envelope height accepts `left` or `right` in the front view. These hints can separate
42
+ hole sizes from vertical location dimensions without moving annotation coordinates:
43
+
44
+ ```python
45
+ sheet.dimension(lower_hole, "bore.diameter", side="left")
46
+ sheet.dimension(upper_hole, "bore.diameter", side="left")
47
+ sheet.dimension(envelope, "height.length", side="left")
48
+ sheet.dimension(lower_hole, "location")
49
+ sheet.dimension(upper_hole, "location")
50
+ ```
51
+
52
+ Each side belongs to its named view: the front-view height and side-view hole sizes
53
+ occupy different boundaries. The layout reserves space for the left height and short
54
+ step rises together before choosing scale and page. The shared solver places the
55
+ annotations within those boundaries; generated scripts retain the authored sides.
56
+ An explicit side is a constraint: an infeasible placement reports the affected
57
+ measurement instead of silently choosing the opposite boundary. Repair preserves an
58
+ authored overall-height side, including after a label adjustment.
59
+
40
60
  The explicit `single_dimension_placement_rules` scope means `supported` reports acceptance by
41
61
  the current planner placement rules. Both documents set `requires_build_validation: true`:
42
62
  whole-part classification can select a different renderer, so this query does not prove actual
@@ -47,8 +67,83 @@ build. Build and lint still assess the resulting Sheet. A
47
67
  query never replaces an existing dimension request. Keep the original feature handle for edits;
48
68
  these reports do not introduce persistent feature identities or new lane, route or pin controls.
49
69
 
70
+ ## Selecting a hole location component
71
+
72
+ Use `location` alone to request the usual location set. To select one component of a hole
73
+ group or pattern, pair its declared member index with the measured world axis:
74
+
75
+ ```python
76
+ options = sheet.dimension_options(holes, "location")
77
+ print(options["location_components"]) # valid member/axis pairs and their parameter ids
78
+ sheet.dimension(holes, "location", member=1, axis="y")
79
+ ```
80
+
81
+ Member indices start at zero and refer to the declared `members` tuple. A singleton hole
82
+ has member 0. The axis is transverse to the hole's own axis: an X-directed hole can have
83
+ Y and Z location dimensions. A bolt-circle pattern also accepts `member="centre"`.
84
+ Both selectors are required for a fine request; invalid members or axes are refused.
85
+ The existing `validate_dimension()` query accepts the same selectors.
86
+
87
+ The coarse pattern request keeps its automatic anchor policy: the member nearest the
88
+ datum, or the centre of a bolt circle. Adding a fine request selects that extra component
89
+ without selecting its sibling axis. Repeating a request does not duplicate a measurement.
90
+ Generated scripts carry the member and axis so removing one location declaration omits
91
+ that component. Coincident dimensions can share one mark while recording every approved
92
+ owner.
93
+
94
+ These indices address one declaration and its generated script. They do not establish
95
+ correspondence across unrelated recognition runs. The shared solver still chooses where
96
+ to place the dimension; location selection supplies no page coordinates.
97
+
50
98
  ## Sheet
51
99
 
100
+ ### Comparing measurements after a declaration edit
101
+
102
+ `compare_measurements()` compares named compiled measurements, including their owner,
103
+ parameter, nominal value, tolerance, directional span and rendered claim text. Linear
104
+ dimensions also retain their measured path lengths in model units, so exchanging labels
105
+ between two spans cannot hide behind an unchanged label multiset. Recorded per-annotation
106
+ spans and location components also distinguish equal-valued axes under a coarse parameter
107
+ id. These are provenance claims; independent lint must still check physical targets.
108
+ Reuse the feature objects in a declaration when editing placement intent:
109
+
110
+ ```python
111
+ from dataclasses import replace
112
+ from draftwright import build_drawing
113
+ from draftwright.audit import compare_measurements
114
+
115
+ original_model = sheet.model()
116
+ edited_model = replace(
117
+ original_model,
118
+ authored_dimensions=tuple(
119
+ replace(request, side="left") if request.role == "height.length" else request
120
+ for request in original_model.authored_dimensions
121
+ ),
122
+ )
123
+ before = build_drawing(part, model=original_model)
124
+ after = build_drawing(part, model=edited_model)
125
+ result = compare_measurements(before, after)
126
+ print(result["status"], result["lost"], result["changed"], result["unknown"])
127
+ ```
128
+
129
+ The status is `preserved`, `changed` or `unknown`. An unresolved owner or unconfirmed
130
+ claim prevents `preserved`. For an in-place edit, capture
131
+ `before = drawing.measurement_snapshot()` before changing the drawing, then compare
132
+ that snapshot with the edited drawing. Snapshots retain process-local feature references.
133
+
134
+ Separately rebuilt declarations do not acquire correspondence from equal geometry or
135
+ inventory order. If the caller knows the correspondence, pass
136
+ `feature_pairs=((old_feature, new_feature), ...)`. Each pair must name exact owners in
137
+ the respective snapshots and the correspondence must be one-to-one. These are caller
138
+ assertions; the comparison checks measurement meaning under them. Output owner numbers
139
+ are positions in the before snapshot for presentation, not durable identities.
140
+
141
+ `diff_builds()` includes this result as `measurement_comparison` and detects same-kind
142
+ substitutions between shared declared owners. A clear annotation diff can still have
143
+ unknown measurement correspondence. Use independent `lint_summary()["quality"]`
144
+ evidence alongside the comparison: named-measurement preservation does not establish
145
+ physical completeness, pin preservation or release readiness.
146
+
52
147
  ::: draftwright.sheet.Sheet
53
148
  options:
54
149
  filters: public
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "draftwright"
7
- version = "0.4.20"
7
+ version = "0.4.22"
8
8
  description = "Automated technical-drawing generation for build123d"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -18,7 +18,7 @@ dependencies = [
18
18
  "build123d>=0.9.0,<0.11 ; python_full_version < '3.13'",
19
19
  "build123d>=0.11,<0.12 ; python_full_version >= '3.13'",
20
20
  "build123d-drafting-helpers>=0.14.2",
21
- "quiddity==0.2.2",
21
+ "quiddity==0.2.5",
22
22
  "numpy>=1.24",
23
23
  # PNG raster export (SVG→PDF→PNG). Both permissively licensed (Pillow HPND; pypdfium2
24
24
  # Apache-2.0 wrapping Google PDFium BSD-3) and ship pre-built wheels with NO native system
@@ -81,13 +81,15 @@ include = [
81
81
  [tool.pytest.ini_options]
82
82
  testpaths = ["tests"]
83
83
  timeout = 300
84
- # `slow` covers the heavy end-to-end builds over the large NIST CTC fixtures
84
+ # `slow` covers end-to-end integration builds, including the large NIST CTC fixtures
85
85
  # (minutes each), deselected by default. `smoke` is a curated, build-light subset
86
86
  # (~30 s) for a quick local "did I break something obvious" check — run it with
87
87
  # `uv run pytest -m smoke`. The default run is the full fast tier (`-m 'not slow'`,
88
- # ~8 min); CI owns that plus the slow tier across the OS/Python matrix.
88
+ # runtime depends on worker count). CI runs one real-part canary before merge,
89
+ # the full slow tier after merge, and the fast tier across its compatibility matrix.
89
90
  markers = [
90
- "slow: heavy end-to-end CTC fixture builds (deselected by default)",
91
+ "slow: end-to-end integration builds, including CTC fixtures (deselected by default)",
92
+ "real_part_canary: bounded real STEP semantic/export gate, selected once in PR CI",
91
93
  "smoke: curated build-light subset for a fast local check (~30 s)",
92
94
  "unit: pure-logic inner-loop tier — constructs NO OCC geometry (enforced by conftest, #656)",
93
95
  ]
@@ -165,11 +167,14 @@ dev = [
165
167
  "jsonschema>=4,<5",
166
168
  "mypy>=2.1.0",
167
169
  "pytest>=8",
170
+ # `coverage` is invoked directly by the CI `coverage-report` job (combine/xml/html/
171
+ # report); it arrives transitively via pytest-cov, but the job depends on the console
172
+ # script, so declare it.
173
+ "coverage>=7",
168
174
  "pytest-cov>=5",
169
175
  "pytest-split>=0.8",
170
176
  "pytest-timeout>=2",
171
177
  "pytest-xdist>=3",
172
- # Released provider used by the migration parity tests; production remains on 0.4.14.
173
178
  "ruff>=0.4",
174
179
  "tomli>=2; python_version < '3.11'",
175
180
  ]
@@ -18,7 +18,7 @@ from dataclasses import dataclass
18
18
  from decimal import Decimal
19
19
  from inspect import signature
20
20
 
21
- from build123d import Compound, GeomType, Shape
21
+ from build123d import Compound, Edge, GeomType, Shape, Vector
22
22
  from quiddity import full_cylinders
23
23
 
24
24
  _log = logging.getLogger(__name__)
@@ -1229,3 +1229,24 @@ def _blend_profile_arcs(faces, radius):
1229
1229
  for edge in face.edges()
1230
1230
  if edge.geom_type != GeomType.LINE
1231
1231
  )
1232
+
1233
+
1234
+ def _projected_edge_distance(edge, tip, project):
1235
+ """Distance from a page point's projection line to the actual trimmed OCC edge."""
1236
+ origin = project(0, 0, 0)
1237
+ basis = [project(*(1 if i == j else 0 for i in range(3))) for j in range(3)]
1238
+ u = Vector(*(point[0] - origin[0] for point in basis))
1239
+ v = Vector(*(point[1] - origin[1] for point in basis))
1240
+ normal = u.cross(v).normalized()
1241
+ centre = edge.center()
1242
+ projected = project(*centre)
1243
+ point = (
1244
+ centre
1245
+ + u * ((tip[0] - projected[0]) / u.dot(u))
1246
+ + v * ((tip[1] - projected[1]) / v.dot(v))
1247
+ )
1248
+ # Curve length bounds the distance from its centre to any point on it and
1249
+ # avoids recomputing physical bounding boxes on every repeated lint call.
1250
+ reach = edge.length + 1
1251
+ line = Edge.make_line(point - normal * reach, point + normal * reach)
1252
+ return edge.distance_to(line)
@@ -46,6 +46,8 @@ from draftwright.linting.structural import (
46
46
  _centerline_extent,
47
47
  )
48
48
  from draftwright.model.compiled import resolve_feature
49
+ from draftwright.model.ir import HoleFeature, PatternFeature
50
+ from draftwright.model.planner import hole_location_parameter_id
49
51
 
50
52
  _log = logging.getLogger(__name__)
51
53
 
@@ -67,14 +69,21 @@ def _with_hole_location_coverage(annotation, coverage):
67
69
  def _hole_location_coverage_fact(location):
68
70
  """The directional physical fact rendered by one compiled hole location member.
69
71
 
70
- ``location.id`` remains ADR 4 (was 0016)'s feature-level addressable ``DimensionId``. The
71
- requirement parameter is deliberately separate: critique can distinguish X from Y
72
- without resolving #883's open public naming decision or minting a general identity.
72
+ The physical requirement vocabulary is independent of the public member address.
73
+ Its point identifies the physical member; the compiler's declaration-local index
74
+ must not replace that independent evidence.
73
75
  """
74
76
  feature = resolve_feature(location.ref)
75
77
  if feature is None and location.id is not None:
76
78
  feature = location.id.feature
77
79
  assert feature is not None and location.span is not None
80
+ if isinstance(feature, HoleFeature | PatternFeature):
81
+ parameter = (
82
+ f"{location.role}.{location.discriminator}"
83
+ if isinstance(feature, HoleFeature) and location.axis != "z"
84
+ else f"{location.role}.location.{location.discriminator}"
85
+ )
86
+ return (feature, parameter, tuple(location.span[1]))
78
87
  parameter = location.id.parameter if location.id is not None else location.parameter_id
79
88
  if (
80
89
  parameter
@@ -189,6 +198,16 @@ def _annotation_hole_features(registry, name, annotation) -> frozenset:
189
198
  return frozenset(features)
190
199
 
191
200
 
201
+ def _table_location_parameters(feature) -> set[str]:
202
+ if not isinstance(feature, HoleFeature) or feature.frame.axis != "z":
203
+ return set()
204
+ return {
205
+ hole_location_parameter_id(feature, member, axis)
206
+ for member in range(len(feature.members or (feature.frame.origin,)))
207
+ for axis in ("x", "y")
208
+ }
209
+
210
+
192
211
  def _hole_table_replaceable_annotation(registry, name, annotation) -> bool:
193
212
  """Whether a table can replace *all* semantic facts carried by an annotation.
194
213
 
@@ -214,6 +233,8 @@ def _hole_table_replaceable_annotation(registry, name, annotation) -> bool:
214
233
  "location.location",
215
234
  "location_pattern.location",
216
235
  }
236
+ for feature in features:
237
+ table_parameters.update(_table_location_parameters(feature))
217
238
  return all(
218
239
  getattr(measurement, "parameter", None) in table_parameters
219
240
  for measurement in registry.measurement_of(name)
@@ -233,6 +254,8 @@ def _hole_table_replaceable_location_annotation(registry, name, annotation) -> b
233
254
  measurements = tuple(registry.measurement_of(name))
234
255
  return bool(measurements) and all(
235
256
  getattr(measurement, "parameter", None) == "location.location"
257
+ or getattr(measurement, "parameter", None)
258
+ in _table_location_parameters(getattr(measurement, "feature", None))
236
259
  for measurement in measurements
237
260
  )
238
261
 
@@ -875,6 +875,8 @@ def render_locations(dwg, plan, a, *, ctx, only=None, pinned=None) -> int:
875
875
  PX, PY = a.proj.plan_x, a.proj.plan_y
876
876
  x_refs: list = []
877
877
  for r in refs:
878
+ if r[4] not in (None, "x"):
879
+ continue
878
880
  for u in x_refs:
879
881
  if _same_location_ordinate(r[0], u[0]):
880
882
  u[3] = u[3] or r[2] in pinned_set
@@ -932,7 +934,10 @@ def render_locations(dwg, plan, a, *, ctx, only=None, pinned=None) -> int:
932
934
  # A single X-location dim shared by two *distinct* features at this X belongs to
933
935
  # neither exclusively — leave it unowned so drop() cannot over-strip a sibling's
934
936
  # dimension and annotations_of never over-claims it (review #406, ADR 5 (was 0010)).
935
- _shared_x = any(_same_location_ordinate(o[0], rx) and o[2] != feat for o in refs)
937
+ _shared_x = any(
938
+ o[4] in (None, "x") and _same_location_ordinate(o[0], rx) and o[2] != feat
939
+ for o in refs
940
+ )
936
941
  _xfeat = None if _shared_x else feat
937
942
  # The measurement does NOT follow the feature (#1002 r4). Feature-unowned is an
938
943
  # ADR 5 (was 0010) *ownership* rule — it stops drop(feature) stripping a sibling's dim. It
@@ -995,6 +1000,8 @@ def render_locations(dwg, plan, a, *, ctx, only=None, pinned=None) -> int:
995
1000
  iso_x0, iso_y0, _, _ = _iso_bbox(dwg)
996
1001
  y_refs: list = []
997
1002
  for r in refs:
1003
+ if r[4] not in (None, "y"):
1004
+ continue
998
1005
  for u in y_refs:
999
1006
  if _same_location_ordinate(r[1], u[1]):
1000
1007
  u[3] = u[3] or r[2] in pinned_set
@@ -1053,7 +1060,10 @@ def render_locations(dwg, plan, a, *, ctx, only=None, pinned=None) -> int:
1053
1060
  continue
1054
1061
  n += 1
1055
1062
  # Shared-Y location dim → unowned (see the X loop; review #406).
1056
- _shared_y = any(_same_location_ordinate(o[1], ry) and o[2] != feat for o in refs)
1063
+ _shared_y = any(
1064
+ o[4] in (None, "y") and _same_location_ordinate(o[1], ry) and o[2] != feat
1065
+ for o in refs
1066
+ )
1057
1067
  _yfeat = None if _shared_y else feat
1058
1068
  # Every collapsed feature-level location; the structured facts carry Y (see X above).
1059
1069
  _ymid = tuple(mids)
@@ -2580,7 +2590,7 @@ def _render_radius_callouts(
2580
2590
  # Group by what the drawing will actually print. Authored Blend radii can carry
2581
2591
  # more precision than provider geometry, and two distinct display values must
2582
2592
  # never share one n× label while receiving separate measurement credit (#1433).
2583
- collapse.setdefault(pd.value_text, []).append((g, pd))
2593
+ collapse.setdefault((pd.value_text, _tol_suffix(pd.tolerance, draft)), []).append((g, pd))
2584
2594
  jobs = []
2585
2595
  ordered_groups = sorted(
2586
2596
  collapse.items(),
@@ -3105,7 +3115,9 @@ def _ray_exit_dist(px, py, ux, uy, rect) -> float:
3105
3115
  return max(min([t for t in ts if t > 0], default=0.0), 0.0)
3106
3116
 
3107
3117
 
3108
- def _pocket_label(width_text, length_text, depth_text, wsfx="", lsfx="", dsfx="") -> str:
3118
+ def _pocket_label(
3119
+ width_text, length_text, depth_text, wsfx="", lsfx="", dsfx="", *, maximum_depth=False
3120
+ ) -> str:
3109
3121
  """The pocket callout string: ``{width} × {length} × {depth} DEEP`` (#148a). The values
3110
3122
  are the PLANNED ones (``pd.param.value``, #728); *wsfx*/*lsfx*/*dsfx* are each value's
3111
3123
  pre-formatted tolerance suffix, interleaved so a tolerance rides its own number. (All
@@ -3115,7 +3127,20 @@ def _pocket_label(width_text, length_text, depth_text, wsfx="", lsfx="", dsfx=""
3115
3127
  helper's hole callouts, not as font text — a plain :class:`Leader` label has no access
3116
3128
  to it, so this uses the font-safe ``DEEP`` word (the vendored Plex Mono lacks ↧).
3117
3129
  Formatting lives in the render layer (ADR 3 (was 0013 §7))."""
3118
- return f"{width_text}{wsfx} × {length_text}{lsfx} × {depth_text}{dsfx} DEEP"
3130
+ depth_word = "MAX DEEP" if maximum_depth else "DEEP"
3131
+ if all(value is not None for value in (width_text, length_text, depth_text)):
3132
+ label = f"{width_text}{wsfx} × {length_text}{lsfx} × {depth_text}{dsfx} {depth_word}"
3133
+ else:
3134
+ label = "POCKET " + ", ".join(
3135
+ f"{value}{suffix} {role}"
3136
+ for value, suffix, role in (
3137
+ (width_text, wsfx, "WIDE"),
3138
+ (length_text, lsfx, "LONG"),
3139
+ (depth_text, dsfx, depth_word),
3140
+ )
3141
+ if value is not None
3142
+ )
3143
+ return label
3119
3144
 
3120
3145
 
3121
3146
  def _rectangular_blind_slot_label(
@@ -3549,8 +3574,9 @@ def render_pockets(dwg, plan, a, *, ctx, only=None) -> int:
3549
3574
  by_key = {(pd.role, pd.kind): pd for pd in g.dims}
3550
3575
  wpd = by_key.get(("pocket_width", "length"))
3551
3576
  lpd = by_key.get(("pocket_length", "length"))
3552
- dpd = by_key.get(("pocket_depth", "length"))
3553
- if wpd is None or lpd is None or dpd is None:
3577
+ dpd = by_key.get(("pocket_depth", "length")) or by_key.get(("pocket_max_depth", "length"))
3578
+ dimensions = tuple(d for d in (wpd, lpd, dpd) if d is not None)
3579
+ if not dimensions:
3554
3580
  continue
3555
3581
  view = view_of.get(pk.depth_axis)
3556
3582
  if view is None:
@@ -3564,12 +3590,13 @@ def render_pockets(dwg, plan, a, *, ctx, only=None) -> int:
3564
3590
  view,
3565
3591
  vb,
3566
3592
  _pocket_label(
3567
- wpd.value_text,
3568
- lpd.value_text,
3569
- dpd.value_text,
3570
- wsfx=_tol_suffix(wpd.tolerance, draft),
3571
- lsfx=_tol_suffix(lpd.tolerance, draft),
3572
- dsfx=_tol_suffix(dpd.tolerance, draft),
3593
+ wpd.value_text if wpd is not None else None,
3594
+ lpd.value_text if lpd is not None else None,
3595
+ dpd.value_text if dpd is not None else None,
3596
+ wsfx=_tol_suffix(wpd.tolerance, draft) if wpd is not None else "",
3597
+ lsfx=_tol_suffix(lpd.tolerance, draft) if lpd is not None else "",
3598
+ maximum_depth=dpd is not None and dpd.role == "pocket_max_depth",
3599
+ dsfx=_tol_suffix(dpd.tolerance, draft) if dpd is not None else "",
3573
3600
  ),
3574
3601
  _radial_candidates(
3575
3602
  dwg,
@@ -3585,10 +3612,12 @@ def render_pockets(dwg, plan, a, *, ctx, only=None) -> int:
3585
3612
  width_axis=pk.width_axis,
3586
3613
  length=lpd.value,
3587
3614
  width=wpd.value,
3588
- ),
3615
+ )
3616
+ if wpd is not None and lpd is not None
3617
+ else None,
3589
3618
  provenance=g.ref,
3590
3619
  ),
3591
- (wpd.id, lpd.id, dpd.id), # width × length × depth, one callout (#1002)
3620
+ tuple(d.id for d in dimensions),
3592
3621
  )
3593
3622
  )
3594
3623
  return place_machined_leader_jobs(
@@ -5811,9 +5840,9 @@ def ladder_plan_for(plan, *, step_height: bool, overall: bool):
5811
5840
 
5812
5841
 
5813
5842
  def render_height_ladder(dwg, plan, frame, *, ctx, detail_view: bool = False) -> int:
5814
- """Front-view right ladder: prismatic step heights stacked inner→outer, then the overall
5815
- height outermost registered as :class:`CorridorCandidate`s in the shared
5816
- ``(front, right)`` corridor (#636). The leapfrog witness cursor (#237) survives as a
5843
+ """Front-view ladder: prismatic step heights stacked inner→outer, then the overall
5844
+ height outermost. The overall height can be authored on the left; candidates enter
5845
+ the shared corridor for their side. The leapfrog witness cursor (#237) survives as a
5817
5846
  *build-time chain*: candidates share a ``solved`` position map, and each dim's witness
5818
5847
  anchors on its nearest already-built predecessor's line (the view edge for the first).
5819
5848
 
@@ -5850,8 +5879,6 @@ def render_height_ladder(dwg, plan, frame, *, ctx, detail_view: bool = False) ->
5850
5879
  frame.project("front", entry.span[1])[1],
5851
5880
  )
5852
5881
 
5853
- strip = frame.fv_zones.right
5854
-
5855
5882
  rung_set = plan.ladder("step_height")
5856
5883
  rungs = list(rung_set.rungs) if rung_set is not None else []
5857
5884
  # An OPAQUE handle, passed straight through to the corridor candidate and the
@@ -6003,6 +6030,12 @@ def render_height_ladder(dwg, plan, frame, *, ctx, detail_view: bool = False) ->
6003
6030
  _tolerances = {c[0]: c[8] for c in chain}
6004
6031
 
6005
6032
  names = [c[0] for c in chain]
6033
+ sides = {
6034
+ name: (overall.rungs[0].side or "right")
6035
+ if name == "dim_height" and overall is not None
6036
+ else "right"
6037
+ for name in names
6038
+ }
6006
6039
  solved: dict[str, float] = {}
6007
6040
  for k, (
6008
6041
  name,
@@ -6016,6 +6049,21 @@ def render_height_ladder(dwg, plan, frame, *, ctx, detail_view: bool = False) ->
6016
6049
  _rt,
6017
6050
  measurement_span,
6018
6051
  ) in enumerate(chain):
6052
+ side = sides[name]
6053
+ direction = 1 if side == "right" else -1
6054
+ edge = edge2 if side == "right" else _left - 2
6055
+ strip = frame.fv_zones.right if side == "right" else frame.fv_zones.left
6056
+ predecessors = [pn for pn in names[:k] if sides[pn] == side]
6057
+
6058
+ def _witness_base(pos, predecessors=predecessors, direction=direction, edge=edge):
6059
+ base = edge
6060
+ for pn in reversed(predecessors):
6061
+ if pn in solved:
6062
+ base = solved[pn]
6063
+ break
6064
+ # A retry can revisit the inner position after a predecessor was built.
6065
+ # Prediction and rendering must use the same non-degenerate witness.
6066
+ return edge if direction * (pos - base) < 0.5 else base
6019
6067
 
6020
6068
  def _build(
6021
6069
  pos,
@@ -6023,22 +6071,23 @@ def render_height_ladder(dwg, plan, frame, *, ctx, detail_view: bool = False) ->
6023
6071
  zbase=zbase,
6024
6072
  ztop=ztop,
6025
6073
  label=label,
6026
- k=k,
6074
+ witness_base=_witness_base,
6075
+ side=side,
6076
+ direction=direction,
6077
+ authored_side=overall.rungs[0].side
6078
+ if name == "dim_height" and overall is not None
6079
+ else None,
6027
6080
  per_unit=per_unit,
6028
6081
  _tol=_tolerances.get(name),
6029
6082
  measurement_span=measurement_span,
6030
6083
  ):
6031
- base = edge2
6032
- for pn in reversed(names[:k]): # nearest already-built predecessor's line
6033
- if pn in solved:
6034
- base = solved[pn]
6035
- break
6084
+ base = witness_base(pos)
6036
6085
  solved[name] = pos
6037
6086
  dim = _dim(
6038
6087
  (base, zbase, 0),
6039
6088
  (base, ztop, 0),
6040
- "right",
6041
- pos - base,
6089
+ side,
6090
+ direction * (pos - base),
6042
6091
  draft,
6043
6092
  label=label + _tol_suffix(_tol, draft),
6044
6093
  )
@@ -6049,6 +6098,8 @@ def render_height_ladder(dwg, plan, frame, *, ctx, detail_view: bool = False) ->
6049
6098
  # step, while a hole pitch spans the whole run. Same seam as `_dw_scale`.
6050
6099
  dim._dw_label_value = per_unit
6051
6100
  dim._dw_measurement_span = measurement_span
6101
+ if authored_side is not None:
6102
+ dim._dw_authored_side = authored_side
6052
6103
  return dim
6053
6104
 
6054
6105
  # The footprint measures the RENDERED string, so it carries the same suffix the
@@ -6059,25 +6110,22 @@ def render_height_ladder(dwg, plan, frame, *, ctx, detail_view: bool = False) ->
6059
6110
  zbase=zbase,
6060
6111
  ztop=ztop,
6061
6112
  label=label + _tol_suffix(_tolerances.get(name), draft),
6062
- k=k,
6113
+ witness_base=_witness_base,
6114
+ side=side,
6115
+ direction=direction,
6063
6116
  ):
6064
6117
  # Predecessor-aware prediction (#689 review): the conservative edge-anchored
6065
6118
  # witness can falsely exhaust the strip when an inner obstacle sits in the
6066
- # already-traversed region. Use the same solved map the build chain uses.
6067
- base = edge2
6068
- for pn in reversed(names[:k]):
6069
- if pn in solved:
6070
- base = solved[pn]
6071
- break
6072
- if pos - base < 0.5: # degenerate guard: never model a zero-length offset
6073
- base = edge2
6119
+ # already-traversed region. Use the build chain's witness calculation.
6120
+ base = witness_base(pos)
6074
6121
  return dim_footprint(
6075
- (base, zbase, 0), (base, ztop, 0), "right", pos - base, draft, label
6122
+ (base, zbase, 0), (base, ztop, 0), side, direction * (pos - base), draft, label
6076
6123
  )
6077
6124
 
6078
6125
  def _drop(
6079
6126
  nm,
6080
- drop_msg=drop_msg,
6127
+ drop_msg=drop_msg.replace("right strip", f"{side} strip"),
6128
+ strip=strip,
6081
6129
  name=name,
6082
6130
  measurement=mid,
6083
6131
  measurement_span=measurement_span,
@@ -6097,7 +6145,7 @@ def render_height_ladder(dwg, plan, frame, *, ctx, detail_view: bool = False) ->
6097
6145
 
6098
6146
  register_corridor(
6099
6147
  ctx,
6100
- ("front", "right"),
6148
+ ("front", side),
6101
6149
  strip,
6102
6150
  "front",
6103
6151
  "x",