draftwright 0.5.0rc2__tar.gz → 0.5.0rc4__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 (1057) hide show
  1. draftwright-0.5.0rc4/CHANGELOG.md +3217 -0
  2. draftwright-0.5.0rc4/PKG-INFO +1074 -0
  3. draftwright-0.5.0rc4/README.md +371 -0
  4. draftwright-0.5.0rc4/docs/reference/recogniser-capabilities.md +820 -0
  5. draftwright-0.5.0rc4/pyproject.toml +205 -0
  6. draftwright-0.5.0rc4/skills/SKILL.md +378 -0
  7. draftwright-0.5.0rc4/src/draftwright/_build_profile.py +186 -0
  8. draftwright-0.5.0rc4/src/draftwright/_core.py +1539 -0
  9. draftwright-0.5.0rc4/src/draftwright/_geometry.py +1298 -0
  10. draftwright-0.5.0rc4/src/draftwright/_pmi_part21.py +1848 -0
  11. draftwright-0.5.0rc4/src/draftwright/_pmi_support_blockers.py +241 -0
  12. draftwright-0.5.0rc4/src/draftwright/_pmi_topology.py +222 -0
  13. draftwright-0.5.0rc4/src/draftwright/analysis.py +2494 -0
  14. draftwright-0.5.0rc4/src/draftwright/annotate.py +25 -0
  15. draftwright-0.5.0rc4/src/draftwright/annotations/_axial_render.py +1115 -0
  16. draftwright-0.5.0rc4/src/draftwright/annotations/_common.py +2496 -0
  17. draftwright-0.5.0rc4/src/draftwright/annotations/_diameters.py +981 -0
  18. draftwright-0.5.0rc4/src/draftwright/annotations/_dimension_ink_repair.py +506 -0
  19. draftwright-0.5.0rc4/src/draftwright/annotations/_edge_callouts.py +573 -0
  20. draftwright-0.5.0rc4/src/draftwright/annotations/_envelope.py +331 -0
  21. draftwright-0.5.0rc4/src/draftwright/annotations/_gdt.py +787 -0
  22. draftwright-0.5.0rc4/src/draftwright/annotations/_leader_commit.py +503 -0
  23. draftwright-0.5.0rc4/src/draftwright/annotations/_leader_fixed_ink.py +511 -0
  24. draftwright-0.5.0rc4/src/draftwright/annotations/_locations.py +829 -0
  25. draftwright-0.5.0rc4/src/draftwright/annotations/_machined_leaders.py +436 -0
  26. draftwright-0.5.0rc4/src/draftwright/annotations/_patterns.py +586 -0
  27. draftwright-0.5.0rc4/src/draftwright/annotations/_placement_occupancy.py +715 -0
  28. draftwright-0.5.0rc4/src/draftwright/annotations/_pmi_dimensions.py +1771 -0
  29. draftwright-0.5.0rc4/src/draftwright/annotations/_sheet_furniture.py +560 -0
  30. draftwright-0.5.0rc4/src/draftwright/annotations/_slots.py +717 -0
  31. draftwright-0.5.0rc4/src/draftwright/annotations/_step_lengths.py +934 -0
  32. draftwright-0.5.0rc4/src/draftwright/annotations/_thin_profiles.py +299 -0
  33. draftwright-0.5.0rc4/src/draftwright/annotations/angular.py +142 -0
  34. draftwright-0.5.0rc4/src/draftwright/annotations/balloons.py +1164 -0
  35. draftwright-0.5.0rc4/src/draftwright/annotations/drawing_port.py +128 -0
  36. draftwright-0.5.0rc4/src/draftwright/annotations/from_model.py +2493 -0
  37. draftwright-0.5.0rc4/src/draftwright/annotations/hole_locations.py +700 -0
  38. draftwright-0.5.0rc4/src/draftwright/annotations/holes.py +2478 -0
  39. draftwright-0.5.0rc4/src/draftwright/annotations/leaders.py +2474 -0
  40. draftwright-0.5.0rc4/src/draftwright/annotations/orchestrator.py +1940 -0
  41. draftwright-0.5.0rc4/src/draftwright/annotations/routed.py +142 -0
  42. draftwright-0.5.0rc4/src/draftwright/annotations/sections.py +1572 -0
  43. draftwright-0.5.0rc4/src/draftwright/audit.py +1430 -0
  44. draftwright-0.5.0rc4/src/draftwright/build_once.py +624 -0
  45. draftwright-0.5.0rc4/src/draftwright/build_options.py +159 -0
  46. draftwright-0.5.0rc4/src/draftwright/builder.py +2901 -0
  47. draftwright-0.5.0rc4/src/draftwright/cli.py +500 -0
  48. draftwright-0.5.0rc4/src/draftwright/compose.py +2878 -0
  49. draftwright-0.5.0rc4/src/draftwright/drawing.py +2591 -0
  50. draftwright-0.5.0rc4/src/draftwright/drawing_diagnostics.py +549 -0
  51. draftwright-0.5.0rc4/src/draftwright/drawing_evidence.py +425 -0
  52. draftwright-0.5.0rc4/src/draftwright/drawing_export.py +244 -0
  53. draftwright-0.5.0rc4/src/draftwright/drawing_state.py +163 -0
  54. draftwright-0.5.0rc4/src/draftwright/evaluation/_edge_profile_evidence.py +483 -0
  55. draftwright-0.5.0rc4/src/draftwright/evaluation/_hole_family_evidence.py +577 -0
  56. draftwright-0.5.0rc4/src/draftwright/evaluation/_pocket_evidence.py +628 -0
  57. draftwright-0.5.0rc4/src/draftwright/evaluation/_turned_step_evidence.py +364 -0
  58. draftwright-0.5.0rc4/src/draftwright/evaluation/step_analysis.py +2454 -0
  59. draftwright-0.5.0rc4/src/draftwright/export.py +839 -0
  60. draftwright-0.5.0rc4/src/draftwright/family_table.py +359 -0
  61. draftwright-0.5.0rc4/src/draftwright/inspection.py +800 -0
  62. draftwright-0.5.0rc4/src/draftwright/inspection_contract.py +279 -0
  63. draftwright-0.5.0rc4/src/draftwright/intent_routing.py +283 -0
  64. draftwright-0.5.0rc4/src/draftwright/layout.py +1400 -0
  65. draftwright-0.5.0rc4/src/draftwright/layout_selection.py +637 -0
  66. draftwright-0.5.0rc4/src/draftwright/linting/__init__.py +137 -0
  67. draftwright-0.5.0rc4/src/draftwright/linting/_coverage_common.py +198 -0
  68. draftwright-0.5.0rc4/src/draftwright/linting/_parameter_coverage.py +181 -0
  69. draftwright-0.5.0rc4/src/draftwright/linting/angular.py +618 -0
  70. draftwright-0.5.0rc4/src/draftwright/linting/blend_coverage.py +318 -0
  71. draftwright-0.5.0rc4/src/draftwright/linting/boss_coverage.py +247 -0
  72. draftwright-0.5.0rc4/src/draftwright/linting/chamfer_coverage.py +99 -0
  73. draftwright-0.5.0rc4/src/draftwright/linting/circular_blind_step_coverage.py +225 -0
  74. draftwright-0.5.0rc4/src/draftwright/linting/coverage.py +2592 -0
  75. draftwright-0.5.0rc4/src/draftwright/linting/envelope_coverage.py +211 -0
  76. draftwright-0.5.0rc4/src/draftwright/linting/fillet_coverage.py +96 -0
  77. draftwright-0.5.0rc4/src/draftwright/linting/gear_coverage.py +187 -0
  78. draftwright-0.5.0rc4/src/draftwright/linting/hole_coverage.py +1588 -0
  79. draftwright-0.5.0rc4/src/draftwright/linting/orchestration.py +693 -0
  80. draftwright-0.5.0rc4/src/draftwright/linting/paired_ramp_step_coverage.py +105 -0
  81. draftwright-0.5.0rc4/src/draftwright/linting/pmi_coverage.py +789 -0
  82. draftwright-0.5.0rc4/src/draftwright/linting/polygonal_boss_coverage.py +368 -0
  83. draftwright-0.5.0rc4/src/draftwright/linting/polygonal_stock_coverage.py +424 -0
  84. draftwright-0.5.0rc4/src/draftwright/linting/quality.py +1017 -0
  85. draftwright-0.5.0rc4/src/draftwright/linting/rectangular_blind_slot_coverage.py +190 -0
  86. draftwright-0.5.0rc4/src/draftwright/linting/requirements.py +222 -0
  87. draftwright-0.5.0rc4/src/draftwright/linting/round_bottom_blind_slot_coverage.py +196 -0
  88. draftwright-0.5.0rc4/src/draftwright/linting/structural.py +1697 -0
  89. draftwright-0.5.0rc4/src/draftwright/linting/through_step_coverage.py +673 -0
  90. draftwright-0.5.0rc4/src/draftwright/make_drawing.py +30 -0
  91. draftwright-0.5.0rc4/src/draftwright/measurement_support.py +144 -0
  92. draftwright-0.5.0rc4/src/draftwright/model/callout.py +810 -0
  93. draftwright-0.5.0rc4/src/draftwright/model/compiled.py +2485 -0
  94. draftwright-0.5.0rc4/src/draftwright/model/declare.py +2796 -0
  95. draftwright-0.5.0rc4/src/draftwright/model/detect.py +2881 -0
  96. draftwright-0.5.0rc4/src/draftwright/model/detect_inventory.py +281 -0
  97. draftwright-0.5.0rc4/src/draftwright/model/detect_ownership.py +545 -0
  98. draftwright-0.5.0rc4/src/draftwright/model/ir.py +2944 -0
  99. draftwright-0.5.0rc4/src/draftwright/model/ir_foundation.py +1115 -0
  100. draftwright-0.5.0rc4/src/draftwright/model/planner.py +2177 -0
  101. draftwright-0.5.0rc4/src/draftwright/model/pmi_lowering.py +1750 -0
  102. draftwright-0.5.0rc4/src/draftwright/pdf_text.py +666 -0
  103. draftwright-0.5.0rc4/src/draftwright/pmi.py +2917 -0
  104. draftwright-0.5.0rc4/src/draftwright/profile_angles.py +312 -0
  105. draftwright-0.5.0rc4/src/draftwright/projection.py +660 -0
  106. draftwright-0.5.0rc4/src/draftwright/recogniser_contract.py +1085 -0
  107. draftwright-0.5.0rc4/src/draftwright/recogniser_policy.py +163 -0
  108. draftwright-0.5.0rc4/src/draftwright/recogniser_schema.py +123 -0
  109. draftwright-0.5.0rc4/src/draftwright/recognition_ownership.py +1254 -0
  110. draftwright-0.5.0rc4/src/draftwright/repair.py +340 -0
  111. draftwright-0.5.0rc4/src/draftwright/reporting.py +2873 -0
  112. draftwright-0.5.0rc4/src/draftwright/section_recess_contract.py +1000 -0
  113. draftwright-0.5.0rc4/src/draftwright/sheet.py +2913 -0
  114. draftwright-0.5.0rc4/src/draftwright/sheet_emit.py +2126 -0
  115. draftwright-0.5.0rc4/src/draftwright/sheet_feature_lines.py +969 -0
  116. draftwright-0.5.0rc4/src/draftwright/sheet_metadata.py +47 -0
  117. draftwright-0.5.0rc4/src/draftwright/sheet_views.py +667 -0
  118. draftwright-0.5.0rc4/tests/_complexity_baseline.json +138 -0
  119. draftwright-0.5.0rc4/tests/_function_length_baseline.json +50 -0
  120. draftwright-0.5.0rc4/tests/_issue_module_baseline.txt +159 -0
  121. draftwright-0.5.0rc4/tests/_tier_manifest.py +975 -0
  122. draftwright-0.5.0rc4/tests/fixtures/evaluation/README.md +251 -0
  123. draftwright-0.5.0rc4/tests/fixtures/evaluation/corpus-hole-patterns-v1.json +159 -0
  124. draftwright-0.5.0rc4/tests/refactor_golden/grid_plate.json +527 -0
  125. draftwright-0.5.0rc4/tests/test_adr0018_view_selection.py +712 -0
  126. draftwright-0.5.0rc4/tests/test_adr_corpus.py +164 -0
  127. draftwright-0.5.0rc4/tests/test_agent_improvement_canary_issue_1716.py +302 -0
  128. draftwright-0.5.0rc4/tests/test_annotation_box_composition.py +169 -0
  129. draftwright-0.5.0rc4/tests/test_annotation_box_corpus.py +81 -0
  130. draftwright-0.5.0rc4/tests/test_annotation_layout_product.py +701 -0
  131. draftwright-0.5.0rc4/tests/test_annotation_scheme_compare_script.py +776 -0
  132. draftwright-0.5.0rc4/tests/test_architecture_docs.py +53 -0
  133. draftwright-0.5.0rc4/tests/test_boss_ownership.py +731 -0
  134. draftwright-0.5.0rc4/tests/test_build_drawing_entrypoints.py +288 -0
  135. draftwright-0.5.0rc4/tests/test_channel_ownership.py +391 -0
  136. draftwright-0.5.0rc4/tests/test_circular_blind_step_semantics.py +1095 -0
  137. draftwright-0.5.0rc4/tests/test_compiled_plan_boundary.py +1383 -0
  138. draftwright-0.5.0rc4/tests/test_compose_then_pack.py +504 -0
  139. draftwright-0.5.0rc4/tests/test_declare.py +2300 -0
  140. draftwright-0.5.0rc4/tests/test_declared_recognition_gate.py +572 -0
  141. draftwright-0.5.0rc4/tests/test_deprecation_dates.py +260 -0
  142. draftwright-0.5.0rc4/tests/test_detect_registry.py +338 -0
  143. draftwright-0.5.0rc4/tests/test_diameter_leaders.py +400 -0
  144. draftwright-0.5.0rc4/tests/test_dimension_lane_canary_issue_1757.py +165 -0
  145. draftwright-0.5.0rc4/tests/test_dimension_role_vocabulary.py +399 -0
  146. draftwright-0.5.0rc4/tests/test_document_build.py +498 -0
  147. draftwright-0.5.0rc4/tests/test_drawing_encapsulation.py +667 -0
  148. draftwright-0.5.0rc4/tests/test_e2e_standards.py +557 -0
  149. draftwright-0.5.0rc4/tests/test_edge_profile_coverage.py +75 -0
  150. draftwright-0.5.0rc4/tests/test_export_formats.py +247 -0
  151. draftwright-0.5.0rc4/tests/test_family_table.py +131 -0
  152. draftwright-0.5.0rc4/tests/test_feature_edit_verbs.py +399 -0
  153. draftwright-0.5.0rc4/tests/test_feature_leader_candidate_regions.py +1273 -0
  154. draftwright-0.5.0rc4/tests/test_gdt_ink_shared.py +304 -0
  155. draftwright-0.5.0rc4/tests/test_gdt_placement.py +529 -0
  156. draftwright-0.5.0rc4/tests/test_groove_completeness_evidence.py +656 -0
  157. draftwright-0.5.0rc4/tests/test_hole_annotations.py +521 -0
  158. draftwright-0.5.0rc4/tests/test_hole_pattern_callouts.py +458 -0
  159. draftwright-0.5.0rc4/tests/test_hole_table.py +772 -0
  160. draftwright-0.5.0rc4/tests/test_import_boundaries.py +1497 -0
  161. draftwright-0.5.0rc4/tests/test_inspection_contract.py +180 -0
  162. draftwright-0.5.0rc4/tests/test_interior_label_placement.py +348 -0
  163. draftwright-0.5.0rc4/tests/test_issue_1058_wheel_profile.py +911 -0
  164. draftwright-0.5.0rc4/tests/test_issue_1116_ap242_hole_tolerance_lowering.py +496 -0
  165. draftwright-0.5.0rc4/tests/test_issue_1142_hole_leader_labels.py +262 -0
  166. draftwright-0.5.0rc4/tests/test_issue_1143_hole_completeness.py +2929 -0
  167. draftwright-0.5.0rc4/tests/test_issue_1166_cross_pass_feature_leaders.py +2787 -0
  168. draftwright-0.5.0rc4/tests/test_issue_1176_quality_fidelity.py +1032 -0
  169. draftwright-0.5.0rc4/tests/test_issue_1188_per_view_assignment.py +125 -0
  170. draftwright-0.5.0rc4/tests/test_issue_1190_section_decision.py +256 -0
  171. draftwright-0.5.0rc4/tests/test_issue_1202_observed_drawing_consumer.py +634 -0
  172. draftwright-0.5.0rc4/tests/test_issue_1215_envelope_tolerance.py +321 -0
  173. draftwright-0.5.0rc4/tests/test_issue_1215_no_approved_tolerance_is_dropped.py +957 -0
  174. draftwright-0.5.0rc4/tests/test_issue_1217_the_facility_is_shared.py +429 -0
  175. draftwright-0.5.0rc4/tests/test_issue_1219_location_claims_its_own_axis.py +287 -0
  176. draftwright-0.5.0rc4/tests/test_issue_1298_manufacturing_requirements.py +1886 -0
  177. draftwright-0.5.0rc4/tests/test_issue_1308_machined_leader_analytics.py +427 -0
  178. draftwright-0.5.0rc4/tests/test_issue_1352_searchable_pdf_text.py +1103 -0
  179. draftwright-0.5.0rc4/tests/test_issue_1357_framed_activation.py +241 -0
  180. draftwright-0.5.0rc4/tests/test_issue_1370_double_d_completeness_evidence.py +731 -0
  181. draftwright-0.5.0rc4/tests/test_issue_1370_hole_pattern_completeness_evidence.py +308 -0
  182. draftwright-0.5.0rc4/tests/test_issue_1371_polygonal_stock_completeness_evidence.py +1226 -0
  183. draftwright-0.5.0rc4/tests/test_issue_1460_step_inspection.py +1062 -0
  184. draftwright-0.5.0rc4/tests/test_issue_1505_diameter_coverage.py +166 -0
  185. draftwright-0.5.0rc4/tests/test_issue_1518_explicit_rear.py +703 -0
  186. draftwright-0.5.0rc4/tests/test_issue_1531_semantic_hole_counts.py +255 -0
  187. draftwright-0.5.0rc4/tests/test_issue_1544_frame_document_canary.py +443 -0
  188. draftwright-0.5.0rc4/tests/test_issue_1596_bolt_circle_corroboration.py +441 -0
  189. draftwright-0.5.0rc4/tests/test_issue_740_leader_assignment.py +906 -0
  190. draftwright-0.5.0rc4/tests/test_issue_798_material_field.py +496 -0
  191. draftwright-0.5.0rc4/tests/test_issue_798_silhouette_lint.py +390 -0
  192. draftwright-0.5.0rc4/tests/test_issue_883_location_identity.py +644 -0
  193. draftwright-0.5.0rc4/tests/test_issue_916_pocket_leader.py +118 -0
  194. draftwright-0.5.0rc4/tests/test_label_provenance.py +190 -0
  195. draftwright-0.5.0rc4/tests/test_layout_override_lane_issue_1757.py +263 -0
  196. draftwright-0.5.0rc4/tests/test_layout_safety.py +758 -0
  197. draftwright-0.5.0rc4/tests/test_layout_scheme.py +583 -0
  198. draftwright-0.5.0rc4/tests/test_lint_summary.py +404 -0
  199. draftwright-0.5.0rc4/tests/test_location_dimensions.py +635 -0
  200. draftwright-0.5.0rc4/tests/test_manufacturing_pmi_lowering.py +745 -0
  201. draftwright-0.5.0rc4/tests/test_note_verb.py +61 -0
  202. draftwright-0.5.0rc4/tests/test_pad_rendering.py +491 -0
  203. draftwright-0.5.0rc4/tests/test_parameter_id.py +758 -0
  204. draftwright-0.5.0rc4/tests/test_pareto_loop_canary_issue_1753.py +170 -0
  205. draftwright-0.5.0rc4/tests/test_part_model.py +381 -0
  206. draftwright-0.5.0rc4/tests/test_pitch_dim_footprint.py +290 -0
  207. draftwright-0.5.0rc4/tests/test_pmi.py +2672 -0
  208. draftwright-0.5.0rc4/tests/test_pmi_datum_lowering.py +772 -0
  209. draftwright-0.5.0rc4/tests/test_pmi_gtol_lowering.py +883 -0
  210. draftwright-0.5.0rc4/tests/test_pmi_part21.py +1888 -0
  211. draftwright-0.5.0rc4/tests/test_pmi_records.py +88 -0
  212. draftwright-0.5.0rc4/tests/test_pocket_completeness_evidence.py +474 -0
  213. draftwright-0.5.0rc4/tests/test_pocket_pattern_completeness_evidence.py +973 -0
  214. draftwright-0.5.0rc4/tests/test_pocket_pattern_recognition.py +208 -0
  215. draftwright-0.5.0rc4/tests/test_private_test_attr_reads.py +308 -0
  216. draftwright-0.5.0rc4/tests/test_profile_angles.py +207 -0
  217. draftwright-0.5.0rc4/tests/test_quality_components.py +385 -0
  218. draftwright-0.5.0rc4/tests/test_recogniser_capabilities.py +1714 -0
  219. draftwright-0.5.0rc4/tests/test_recogniser_contract.py +672 -0
  220. draftwright-0.5.0rc4/tests/test_refactor_golden.py +463 -0
  221. draftwright-0.5.0rc4/tests/test_remaining_ink_overlaps.py +243 -0
  222. draftwright-0.5.0rc4/tests/test_render_seam.py +444 -0
  223. draftwright-0.5.0rc4/tests/test_report_projection.py +1024 -0
  224. draftwright-0.5.0rc4/tests/test_requirement_catalog.py +405 -0
  225. draftwright-0.5.0rc4/tests/test_requirement_catalog_families.py +151 -0
  226. draftwright-0.5.0rc4/tests/test_sheet_emit.py +4987 -0
  227. draftwright-0.5.0rc4/tests/test_sheet_furniture.py +149 -0
  228. draftwright-0.5.0rc4/tests/test_sheet_section.py +162 -0
  229. draftwright-0.5.0rc4/tests/test_sheet_tables.py +309 -0
  230. draftwright-0.5.0rc4/tests/test_slanted_blind_step.py +265 -0
  231. draftwright-0.5.0rc4/tests/test_slot_completeness.py +752 -0
  232. draftwright-0.5.0rc4/tests/test_slot_recognition.py +571 -0
  233. draftwright-0.5.0rc4/tests/test_source_shape.py +302 -0
  234. draftwright-0.5.0rc4/tests/test_step_analysis_evaluation.py +561 -0
  235. draftwright-0.5.0rc4/tests/test_step_profile_grouping.py +34 -0
  236. draftwright-0.5.0rc4/tests/test_strip_layout.py +2034 -0
  237. draftwright-0.5.0rc4/tests/test_suite_shape.py +104 -0
  238. draftwright-0.5.0rc4/tests/test_through_step_semantics.py +1811 -0
  239. draftwright-0.5.0rc4/tests/test_tier_manifest.py +781 -0
  240. draftwright-0.5.0rc4/tests/test_title_block_units_format.py +522 -0
  241. draftwright-0.5.0rc4/tests/test_turned_diameters.py +927 -0
  242. draftwright-0.5.0rc4/tests/test_turned_steps.py +118 -0
  243. draftwright-0.5.0rc4/tests/test_workflows.py +769 -0
  244. draftwright-0.5.0rc2/CHANGELOG.md +0 -3198
  245. draftwright-0.5.0rc2/PKG-INFO +0 -1074
  246. draftwright-0.5.0rc2/README.md +0 -371
  247. draftwright-0.5.0rc2/docs/reference/recogniser-capabilities.md +0 -802
  248. draftwright-0.5.0rc2/pyproject.toml +0 -200
  249. draftwright-0.5.0rc2/skills/SKILL.md +0 -381
  250. draftwright-0.5.0rc2/src/draftwright/_build_profile.py +0 -186
  251. draftwright-0.5.0rc2/src/draftwright/_core.py +0 -2277
  252. draftwright-0.5.0rc2/src/draftwright/_geometry.py +0 -1280
  253. draftwright-0.5.0rc2/src/draftwright/_pmi_part21.py +0 -1533
  254. draftwright-0.5.0rc2/src/draftwright/_pmi_support_blockers.py +0 -176
  255. draftwright-0.5.0rc2/src/draftwright/analysis.py +0 -2442
  256. draftwright-0.5.0rc2/src/draftwright/annotate.py +0 -26
  257. draftwright-0.5.0rc2/src/draftwright/annotations/_axial_render.py +0 -1091
  258. draftwright-0.5.0rc2/src/draftwright/annotations/_common.py +0 -2408
  259. draftwright-0.5.0rc2/src/draftwright/annotations/_diameters.py +0 -882
  260. draftwright-0.5.0rc2/src/draftwright/annotations/_dimension_ink_repair.py +0 -523
  261. draftwright-0.5.0rc2/src/draftwright/annotations/_edge_callouts.py +0 -573
  262. draftwright-0.5.0rc2/src/draftwright/annotations/_envelope.py +0 -329
  263. draftwright-0.5.0rc2/src/draftwright/annotations/_gdt.py +0 -622
  264. draftwright-0.5.0rc2/src/draftwright/annotations/_leader_commit.py +0 -502
  265. draftwright-0.5.0rc2/src/draftwright/annotations/_leader_fixed_ink.py +0 -492
  266. draftwright-0.5.0rc2/src/draftwright/annotations/_locations.py +0 -798
  267. draftwright-0.5.0rc2/src/draftwright/annotations/_machined_leaders.py +0 -437
  268. draftwright-0.5.0rc2/src/draftwright/annotations/_patterns.py +0 -585
  269. draftwright-0.5.0rc2/src/draftwright/annotations/_placement_occupancy.py +0 -626
  270. draftwright-0.5.0rc2/src/draftwright/annotations/_pmi_dimensions.py +0 -1768
  271. draftwright-0.5.0rc2/src/draftwright/annotations/_slots.py +0 -701
  272. draftwright-0.5.0rc2/src/draftwright/annotations/_step_lengths.py +0 -923
  273. draftwright-0.5.0rc2/src/draftwright/annotations/_thin_profiles.py +0 -299
  274. draftwright-0.5.0rc2/src/draftwright/annotations/angular.py +0 -142
  275. draftwright-0.5.0rc2/src/draftwright/annotations/balloons.py +0 -1161
  276. draftwright-0.5.0rc2/src/draftwright/annotations/from_model.py +0 -2485
  277. draftwright-0.5.0rc2/src/draftwright/annotations/hole_locations.py +0 -705
  278. draftwright-0.5.0rc2/src/draftwright/annotations/holes.py +0 -2469
  279. draftwright-0.5.0rc2/src/draftwright/annotations/leaders.py +0 -2430
  280. draftwright-0.5.0rc2/src/draftwright/annotations/orchestrator.py +0 -1918
  281. draftwright-0.5.0rc2/src/draftwright/annotations/routed.py +0 -138
  282. draftwright-0.5.0rc2/src/draftwright/annotations/sections.py +0 -1580
  283. draftwright-0.5.0rc2/src/draftwright/audit.py +0 -1430
  284. draftwright-0.5.0rc2/src/draftwright/build_once.py +0 -660
  285. draftwright-0.5.0rc2/src/draftwright/build_options.py +0 -152
  286. draftwright-0.5.0rc2/src/draftwright/builder.py +0 -2863
  287. draftwright-0.5.0rc2/src/draftwright/cli.py +0 -500
  288. draftwright-0.5.0rc2/src/draftwright/compose.py +0 -2745
  289. draftwright-0.5.0rc2/src/draftwright/drawing.py +0 -1956
  290. draftwright-0.5.0rc2/src/draftwright/drawing_diagnostics.py +0 -544
  291. draftwright-0.5.0rc2/src/draftwright/drawing_edits.py +0 -863
  292. draftwright-0.5.0rc2/src/draftwright/drawing_evidence.py +0 -419
  293. draftwright-0.5.0rc2/src/draftwright/drawing_export.py +0 -329
  294. draftwright-0.5.0rc2/src/draftwright/drawing_state.py +0 -160
  295. draftwright-0.5.0rc2/src/draftwright/evaluation/_edge_profile_evidence.py +0 -481
  296. draftwright-0.5.0rc2/src/draftwright/evaluation/_hole_family_evidence.py +0 -564
  297. draftwright-0.5.0rc2/src/draftwright/evaluation/_pocket_evidence.py +0 -626
  298. draftwright-0.5.0rc2/src/draftwright/evaluation/_turned_step_evidence.py +0 -363
  299. draftwright-0.5.0rc2/src/draftwright/evaluation/step_analysis.py +0 -2717
  300. draftwright-0.5.0rc2/src/draftwright/export.py +0 -843
  301. draftwright-0.5.0rc2/src/draftwright/inspection.py +0 -789
  302. draftwright-0.5.0rc2/src/draftwright/inspection_contract.py +0 -279
  303. draftwright-0.5.0rc2/src/draftwright/intent_routing.py +0 -279
  304. draftwright-0.5.0rc2/src/draftwright/layout.py +0 -1153
  305. draftwright-0.5.0rc2/src/draftwright/layout_selection.py +0 -638
  306. draftwright-0.5.0rc2/src/draftwright/linting/__init__.py +0 -135
  307. draftwright-0.5.0rc2/src/draftwright/linting/_coverage_common.py +0 -191
  308. draftwright-0.5.0rc2/src/draftwright/linting/angular.py +0 -618
  309. draftwright-0.5.0rc2/src/draftwright/linting/blend_coverage.py +0 -316
  310. draftwright-0.5.0rc2/src/draftwright/linting/boss_coverage.py +0 -247
  311. draftwright-0.5.0rc2/src/draftwright/linting/chamfer_coverage.py +0 -162
  312. draftwright-0.5.0rc2/src/draftwright/linting/circular_blind_step_coverage.py +0 -322
  313. draftwright-0.5.0rc2/src/draftwright/linting/coverage.py +0 -2574
  314. draftwright-0.5.0rc2/src/draftwright/linting/envelope_coverage.py +0 -211
  315. draftwright-0.5.0rc2/src/draftwright/linting/fillet_coverage.py +0 -159
  316. draftwright-0.5.0rc2/src/draftwright/linting/gear_coverage.py +0 -185
  317. draftwright-0.5.0rc2/src/draftwright/linting/hole_coverage.py +0 -1533
  318. draftwright-0.5.0rc2/src/draftwright/linting/orchestration.py +0 -672
  319. draftwright-0.5.0rc2/src/draftwright/linting/paired_ramp_step_coverage.py +0 -198
  320. draftwright-0.5.0rc2/src/draftwright/linting/pmi_coverage.py +0 -535
  321. draftwright-0.5.0rc2/src/draftwright/linting/polygonal_boss_coverage.py +0 -352
  322. draftwright-0.5.0rc2/src/draftwright/linting/polygonal_stock_coverage.py +0 -432
  323. draftwright-0.5.0rc2/src/draftwright/linting/quality.py +0 -1007
  324. draftwright-0.5.0rc2/src/draftwright/linting/rectangular_blind_slot_coverage.py +0 -269
  325. draftwright-0.5.0rc2/src/draftwright/linting/requirements.py +0 -222
  326. draftwright-0.5.0rc2/src/draftwright/linting/round_bottom_blind_slot_coverage.py +0 -275
  327. draftwright-0.5.0rc2/src/draftwright/linting/structural.py +0 -1671
  328. draftwright-0.5.0rc2/src/draftwright/linting/through_step_coverage.py +0 -673
  329. draftwright-0.5.0rc2/src/draftwright/make_drawing.py +0 -30
  330. draftwright-0.5.0rc2/src/draftwright/measurement_support.py +0 -95
  331. draftwright-0.5.0rc2/src/draftwright/model/callout.py +0 -731
  332. draftwright-0.5.0rc2/src/draftwright/model/compiled.py +0 -2458
  333. draftwright-0.5.0rc2/src/draftwright/model/declare.py +0 -2771
  334. draftwright-0.5.0rc2/src/draftwright/model/detect.py +0 -2816
  335. draftwright-0.5.0rc2/src/draftwright/model/detect_inventory.py +0 -276
  336. draftwright-0.5.0rc2/src/draftwright/model/detect_ownership.py +0 -527
  337. draftwright-0.5.0rc2/src/draftwright/model/ir.py +0 -2922
  338. draftwright-0.5.0rc2/src/draftwright/model/ir_foundation.py +0 -1048
  339. draftwright-0.5.0rc2/src/draftwright/model/planner.py +0 -2141
  340. draftwright-0.5.0rc2/src/draftwright/model/pmi_lowering.py +0 -1403
  341. draftwright-0.5.0rc2/src/draftwright/pdf_text.py +0 -672
  342. draftwright-0.5.0rc2/src/draftwright/pmi.py +0 -2910
  343. draftwright-0.5.0rc2/src/draftwright/profile_angles.py +0 -312
  344. draftwright-0.5.0rc2/src/draftwright/projection.py +0 -658
  345. draftwright-0.5.0rc2/src/draftwright/recogniser_contract.py +0 -1074
  346. draftwright-0.5.0rc2/src/draftwright/recogniser_policy.py +0 -100
  347. draftwright-0.5.0rc2/src/draftwright/recogniser_schema.py +0 -102
  348. draftwright-0.5.0rc2/src/draftwright/recognition_ownership.py +0 -1215
  349. draftwright-0.5.0rc2/src/draftwright/repair.py +0 -341
  350. draftwright-0.5.0rc2/src/draftwright/reporting.py +0 -2851
  351. draftwright-0.5.0rc2/src/draftwright/section_recess_contract.py +0 -998
  352. draftwright-0.5.0rc2/src/draftwright/sheet.py +0 -2917
  353. draftwright-0.5.0rc2/src/draftwright/sheet_emit.py +0 -2162
  354. draftwright-0.5.0rc2/src/draftwright/sheet_feature_lines.py +0 -961
  355. draftwright-0.5.0rc2/src/draftwright/sheet_views.py +0 -667
  356. draftwright-0.5.0rc2/tests/_tier_manifest.py +0 -661
  357. draftwright-0.5.0rc2/tests/fixtures/evaluation/README.md +0 -248
  358. draftwright-0.5.0rc2/tests/fixtures/evaluation/corpus-hole-patterns-v1.json +0 -151
  359. draftwright-0.5.0rc2/tests/refactor_golden/grid_plate.json +0 -503
  360. draftwright-0.5.0rc2/tests/test_adr0018_view_selection.py +0 -714
  361. draftwright-0.5.0rc2/tests/test_adr_corpus.py +0 -134
  362. draftwright-0.5.0rc2/tests/test_agent_improvement_canary_issue_1716.py +0 -290
  363. draftwright-0.5.0rc2/tests/test_annotation_box_composition.py +0 -131
  364. draftwright-0.5.0rc2/tests/test_annotation_box_corpus.py +0 -81
  365. draftwright-0.5.0rc2/tests/test_annotation_layout_product.py +0 -740
  366. draftwright-0.5.0rc2/tests/test_annotation_scheme_compare_script.py +0 -764
  367. draftwright-0.5.0rc2/tests/test_architecture_docs.py +0 -28
  368. draftwright-0.5.0rc2/tests/test_boss_ownership.py +0 -763
  369. draftwright-0.5.0rc2/tests/test_build_drawing_entrypoints.py +0 -279
  370. draftwright-0.5.0rc2/tests/test_channel_ownership.py +0 -390
  371. draftwright-0.5.0rc2/tests/test_circular_blind_step_semantics.py +0 -1073
  372. draftwright-0.5.0rc2/tests/test_compiled_plan_boundary.py +0 -1383
  373. draftwright-0.5.0rc2/tests/test_compose_then_pack.py +0 -504
  374. draftwright-0.5.0rc2/tests/test_declare.py +0 -2246
  375. draftwright-0.5.0rc2/tests/test_declared_recognition_gate.py +0 -573
  376. draftwright-0.5.0rc2/tests/test_deprecation_dates.py +0 -227
  377. draftwright-0.5.0rc2/tests/test_detect_registry.py +0 -337
  378. draftwright-0.5.0rc2/tests/test_diameter_leaders.py +0 -316
  379. draftwright-0.5.0rc2/tests/test_dimension_lane_canary_issue_1757.py +0 -153
  380. draftwright-0.5.0rc2/tests/test_dimension_role_vocabulary.py +0 -397
  381. draftwright-0.5.0rc2/tests/test_document_build.py +0 -498
  382. draftwright-0.5.0rc2/tests/test_drawing_encapsulation.py +0 -566
  383. draftwright-0.5.0rc2/tests/test_e2e_standards.py +0 -551
  384. draftwright-0.5.0rc2/tests/test_export_formats.py +0 -327
  385. draftwright-0.5.0rc2/tests/test_feature_edit_verbs.py +0 -396
  386. draftwright-0.5.0rc2/tests/test_feature_leader_candidate_regions.py +0 -1273
  387. draftwright-0.5.0rc2/tests/test_gdt_ink_shared.py +0 -288
  388. draftwright-0.5.0rc2/tests/test_gdt_placement.py +0 -512
  389. draftwright-0.5.0rc2/tests/test_groove_completeness_evidence.py +0 -655
  390. draftwright-0.5.0rc2/tests/test_hole_annotations.py +0 -316
  391. draftwright-0.5.0rc2/tests/test_hole_pattern_callouts.py +0 -173
  392. draftwright-0.5.0rc2/tests/test_hole_table.py +0 -759
  393. draftwright-0.5.0rc2/tests/test_import_boundaries.py +0 -1344
  394. draftwright-0.5.0rc2/tests/test_inspection_contract.py +0 -182
  395. draftwright-0.5.0rc2/tests/test_interior_label_placement.py +0 -337
  396. draftwright-0.5.0rc2/tests/test_issue_1058_wheel_profile.py +0 -911
  397. draftwright-0.5.0rc2/tests/test_issue_1116_ap242_hole_tolerance_lowering.py +0 -484
  398. draftwright-0.5.0rc2/tests/test_issue_1142_hole_leader_labels.py +0 -262
  399. draftwright-0.5.0rc2/tests/test_issue_1143_hole_completeness.py +0 -2902
  400. draftwright-0.5.0rc2/tests/test_issue_1166_cross_pass_feature_leaders.py +0 -2615
  401. draftwright-0.5.0rc2/tests/test_issue_1176_quality_fidelity.py +0 -1028
  402. draftwright-0.5.0rc2/tests/test_issue_1188_per_view_assignment.py +0 -109
  403. draftwright-0.5.0rc2/tests/test_issue_1190_section_decision.py +0 -255
  404. draftwright-0.5.0rc2/tests/test_issue_1202_observed_drawing_consumer.py +0 -633
  405. draftwright-0.5.0rc2/tests/test_issue_1215_envelope_tolerance.py +0 -321
  406. draftwright-0.5.0rc2/tests/test_issue_1215_no_approved_tolerance_is_dropped.py +0 -957
  407. draftwright-0.5.0rc2/tests/test_issue_1217_the_facility_is_shared.py +0 -427
  408. draftwright-0.5.0rc2/tests/test_issue_1219_location_claims_its_own_axis.py +0 -286
  409. draftwright-0.5.0rc2/tests/test_issue_1298_manufacturing_requirements.py +0 -1868
  410. draftwright-0.5.0rc2/tests/test_issue_1308_machined_leader_analytics.py +0 -420
  411. draftwright-0.5.0rc2/tests/test_issue_1352_searchable_pdf_text.py +0 -1101
  412. draftwright-0.5.0rc2/tests/test_issue_1357_framed_activation.py +0 -237
  413. draftwright-0.5.0rc2/tests/test_issue_1370_double_d_completeness_evidence.py +0 -731
  414. draftwright-0.5.0rc2/tests/test_issue_1370_hole_pattern_completeness_evidence.py +0 -306
  415. draftwright-0.5.0rc2/tests/test_issue_1371_polygonal_stock_completeness_evidence.py +0 -1211
  416. draftwright-0.5.0rc2/tests/test_issue_1460_step_inspection.py +0 -1063
  417. draftwright-0.5.0rc2/tests/test_issue_1505_diameter_coverage.py +0 -156
  418. draftwright-0.5.0rc2/tests/test_issue_1518_explicit_rear.py +0 -686
  419. draftwright-0.5.0rc2/tests/test_issue_1531_semantic_hole_counts.py +0 -255
  420. draftwright-0.5.0rc2/tests/test_issue_1544_frame_document_canary.py +0 -419
  421. draftwright-0.5.0rc2/tests/test_issue_1596_bolt_circle_corroboration.py +0 -452
  422. draftwright-0.5.0rc2/tests/test_issue_740_leader_assignment.py +0 -774
  423. draftwright-0.5.0rc2/tests/test_issue_798_material_field.py +0 -495
  424. draftwright-0.5.0rc2/tests/test_issue_798_silhouette_lint.py +0 -346
  425. draftwright-0.5.0rc2/tests/test_issue_883_location_identity.py +0 -574
  426. draftwright-0.5.0rc2/tests/test_issue_916_pocket_leader.py +0 -110
  427. draftwright-0.5.0rc2/tests/test_label_provenance.py +0 -186
  428. draftwright-0.5.0rc2/tests/test_layout_override_lane_issue_1757.py +0 -264
  429. draftwright-0.5.0rc2/tests/test_layout_safety.py +0 -756
  430. draftwright-0.5.0rc2/tests/test_layout_scheme.py +0 -583
  431. draftwright-0.5.0rc2/tests/test_lint_summary.py +0 -404
  432. draftwright-0.5.0rc2/tests/test_location_dimensions.py +0 -538
  433. draftwright-0.5.0rc2/tests/test_note_verb.py +0 -71
  434. draftwright-0.5.0rc2/tests/test_pad_rendering.py +0 -415
  435. draftwright-0.5.0rc2/tests/test_parameter_id.py +0 -757
  436. draftwright-0.5.0rc2/tests/test_pareto_loop_canary_issue_1753.py +0 -169
  437. draftwright-0.5.0rc2/tests/test_part_model.py +0 -367
  438. draftwright-0.5.0rc2/tests/test_pitch_dim_footprint.py +0 -290
  439. draftwright-0.5.0rc2/tests/test_pmi.py +0 -2506
  440. draftwright-0.5.0rc2/tests/test_pmi_datum_lowering.py +0 -480
  441. draftwright-0.5.0rc2/tests/test_pmi_gtol_lowering.py +0 -300
  442. draftwright-0.5.0rc2/tests/test_pmi_part21.py +0 -1506
  443. draftwright-0.5.0rc2/tests/test_pmi_records.py +0 -88
  444. draftwright-0.5.0rc2/tests/test_pocket_completeness_evidence.py +0 -474
  445. draftwright-0.5.0rc2/tests/test_pocket_pattern_completeness_evidence.py +0 -970
  446. draftwright-0.5.0rc2/tests/test_pocket_pattern_recognition.py +0 -206
  447. draftwright-0.5.0rc2/tests/test_private_test_attr_reads.py +0 -309
  448. draftwright-0.5.0rc2/tests/test_profile_angles.py +0 -195
  449. draftwright-0.5.0rc2/tests/test_quality_components.py +0 -382
  450. draftwright-0.5.0rc2/tests/test_recogniser_capabilities.py +0 -1709
  451. draftwright-0.5.0rc2/tests/test_recogniser_contract.py +0 -646
  452. draftwright-0.5.0rc2/tests/test_refactor_golden.py +0 -458
  453. draftwright-0.5.0rc2/tests/test_remaining_ink_overlaps.py +0 -242
  454. draftwright-0.5.0rc2/tests/test_render_seam.py +0 -430
  455. draftwright-0.5.0rc2/tests/test_report_projection.py +0 -1023
  456. draftwright-0.5.0rc2/tests/test_requirement_catalog.py +0 -403
  457. draftwright-0.5.0rc2/tests/test_requirement_catalog_families.py +0 -132
  458. draftwright-0.5.0rc2/tests/test_sheet_emit.py +0 -4974
  459. draftwright-0.5.0rc2/tests/test_sheet_furniture.py +0 -145
  460. draftwright-0.5.0rc2/tests/test_sheet_section.py +0 -159
  461. draftwright-0.5.0rc2/tests/test_sheet_tables.py +0 -307
  462. draftwright-0.5.0rc2/tests/test_slanted_blind_step.py +0 -260
  463. draftwright-0.5.0rc2/tests/test_slot_completeness.py +0 -706
  464. draftwright-0.5.0rc2/tests/test_slot_recognition.py +0 -558
  465. draftwright-0.5.0rc2/tests/test_source_shape.py +0 -104
  466. draftwright-0.5.0rc2/tests/test_step_analysis_evaluation.py +0 -417
  467. draftwright-0.5.0rc2/tests/test_strip_layout.py +0 -1935
  468. draftwright-0.5.0rc2/tests/test_suite_shape.py +0 -64
  469. draftwright-0.5.0rc2/tests/test_through_step_semantics.py +0 -1793
  470. draftwright-0.5.0rc2/tests/test_tier_manifest.py +0 -699
  471. draftwright-0.5.0rc2/tests/test_title_block_units_format.py +0 -189
  472. draftwright-0.5.0rc2/tests/test_turned_diameters.py +0 -821
  473. draftwright-0.5.0rc2/tests/test_turned_steps.py +0 -117
  474. draftwright-0.5.0rc2/tests/test_workflows.py +0 -761
  475. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/.gitignore +0 -0
  476. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/LICENSE +0 -0
  477. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/docs/adr/README.md +0 -0
  478. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/docs/reference/sheet.md +0 -0
  479. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/docs/research/1062-repeating-radial-profile-evidence.md +0 -0
  480. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/__init__.py +0 -0
  481. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/_pmi_linear_geometry.py +0 -0
  482. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/_pmi_schema.py +0 -0
  483. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/_warnings.py +0 -0
  484. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/angular_geometry.py +0 -0
  485. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotation_layout_profile.py +0 -0
  486. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/__init__.py +0 -0
  487. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_circular_recesses.py +0 -0
  488. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_dimension_ink.py +0 -0
  489. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_height_ladder.py +0 -0
  490. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_hole_leader_placement.py +0 -0
  491. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_leader_candidates.py +0 -0
  492. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_placement_geometry.py +0 -0
  493. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_pocket_pad.py +0 -0
  494. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_polygonal_prisms.py +0 -0
  495. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/_strip_postsolve.py +0 -0
  496. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/gears.py +0 -0
  497. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/hole_leader_candidates.py +0 -0
  498. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/annotations/solve_trace.py +0 -0
  499. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/auxiliary_layout.py +0 -0
  500. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/blend_contract.py +0 -0
  501. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/build_policy.py +0 -0
  502. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/contract_values.py +0 -0
  503. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/document.py +0 -0
  504. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/document_evidence.py +0 -0
  505. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/document_input.py +0 -0
  506. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/drawing_tables.py +0 -0
  507. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/evaluation/__init__.py +0 -0
  508. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/evaluation/_double_d_evidence.py +0 -0
  509. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/evaluation/_flat_polygonal_evidence.py +0 -0
  510. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/evaluation/_groove_evidence.py +0 -0
  511. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/evaluation/_prismatic_evidence.py +0 -0
  512. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/explicit_scale.py +0 -0
  513. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/feature_identity.py +0 -0
  514. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/fits.py +0 -0
  515. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
  516. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
  517. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
  518. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
  519. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/fonts/__init__.py +0 -0
  520. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/intent_drain.py +0 -0
  521. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/intents.py +0 -0
  522. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/layout_safety.py +0 -0
  523. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/layout_scheme.py +0 -0
  524. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/leader_policy.py +0 -0
  525. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/_registry.py +0 -0
  526. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/angled_step_coverage.py +0 -0
  527. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/channel_coverage.py +0 -0
  528. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/evidence.py +0 -0
  529. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/flat_coverage.py +0 -0
  530. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/groove_coverage.py +0 -0
  531. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/gusset_rib_coverage.py +0 -0
  532. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/ink_overlap.py +0 -0
  533. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/issues.py +0 -0
  534. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/oriented_slot_coverage.py +0 -0
  535. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/pad_coverage.py +0 -0
  536. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/passage_coverage.py +0 -0
  537. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/plate_coverage.py +0 -0
  538. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/pocket_coverage.py +0 -0
  539. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/pocket_pattern_coverage.py +0 -0
  540. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/prismatic_pocket_coverage.py +0 -0
  541. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/profiled_bore_coverage.py +0 -0
  542. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/schedule_evidence.py +0 -0
  543. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/section_recess_coverage.py +0 -0
  544. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/slot_coverage.py +0 -0
  545. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/suggest.py +0 -0
  546. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/linting/turned_step_coverage.py +0 -0
  547. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/location_contract.py +0 -0
  548. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/model/__init__.py +0 -0
  549. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/model/dimension_intent.py +0 -0
  550. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/model/manufacturing_schedule.py +0 -0
  551. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/model/oriented_slot_geometry.py +0 -0
  552. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/obligations.py +0 -0
  553. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/oriented_slot_contract.py +0 -0
  554. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/plate_correspondence.py +0 -0
  555. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/progress.py +0 -0
  556. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/py.typed +0 -0
  557. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/recognition/__init__.py +0 -0
  558. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/recognition_cache.py +0 -0
  559. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/recognition_frame.py +0 -0
  560. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/registry.py +0 -0
  561. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/replay_assessment.py +0 -0
  562. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/score.py +0 -0
  563. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/sheet_features.py +0 -0
  564. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/sheet_layout_controls.py +0 -0
  565. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/sheet_object_source.py +0 -0
  566. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/src/draftwright/view_plan.py +0 -0
  567. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_burden_report.py +0 -0
  568. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_corpus_cover.py +0 -0
  569. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_drawing_helpers.py +0 -0
  570. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_evidence_contract.py +0 -0
  571. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_kernel.py +0 -0
  572. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_layout_helpers.py +0 -0
  573. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_layout_sig.py +0 -0
  574. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_mutation_corpus.py +0 -0
  575. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_pad_rendering_fixtures.py +0 -0
  576. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_parts.py +0 -0
  577. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_pattern_contract.py +0 -0
  578. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_recogniser_public_contract.py +0 -0
  579. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_section_recess_cases.py +0 -0
  580. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/_unit_manifest.py +0 -0
  581. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/conftest.py +0 -0
  582. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/annotation-layout-corpus-v1.json +0 -0
  583. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/annotation-layout-corpus-v2.json +0 -0
  584. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/annotation-layout-holdout-ftc09-v1.json +0 -0
  585. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/annotation-layout-holdout-v1.json +0 -0
  586. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/ap242_single_cylinder_diameter.step +0 -0
  587. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/ambiguous-open-semicircle.step +0 -0
  588. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/blind-hole.step +0 -0
  589. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-asymmetric.step +0 -0
  590. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-compound.step +0 -0
  591. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-overlap.step +0 -0
  592. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-plain.step +0 -0
  593. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-planar-x.step +0 -0
  594. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-planar-y.step +0 -0
  595. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-planar-z.step +0 -0
  596. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-topology-a.step +0 -0
  597. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-topology-b.step +0 -0
  598. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/chamfer-turned.step +0 -0
  599. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-chamfers-v1.json +0 -0
  600. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-countersinks-v1.json +0 -0
  601. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-double-d-bores-v1.json +0 -0
  602. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-fillets-v1.json +0 -0
  603. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-flats-v1.json +0 -0
  604. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-grooves-v1.json +0 -0
  605. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-plates-v1.json +0 -0
  606. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-pocket-patterns-v1.json +0 -0
  607. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-pockets-v1.json +0 -0
  608. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-polygonal-bosses-v1.json +0 -0
  609. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-polygonal-stock-v1.json +0 -0
  610. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-rectangular-pads-v1.json +0 -0
  611. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-turned-steps-v1.json +0 -0
  612. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/corpus-v1.json +0 -0
  613. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/countersink-deburr.step +0 -0
  614. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/countersink-external-cone.step +0 -0
  615. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/countersink-mixed-pair.step +0 -0
  616. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/countersink-single.step +0 -0
  617. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/countersink-topology-a.step +0 -0
  618. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/countersink-topology-b.step +0 -0
  619. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-axis-x.step +0 -0
  620. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-axis-y.step +0 -0
  621. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-blind.step +0 -0
  622. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-coaxial-compound.step +0 -0
  623. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-opposed-blind.step +0 -0
  624. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-roll-30.step +0 -0
  625. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-round-bore.step +0 -0
  626. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-single-z.step +0 -0
  627. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-topology-a.step +0 -0
  628. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/double-d-topology-b.step +0 -0
  629. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-compound.step +0 -0
  630. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-overlap.step +0 -0
  631. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-plain.step +0 -0
  632. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-planar-x.step +0 -0
  633. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-planar-y.step +0 -0
  634. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-planar-z.step +0 -0
  635. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-repeated.step +0 -0
  636. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-topology-a.step +0 -0
  637. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-topology-b.step +0 -0
  638. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/fillet-turned.step +0 -0
  639. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/flat-coaxial.step +0 -0
  640. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/flat-double-d.step +0 -0
  641. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/flat-lone-d.step +0 -0
  642. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/flat-nonround.step +0 -0
  643. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/flat-slanted-double-d.step +0 -0
  644. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/flat-topology-a.step +0 -0
  645. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/flat-topology-b.step +0 -0
  646. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/groove-compound.step +0 -0
  647. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/groove-lone-x.step +0 -0
  648. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/groove-lone-y.step +0 -0
  649. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/groove-lone-z.step +0 -0
  650. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/groove-monotonic-negative.step +0 -0
  651. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/groove-narrow.step +0 -0
  652. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/groove-topology-a.step +0 -0
  653. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/groove-topology-b.step +0 -0
  654. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-compound-equal.step +0 -0
  655. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-detached-body-negative.step +0 -0
  656. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-full-span-ledge-negative.step +0 -0
  657. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-nested-staircase-negative.step +0 -0
  658. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-topology-a.step +0 -0
  659. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-topology-b.step +0 -0
  660. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-x-negative.step +0 -0
  661. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-x-positive.step +0 -0
  662. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-y-negative.step +0 -0
  663. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-y-positive.step +0 -0
  664. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-z-negative.step +0 -0
  665. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pad-z-positive.step +0 -0
  666. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pattern-ambiguous.step +0 -0
  667. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pattern-grid.step +0 -0
  668. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pattern-topology-a.step +0 -0
  669. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pattern-topology-b.step +0 -0
  670. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plain-block.step +0 -0
  671. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-compound-equal.step +0 -0
  672. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-cross-topology-a.step +0 -0
  673. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-cross-topology-b.step +0 -0
  674. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-detached-negative.step +0 -0
  675. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-rotational-negative.step +0 -0
  676. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-single-negative.step +0 -0
  677. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-t-xz.step +0 -0
  678. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-t-yz.step +0 -0
  679. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-thick-negative.step +0 -0
  680. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-u-additive.step +0 -0
  681. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/plate-u-cut.step +0 -0
  682. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-compound.step +0 -0
  683. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-edge-anchored.step +0 -0
  684. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-lone.step +0 -0
  685. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-opposed.step +0 -0
  686. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-pattern-ambiguous.step +0 -0
  687. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-pattern-grid.step +0 -0
  688. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-pattern-linear.step +0 -0
  689. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-pattern-pair.step +0 -0
  690. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-pattern-topology-a.step +0 -0
  691. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-pattern-topology-b.step +0 -0
  692. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-prismatic-negative.step +0 -0
  693. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-side.step +0 -0
  694. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-through-negative.step +0 -0
  695. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-topology-a.step +0 -0
  696. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-topology-b.step +0 -0
  697. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/pocket-two-equal.step +0 -0
  698. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-compound-equal.step +0 -0
  699. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-detached-negative.step +0 -0
  700. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-in-plane-rotated.step +0 -0
  701. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-other-protrusions-negative.step +0 -0
  702. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-recess-negative.step +0 -0
  703. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-stock-negative.step +0 -0
  704. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-topology-a.step +0 -0
  705. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-topology-b.step +0 -0
  706. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-x.step +0 -0
  707. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-y.step +0 -0
  708. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-boss-z.step +0 -0
  709. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-compound-negative.step +0 -0
  710. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-cylinder-negative.step +0 -0
  711. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-irregular-negative.step +0 -0
  712. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-octagon-negative.step +0 -0
  713. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-recess-negative.step +0 -0
  714. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-roll17.step +0 -0
  715. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-topology-a.step +0 -0
  716. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-topology-b.step +0 -0
  717. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-translated.step +0 -0
  718. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-x.step +0 -0
  719. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/polygonal-stock-y.step +0 -0
  720. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/topology-a.step +0 -0
  721. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/topology-b.step +0 -0
  722. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-axis-x.step +0 -0
  723. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-axis-y.step +0 -0
  724. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-axis-z.step +0 -0
  725. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-compound.step +0 -0
  726. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-repeated-lengths.step +0 -0
  727. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-through-bore.step +0 -0
  728. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-topology-a.step +0 -0
  729. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-topology-b.step +0 -0
  730. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/evaluation/turned-step-translated-blind-bore.step +0 -0
  731. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
  732. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/grm03_thumbwheel_drive_screw_ap242_pmi.step +0 -0
  733. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/grm04_drive_plate.step +0 -0
  734. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
  735. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
  736. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/issue_1247_angled_blind_step.step +0 -0
  737. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/issue_1595_whistle_key_frame.step +0 -0
  738. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/issue_1595_whistle_key_lever.step +0 -0
  739. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
  740. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/issue_915_case_study_2.step +0 -0
  741. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
  742. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
  743. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
  744. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
  745. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
  746. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
  747. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
  748. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
  749. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
  750. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
  751. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/nist_ftc_09_asme1_ap242-e1.stp +0 -0
  752. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
  753. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/fixtures/whistle_frame_reference.step +0 -0
  754. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/bracket_section.json +0 -0
  755. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/centered_rebate.json +0 -0
  756. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/chamfered.json +0 -0
  757. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/filleted.json +0 -0
  758. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/flange_dense.json +0 -0
  759. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/grooved_shaft.json +0 -0
  760. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/hex_bar.json +0 -0
  761. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/holed_slot.json +0 -0
  762. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/pocketed.json +0 -0
  763. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/prismatic_ladder.json +0 -0
  764. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/scattered_plate.json +0 -0
  765. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/side_drilled.json +0 -0
  766. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/refactor_golden/turned_stepped.json +0 -0
  767. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_add_dimension.py +0 -0
  768. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_adr0018_view_routing.py +0 -0
  769. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_agents_guide.py +0 -0
  770. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_analysis_geometry.py +0 -0
  771. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_angle_patterns.py +0 -0
  772. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_angular_references.py +0 -0
  773. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_angular_rendering.py +0 -0
  774. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_annotation_scheme_corpus_script.py +0 -0
  775. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_annotation_scheme_shadow_script.py +0 -0
  776. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_api_docs.py +0 -0
  777. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_arrangement_gate.py +0 -0
  778. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_assessment_comparison_issue_1712.py +0 -0
  779. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_audit_differential.py +0 -0
  780. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_auxiliary_layout.py +0 -0
  781. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_axial_dimensions.py +0 -0
  782. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_balloon_ring_standoff.py +0 -0
  783. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_blind_slot_completeness.py +0 -0
  784. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_blind_slot_semantics.py +0 -0
  785. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_build_profile_harness.py +0 -0
  786. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_burden_report.py +0 -0
  787. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_candidate_evidence_issue_1726.py +0 -0
  788. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_candidate_preview_sheet_holdout.py +0 -0
  789. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_canonical_angles.py +0 -0
  790. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_carve_free_position_callers.py +0 -0
  791. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_cli_behavior.py +0 -0
  792. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_cli_report_sidecar.py +0 -0
  793. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_clone_budget.py +0 -0
  794. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_contract_values.py +0 -0
  795. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_corpus_cover.py +0 -0
  796. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_counting_calls.py +0 -0
  797. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_coverage_mode.py +0 -0
  798. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_declaration_identity_issue_1710.py +0 -0
  799. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_declaration_report_issue_1710.py +0 -0
  800. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_declared_report_issue_1688.py +0 -0
  801. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_deferred_edits.py +0 -0
  802. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_dense_sheet_canary.py +0 -0
  803. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_depth_estimators.py +0 -0
  804. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_derived_view_integration.py +0 -0
  805. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_derived_view_planning.py +0 -0
  806. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_detail_views.py +0 -0
  807. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_detect_once.py +0 -0
  808. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_diameter_deduplication.py +0 -0
  809. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_dimension_intent_contract.py +0 -0
  810. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_document_claim_authority.py +0 -0
  811. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_document_claims.py +0 -0
  812. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_document_coverage.py +0 -0
  813. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_document_report.py +0 -0
  814. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_document_report_authority.py +0 -0
  815. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_drawing_operations.py +0 -0
  816. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_drawing_state.py +0 -0
  817. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_dynamic_corridors.py +0 -0
  818. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_e2e_slice.py +0 -0
  819. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_envelope_view_route_issue_1813.py +0 -0
  820. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_equivalent_dimension_presentations_issue_1759.py +0 -0
  821. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_export_dxf_curves.py +0 -0
  822. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_export_dxf_zoom.py +0 -0
  823. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_export_reproducible.py +0 -0
  824. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_export_shape.py +0 -0
  825. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_external_recognition_boundary.py +0 -0
  826. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_feature_edit_corridors.py +0 -0
  827. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_feature_edit_replay.py +0 -0
  828. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_feature_provenance.py +0 -0
  829. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_feature_queries.py +0 -0
  830. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_fillets_adjacency.py +0 -0
  831. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_fit_calculations.py +0 -0
  832. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_fits.py +0 -0
  833. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_flat_callout_precision.py +0 -0
  834. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_flat_completeness.py +0 -0
  835. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_flat_stock_identity.py +0 -0
  836. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_flat_stock_opposition.py +0 -0
  837. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_format_selector.py +0 -0
  838. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_formatting.py +0 -0
  839. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_framed_step_family_evidence.py +0 -0
  840. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_geometry_graph_spike.py +0 -0
  841. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_grid_lattice_convention.py +0 -0
  842. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_groove_profile.py +0 -0
  843. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_gusset_rib_semantics.py +0 -0
  844. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_ir_foundation.py +0 -0
  845. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_iso_detail_growth.py +0 -0
  846. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_iso_layout.py +0 -0
  847. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_iso_nts_caption_placement.py +0 -0
  848. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_isometric_orientation.py +0 -0
  849. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_isometric_placement.py +0 -0
  850. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1000_envelope_reuse.py +0 -0
  851. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1006_measurement_comparison.py +0 -0
  852. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1073_cross_body_patterns.py +0 -0
  853. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1082_polygonal_stock.py +0 -0
  854. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1086_declared_gears.py +0 -0
  855. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1126_requesting_less_never_scores_better.py +0 -0
  856. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1130_view_planning_evidence.py +0 -0
  857. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1132_turned_profile_span_is_reported.py +0 -0
  858. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1144_transactional_hole_table.py +0 -0
  859. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1146_scale_completeness.py +0 -0
  860. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1147_legibility_pair_aggregation.py +0 -0
  861. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1153_contradictory_dimensions.py +0 -0
  862. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1154_one_owner_per_measurement.py +0 -0
  863. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1155_grm04_sheet_use.py +0 -0
  864. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1157_no_implicit_general_tolerance.py +0 -0
  865. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1177_angular_dimensions.py +0 -0
  866. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1187_unroutable_leaders.py +0 -0
  867. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1196_deterministic_view_names.py +0 -0
  868. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1204_multiscale_view_issues.py +0 -0
  869. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1209_linear_pmi_witnesses.py +0 -0
  870. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1216_callout_reservation_measures_ink.py +0 -0
  871. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1216_pairwise_across_scale_groups.py +0 -0
  872. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1217_claimed_representations.py +0 -0
  873. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1229_ledger_member_keying.py +0 -0
  874. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1240_iso_above_strip_visibility.py +0 -0
  875. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1245_passage_disposition.py +0 -0
  876. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1246_prismatic_pocket_disposition.py +0 -0
  877. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1247_angled_step_disposition.py +0 -0
  878. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1254_turned_chamfers.py +0 -0
  879. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1260_view_constraints.py +0 -0
  880. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1262_automatic_turned_views.py +0 -0
  881. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1276_turned_leader_targets.py +0 -0
  882. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1281_turned_fillets.py +0 -0
  883. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1296_cylindrical_diameter_pmi.py +0 -0
  884. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1299_page_escalation.py +0 -0
  885. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1312_engine_costs.py +0 -0
  886. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1325_nominal_agreement.py +0 -0
  887. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1332_overlap_remedy.py +0 -0
  888. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1333_bounded_ink_repair.py +0 -0
  889. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1334_prevent_ink.py +0 -0
  890. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1336_grm03_ap242_pmi_fixture.py +0 -0
  891. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1338_scale_before_page_escalation.py +0 -0
  892. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1349_precision_display.py +0 -0
  893. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1350_detected_takeover.py +0 -0
  894. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1351_structured_note_coverage.py +0 -0
  895. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1353_cnc_authoritative_workflow.py +0 -0
  896. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1357_framed_boundary.py +0 -0
  897. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1357_plural_turned_profiles.py +0 -0
  898. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1357_pmi_frame.py +0 -0
  899. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1360_thread_depth.py +0 -0
  900. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1369_hole_completeness_evidence.py +0 -0
  901. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1370_countersink_completeness_evidence.py +0 -0
  902. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1371_flat_completeness_evidence.py +0 -0
  903. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1373_plate_completeness_evidence.py +0 -0
  904. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1374_chamfer_completeness_evidence.py +0 -0
  905. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1374_fillet_completeness_evidence.py +0 -0
  906. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1374_turned_step_completeness_evidence.py +0 -0
  907. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1378_diameter_leader_targets.py +0 -0
  908. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1392_recognisers_048.py +0 -0
  909. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1395_degenerate_iso_region.py +0 -0
  910. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1396_layout_advisories.py +0 -0
  911. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1397_unverifiable_requirement_message.py +0 -0
  912. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1432_adoption.py +0 -0
  913. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1432_boundary_failures.py +0 -0
  914. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1432_oriented_slot_semantics.py +0 -0
  915. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1433_blend_semantics.py +0 -0
  916. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1450_boss_blend_ownership.py +0 -0
  917. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1466_dimension_options.py +0 -0
  918. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1466_semantic_sides.py +0 -0
  919. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1479_radius_leader_targets.py +0 -0
  920. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1485_curved_pockets.py +0 -0
  921. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1485_quiddity_024_boundary.py +0 -0
  922. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1485_rounded_pockets.py +0 -0
  923. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1505_short_axial_chains.py +0 -0
  924. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1511_turned_boss_heights.py +0 -0
  925. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1514_projection_safeguard.py +0 -0
  926. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1515_first_angle.py +0 -0
  927. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1516_dimension_text.py +0 -0
  928. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1517_through_indicator.py +0 -0
  929. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1524_tolerance_magnitude.py +0 -0
  930. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1530_section_provenance.py +0 -0
  931. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1532_authored_section_examples.py +0 -0
  932. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1533_review_diagnostics.py +0 -0
  933. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1534_build_progress.py +0 -0
  934. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1536_title_overflow.py +0 -0
  935. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1543_feature_schedules.py +0 -0
  936. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1543_schedule_requirements.py +0 -0
  937. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1555_turned_recognition_format_invariance.py +0 -0
  938. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1560_envelope_axes.py +0 -0
  939. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1563_sheet_pmi_reconciliation.py +0 -0
  940. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1585_title_block_date.py +0 -0
  941. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1593_title_block_keepout.py +0 -0
  942. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1598_hex_pockets.py +0 -0
  943. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1604_internal_section.py +0 -0
  944. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1612_frame_sergio_coverage.py +0 -0
  945. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1612_sheet_margins.py +0 -0
  946. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_1613_circular_channel.py +0 -0
  947. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_367_leader_ink.py +0 -0
  948. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_443_grm03_axial_coverage.py +0 -0
  949. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_563_placement_intent.py +0 -0
  950. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_676_polygonal_boss.py +0 -0
  951. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
  952. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_798_floor_cardinality.py +0 -0
  953. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_827_real_part_canary.py +0 -0
  954. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_885_prismatic_coverage.py +0 -0
  955. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_909_sloped_profile.py +0 -0
  956. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_915_dense_case.py +0 -0
  957. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_917_open_channel.py +0 -0
  958. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_924_zero_length_shoulders.py +0 -0
  959. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_955_height_contingency.py +0 -0
  960. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_issue_958_cross_solid_recognition.py +0 -0
  961. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_layout.py +0 -0
  962. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_layout_cleanliness.py +0 -0
  963. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_layout_constants.py +0 -0
  964. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_layout_evidence_issue_1711.py +0 -0
  965. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_layout_generalisation.py +0 -0
  966. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_layout_hypothesis.py +0 -0
  967. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_layout_override_side_issue_1757.py +0 -0
  968. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_layout_property.py +0 -0
  969. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_leader_footprint.py +0 -0
  970. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_leader_region_policy.py +0 -0
  971. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_lint_box_cache.py +0 -0
  972. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_lint_coverage.py +0 -0
  973. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_lint_coverage_common.py +0 -0
  974. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_lint_ink_overlap.py +0 -0
  975. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_lint_reconciliation.py +0 -0
  976. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_lint_structural.py +0 -0
  977. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_lint_suggestions.py +0 -0
  978. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_linting.py +0 -0
  979. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_location_vocabulary.py +0 -0
  980. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_machined_feature_callouts.py +0 -0
  981. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_make_drawing_entrypoints.py +0 -0
  982. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_manufacturing_schedule.py +0 -0
  983. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_nested_recognition_ownership.py +0 -0
  984. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_nominal_step_pmi.py +0 -0
  985. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_object_aspects.py +0 -0
  986. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_occupancy_boxes.py +0 -0
  987. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_occurrence_recognition_ownership.py +0 -0
  988. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_overall_height_coverage.py +0 -0
  989. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_pad_completeness_evidence.py +0 -0
  990. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_paired_ramp_semantics.py +0 -0
  991. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_part_classification.py +0 -0
  992. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_pattern_contract.py +0 -0
  993. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_pattern_ownership.py +0 -0
  994. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_place_dimension.py +0 -0
  995. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_plate_ownership.py +0 -0
  996. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_pmi_common_labels.py +0 -0
  997. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_pocket_pattern.py +0 -0
  998. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_polygonal_boss_completeness_evidence.py +0 -0
  999. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_pre_render_profile_choice.py +0 -0
  1000. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_principal_profile_classifier.py +0 -0
  1001. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_prismatic_boss_diameter.py +0 -0
  1002. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_prismatic_classification.py +0 -0
  1003. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_prismatic_dimensions.py +0 -0
  1004. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_private_test_imports.py +0 -0
  1005. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_projection_symbol_default.py +0 -0
  1006. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_quiddity_lifecycle_boundary.py +0 -0
  1007. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_real_part_fixtures.py +0 -0
  1008. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_recogniser_adoption.py +0 -0
  1009. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_recogniser_policy.py +0 -0
  1010. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_recogniser_step_inventory.py +0 -0
  1011. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_recognition_boundary_failures.py +0 -0
  1012. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_recognition_evidence_schema.py +0 -0
  1013. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_recognition_policy_dispositions.py +0 -0
  1014. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_recognition_result.py +0 -0
  1015. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_registry.py +0 -0
  1016. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_repack_geometry_seam.py +0 -0
  1017. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_repair.py +0 -0
  1018. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_replay_assessment_issue_1715.py +0 -0
  1019. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_report_status_reasons_issue_1618.py +0 -0
  1020. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_report_writer.py +0 -0
  1021. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_requirement_applicability.py +0 -0
  1022. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_requirement_carriers.py +0 -0
  1023. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_requirement_dimension_sharing.py +0 -0
  1024. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_scale_policy.py +0 -0
  1025. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_scale_selection.py +0 -0
  1026. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_score.py +0 -0
  1027. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_script_detail_parity.py +0 -0
  1028. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_section_hatching.py +0 -0
  1029. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_section_recess_contract.py +0 -0
  1030. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_section_recess_pockets.py +0 -0
  1031. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_shared_box_memo.py +0 -0
  1032. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_shared_drawing_cache.py +0 -0
  1033. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_sheet_fallback_issue_1797.py +0 -0
  1034. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_sheet_gdt.py +0 -0
  1035. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_sheet_generation_snapshot.py +0 -0
  1036. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_sheet_identity_invariant.py +0 -0
  1037. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_sheet_notes.py +0 -0
  1038. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_sheet_of.py +0 -0
  1039. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_slot_pattern.py +0 -0
  1040. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_slot_pattern_recognition.py +0 -0
  1041. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_soft_deprecation.py +0 -0
  1042. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_solve_trace.py +0 -0
  1043. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_strip_zones.py +0 -0
  1044. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_suppression_ledger.py +0 -0
  1045. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_suppression_marks.py +0 -0
  1046. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_through_step_ownership.py +0 -0
  1047. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_tolerances.py +0 -0
  1048. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_turned_lengths.py +0 -0
  1049. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_turned_step_ownership.py +0 -0
  1050. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_two_pass_layout.py +0 -0
  1051. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_typ_dimensions.py +0 -0
  1052. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_typed_remediation_projection_issue_1759.py +0 -0
  1053. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_version_bump_ci.py +0 -0
  1054. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_view_blocks.py +0 -0
  1055. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_view_coordinates.py +0 -0
  1056. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_view_plan.py +0 -0
  1057. {draftwright-0.5.0rc2 → draftwright-0.5.0rc4}/tests/test_witness_label_reconciliation.py +0 -0
@@ -0,0 +1,3217 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ### Changed
6
+
7
+ - Pin Quiddity 0.3.10 and classify its seven new recognition families explicitly. Four-hole
8
+ rectangular sets now use the existing grid grammar and state both pitches instead of a
9
+ bolt-circle claim; paired-ramp and through-step schema 3 records retain their established
10
+ drawing claims.
11
+ - The agent usage guide moved from `AGENTS.md` to `docs/using-draftwright.md`; `AGENTS.md`
12
+ now holds the contributor instructions formerly in `CLAUDE.md` (#1938).
13
+
14
+ ### Removed
15
+
16
+ - Removed the eight expired `Drawing` methods (`add`, `add_view`, `clear_annotations`,
17
+ `set_view_coordinates`, `drop_view_coordinates`, `attach_part_model`,
18
+ `attach_solve_trace`, and `export_pdf`) and the legacy `Drawing.export` tuple
19
+ forms at their published 0.5.0 target. Pass `formats=` explicitly; export
20
+ returns a `{format: path}` dict. `Drawing.place_dim`, `Sheet.add_view`,
21
+ `Sheet.export`'s PDF default, and `make_drawing`'s SVG/DXF tuple remain (#2113).
22
+
23
+ ## v0.4.11–v0.5.0-rc1 — cumulative notes since v0.4.10
24
+
25
+ These entries accumulated across patch releases and were all present by v0.4.31.
26
+ They describe changes included by RC1; they do not assign each change to its first
27
+ released version.
28
+
29
+ ### Added
30
+
31
+ - A hash-pinned AP242 CTC-01 canary now proves the full Pareto-first layout loop against
32
+ a reviewed 80-measurement denominator: read typed finding remedies, apply only two public
33
+ declaration-side overrides, replay once, and establish legibility-only dominance while 12
34
+ unresolved claims remain carried and certification remains unavailable (#1753). Detailed
35
+ diagnostic prose remains optional presentation derived from structured evidence, never a
36
+ comparison identity or decision input.
37
+ - Declared slot width and length dimensions now expose bounded, feature-relative lanes through
38
+ `DimensionIntent.place(lane=...)` and declaration-scoped
39
+ `layout_override(..., parameter=..., lane=...)`. Generated scripts and report-v8 evidence
40
+ retain the layout-only policy; the shared solver admits only wholly interior or exterior,
41
+ page-safe, ink-clear candidates and reports bounded blocker evidence when a requested lane is
42
+ infeasible. Automatic drawings and scripts without lane intent remain unchanged (#1757).
43
+ - Declared sheets now support append-only, build-scoped `layout_override(..., side=...)`
44
+ edits plus structured capability and validation queries. Overrides select only a semantic
45
+ corridor, retain declaration identity through generated-script replay, and appear as
46
+ layout-only resolution evidence in declared report v8; coordinates and engineering meaning
47
+ remain unchanged, and sheets without overrides preserve existing behaviour (#1757).
48
+ - Raw automatic report assessments now include a typed remediation projection that separates
49
+ contradictions, missing carriers, layout, importer/lowering, source ambiguity, and unavailable
50
+ evidence. It retains affected evidence identities, names preservation invariants, advertises
51
+ only bounded public actions, and leaves unknown findings unclassified without guessing (#1759).
52
+ - Raw automatic reports now lead agents with a vector-first `lint.assessment`: per-axis status,
53
+ affected counts, available denominators, unavailable reasons, and stable structured reasons
54
+ for every `needs-attention` verdict. Its human summary is rendered from the same reason rows,
55
+ while legacy lint scores remain compatibility-only diagnostics and cannot select status
56
+ (#1609, #1618).
57
+ - Replay comparison now matches unresolved measurement evidence by its serialized annotation,
58
+ table cell, or ownerless parameter identity and classifies it as carried, resolved,
59
+ introduced, changed, or ambiguous. Carried uncertainty keeps certification unavailable but
60
+ no longer erases an otherwise Pareto-dominant layout improvement; new, changed, or duplicate
61
+ identities fail closed (#1753).
62
+ - Assessment comparison v2 reports independently evidenced requirement, completeness,
63
+ fidelity, legibility, and restraint axes plus their Pareto relation. Dominance,
64
+ equivalence, domination, incomparability, and unavailable evidence are derived from concrete
65
+ transitions rather than any legacy diagnostic, component, or audited-completeness score
66
+ (#1609). The v1 `decision` remains temporarily as a deprecated compatibility policy result.
67
+ - Stadium through-slots now retain Quiddity 0.3.2's proved end radius through detection,
68
+ declared `Sheet` scripts, compiled measurement identity, placement, and completeness. A
69
+ normal-to-arc `2× R…` leader distinguishes an obround from an equal-sized rectangular slot
70
+ without introducing coordinate placement (#1752).
71
+ - The Quiddity 0.3.2 adoption also retains corrected overall extents for rounded-corner
72
+ rectangular slots. Hole-pattern records whose members differ only along the drilling axis
73
+ are explicitly refused at the drafting boundary, preserving their ordinary grouped-hole
74
+ requirements instead of emitting a coincident, undrawable pitch pattern.
75
+ - Feature-leader placement now has one coordinate-free drawing policy across
76
+ `build_drawing`, `make_drawing`, `Sheet`, generated scripts, and the CLI. The default
77
+ `auto` policy keeps the shared interior/exterior solve, `interior` requires proved
78
+ interior candidates for eligible unconstrained families, and `exterior` restores the
79
+ historical exterior-only layout for existing diagrams.
80
+ - The standard title block now states drawing `UNITS` (`mm`) and the effective sheet `FORMAT`
81
+ (for example `A2`). Dimension-specified A-series pages receive the standard designation;
82
+ non-standard pages state their actual `WIDTHxHEIGHT`. Its rows share a four-line column grid,
83
+ avoiding visually offset dividers between adjacent fields.
84
+ - A bounded real-part canary now proves the complete agent improvement loop on NIST CTC-01
85
+ (#1716): inspection-to-declaration joins, traced baseline, one sanctioned `Sheet` layout edit,
86
+ replay assessment, fixed-denominator comparison, and fail-closed deletion/owner/provenance
87
+ checks. It performs exactly two CAD builds in the existing single PR canary lane.
88
+ - Replay assessments v2 now retain exact confirmed compiled measurement meanings, and
89
+ `compare_assessments()` evaluates two same-source replays as separate semantic, lint,
90
+ completeness, fidelity, layout, and availability deltas (#1712). Caller-fixed requirements
91
+ expose shared omissions; authorised design changes remain separate; no composite score is
92
+ produced.
93
+ - Generated scripts now atomically write a versioned post-build replay assessment beside their
94
+ outputs (#1715). It binds the exact STEP and edited Python bytes, producer/run options, output
95
+ hashes, declaration/representation map, layout evidence, and lint from the same finalized
96
+ drawing. Failed replays clear only stale Draftwright-owned assessments; `--no-report` opts out.
97
+ - Declared drawing report schema v8 publishes page/view geometry, named annotation ink and
98
+ public line segments, raw-lint joins, semantic remedy choices, and explicit opt-in solver
99
+ evidence (#1711). Coordinates are review evidence only; editable links remain final-IR
100
+ declarations, and an untraced or partially traced build says so instead of appearing clear.
101
+ - STEP inspection schema v4 projects a deterministic report-local source-face roster and
102
+ Quiddity 0.3.1's closed graph rooted at rejected candidates (#1726). Accepted occurrences and
103
+ candidates carry exact defining/constituent face links; rejected roots and direct relationships
104
+ reconcile with aggregate lifecycle counts. The report still states that this is diagnostic
105
+ evidence, not precision, recall, missed-feature truth, or persistent topology identity.
106
+ - Circular-seat recognition, declared features, and generated-script replay (#1613).
107
+ Diameter, open run, arc sweep, and three datum-to-axis coordinates retain each physical
108
+ seat's measurement identities, including counted coaxial callouts and shared axis marks.
109
+ - Opt-in independent sheet margins and exact title-block width on `Sheet`, `build_drawing`,
110
+ `make_drawing`, and the CLI, including generated-script replay (#1612, #1589). Sergio's
111
+ 25/10/10/10 mm margins and 175 mm block are explicit options; existing defaults remain.
112
+
113
+ ### Fixed
114
+
115
+ - Assessment comparison now rejects an apparent resolution of measurement uncertainty when the
116
+ declaration carrying that unresolved annotation was removed. Deleting crossed GD&T ink can no
117
+ longer masquerade as evidence improvement; genuine resolution with the carrier retained still
118
+ compares normally (#1753).
119
+ - Imported upper/lower limit dimensions now retain their typed absolute interval through the
120
+ compiled evidence boundary. Both bounds together satisfy the owned nominal claim, while a
121
+ changed/missing bound or two coincidental numbers around the nominal still produces
122
+ `claimed_value_absent`; count and through requirements remain separate (#1759).
123
+ - Adopt Quiddity 0.3.1 and require its public rejected-candidate reference in the
124
+ evidence contract (#1726). The provider now suppresses rectangular recess candidates
125
+ that are not material-empty and restores passage discovery used during rotational
126
+ reconciliation; Draftwright's recognition and real-part regression corpora retain
127
+ their reviewed expectations.
128
+
129
+ - Direct and generated builds use the same final IR step ladder for page layout (#1592).
130
+ Raw face levels owned by another feature no longer reserve a phantom step corridor at
131
+ the title-block clearance threshold.
132
+
133
+ - Location intents now accept `.format(decimals=...)` for all selected directions (#1610),
134
+ including plan locations, side-drilled holes and slots. Compiler-owned labels reach
135
+ placement footprints, live location edits and generated scripts. Coincident marks with
136
+ incompatible explicit display text are refused instead of overriding an intent.
137
+
138
+ - Explicit dimension precision, including `.format(decimals=0)`, now reaches geometric
139
+ label critique (#1620). Correct rounding is reported as information; labels outside
140
+ the requested rounding interval still produce `label_vs_measured`. Height and step
141
+ ladders retain the same compiler policy as ordinary dimensions.
142
+
143
+ - **`label_vs_measured` no longer reports the drawing's own rounding as a topology error
144
+ (#1600).** A sheet at one decimal place prints 4.450 as `4.5`; the check then compared
145
+ `4.5` against 4.450 and said "possible axis swap or wrong endpoint". The comparison was
146
+ *relative*, so the same 0.05 mm of rounding passed silently on a 100 mm dimension and was
147
+ an error on a 4 mm one — it fired on smallness, not on wrongness.
148
+
149
+ A label now states its own precision: `4.5` shows one place, `4.45` two, `12` none. A
150
+ difference within half of the last displayed place is the label doing its job; anything
151
+ beyond it is still reported, and an axis swap or wrong endpoint misses by far more.
152
+
153
+ This is a prerequisite for #1602's proposed CI gate: `label_vs_measured` sits in the
154
+ register of codes meaning *the sheet says something untrue*, and a code that fires on
155
+ correct rounding cannot be hard-failed on.
156
+
157
+ ### Added
158
+
159
+ - **`nominal_rounded` says where the sheet's precision loses the model value.** Silencing
160
+ the false alarm above left nothing reporting it at all, and `4.5` for 4.450 is still a
161
+ real 0.05 mm between drawing and model — on a part carrying 0.05 mm clearances, the whole
162
+ clearance. One `info` per sheet, naming the worst case:
163
+
164
+ > 7 dimension(s) print a nominal the model does not have, rounded to the sheet's
165
+ > precision; the largest is '15.7' for 15.6500 (0.0500 mm). Within a general tolerance
166
+ > this is ordinary; where a fit depends on it, raise that dimension's places with
167
+ > `.format(decimals=...)`
168
+
169
+ `.format(decimals=…)` cannot yet reach a **location** dimension (#1610), and on the
170
+ fixture this was measured against six of the seven offenders are locations — so the
171
+ message says so rather than prescribing a remedy that raises `ValueError`.
172
+
173
+ It does **not** guess, and does not change any rounding. `4.450` locating a hinge axis is
174
+ design intent; `71.595` as an overall envelope is parametric fallout no drafter would
175
+ print in full — and telling them apart needs to know what the part is *for*, which a STEP
176
+ file does not carry (the same gap as #1597). So the engine reports and the author decides:
177
+ `.format(decimals=…)` already sets places per dimension and survives the `--script` round
178
+ trip.
179
+
180
+ Measured: 7 of 25 numeric dimension labels on `whistle_frame_reference.step`; silent on
181
+ every golden fixture, all of which are built from round numbers.
182
+
183
+ ### Fixed
184
+ - Adopt published Quiddity 0.2.9 (#1619). The frame document now retains six separate
185
+ hinge bores instead of a single path spanning air gaps. Three cylindrical seat occurrences
186
+ are reported explicitly; their drawing grammar remains pending #1613.
187
+ - Refused hole patterns retain their members in the hole-completeness ledger (#1607).
188
+ The adapter records the refusal against the exact recognition run, so a six-hole group
189
+ is no longer reported as an unverifiable four-hole pattern and two loose holes. Declared
190
+ pattern mismatches still undergo the independent correspondence checks.
191
+
192
+ - **A required dimension with nowhere to go now re-plans the sheet instead of being
193
+ reported and left (#1590).** The automatic recovery ladder — larger scales on the
194
+ selected page, then dropping the optional pictorial, then a larger page — already
195
+ existed and already refused any candidate that had not fixed the problem. It ran on
196
+ two symptoms: a turned part missing an axial station, and an **authored** intent that
197
+ failed to place. A required *envelope* dimension that found no room was neither, so
198
+ these drawings never entered the ladder at all — `scale_decision["attempts"]` came
199
+ back empty and an `overall_dim_withheld` error was reported on the first sheet tried.
200
+
201
+ `overall_dim_withheld` is deliberately not a scale blocker, and that is right:
202
+ reporting it through `placement_unsatisfiable` once made `build_drawing(part,
203
+ scale=...)` raise on parts that had always built. But that argument is about refusing
204
+ the scale a **caller asked for**. Choosing one automatically is a different question,
205
+ and one predicate was answering both.
206
+
207
+ What it changes, measured:
208
+
209
+ | part | before | after |
210
+ |---|---|---|
211
+ | stepped `Box(80, 60, 30)` | A4 1:1, overall depth withheld (error) | A4 1:1, **clean**, pictorial dropped |
212
+ | 80 × 60 plate, two close bores | A4 1:1, `incomplete`: unlocated hole, dropped location, withheld depth | A2 2:1, **clean**, every dimension placed |
213
+
214
+ **ADR 2 invariant 13 is amended** to match. It read "the optional isometric yields only
215
+ to close missing axial coverage", which had been one trigger behind the code since
216
+ 2026-08-23 — the record was consolidated twelve days later and transcribed the narrower
217
+ claim. It now states the principle the triggers share: the isometric yields only to
218
+ preserve manufacturing completeness, never for space, crowding or preference. The list
219
+ itself is `builder._ISO_YIELD_TRIGGERS`, and a guard fails if it changes, so the next
220
+ widening is caught by a test rather than by a reader.
221
+
222
+ Dropping the optional pictorial is the first lever, which is the order #443/#1299
223
+ already argue for — a pictorial cannot outrank a dimension needed to manufacture the
224
+ part. Pin `page=` or `scale=` to opt out; a pinned sheet still reports honestly rather
225
+ than replanning.
226
+
227
+ - **A generated `--script` reproduces a re-planned layout.** A declared build does what
228
+ it is told and never enters the recovery ladder, so `generate_sheet_script` pins the
229
+ settled page, scale and view set into the script. It used to do that only for the two
230
+ families whose replan could be predicted from the model; nothing predicts the new
231
+ trigger, because whether a mark fits is a fact about a measured sheet. The reference
232
+ build is now unconditional — one extra `build_drawing` per generated script, measured
233
+ at 0.19 s for a plain box, 0.34 s for a pocket and 1.17 s for a part that actually
234
+ replans — and an **undrawable** source can never be the reason a script is not
235
+ written, the standard the inspection sidecar beside it already meets. A deliberate
236
+ refusal (an unknown page size, `ScaleIncompatibilityError`, `ViewPlanIncomplete`)
237
+ still reaches the caller.
238
+
239
+ A script that keeps `auto_dimensions()` is not handed a pinned view set: `Sheet`
240
+ refuses `authored_views()` beside it, so the two together wrote a script that raised
241
+ when run. The settled set is reported in a comment instead.
242
+
243
+ - **A bolt circle is no longer stated unless something other than the fit supports it
244
+ (#1596).** `EQ SP ON ø… BC` tells a machinist to work from a centre; #1595 found a real
245
+ part where that centre was in mid-air. Six ⌀2.4 holes in a 2×3 rectangular grid came out
246
+ as `4× ⌀2.4 THRU EQ SP ON ø34.4 BC` plus an unrelated `2× ⌀2.4 THRU`, the circle centred
247
+ on (0, −20.11) — nothing is concentric with it.
248
+
249
+ The fit was not a near miss. **Any three non-collinear points are concyclic, and so are the
250
+ four corners of any rectangle** — for those arrangements the residual is zero by
251
+ construction, so no tolerance on it can help. Nor does equal spacing rescue it: that
252
+ rectangle is near-square (24.30 × 24.38), so its corners sit 90.19° / 89.81° apart and
253
+ would pass an `EQ SP` check too.
254
+
255
+ What gives it away is the leftover callout beside it. Corroboration must now come from
256
+ somewhere the fit cannot reach — any one of:
257
+
258
+ 1. **five or more members** — a circle through five holes could have failed to fit;
259
+ 2. **a concentric physical feature** — a boss, spigot, central bore, or a round body whose
260
+ own axis is the circle's, something a machinist can indicate off;
261
+ 3. **it accounts for every hole of its kind** — no identical hole left off the circle.
262
+
263
+ (3) is the discriminator here: six identical holes and the circle claimed four. A drawing
264
+ does not put two of six bolts on a different plan. It is also a question about the part
265
+ rather than about its angle to the axes, unlike the row/column test it replaced.
266
+
267
+ Refused patterns keep every hole: the members fall through to ordinary grouping, so the
268
+ #1595 part now reads `6× ⌀2.4 THRU` — which also repairs the fragmentation into 4 + 2.
269
+
270
+ **What refusing costs, measured.** `hole_requirement_outcomes` builds its pattern-member
271
+ set from the recogniser's patterns rather than the ones the IR adopted, so after a refusal
272
+ it keeps those holes out of the loose groups, finds no `PatternFeature` to join, and warns
273
+ `hole_requirement_unverifiable` about holes the sheet states correctly — two on this part,
274
+ 26 issues against 23. The pre-existing oblique-pattern refusal (#971) has the same hole and
275
+ was simply never reachable; #1596 makes it so. Tracked as **#1607** with three attempted
276
+ fixes recorded, and guarded by an `xfail(strict)` here.
277
+
278
+ Refused at the recognition→IR adapter, not in the recogniser: a circle through those holes
279
+ is genuinely findable, and ADR 3 leaves the recogniser to report the geometry it finds.
280
+ Whether it may be *stated as a drafting datum* is drafting policy, which is draftwright's.
281
+
282
+ ### Changed
283
+
284
+ - **The title block carries every ISO 7200:2004 mandatory data field.** Three had
285
+ no representation at all before — segment/sheet number (5.1.6), approval person
286
+ (5.3.4) and document type (5.3.6) — and are now `sheet=`, `approved_by=` and
287
+ `document_type=` on `build_drawing`, `make_drawing`, `Sheet`, the CLI and the
288
+ `--script` emitter. Cell widths come from the standard's own recommended
289
+ character counts (via `build123d-drafting-helpers>=0.15.4`), so they follow ISO
290
+ 7200 and the font rather than a chosen proportion.
291
+
292
+ Eleven fields need four rows, so **the block is 32 mm tall rather than 16**, and
293
+ the band reserved for it goes 35 → 40 mm. `material` and `general_tolerance`
294
+ stay although neither is an ISO 7200 title-block field; they are on every real
295
+ drawing.
296
+
297
+ - **The scale is stated outside the title block**, beside the projection symbol,
298
+ per ISO 7200 §4 ("presented outside the title block only when used, e.g. scale,
299
+ projection symbol"). It is drawn unconditionally, and the new **`scale_not_stated`
300
+ error** checks the rendered sheet rather than trusting the code that draws it.
301
+ The `SCALE` cell is gone from the block.
302
+
303
+ **Two consequences worth knowing before you upgrade:**
304
+
305
+ - A dense sheet may escalate a page size. In the golden corpus one fixture of
306
+ fourteen (`flange_dense`) moves A4 → A3; the other thirteen keep their page
307
+ and scale, and a sweep of ten ordinary shapes showed no change at all.
308
+ - **Dimension placement now avoids the title block (#1593)**, which it never
309
+ did: the block is drawn after placement, so only GD&T frames ever avoided it
310
+ (#481). A two-row block kept that out of reach; a four-row one does not.
311
+ `build_drawing(Box(40, 30, 12))` used to run its `30` dimension 2.1 mm into
312
+ the block with `lint()` reporting a clean sheet; a stepped
313
+ `Box(80, 60, 30)` put the whole `60` label inside it, reported only as an
314
+ `annotation_overlap` between the labels `60` and `DRAWING`.
315
+
316
+ Most displaced dimensions move to the view's opposite strip and stay on the
317
+ sheet. Where both strips are full the dimension is **withheld as an error
318
+ naming the block**, rather than drawn through it: in the golden corpus
319
+ `pocketed` and `prismatic_ladder` lose their overall depth at the automatic
320
+ A4/1:1, and place it on A3 or at 1:2. Likewise a **pinned** A4 for a
321
+ five-step turned shaft no longer reaches 5:1, because at that scale the front
322
+ view runs down past the block and the overall height cannot be placed; the
323
+ 2:1 sheet is returned with `axial_length_missing` stated. Leaving the page
324
+ automatic still carries every station, on A3 at 5:1.
325
+
326
+ Choosing a page and scale without knowing which dimensions will still need
327
+ room is #1590.
328
+
329
+ ### Changed
330
+
331
+ - **Exports are reproducible by default.** `reproducible` now defaults to `True`
332
+ on `build_drawing`, `make_drawing`, `Drawing` and `Drawing.export`, so two
333
+ exports of one drawing are byte-identical without asking. Pass
334
+ `reproducible=False` to opt out.
335
+
336
+ It was opt-in on a cost argument, and the cost turns out to be small at the
337
+ scale most drawings are made: on the NIST CTC-01 AP242 fixture, +0.27 s on a
338
+ 13.4 s job (+3.2% of export, +2.0% overall). The +32% of export measured when
339
+ the flag was introduced was a 358-part assembly; the ordering work is one
340
+ bounding box and one edge walk per part, so it scales with part count and a
341
+ part-heavy sheet is still the case to opt out of.
342
+
343
+ Two things pushed the default over. A file that changes between runs cannot be
344
+ diffed, checksummed or cached — the reason to want it is not speed-shaped. And
345
+ neither the CLI nor `Sheet` exposes the flag at all, so every drawing made
346
+ through those surfaces was non-reproducible with no way to change it.
347
+
348
+ One behaviour follows from the default rather than from this change: a DXF
349
+ export that cannot reach the ezdxf document to pin its metadata now raises
350
+ rather than writing live data, because every export now claims
351
+ reproducibility. Opting out restores the old fallback.
352
+ ### Fixed
353
+
354
+ - **A supplied title-block `date` is stated on the sheet** (#1585). `TitleBlock`
355
+ could show only one of `revision`/`date` in its top-right cell and revision
356
+ won, while draftwright defaults `revision` to `"A"` — so every drawing that
357
+ set a date lost it, silently, in every version that has had the parameter.
358
+ Adopting `build123d-drafting-helpers>=0.15.4` gives the date a cell of its
359
+ own whenever a revision is also set, and `_make_title_block` now names that
360
+ cell, so the date reaches the PDF text layer and the cell-overflow lint
361
+ rather than only the rendered geometry.
362
+
363
+ `date`, `revision` and `company` are stripped before use, matching what the
364
+ block itself does: a whitespace-only `company` previously raised `KeyError`
365
+ from `cell_bbox`, and padded values were measured at a width the sheet never
366
+ drew. (The DRAWN BY narrowing this entry originally described, and the shared
367
+ top-right cell it named, are both gone with the ISO 7200 layout above — the
368
+ date has its own cell in its own row, so it costs DRAWN BY nothing.)
369
+
370
+
371
+ ### Added
372
+
373
+ - Report schema v1 now projects one recognition-owned physical requirement ledger shared with
374
+ completeness. Each bounded requirement identifies its accepted source occurrence(s), final IR
375
+ owner(s), semantic parameter where applicable, placement/satisfaction state, annotation names,
376
+ and representation rationale. The roster is evaluated once per report and fails closed when
377
+ exact same-run evidence or provenance is absent; it does not create a second recognition scan,
378
+ persistent topology identity, inferred manufacturing intent, or visual change (#1438, ADR 0017
379
+ Amendment 26).
380
+
381
+ - Direct CLI rendering now writes `<output>.draftwright.json` by default, after successful visual
382
+ export, and prints its path with the requested artefacts. `--no-report` is the explicit opt-out;
383
+ library `Drawing.export()` remains free of report-writing side effects (#1438, ADR 0017
384
+ Amendment 25).
385
+
386
+ - Generated Sheet Python now embeds a deterministic, versioned, JSON-compatible snapshot of
387
+ actionable accepted-occurrence gaps from the exact recognition run that produced its model.
388
+ The compact snapshot carries only unsupported, deferred, evidence-only, and unexpectedly
389
+ missing public records; STEP inputs are read once and recognition uses a private copy of the
390
+ exact bytes named by the embedded basename and SHA-256, with a best-effort final replay-target
391
+ check before writing. The hash is a later reconciliation boundary, not a lock on the mutable
392
+ source path. It is explicitly generation-time evidence, never current completeness authority
393
+ (#1438, ADR 0017 Amendment 24).
394
+
395
+ - Accepted Plate occurrences now retain exact conditional ownership. Genuine multi-axis plates
396
+ keep their direct `PlateFeature`; other slabs are absorbed only when released same-run AAG
397
+ evidence joins them to the exact retained face-level, riser-shoulder, or slot-pattern owner.
398
+ Multi-owner claims are conjunctive, while whole-envelope, polygonal-boss, disconnected,
399
+ ambiguous, or malformed cases remain visibly `unexpectedly_missing` rather than borrowing an
400
+ owner from matching values or traversal order (#1438, ADR 0017 Amendment 23).
401
+
402
+ - `Drawing.write_report(path)` now atomically writes the versioned report as deterministic,
403
+ strict UTF-8 JSON without coupling library export to sidecar generation. Report and filesystem
404
+ failures leave any existing destination untouched, with best-effort temporary cleanup that does
405
+ not mask the primary error (#1438, ADR 0017 Amendment 22).
406
+
407
+ - Raw automatic drawings now expose `Drawing.report()`, a strict version-1 JSON-compatible
408
+ projection of every accepted recognition occurrence, its explicit consumer disposition and
409
+ report-local final IR owner(s), plus the existing lint summary. The published schema never
410
+ serializes provider references, topology IDs, object addresses, or transient member positions;
411
+ it makes requirement coverage an explicit field (subsequently populated by Amendment 26) and
412
+ calls a clean result `bounded-clear`, not manufacturing-ready. Paths without exact occurrence
413
+ ownership and raw paths with an unclassified accepted occurrence fail explicitly rather than
414
+ emit an empty or inferred report. Rendering remains unchanged (#1438, ADR 0017 Amendment 21).
415
+
416
+ - Accepted `FaceLevel` and `RiserEvidence` occurrences now receive explicit `evidence_only`
417
+ outcomes. The released records remain shared substrate for the supported correlated height
418
+ ladder and shoulder projection, not independent inferred features or drawing requirements; no
419
+ IR, Sheet DSL, renderer, or recogniser API change is introduced (#1438, ADR 0017 Amendment 20).
420
+
421
+ - Accepted round-boss occurrences now retain the exact existing consumer outcome: a singleton
422
+ `BossFeature`, one diameter-consolidated representative with transient member lineage, the exact
423
+ turned-step owner, or the exact groove owner reached through that step or the profile-gate
424
+ fallback. Ambiguous or missing correspondence fails closed without another scan, persistent ID,
425
+ generated artefact, or visual change (#1438, ADR 0017 Amendment 19).
426
+
427
+ - Accepted through-step occurrences now retain either their exact `ThroughStepFeature` or every
428
+ exact final `StepLevelFeature`, `PlateFeature`, and `EnvelopeFeature` that jointly carries the
429
+ established compatibility projection. Plate claims are body-local, replacement is atomic, and a
430
+ missing or ambiguous selected owner fails closed rather than granting partial credit. One
431
+ occurrence can name multiple run-local IR owners without a second scan, persistent ID, generated
432
+ artefact, or change to rendering machinery (#1438, ADR 0017 Amendment 18).
433
+
434
+ - Accepted turned-step occurrences now retain the conversion decision that already gives an
435
+ ordinary profile band its exact `StepFeature` or absorbs a groove-floor band into the exact
436
+ same-run `GrooveFeature`. The takeover must be one-to-one in both directions, so an ambiguous
437
+ groove, an adjacent band inside its positional tolerance, or any missing final owner fails closed
438
+ as `unexpectedly_missing`, with no second scan, persistent ID, generated artefact, or visual
439
+ change (#1438, ADR 0017 Amendment 17).
440
+
441
+ - Accepted channel occurrences now receive an exact conditional consumer outcome. A genuine
442
+ multi-axis plate channel is `represented` by its `ChannelFeature`; a monolithic Z-depth rebate is
443
+ `absorbed` only when one retained body-local support carries its floor and both shoulders into the
444
+ final step ladder. Cross-axis, disconnected, or filtered channels fail closed as
445
+ `unexpectedly_missing` instead of inheriting an unrelated ladder, without a second scan,
446
+ persistent ID, generated artefact, or visual change (#1438, ADR 0017 Amendment 16).
447
+
448
+ - Accepted countersink occurrences now receive an exact run-local outcome. A seat carried by a
449
+ `HoleRecord` is `absorbed` by that hole's final feature and transient member lineage; an unattached,
450
+ unowned, or duplicate nested occurrence fails closed without dimension matching, topology IDs, a
451
+ second scan, duplicate IR requirements, or visual changes (#1438, ADR 0017 Amendment 15).
452
+
453
+ - Accepted angled-step, passage, prismatic-pocket, oriented-slot, and repeating-radial-profile
454
+ occurrences now receive exact run-local ownerless outcomes from Draftwright's existing consumer
455
+ capability declaration: `unsupported`, `deferred`, or report-facing `evidence_only`, with closed
456
+ reason codes and existing tracking issues. Equal-valued occurrences stay distinct; policy does
457
+ not invent IR owners, requirements, topology IDs, recognition scans, generated artefacts, or
458
+ visual output (#1438, ADR 0017 Amendment 14).
459
+
460
+ - Accepted physical hole, slot, and pocket occurrences now retain explicit run-local ownership
461
+ when conversion represents a singleton or absorbs members into one same-spec hole group or
462
+ derived pattern feature. Family-specific reason codes and transient member lineage follow
463
+ member-specific AP242 splits to the exact final IR objects; missing lineage fails closed rather
464
+ than retaining a stale owner. Provider-derived patterns do not become invented occurrences, and
465
+ no report ID, extra recognition scan, generated artefact, or visual output is introduced (#1438,
466
+ ADR 0017 Amendment 13).
467
+
468
+ - Raw automatic builds now capture run-local, conversion-time ownership from accepted provider
469
+ occurrences to their exact Draftwright IR features for unconditional one-to-one adapters. Equal
470
+ record values, order, coordinates and topology indices cannot create a binding; an omitted direct
471
+ binding fails closed as unexpectedly missing, while ownership rules not yet implemented remain
472
+ explicitly unclassified. The experimental read-only `Drawing.recognition_ownership()` exposes
473
+ the non-serializable ledger; it stays outside the IR/compiler and changes no generated or visual
474
+ artefact (#1438, ADR 0017 Amendment 12).
475
+
476
+ - Raw automatic recognition now retains the provider's run-scoped `RecognitionEvidence` beside
477
+ its exact `RecognitionResult`, exposed through the experimental read-only
478
+ `Drawing.recognition_evidence()`. Declared drawings acquire the pair lazily on first physical
479
+ critique; bare and framed results remain evidence-less, and Draftwright never rescans merely to
480
+ backfill evidence. This is a non-visual reporting foundation and changes no rendered artefact
481
+ (#1438, ADRs 0015, 0017, 0020).
482
+
483
+ - Automatic drawings can opt into the provider-owned local recognition frame with
484
+ `framed_recognition=True`. The returned drawing exposes caller provenance, the exact downstream
485
+ working solid, frame, and a copied framed/raw/refusal decision. PMI correlation, off-axis pattern
486
+ placement, compilation, projection and physical lint remain in one coordinate system; typed frame
487
+ refusal performs one visible top-level raw fallback. Raw remains the rollout default (#1357,
488
+ ADR 0020).
489
+
490
+ - Referential and measured Sheet dimensions accept optional `view=` / `side=` placement
491
+ intent. Supported pairs survive IR/compiler and generated-script round trips, constrain
492
+ ordinary corridor candidates without exposing page coordinates, and fail clearly when a
493
+ renderer cannot honour the requested view/strip. Compound hole callouts can therefore be
494
+ authored to opposite sides of one end view while retaining normal solve and lint behavior
495
+ (#563).
496
+ - Feature-linked Sheet notes can explicitly satisfy one or more canonical requirement ids with
497
+ `.note(..., satisfies=(...))`. Placed structured notes retain each requirement's identity in
498
+ coverage reports and generated scripts without masquerading as dimensions or parsing prose;
499
+ removing the note restores the ordinary missing/suppressed diagnostics (#1351).
500
+ - `Sheet.from_part(...).take_over(...)` now adopts detected features in place while choosing
501
+ authored or automatic dimension, principal-view, and derived-view sources explicitly. This
502
+ preserves detected handles, supports automatic principal views with authored requirements,
503
+ replaces or suppresses inferred sections without duplicates, and round-trips the adopted view
504
+ request through generated Sheet code (#1350).
505
+ - Referential Sheet dimensions can preserve an explicit number of decimal places with
506
+ `sheet.dimension(feature, "parameter.id").format(decimals=n)`. The policy changes only
507
+ printed display text: numeric reconciliation, tolerances, suppression and dimension
508
+ provenance continue to use the feature parameter, and generated Sheet scripts round-trip the
509
+ policy (#1349).
510
+ - New lint code `annotation_ink_overlap`: reports another annotation's line-work —
511
+ a dimension line, an extension line or a leader shaft — drawn through a label's
512
+ text, which `annotation_overlap` cannot see because it compares label boxes to
513
+ each other and to nothing else. Measured as the longest connected stroke path
514
+ crossing the label's exact polygon (or its axis-aligned box), in millimetres,
515
+ with a 0.5 mm floor. It scores on legibility, so
516
+ dense sheets that previously scored clean now score lower — 17 findings on
517
+ NIST CTC-02 AP203 alone, every sampled one confirmed a real defect by rendering.
518
+ Repair does not yet act on it (#1333) and the corridor solve does not yet avoid
519
+ it (#1334) (#1321, #1332).
520
+
521
+ ### Changed
522
+
523
+ - Updated the exact Quiddity runtime and declared-inspection contract to **0.2.8**, which stops
524
+ a modelled thread being read as a stack of turned shoulders and coalesces contiguous
525
+ equal-diameter bands into the one span they physically are. Measured on the repo corpus,
526
+ NIST CTC-05 is again the only fixture that moves: its ⌀304.8 region was three adjacent
527
+ rungs and is now one, in both STEP encodings (#1135, quiddity#587).
528
+
529
+ - Updated the exact Quiddity runtime and declared-inspection contract to **0.2.7**. Unlike the
530
+ 0.2.5 adoption below, this one changes drawings: the provider now requires circumferential
531
+ cylinder support to establish a turned-profile axis line, so rounded plate corners and parallel
532
+ offset cylinders no longer form a false stepped shaft. Measured on the repo corpus, NIST CTC-05
533
+ is the only fixture whose recognised turned profile moves — its two STEP encodings previously
534
+ disagreed (3 steps and 9 steps for the same solid) and now agree on 5 — and a plate part that
535
+ previously raised out of `generate_sheet_script` now emits and builds. No family, record schema
536
+ or adapter changed (#1555, quiddity#586).
537
+
538
+ - Updated the exact Quiddity runtime and declared-inspection contract to 0.2.5 for the
539
+ provider's recognition performance improvements.
540
+
541
+ - Production recognition now consumes published `quiddity==0.2.2`. Unified section-recess
542
+ occurrences and their same-run patterns feed the existing pocket, channel and blind-slot
543
+ drawing grammar. Original occurrence ownership, independent completeness and explicit
544
+ unsupported/refusal outcomes follow that single aggregate (#1471).
545
+ - Machine-readable reports and STEP inspection documents advance to schema v2 because their
546
+ closed producer object now names `quiddity`. The schema-v1 documents remain available for
547
+ existing files; readers must select the schema using `schema_version` (#1471).
548
+ - Groove membership now retains Quiddity's body-local profile identity. Generated declarations
549
+ use `profile_group` to associate coaxial grooves and steps without copying provider keys.
550
+ Omitted steps do not cause a groove to attach to another body (#1471).
551
+ - Known limitations in Quiddity 0.2.2: some mixed-profile/nested pockets, bore-intersected channels
552
+ and edge-open recesses, and pockets in cylindrical stock can lose automatic annotations.
553
+ Refusals remain visible in lint and evidence. Regression cases are retained as strict expected
554
+ failures while upstream [#536](https://github.com/pzfreo/quiddity/issues/536),
555
+ [#538](https://github.com/pzfreo/quiddity/issues/538) and
556
+ [#541](https://github.com/pzfreo/quiddity/issues/541) are open.
557
+
558
+ - Accepted schema-v3 straight or circular `Blend` chains, including concave occurrences, now cross
559
+ a dedicated end-to-end consumer path:
560
+ `BlendFeature`, explicit `Sheet.blend(...)`, executable generated declarations, one
561
+ compiler-approved `blend.radius` requirement, and a solver-owned grouped `n× R` leader. The
562
+ discriminated path kind, full canonical direction/normal, circular-path major radius,
563
+ rolling-ball radius, and material side remain in IR instead of being coerced to legacy Fillet
564
+ semantics; the provider aggregate remains the sole exact Fillet/Blend precedence authority.
565
+ Exact public-record/IR validation and completeness follow every accepted occurrence to placed,
566
+ structured-note, authored-suppressed, dropped, missing, or unverifiable outcomes. Raw and
567
+ provider-framed arbitrary rigid motions retain straight-path radius meaning; framed recognition
568
+ also retains circular paths, while raw arbitrarily rotated circular recognition remains an
569
+ upstream limitation (b123d-recognisers#491) (#1433; ADRs 0011, 0013–0017, 0020).
570
+
571
+ - The preceding recognition update pinned the immutable `b123d-recognisers` 0.4.14 release and advanced its
572
+ fail-closed capability and inspection joins. `Blend` schema v3 replaces the old flat cylindrical
573
+ fields with exact `StraightBlendPath` and `CircularBlendPath` records and admits proved concave
574
+ and toroidal occurrences; Draftwright preserves those semantics through IR, `Sheet.blend(...)`,
575
+ generated replay, reporting, drawing, and completeness. Passage nested records advance to schema
576
+ v2 without changing Draftwright's unsupported passage policy. The provider's previously
577
+ recognised shallow nonzero `PairedRampStep` keeps its existing
578
+ two-requirement IR, public `Sheet.paired_ramp_step(...)` declaration, generated-code, compiler
579
+ callout, solver placement, and completeness meaning (#1430; ADRs 0011, 0013–0017, 0020).
580
+
581
+ - Rectangular blind slots recognised by `b123d-recognisers` 0.4.10 now cross a dedicated
582
+ Draftwright semantic path: `RectangularBlindSlotFeature`, the explicit
583
+ `Sheet.rectangular_blind_slot(...)` word, generated-code and role-specific tolerance round
584
+ trips, compiler-approved width/capped-run/depth measurements, and one solver-owned
585
+ `OPEN SLOT … DEEP` leader. Authored subsets retain every approved value with explicit
586
+ `WIDE`/`LONG`/`DEEP` roles, and leaders target proved cap/side material rather than the open
587
+ mouth. Native raw and rigidly moved framed builds retain the same three
588
+ measurement outcomes. A seven-case independently authored corpus now pins all six run/open-side
589
+ orientations plus an independently sized specimen: each occurrence contributes separate width,
590
+ capped-run and depth requirements followed by exact structural correspondence and measurement
591
+ provenance to placed, structured-note, suppressed, dropped, missing or unverifiable outcomes.
592
+ Removing recognition cannot shrink that 21-requirement benchmark (#1421; ADRs 0011,
593
+ 0013–0017, 0020).
594
+
595
+ - Round-bottom blind slots recognised by `b123d-recognisers` 0.4.10 now have their own complete
596
+ Draftwright path rather than borrowing through-slot, pocket or rectangular-blind-slot grammar.
597
+ `RoundBottomBlindSlotFeature` retains the released run/opening topology and exposes the three
598
+ independent manufacturing requirements: capped run length, straight bottom-flat width and equal
599
+ side radius. `Sheet.round_bottom_blind_slot(...)`, generated code, role-specific tolerances and
600
+ one solver-owned `ROUND-BOTTOM OPEN SLOT …` leader all preserve those exact identities; derived
601
+ total width and depth are not double-counted. A seven-case independently authored corpus covers
602
+ all six signed principal orientations plus a separately sized specimen (21 physical requirements),
603
+ proves raw/framed drawing parity, and follows exact source-to-IR correspondence to placed,
604
+ structured-note, suppressed, dropped, missing or unverifiable outcomes without labels, views or
605
+ page coordinates. The released 0.4.10 API is sufficient; no recogniser change is required (#1421;
606
+ ADRs 0011, 0013–0017, 0020).
607
+
608
+ - Turned-step completeness is now backed by an independently authored, hash-pinned 11-case STEP
609
+ corpus with 26 physical outside-diameter bands, 52 parameter checks and 104 downstream checks.
610
+ Axis line and station preserve body-local occurrence identity across automatic IR, the existing
611
+ public `Sheet.step(...)` DSL word, executed-and-built generated code, and exact compiler-confirmed
612
+ length and OD drawing evidence. Grouped equal-length chains retain every adjacent member identity
613
+ and their exact compiler-approved multiplier and value; native and global OD labels are checked
614
+ in full. Generated declarations preserve the half-thousandth midpoint of odd-thousandth spans,
615
+ complete body-local profile ownership is compared at its full published precision, and malformed
616
+ or surplus records fail closed—including coercible or non-finite groove evidence that controls
617
+ band ownership. The root turned-step and groove inventories must remain immutable tuples; a
618
+ mutable or one-shot substitute is snapshotted once only to preserve its observable requirement
619
+ denominator, then remains unverifiable; a non-iterable substitute retains one explicit aggregate
620
+ contract outcome rather than masquerading as an empty family. A profile OD may
621
+ satisfy one uniquely identifiable equivalent largest-band diameter
622
+ without multiplying across disjoint bodies; and uniquely groove-owned narrow
623
+ bands are excluded once by a shared fail-closed Groove schema and ownership rule used by lowering,
624
+ lint and the evidence ledger. That join accounts for the provider's 0.001 mm Groove coordinate
625
+ quantum against its eight-decimal profile axis without widening either published identity
626
+ (#1374; ADRs 0010, 0011, 0013, 0015, 0017, 0020).
627
+
628
+ - Framed recognition now carries all three 0.4.6 step families through their complete existing
629
+ consumer paths. `CircularBlindStep`, `PairedRampStep` and `ThroughStep` retain the same
630
+ manufacturing requirements from raw recognition through their public `Sheet` words, generated
631
+ code, compiler-owned drawing ink and lint; the provider-owned local records and Draftwright IR
632
+ remain invariant under a combined non-principal rotation and translation. Circular-step
633
+ validation now derives its bound from each independently published endpoint, centre, radius and
634
+ depth value's six-significant-figure decimal cell, while outside-cell geometric inconsistencies
635
+ still fail closed (#1382; ADRs 0011, 0013, 0015, 0017, 0018, 0020).
636
+
637
+ - Countersink completeness is now backed by an independently authored, hash-pinned seven-case STEP
638
+ corpus covering functional seats, solid external-cone and deburr negatives, grouped equal seats,
639
+ and reverse-serialized topology. Detection identity and all four provider measurements are scored
640
+ separately from the two actual drawing requirements. Exact aggregate object ownership follows
641
+ each seat once, validates the provider-selected hole semantically, and fails closed on swapped,
642
+ duplicated, or canonically colliding owners. It then crosses automatic IR, public `Sheet.hole`,
643
+ generated code, and role-specific confirmed placed diameter/angle ink without recounting the parent bore. The known second seat of a two-sided bore
644
+ remains explicitly unverifiable rather than hidden or heuristically attached (#1370; ADRs 0010,
645
+ 0013, 0015, 0017).
646
+
647
+ - Double-D completeness is now backed by an independently authored, hash-pinned 11-case STEP
648
+ corpus with ten physical profile facts, 50 provider-parameter checks and 40 downstream checks.
649
+ Full-frame identity preserves disconnected coaxial bodies; exact multiplicity crosses automatic
650
+ IR, public `Sheet.double_d_bore`, generated code and role-specific compiler-confirmed
651
+ `⌀major THRU DOUBLE-D across A/F` ink. Round, blind, opposed-blind, malformed, duplicated and
652
+ foreign-owner mutations fail closed, while the provider's exclusive aggregate ownership keeps
653
+ each profile from being recounted as an ordinary hole (#1370; ADRs 0010, 0011, 0013, 0015,
654
+ 0017).
655
+ - Updated the immutable `b123d-recognisers` pin and fail-closed inspection/capability joins to
656
+ 0.4.10. All 28 previously consumed family schemas remain unchanged. The two new blind-slot
657
+ families were adopted fail-closed under #1421 and remain visible as unscored recognised
658
+ inventories until each gains independent completeness evidence; Draftwright does not coerce
659
+ their depth, terminal/open-end, or round-floor evidence into its ordinary through-slot grammar.
660
+ The provider's additive evidence API is not consumed by
661
+ this bounded adoption slice. Recognition output does change under the unchanged schemas: the
662
+ release closes slot-depth, subdivided paired-ramp/AngledStep, and noisy stubby-pocket gaps; fixes
663
+ Double-D/Hole ownership, external-cone countersink false positives, Plate tie covariance, and
664
+ turned-step translation covariance; and can move an edge-open rectangular recess from `Pocket`
665
+ to the dedicated blind-slot inventory. Draftwright regression tests pin the consumer-visible
666
+ ownership changes while the provider release suite owns its private predicates
667
+ ([provider release](https://github.com/pzfreo/b123d-recognisers/releases/tag/v0.4.10); ADRs 0013,
668
+ 0015, 0017, 0020).
669
+
670
+ - Principal-profile lint now correlates its independent extremal-face evidence with the cached
671
+ public `DoubleDBore` aggregate instead of importing provider-private profile helpers. Exact
672
+ axis/span, end-plane, centre, radius, A/F, chord, arc, and flat-direction checks fail closed;
673
+ occurrence mouths are consumed with matching multiplicity, and malformed records cannot earn
674
+ coverage. The exported low-level check now requires the aggregate projection explicitly so an
675
+ omitted inventory fails loudly; lint still owns its physical denominator and never reruns
676
+ recognition (#1411, ADRs 0013, 0015, 0017).
677
+
678
+ - Whole polygonal-stock completeness is now backed by an independently authored, hash-pinned
679
+ 13-case STEP corpus covering all principal axes, in-plane rotation, translation, topology
680
+ order and circular/rectangular/irregular/octagonal/attached/recessed/compound negatives. The
681
+ provider's exact six-side stock boundary, A/F, axial length and coupled physical flat supports
682
+ are scored separately from four downstream boundaries. A fail-closed two-requirement ledger
683
+ follows A/F and stock length through automatic IR, public `Sheet.polygonal_stock`, generated
684
+ code and solver-placed semantic ink; malformed or duplicate correspondence cannot borrow
685
+ credit from another support ring (#1371).
686
+
687
+ - Multi-plate slab completeness is now backed by an independently authored, hash-pinned 11-case
688
+ STEP corpus covering all principal orientations, additive/cut channels, equal compound
689
+ occurrences, topology order and envelope/detached/thick/rotational negatives. A fail-closed
690
+ one-requirement ledger follows each body-local `thickness.length` through automatic IR, public
691
+ `Sheet.plate`, generated code and solver-placed Dimension provenance; the compiler-derived
692
+ opposite U-channel wall is represented explicitly without inventing duplicate ink. The corpus
693
+ owns 20 physical occurrences and cannot shrink when provider output is weakened or removed.
694
+ Exact step-level, slot-pattern, polygonal-boss and envelope ownership prevents derived material
695
+ spans from becoming duplicate Plate requirements while failing closed when that ownership cannot
696
+ be established (#1373).
697
+
698
+ - Polygonal-boss completeness is now backed by an independently authored, hash-pinned STEP
699
+ corpus covering all three principal axes, in-plane rotation, equal compound occurrences,
700
+ topology order, and recess/detached/stock/other-protrusion negatives. Detection, the provider's
701
+ six-side schema invariant, across-flats, height, physical flat supports, and four downstream
702
+ boundaries are observed; a
703
+ two-requirement fail-closed ledger follows across-flats and boss height through the existing
704
+ IR/compiler/shared-solve path. The corpus owns the independent evaluation denominator; runtime
705
+ drawing lint remains explicitly denominated by the recognition inventory (#1372).
706
+
707
+ - Updated the immutable `b123d-recognisers` pin and fail-closed manifest joins to 0.4.9. The
708
+ provider's body-local FaceLevel, RiserEvidence, and TurnedProfile schemas now cross an owned
709
+ prepared-frame boundary that normalizes, reuses one cylinder substrate, classifies locally,
710
+ and preserves the exact working solid/result pair. Every frame gauge and typed refusal has an
711
+ explicit conservative policy; refusal never hides a raw retry. Multiple physical turned
712
+ profiles fail explicitly where a singular adapter is required, while the unchanged raw
713
+ production route preserves its historical no-global-profile compound behavior with a visible
714
+ #1357 deferral. Production drawing remains on the explicit raw boundary until rigid-motion,
715
+ PMI, platform, and canary activation evidence is complete (ADR 0020, #1357).
716
+
717
+ - Updated the immutable `b123d-recognisers` pin and lock to 0.4.8. Draftwright now names
718
+ its existing caller/world-coordinate aggregate boundary explicitly with
719
+ `build_raw_recognition_result`; framed recognition remains disabled until #1357's
720
+ frame-to-IR adapter is reviewed end to end. Installed-wheel tests exercise the public
721
+ framed fixes for rectangular pads, polygonal bosses, and nested multi-body plates without
722
+ making that API the production path (#1392).
723
+
724
+ - Rectangular pads now consume `RaisedPad` schema v2 without assuming +Z. The IR, public
725
+ `Sheet.pad`, generated code, compiler, semantic-view planner, shared placement solve, and
726
+ lint correspondence preserve the exact footprint, terminal-to-attachment height, two
727
+ in-plane locations, axis, direction, and world bounds for all six signed principal
728
+ orientations. Every height uses one solver-placed `… HIGH` leader with stable measurement
729
+ identity; side-normal pad footprints no longer leak into the unrelated Z-level grammar
730
+ (#1392).
731
+
732
+ - Rectangular-pad completeness is now independently evidenced by a hash-pinned 12-case STEP
733
+ corpus covering all six signed principal orientations, equal compound occurrences, topology
734
+ order, and ledge/staircase/detached-body negatives. Detection, width/length/height fidelity,
735
+ and four downstream boundaries are scored separately; a five-requirement fail-closed ledger
736
+ follows both footprint sizes, height and directional location evidence through the shared
737
+ compiler and placement solve (#1372).
738
+
739
+ - Circular blind steps recognised by `b123d-recognisers` now have a complete consumer path.
740
+ The oriented terminal-to-open centreline and transverse quarter-arc lower without rescanning
741
+ to an explicit-only IR/Sheet feature; generated code preserves the full correspondence.
742
+ Radius and stopped depth remain independently addressable and tolerance-aware while sharing
743
+ one solver-placed end-view `R… × … DEEP` leader, and a fail-closed two-requirement ledger
744
+ follows each outcome without parsing labels or guessing by order (#1382).
745
+
746
+ - Paired-ramp steps recognised by `b123d-recognisers` now have one complete consumer path:
747
+ exact IR conversion, explicit `Sheet.paired_ramp_step(...)` declaration, generated-code
748
+ round trip, end-view solver-placed `2× angle × run RUN` leader, independent angle/run
749
+ tolerances and authored suppression, and a two-requirement fail-closed completeness ledger
750
+ (#1382).
751
+
752
+ - Rectangular through steps recognised by `b123d-recognisers` now lower without rescanning to
753
+ an explicit-only IR/Sheet feature. Automatic and explicit lowering support every principal
754
+ axis; an X/Y occurrence stays with the established face-level and shoulder/plate grammar only
755
+ when that grammar proves both exact leg intervals, avoiding both silent loss and duplicate
756
+ dimensions. The two
757
+ orthogonal open-section legs are independently addressable, tolerance-aware, solver-placed in
758
+ the end view, preserved by generated code and scored by a fail-closed two-requirement
759
+ completeness ledger. The through extent stays owned by the envelope (#1382).
760
+
761
+ - Hole completeness is now supported by a versioned, independently authored STEP corpus rather
762
+ than a Draftwright-derived census. Detection, parameter fidelity and downstream usefulness stay
763
+ separate, while the IR adapter, public Sheet declaration, executed generated code and placed
764
+ drawing are each observed through their real seam. Mutations prove that a missing provider hole
765
+ or a broken downstream boundary lowers the relevant evidence even when internal state remains
766
+ self-consistent (#1369).
767
+ - Hole-pattern completeness is now supported by a separate independently authored STEP
768
+ arrangement corpus covering grids, bolt circles, linear arrays, false-positive controls,
769
+ compound cases and topology-order variants. It scores one grouping fact per pattern while the
770
+ hole corpus remains the sole owner of member diameter/depth/location requirements, and follows
771
+ each group through the IR, public Sheet declaration, generated code and placed pattern
772
+ dimensions (#1370).
773
+ - Flat completeness is now supported by an independently authored STEP corpus covering lone,
774
+ opposed, slanted, parallel and coaxial stock plus negative and topology-order controls. It
775
+ counts one physical across-flats requirement per stock line and span, then observes automatic
776
+ IR, public `Sheet.flat`, executed generated code and placed measurement provenance independently
777
+ (#1371).
778
+ - Lone-pocket completeness is now supported by an independently authored STEP corpus covering
779
+ off-centre, edge-anchored, equal, opposed-opening, side-opening, compound, ownership-negative
780
+ and topology-order cases. Provider `open_sign` now survives the IR, declaration and generated
781
+ code boundaries; pocket sizes and both physical location ordinates are followed through exact
782
+ semantic placement evidence without counting pocket-pattern members twice (#1372).
783
+ - Pocket-pattern completeness is now supported by a separate independently authored STEP corpus
784
+ covering axis-aligned and 30° linear/grid arrangements, threshold and unequal-spacing
785
+ negatives, a compound underside pattern and reverse-serialized topology variants. One grouped
786
+ count/size/lattice/location requirement reaches automatic IR, public `Sheet.pocket_pattern`,
787
+ generated code and exact placed ink—including approved pitch tolerances—without recounting its
788
+ member pockets. Diagonal pocket-pattern dimensions use exact label-ink clearance instead of a
789
+ false-blocking hull; generated code preserves the member opening side and grouped callouts
790
+ retain structured count provenance (#1372).
791
+ - Groove completeness is now supported by an independently authored STEP corpus covering a
792
+ monotonic-step negative, all principal axes, a narrow circlip ownership contest, equal grooves
793
+ on separate bodies and reversed Boolean order. Each physical groove's width and floor diameter
794
+ pass independently through automatic IR, public `Sheet.groove`, executed generated code and
795
+ one exact compiler-backed `WIDE × ø` callout; mutations prove that recognition, identity,
796
+ parameters, provenance and printed syntax cannot disappear without lowering evidence (#1372).
797
+ - Chamfer completeness is now supported by an independently authored STEP corpus covering all
798
+ principal axes, equal and asymmetric planar bevels, conical turned treatments, equal features
799
+ on separate bodies, topology-order variants, a plain negative and the AngledStep ownership
800
+ boundary. Each physical bevel is followed through exact IR, public `Sheet.chamfer`, executed
801
+ generated code, compiler measurement provenance, callout syntax and its physical leader target;
802
+ equal requirements may share ink without collapsing occurrence identity (#1374).
803
+ - Fillet completeness is now supported by an independently authored STEP corpus covering all
804
+ principal axes, repeated equal rounds, turned toroidal treatments, separate bodies,
805
+ topology-order variants, a plain negative and the CircularBlindStep ownership boundary. Each
806
+ physical round is followed through exact IR, public `Sheet.fillet`, executed generated code,
807
+ compiler measurement provenance, `R`/`n× R` syntax and its physical leader target; grouped ink
808
+ retains every rounded edge's measurement identity (#1374).
809
+
810
+ - Recognised angled blind steps now fail visibly instead of remaining an undecided inventory.
811
+ Each aggregate-reconciled `AngledStep` produces an actionable warning and one `unsupported`
812
+ completeness outcome. A frozen STEP corpus fixture pins the AngledStep/Chamfer ownership
813
+ boundary, while Draftwright deliberately invents no angle/run, IR, Sheet, generated-code,
814
+ section/detail, or annotation semantics (#1247).
815
+
816
+ - Aggregate-reconciled `PrismaticPocket` occurrences now fail visibly instead of
817
+ remaining an unscored inventory. Each produces an actionable warning and one `unsupported`
818
+ completeness outcome, without repeating the provider's cross-family reconciliation or
819
+ inventing width/length, across-flats, IR, Sheet, generated-code, or annotation semantics (#1246).
820
+
821
+ - Recognised prismatic passages now fail visibly instead of disappearing into an unscored
822
+ inventory. Each authoritative `SectionPassage` produces an actionable warning and an
823
+ `unsupported` completeness outcome; the legacy `Passage` compatibility projection is never
824
+ double-counted. Draftwright retains the family-level unsupported disposition rather than
825
+ claiming a regular-polygon callout represents the provider's complete line/arc schema (#1245).
826
+
827
+ - Declared Double-D bore, countersink, chamfer, fillet, and groove geometry reads now use the
828
+ stable `b123d_recognisers.inspection` namespace from the exact 0.4.6 wheel. A separate
829
+ Draftwright-owned format-1 contract fails closed on changes to the consumed signatures, result
830
+ schemas, units, bevel rejection reasons, or cylindrical surface layout (#1362).
831
+
832
+ - Updated the exact `b123d-recognisers` production lock to 0.4.6. At adoption time, circular
833
+ blind, paired ramp, and through-step inventories were carried from the one aggregate and
834
+ surfaced as unscored completeness evidence pending the #1382 semantic slices now recorded
835
+ above. Rectangular-grid pitch placement also uses a
836
+ deterministic outer witness line across plan, front, and side views despite the release's
837
+ coordinate-rounding differences.
838
+
839
+ - `annotation_overlap` now reports the whole defect when the same pair also draws
840
+ line-work through one of the two labels: the message names the crosser, the label
841
+ crossed and the millimetres, and replaces the "use `label_offset_x`" remedy, which
842
+ is the advice that case contradicts. `LintIssue.suggestion` is `None` for those
843
+ issues rather than a snippet that re-places the text (#1321, #1332).
844
+
845
+ - Recogniser integration now uses only the exact `b123d-recognisers` dependency in
846
+ `pyproject.toml` and `uv.lock`. The separate release-evidence record, dependency-bump workflow,
847
+ candidate-checkout harness, and release-time capability-version rewrites have been removed;
848
+ ordinary compatibility tests validate the installed pinned package.
849
+ - Automatic composition now measures the next larger preferred scale when a conservative
850
+ crowded-detail reservation leaves a sparse sheet. A candidate wins only on the same paper
851
+ size, without the recovery detail, structural errors, or required-placement loss. GRM-04 now
852
+ uses A4 at 5:1 instead of 2:1, re-homes its shared side-hole Y location into the plan-view
853
+ corridor, and retains both the Ø2.4 diameter and location requirements (#1155).
854
+ - Closed the remaining dense-sheet leader cases as deliberate Policy-B behavior: exhaustive
855
+ route sweeps prove there is no route clear of both material and committed sheet ink, so the
856
+ required callout remains with an explicit crossing diagnostic instead of being dropped. A
857
+ fast synthetic release gate complements the retained exhaustive corpus characterization
858
+ (#1187).
859
+
860
+ ### Fixed
861
+
862
+ - Overall height and envelope width remain dimensioned when approved turned-step lengths
863
+ cover only part of the respective extent. Redundancy suppression now requires adjoining
864
+ measurements on one profile to reach both overall endpoints; STC618 retains its missing
865
+ 62 mm height (#1509).
866
+ - Non-1:1 orthographic and isometric projection now scales extracted solids about the world
867
+ origin, matching the view-coordinate mapper and solver zones. Solids carrying a non-zero
868
+ build123d `Location` no longer shift their silhouette away from dimensions and callouts.
869
+ - Notes and generic table cells now render U+2300 `⌀` with the established supported drafting
870
+ glyph instead of IBM Plex Mono's missing-glyph square. The substitution happens at the one
871
+ shared measurement/render seam, so source rows remain unchanged and table sizing stays exact
872
+ (#1275).
873
+ - Automatic turned-part composition now gives true shoulder coverage priority over optional
874
+ pictorial content. GRM-03 replans to 5:1 and renders its 0.5, 2, 3, and 18 mm axial lengths;
875
+ `axial_length_missing` disappears because the dimensions are present, not because the
876
+ diagnostic was suppressed (#443).
877
+
878
+ ## v0.4.10 — 2026-08-22
879
+
880
+ **A view-set-aware planning and turned-edge-treatment patch.** Authored view constraints now
881
+ compile into an explicit resolved plan, automatic drawings fail closed when required content is
882
+ lost, AP242 hole tolerances reach their semantic feature owners, and the exact
883
+ `b123d-recognisers` 0.3.0 release supplies turned chamfer and fillet evidence through truthful
884
+ profile-view callouts.
885
+
886
+ ### Added
887
+
888
+ - `Sheet` can now author a complete or augmenting semantic view set with `authored_views()`,
889
+ `auto_views()`, `view()`, `section_view()`, `detail_view()`, and their `add_*` forms. Fluent
890
+ view handles declare relationships, alignment, scale, and whole-view pins without exposing
891
+ annotation coordinates; unsupported or infeasible constraints fail at their declaration
892
+ source instead of being silently relaxed. `sheet.view_constraints` exposes the immutable
893
+ request and `drawing.view_plan` the distinct resolved result (#1260).
894
+ - Drawings now retain their resolved view topology, per-view requirement coverage, and
895
+ arrangement decision as inspectable values. The engine can build and semantically compare
896
+ alternative arrangements and requested view sets while keeping projection and page placement
897
+ derived from the same plan (#1130).
898
+
899
+ ### Changed
900
+
901
+ - Dimension planning is now aware of the selected principal view set. Requirements supported by
902
+ another planned view are re-homed before projection; an unsupported set raises one aggregated
903
+ `ViewPlanIncomplete` naming every unmet requirement rather than failing late or producing a
904
+ plausible-looking drawing with missing dimensions (#1259).
905
+ - Three sparse corpus drawings now use a `stacked-iso` arrangement and fit A4 instead of A3 at
906
+ the same scale, with identical annotations, labels, and build issues. An alternative may only
907
+ compact the scale already selected by the default arrangement and must survive a real compile
908
+ without semantic loss; otherwise the default is retained (#1130).
909
+ - Updated the exact `b123d-recognisers` production lock from 0.2.9 to 0.3.0.
910
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest `6645def94cbabaca7b9676fdb029b02e467266d1f020ea536e903b7d86b52685` are
911
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
912
+ normal consumer gates run on the generated PR.
913
+ - Accepted the additive Chamfer and Fillet schema-2 recogniser contract alongside schema 1 for
914
+ the `b123d-recognisers` 0.3.0 cutover; all other record schemas remain fail-closed at version 1
915
+ (#1276).
916
+
917
+ - Updated the exact `b123d-recognisers` production lock from 0.2.6 to 0.2.9.
918
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest `805876905d548fd0ef1301e2cf24427a86d34bc2e592b8d2d0cb43db94e1d3bd` are
919
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
920
+ normal consumer gates run on the generated PR. The aggregate now inventories chamfers and
921
+ fillets on turned parts, and the fixed coaxial-post slot regression retires its downstream
922
+ strict xfail.
923
+
924
+ ### Fixed
925
+
926
+ - Automatic builds now apply the same required-outcome gate as explicit-scale builds. A settled
927
+ drawing that lost required placement outcomes records `plan_incomplete`, reports an
928
+ `incomplete` scale decision, preserves measurement/hole/source provenance, and fails lint
929
+ rather than presenting a diagnostic drawing as complete (#1250).
930
+
931
+ - Supported AP242 diameter tolerances now correlate with their canonical hole or pattern owner at
932
+ the completed IR boundary and round-trip through emitted `Sheet` declarations with source
933
+ provenance. Unmatched, ambiguous, conflicting, partial-pattern, and unsupported requirements
934
+ fail closed to provenance-preserving measured dimensions rather than being duplicated or lost
935
+ (#1116).
936
+
937
+ - A declared step shoulder coincident with its datum is now pruned before rendering instead of
938
+ sending identical endpoints to the border-dimension helper and raising `ValueError`. Valid
939
+ siblings remain, while the omission is recorded in `Drawing.suppressions()` and reported as
940
+ `step_position_coincident_with_datum` (#924).
941
+
942
+ - Turned chamfer and fillet leaders now read in a shaft profile view and land on their physical
943
+ edge-treatment sites instead of collapsing axial stations in the end view. Cylindrical
944
+ feature notes, knurl callouts, and surface finishes likewise derive a model-space surface
945
+ target rather than pointing at the shaft axis; all continue through the shared placement
946
+ solves, with X/Y/Z parity and declared Sheet round-trip (#1276).
947
+
948
+ - Toroidal fillets returned for turned parts by the shared recognition inventory now lower into
949
+ the existing one-feature-per-round IR and render through the common leader solve. Equal radii
950
+ share one `n× R` callout carrying every physical fillet's measurement provenance, and emitted
951
+ Sheet programs preserve the full inventory without rescanning geometry (#1281).
952
+
953
+ - Turned chamfers returned by the shared recognition inventory now lower into the same
954
+ one-feature-per-edge-treatment IR as prismatic chamfers. Equal specifications render as one
955
+ `n×` callout carrying every member's measurement provenance; different chamfer forms do not
956
+ collapse merely because their first legs match, and distinct tolerances split into truthful
957
+ requirements instead of being lost or applied to untoleranced siblings (#1254).
958
+
959
+ - Recogniser dependency automation now starts a fresh `Unreleased` section when the previous
960
+ Draftwright release has finalized the changelog, so a published recogniser can enter through
961
+ the documented immutable update workflow instead of failing before it opens a PR (#1279).
962
+
963
+ ## v0.4.9 — 2026-08-21
964
+
965
+ ### Added
966
+
967
+ - Lint now verifies that an annotation claiming to carry a measurement actually renders it
968
+ (#1217). Coverage checks read provenance riders the annotations carry about themselves and
969
+ believed them; nothing checked the claim against what the annotation draws. Four new codes —
970
+ `claimed_value_absent`, `claimed_measurement_not_compiled`, and two that report an
971
+ unverifiable claim rather than passing it — and `Drawing.add_table` now keeps the rows it
972
+ draws so a table's claims are readable back. Measured over all 21 STEP fixtures: 1,081
973
+ claims, 1,078 confirmed, and three findings — all instances of one genuine provenance
974
+ defect, see #1219.
975
+
976
+ - `lint_summary()["quality"]` gains a fourth component, `fidelity`: whether what the drawing
977
+ says is TRUE. The existing three ask whether required content landed (completeness),
978
+ whether there is too much of it (restraint), and whether a reader can make it out
979
+ (legibility) — none asked whether it is correct, so a drawing labelled 99 over a 16 mm
980
+ path reported a perfect score on every component available to it. Also covers a callout
981
+ drawn for a feature the part does not have, and a gear data table stating a tooth count
982
+ the geometry contradicts — the last of which reports `passed: True` and zero errors, so
983
+ the only existing channel that names it is `by_code`. Completeness, restraint and fidelity
984
+ report `available: False` with a `reason` rather than a flattering number when they have no
985
+ evidence, and every lint code the engine emits is classified onto exactly one component,
986
+ an explicit unscored register, or a register of codes whose component depends on the
987
+ issue's `outcome_stage`. `quality["unscored"]` reports the findings that reached no
988
+ component at all and flags any whose classification nobody made — `section_dropped` was
989
+ found scoring on nothing while a comment beside its emission said the opposite, and eleven
990
+ more `*_dropped` codes reach the engine as `drop_code` data with the same shape (#1176).
991
+
992
+ ### Changed
993
+
994
+ - Updated the exact `b123d-recognisers` production lock from 0.2.4 to 0.2.6.
995
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest `aa52bc04216cd1eccc05796d2262547c7f476b80a3ec77334a77b9d8e3241a58` are
996
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
997
+ normal consumer gates run on the generated PR.
998
+ - `HoleRequirementOutcome` gains `members` and `features`, `ClaimOutcome` gains `measurement`,
999
+ and `linting.hole_coverage.canonical_hole_sites` is public (#1217). A consumer attributing a
1000
+ requirement outcome to a specific recognised hole needs the evidence the outcome accounts
1001
+ for, and needs to key into the canonical space the ledger publishes it in — a through hole's
1002
+ own-axis coordinate is zeroed there.
1003
+ - The STEP-analysis benchmark's `drawing_consumer` boundary now consumes
1004
+ `hole_requirement_outcomes` instead of a second correspondence implementation of its own
1005
+ (#1206), and follows the ledger's pointer through `linting.evidence` rather than trusting it.
1006
+ Holes the ledger reports `unverifiable` now score `unknown` where the duplicate credited them
1007
+ by guessing: 26 holes, five on each CTC-03 fixture and eight on each CTC-04 fixture. The
1008
+ five-case evaluation corpus is unchanged, and one lint message on CTC-03 now reports its
1009
+ coordinates in the ledger's own canonical space.
1010
+
1011
+ - `Drawing.suppressions()` rows gain a `conveyed_by` key: the dimension that states a
1012
+ withheld measurement instead, or `None` when nothing takes the fact over (#1154). It is not
1013
+ a synonym for the existing `authored` flag — that says whose decision it was, this says
1014
+ where the measurement went.
1015
+
1016
+ - Updated the exact `b123d-recognisers` production lock from 0.2.2 to 0.2.4.
1017
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest `d0023d30e583c03abc55f09dfeaaa56fddf56334afa0417818480b2fa2ce3f0f` are
1018
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
1019
+ normal consumer gates run on the generated PR.
1020
+ - Updated the exact `b123d-recognisers` production lock from 0.2.1 to 0.2.2.
1021
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest `7985befc8572bfe6d1c0805dfdf690d74a5a4a5f14d5988cc29c366315ff04ca` are
1022
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
1023
+ normal consumer gates run on the generated PR.
1024
+
1025
+ ### Fixed
1026
+
1027
+ - An authored tolerance the compiler approved now reaches the sheet wherever the measurement
1028
+ is drawn (#1215, #1216). `build123d_drafting` resolves a dimension's text as
1029
+ `label if label is not None else _number_with_units(measured, tolerance)`, and every
1030
+ dimension this engine emits passes a label — so a forwarded `tolerance=` rendered nothing
1031
+ while type-checking and reading back correctly from `Dimension.label`. Ten render paths
1032
+ discarded the band that way: envelope extents, the height ladder, the turned-step chain, two
1033
+ public `Drawing` verbs, the deferred corridor route, the detail-view redraw, the short-rise
1034
+ escape, step shoulders, pattern pitch, compound-callout recess terms, grid pitch, and both
1035
+ hole tables. A general guard now decorates every parameter of every feature across a corpus,
1036
+ through both `decorations=` key shapes, and joins what the compiler approved against what the
1037
+ claiming annotation renders.
1038
+
1039
+ - Collapsed `N×` marks no longer state a tolerance their members do not share (#1216). A
1040
+ pattern's pitch claimed the authored band of every gap although the recogniser admits 2%
1041
+ jitter — holes at −30, −10, 10.3, 30 printed `3× 20 ±0.1` over a 20.3 mm gap — and fillet and
1042
+ flat collapses took the "first-authored" band, so one of four fillets at ±0.1 printed
1043
+ `4× R5 ±0.1`. Equality is now judged at the drawn precision, and a withheld band is reported
1044
+ (`pattern_pitch_tolerance_withheld`, `collapsed_tolerance_withheld`) rather than dropped
1045
+ silently.
1046
+
1047
+ - An approved dimension that cannot be placed is now reported instead of vanishing (#1216).
1048
+ The overall extents' corridor candidate carried a no-op drop handler — the only drop in the
1049
+ engine that recorded nothing — and the step ladder's legibility floor skipped rungs without
1050
+ incrementing any counter. Both now record against the measurement they concern, and a
1051
+ withholding is withdrawn if a later pass draws the measurement after all.
1052
+
1053
+ - A feature leader filling a view's below corridor no longer costs the part its overall extent
1054
+ (#1236). The mandatory width or depth now retries on the view's above strip after every
1055
+ corridor has drained, the fallthrough starved slot and plate dimensions already use. Live on
1056
+ three NIST CTC fixtures, which drew no overall width.
1057
+
1058
+ - The iso view and the above strips are no longer invisible to each other (#1240). Strip
1059
+ placement never saw the iso, and the iso fit re-scaled after annotation without consulting
1060
+ what had been placed — so growth could invade dimension ink, which it did on CTC-01. The fit
1061
+ is now capped by measured re-projection rather than by a linear model of it: the projected
1062
+ bbox translates as it scales, by up to 7.35 mm on a part carrying a Location, which no
1063
+ prediction about the page centre can express.
1064
+
1065
+
1066
+ - A part whose feature-local extent runs between the same two faces as an overall extent
1067
+ is no longer dimensioned twice (#1154). GRM-04's drive plate printed `4.5` twice — once
1068
+ as the hub's height and once as the overall thickness — because two detected records
1069
+ claim one physical fact. The overall extent keeps it; the feature-local one is withheld
1070
+ and records which dimension the reader finds it on. Reconciliation is by exact
1071
+ support-plane coincidence, never by value: a square part's two equal extents run between
1072
+ different faces and remain two facts (#997). A measurement is handed over only when the
1073
+ extent receiving it is actually drawn — by the planner's rules, by the compiler's
1074
+ overall-height rules, and by an authored set — and never when the yielding dimension
1075
+ carries a tolerance, since a toleranced dimension and an untoleranced one are not the same
1076
+ requirement. (A tolerance on the *receiving* extent is the same requirement on the same two
1077
+ faces, and does not prevent the consolidation.)
1078
+
1079
+ ## v0.4.8 — 2026-08-16
1080
+
1081
+ ### Fixed
1082
+
1083
+ - Compatible automatic/deferred sparse ordinary side/plan hole and post-drain
1084
+ machined-feature callouts now share one bounded same-view leader inventory
1085
+ (#1166). Pattern, profiled-bore, and dense table-eligible hole queues retain
1086
+ their specialised established paths because their winners affect downstream
1087
+ furniture/table semantics or await #798 silhouette-aware routing. The solve
1088
+ preserves required/priority-ranked evidence, rejects new-leader crossings,
1089
+ checks complete leader ink against actual-width fixed dimension/witness,
1090
+ arrow, label, and centre-furniture components (including rendered arrows on
1091
+ shifted dimensions and component-local dashed circular centre furniture);
1092
+ fixed residual faces and the selected survivor both require each tessellation
1093
+ triangle to fit one same analytical component rather than merely the union,
1094
+ plus exact continuous OCC containment for curved faces; residual lowering
1095
+ also retains filled datum/GD&T faces absent from segment metadata; if candidate
1096
+ or selected-survivor construction raises, or a survivor fails validation, the
1097
+ bad alternative fails closed and the canonical lazy producer tail is replayed
1098
+ instead of falsely dropping an otherwise valid callout; an all-invalid tail is
1099
+ reported as validation-stage geometry failure rather than no clear room (so
1100
+ scale fallback does not retry a non-spatial helper defect), while compiler
1101
+ invariant violations such as a missing semantic callout label remain loud;
1102
+ only explicitly marked global turning axes permit an arrow-local leader-tip
1103
+ attachment; near-collinear shaft travel and ownerless section/cutting lines
1104
+ remain fixed ink. The solve prefers
1105
+ clear routes over explicit Policy-B fallbacks, and then minimises real leader
1106
+ length. Provisional sections cannot veto that primary result: one separately
1107
+ bounded refinement may choose an equally complete leader inventory that clears
1108
+ their exact components, after which the final section performs one bounded
1109
+ end-symbol repair and yields if exact ink still conflicts. The selected OCC survivor is validated against the same analytical
1110
+ ink contract. A retained fixed-ink crossing now emits the machine-readable
1111
+ `feature_leader_crossing` info finding instead of appearing lint-clean. Candidate,
1112
+ fixed-component lowering, fixed-obstacle-probe, pair, and exact-search work is
1113
+ bounded; the fixed inventory is lowered once and reused with the producer's
1114
+ legacy lazy result as the floor, including state-cap exhaustion; page,
1115
+ own-silhouette, and title-reservation constraints remain hard in every fallback.
1116
+ A replay that exhausts exact fixed-probe classification is retained visibly as
1117
+ `feature_leader_fixed_ink_unverified` rather than rescanning past the cap, and
1118
+ that uncertainty participates in the legibility quality inventory.
1119
+ Trace output preserves an abandoned fully admitted inventory when state search
1120
+ exhausts, records the producer-floor replay separately, and names exact committed/provisional components,
1121
+ refinement status, and competing blockers; live
1122
+ single-callout edits remain immediate.
1123
+
1124
+ ## v0.4.7 — 2026-08-16
1125
+
1126
+ ### Added
1127
+
1128
+ - Recogniser co-development now has a one-command, wheel-based two-checkout check; an exact
1129
+ registry-release evidence record; automated dependency PR preparation; and documented landing,
1130
+ compatibility, deprecation, rollback, ownership, and CI-budget rules. Dependency PRs retain the
1131
+ canonical coverage/Codecov gate while avoiding four duplicate platform suites and the slow tier
1132
+ (#1170).
1133
+ - A versioned, independently authored STEP-analysis corpus and evaluator now report detection
1134
+ recall/false positives, parameter fidelity, and downstream usefulness as separate evidence
1135
+ layers. Hash-pinned positive, negative, ambiguous, compound, and topology-order-variant fixtures,
1136
+ per-layer diagnostics, and an anti-self-validation mutation prevent recogniser output from
1137
+ defining its own denominator (#1169).
1138
+ - Draftwright now validates the released `b123d-recognisers` 0.2.0 capability manifest against
1139
+ an exhaustive consumer-owned declaration of IR, DSL, generated-code, drawing, completeness,
1140
+ and documentation states. Unknown families, stale implementations, record-schema drift, and
1141
+ unevidenced state transitions fail closed; intentionally geometry-only repeating-profile
1142
+ evidence remains usable without acquiring invented drafting semantics.
1143
+
1144
+ ### Changed
1145
+
1146
+ - Updated the exact `b123d-recognisers` production lock from 0.2.0 to 0.2.1.
1147
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest
1148
+ `383a196329ed6a3d1bf88f18ae4ff75613c032f35d40f2b53302b6744da4d19e` are
1149
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
1150
+ normal consumer gates run on the generated PR.
1151
+
1152
+ ### Fixed
1153
+
1154
+ - Stable release and TestPyPI snapshot builds now update package metadata and the independently
1155
+ validated Draftwright consumer-contract identity atomically. This prevents stripped `.dev0`
1156
+ and numbered development artifacts from failing their own capability-contract validation.
1157
+ - Installed wheels validate every portable recogniser-contract property without requiring the
1158
+ repository-only behavior-test files whose existence remains a strict source/CI gate. This keeps
1159
+ the packaged STEP-analysis evaluator usable without weakening evidence checks in development.
1160
+ - Post-release development-version bumps now update the consumer-contract identity on a branch,
1161
+ open a protected PR, and dispatch the narrow maintenance gate instead of attempting a rejected
1162
+ direct push to protected `main` (#1170).
1163
+ - Machined-feature leader callouts now collect all clear alternatives within each
1164
+ post-drain pass and use a bounded maximum-cardinality, minimum-leader-length
1165
+ assignment instead of taking corners greedily in feature order (#740). If a
1166
+ deterministic alternative-count, pass-size, candidate-pair, or search budget is
1167
+ reached, placement retains the legacy greedy result or a result no worse than it
1168
+ rather than losing additional callouts. Pre-drain diameter clearance ranking and
1169
+ its Policy-B fallback remain unchanged.
1170
+
1171
+ ## v0.4.6 — 2026-08-15
1172
+
1173
+ ### Changed
1174
+
1175
+ - **Geometry recognition is supplied by the standalone Apache-2.0
1176
+ [`b123d-recognisers`](https://github.com/pzfreo/b123d-recognisers) package.** The cutover
1177
+ pins the published stable release `v0.1.0` (built from commit
1178
+ `9e622716c14d491729b5191aa6e5d8351c982a51`), removes the
1179
+ duplicate embedded implementation, and keeps only identity-preserving
1180
+ `draftwright.recognition` / `draftwright.score` compatibility re-exports until 0.6.0.
1181
+ Draftwright continues to own the one-result build/lint cache, record→IR conversion, drafting
1182
+ policy, placement, and critique. The package migration is manifest-pinned and golden-backed;
1183
+ its sole normalization makes an exact dominant-axis tie deterministic across operating
1184
+ systems without changing feature policy.
1185
+
1186
+ ### Fixed
1187
+
1188
+ - Hole-callout obstacle checks now account for the rendered leader shaft width and
1189
+ tip-local arrowhead flare instead of treating the leader as a zero-width centreline
1190
+ (#367). The analytical check remains geometry-only and does not over-reserve the
1191
+ arrowhead's clearance along the whole shaft.
1192
+ - Exact X/Z slanted flats retain their established lint-clean side-view callout after the
1193
+ recogniser package's cross-platform semantic-axis normalization. The normalized recognition
1194
+ record remains unchanged; `FlatFeature.presentation_axis` carries the downstream drawing-view
1195
+ choice, preventing a leader/silhouette crossing without duplicating recognition.
1196
+
1197
+ ## v0.4.5 — 2026-08-15
1198
+
1199
+ **A focused technical-drawing correctness patch.** Cross-drilled hole locations now remain
1200
+ complete and ordered, datum-starting pocket annotations avoid redundant dimensions, and repeated
1201
+ equal-radius fillets collapse into one manufacturing callout.
1202
+
1203
+ ### Fixed
1204
+
1205
+ - Side-drilled hole heights that relocate around callout leaders now join the alternate
1206
+ view's shared dimension ladder before placement, preserving every height and keeping
1207
+ feature locations inside the overall dimension. Datum-starting open pockets no longer
1208
+ print a redundant half-length centre offset, and equal-radius fillets across different
1209
+ edge axes collapse into one counted ``n× R`` callout.
1210
+
1211
+ ## v0.4.4 — 2026-08-15
1212
+
1213
+ **A semantic-completeness and fail-closed layout patch release.** Hole requirements and drawing
1214
+ quality are now machine-readable, hole-table replacement is transactional, explicit scales
1215
+ preserve required output, notes tables use all viable sheet space, and gear declarations,
1216
+ recognition evidence, and callout metadata improve diagnostic trust.
1217
+
1218
+ ### Added
1219
+
1220
+ - **External spur gears can be declared through the `Sheet` façade** (#1086). The declaration
1221
+ records the correlated ISO 1328-1 manufacturing requirements, renders a structured gear-data
1222
+ table, and survives script emission, declaration, export, and lint without inferring gear data
1223
+ that the caller did not supply.
1224
+
1225
+ - **Recognition carries serialisable repeating radial-profile evidence** (#1087). Exact
1226
+ rotational whole-wire proof can reconcile a declared gear with its physical profile while
1227
+ preserving ambiguity across separate bodies and without inventing module, pressure angle, or
1228
+ other manufacturing intent.
1229
+
1230
+ - **Explicit drawing scales now protect required annotation completeness** (#1146).
1231
+ `build_drawing(...)`, `make_drawing(...)`, `Sheet(...)`, and the CLI accept
1232
+ `scale_policy="fallback"|"strict"|"permissive"`. The safe default retries preferred ISO
1233
+ 5455 reductions and returns the largest complete scale; strict mode raises with structured
1234
+ requirement blockers, while permissive mode is an explicit warned opt-in to the historical
1235
+ best-effort result. Every returned `Drawing.scale_decision` reports the requested and
1236
+ effective scales, status, attempted scales, and any semantic placement blockers.
1237
+
1238
+ - **Hole and hole-pattern requirements now participate in semantic completeness accounting**
1239
+ (#1143). `lint_summary()["quality"]["completeness"]` reconciles recognition-owned bore,
1240
+ depth/through, grouping, pattern, and location requirements to placed, suppressed, dropped,
1241
+ missing, or explicitly unverifiable outcomes. Ambiguous declared/automatic correspondence
1242
+ fails closed without shrinking the physical denominator. Z-normal hole locations retain ADR
1243
+ 0016's feature-level `location.location` addressability while structured physical evidence
1244
+ distinguishes the independently required X and Y ordinates for critique.
1245
+
1246
+ - **`lint_summary()` exposes drawing-quality evidence as separate completeness, restraint,
1247
+ and legibility components** (#1127). The legacy `score` remains byte-compatible and is also
1248
+ returned as the honestly named `diagnostic_score`; no composite quality verdict is invented.
1249
+ Completeness scores recognition-owned requirements in the families with semantic outcome
1250
+ ledgers and states that conditional scope explicitly; recognized families not yet scored are
1251
+ listed separately. Its scalar is `audited_score`, not `score`, because a feature recognition
1252
+ missed never became a requirement — so a part with a recogniser gap can reach 1.0, and the
1253
+ qualifier belongs where it survives being quoted. The block also lists what its denominator
1254
+ `excludes` and counts `unrecognised_geometry_reports` (a floor on that gap, not a measure).
1255
+ It is not a completion gate. Legibility includes only layout and placement diagnostics, and
1256
+ scores information-severity ones — "place what fits" drops, a leader crossing a silhouette —
1257
+ against the warning floor, so it cannot read 1.0 while itemising output it calls unreadable.
1258
+ A drop is identified by its `*_dropped` code suffix unless its producer records an explicit
1259
+ `outcome_stage`, so a drop code added later counts without being registered anywhere.
1260
+ Restraint fails closed as unavailable until measurement provenance can classify every
1261
+ annotation.
1262
+
1263
+ ### Fixed
1264
+
1265
+ - **Generic notes/data tables use every viable free sheet region and explain failed placement**
1266
+ (#1145). A drawn sheet frame or zone ruler now defines the usable inset instead of becoming a
1267
+ page-sized solid obstacle, so a measured four-row notes block can use clear lower-left A4
1268
+ space. Tables retain the drafting preset's external text clearance from named and anonymous
1269
+ existing annotations. Failed placement diagnostics report the measured page-space footprint,
1270
+ attempted candidate regions, and the named obstacles or clearance bands that blocked the
1271
+ nearest candidates without materialising the candidate Cartesian product.
1272
+
1273
+ - **Automatic hole-table escalation is transactional per semantic requirement** (#1144). The
1274
+ engine suppresses replaceable callouts and location dimensions only after the table fits and
1275
+ every visible row has its required feature-owned balloon. A failed or partial balloon attempt
1276
+ rolls the shared table back as a unit and restores the exact annotations, ordering, registry
1277
+ identities, coverage, and issues. Precise ring, glyph, and leader-segment tests keep placement
1278
+ clear of retained callout labels, leader shafts, and existing public-balloon components without
1279
+ changing the existing additive `add_hole_table()`/`add_balloons()` behavior; malformed component
1280
+ metadata—including Boolean cardinalities or coordinate payloads—falls back conservatively or fails the
1281
+ optional replacement closed. New balloon shafts must also remain mutually crossing-free.
1282
+ Compound, thread,
1283
+ profile, pattern, tolerance, and fit facts the table cannot state remain active, while
1284
+ independently replaceable X/Y locations may still move to the table. Guarded interval solving
1285
+ has deterministic pre-lane, pre-carve, and work/state budgets; an adversarially fragmented label
1286
+ inventory or band fails the replacement closed before excessive work or allocation. Required row-key
1287
+ balloons take priority over optional non-certifying pattern markers, so an independently complete
1288
+ table cannot be displaced by an auxiliary marker.
1289
+ Hole outcomes identify the winning representation per requirement;
1290
+ mixed table/feature evidence deliberately claims no single winner. Exceptions restore those
1291
+ semantic markers, and automatic escalation fails closed rather than overwrite an existing
1292
+ annotation that owns a reserved table or balloon name.
1293
+
1294
+ - **Generated hole-callout leaders expose their exact rendered semantic text through the public
1295
+ `Leader.label` field** (#1142). Geometry and placement are unchanged, and structured coverage
1296
+ remains authoritative instead of requiring downstream tools to parse display text.
1297
+
1298
+ - **Leader placement probes use analytic footprint arithmetic instead of constructing temporary
1299
+ OCCT geometry** (#1138). Representative probe-heavy layouts are substantially faster while
1300
+ preserving custom-style containment and placement behavior.
1301
+
1302
+ ### Changed
1303
+
1304
+ - **Pairwise lint detail no longer multiplies the legibility score penalty** (#1147). Raw
1305
+ severity and code counts retain their existing finding-level semantics, and every offending
1306
+ pair remains in `issues`. The legibility component now also reports its primary issue counts
1307
+ and scores one producer-identified annotation/failure mechanism once, with `affected_pairs`
1308
+ exposing how many raw pair findings were aggregated. The legacy top-level `score` and
1309
+ `diagnostic_score` remain unchanged.
1310
+
1311
+ - **Pull-request validation now reports a stable aggregate `ci-ok` check and runs the supported
1312
+ Python matrix on Linux** (#1134/#1136). Weekly, manual, and labelled workflows retain the
1313
+ cross-platform escape hatch, reducing routine Actions cost without narrowing supported Python
1314
+ coverage.
1315
+
1316
+ ## v0.4.3 — 2026-08-09
1317
+
1318
+ **A trust and validation-throughput patch release.** Whole-part hexagonal stock now receives its
1319
+ manufacturing definition, assembly recognition no longer combines evidence across bodies, and
1320
+ performance validation is both more stable and faster to run.
1321
+
1322
+ ### Added
1323
+
1324
+ - **Whole-part regular hexagonal stock is recognised independently of an attached boss and
1325
+ dimensioned as `HEX … A/F` plus its axial length** (#1082). In-plane rotation preserves the
1326
+ geometric across-flats size instead of substituting an orientation-dependent bounding-box
1327
+ width. Automatic and declared paths share the same compiler and placement machinery, while
1328
+ completeness lint distinguishes placed, authored-suppressed, dropped, missing, and ambiguous
1329
+ outcomes.
1330
+
1331
+ ### Fixed
1332
+
1333
+ - **Slot and pocket patterns cannot group members from separate assembly bodies** (#1073).
1334
+ Recognition carries solid ownership through grouping and fails closed when ownership is
1335
+ unavailable, preventing plausible-looking phantom patterns across disconnected parts.
1336
+
1337
+ - **The annotation-obstacle performance guard uses deterministic work-count evidence instead of
1338
+ a wall-clock threshold** (#1071). The regression test remains load-bearing under mutation but
1339
+ no longer flakes because of machine load or suite ordering.
1340
+
1341
+ ### Changed
1342
+
1343
+ - **The post-merge slow tier distributes individual tests across workers** (#1108), using
1344
+ `pytest-xdist`'s load scheduling after local benchmark evidence showed a material speed-up
1345
+ without weakening fixture coverage. Hosted slow validation remains the single post-merge gate.
1346
+
1347
+ ## v0.4.2 — 2026-08-09
1348
+
1349
+ **The trustworthy manufacturing drawings release.** AP242 PMI is now accounted for from its
1350
+ source records through lowering and rendering, imported datum faces retain an auditable topology
1351
+ identity, and several drawing paths that previously omitted, invented, or stalled on defining
1352
+ geometry now fail closed or produce the required annotation.
1353
+
1354
+ ### Added
1355
+
1356
+ - **AP242 geometric tolerances and datum features survive as source-backed manufacturing
1357
+ requirements** (#62/#675/#1094/#1095/#1099/#1100). The importer recovers range-encoded
1358
+ dimensional tolerances, tolerance characteristics, magnitudes, datum chains, and supported
1359
+ all-around/all-over modifiers. Datum representation-item faces are resolved to imported
1360
+ topology through OCCT's exact transfer relationship rather than geometric resemblance, so the
1361
+ NIST CTC-01 datum definitions `A`, `B`, and `C` lower and render without guessing.
1362
+
1363
+ - **PMI completeness lint reconciles every AP242 source record through extraction, projection,
1364
+ lowering, and rendering** (#623). Unsupported or incomplete requirements remain explicit with
1365
+ source-specific outcomes instead of allowing a partial PMI transcription to report clean.
1366
+
1367
+ - **Hexagonal and other regular polygonal bosses are recognised and dimensioned by their
1368
+ across-flats size** (#676). Automatic and declared paths share the same feature model, and
1369
+ mutation tests reject incomplete, unequal, non-planar, and ambiguous face sets.
1370
+
1371
+ - **Generated `Sheet` scripts preserve references to named source objects where correspondence
1372
+ is proven** (#1041). The `--script` path emits declarations such as
1373
+ `sheet.hole(features.tap)` instead of copying detected numbers; ambiguous, stale, non-shape, or
1374
+ unused mappings fail closed.
1375
+
1376
+ - **A curated API reference is published at
1377
+ <https://pzfreo.github.io/draftwright/>** (#846), covering the supported entry points,
1378
+ `Sheet` declaration surface and fluent handles, `Drawing` results, and feature constructors.
1379
+
1380
+ ### Fixed
1381
+
1382
+ - **Recognition no longer combines faces from different solids into phantom slots or pads**
1383
+ (#958). Each physical solid is recognised independently before the results are combined.
1384
+
1385
+ - **`Sheet.from_part(...).envelope()` reuses the detected whole-part envelope** (#1000) instead
1386
+ of appending a duplicate feature whose dimensions were then silently deduplicated on the page.
1387
+
1388
+ - **Crowded Z-turned step chains recover the independently approved overall height when the
1389
+ chain cannot be placed** (#955), and placed step lengths retain measurement identity for
1390
+ differential audit (#1004).
1391
+
1392
+ - **All-over GD&T leaders and complex STEP B-spline paths have bounded, export-safe DXF
1393
+ conversion** (#1097/#1070). All-over scope now reaches the existing export-safe leader
1394
+ representation, while spline conversion reads OCCT knots, poles, and weights through its
1395
+ indexed API instead of expensive sequence wrappers; visible curve geometry is preserved.
1396
+
1397
+ ### Changed
1398
+
1399
+ - **Hosted CI retains every supported Python version and both dependency/kernel generations on
1400
+ macOS and Windows with a smaller compatibility matrix** (#1103). PRs still require lint,
1401
+ coverage, Codecov, and the compatibility gates; the slow standards tier runs once after merge.
1402
+
1403
+ - **The pinned wheel fixture now records mutation-gated evidence for a repeating 13-fold radial
1404
+ profile without claiming gear semantics** (#1085). Runtime recognition remains fail-closed
1405
+ until a separate feature contract has independently sufficient evidence.
1406
+
1407
+ ## v0.4.1 — 2026-08-07
1408
+
1409
+ **The recognition trust release.** Automatic drawings now fail closed on unsupported inner
1410
+ profiles, prove semantic coverage for flats, slots and patterns, and retain the measurement
1411
+ identity needed to explain placed, suppressed, dropped and missing definitions. Several real
1412
+ STEP case studies also move from clean-looking but incomplete output to complete drawings or
1413
+ actionable lint.
1414
+
1415
+ ### Discouraged (supported)
1416
+
1417
+ - **`Sheet.auto_dimensions()` and `Sheet.add_dimension()` now emit
1418
+ `SoftDeprecationWarning`** (#1043). They remain supported with no removal planned; the
1419
+ warning steers declared scripts toward `authored_dimensions()` plus explicit
1420
+ `dimension(feature, parameter_id)` lines, where omission can mean suppression. Automatic
1421
+ `build_drawing(part)` and the `Sheet.from_part(part)` on-ramp remain silent. See
1422
+ `docs/deprecations.md` for filtering and migration guidance.
1423
+
1424
+ ### Added
1425
+
1426
+ - **Finished drawings expose why measurements were omitted and, on covered placement paths,
1427
+ what an annotation measures** (#996/#1002). `Drawing.suppressions()` returns the compiler's
1428
+ omission ledger, `Drawing.measurement_keys(name)` exposes recorded measurement provenance, and
1429
+ `draftwright.audit.diff_builds(before, after)` reports losses, substitutions and candidate
1430
+ suppression explanations. The differential is deliberately a triage aid, not a proof where
1431
+ renderer identity remains incomplete.
1432
+
1433
+ - **Physical-completeness lint now follows semantic correspondence for flats, lone slots and
1434
+ slot patterns** (#1018). Coverage is joined through recognition, IR and compiler identities
1435
+ rather than inferred from label text or page geometry, and distinguishes authored
1436
+ suppression, placement drops, missing definitions and unverifiable provenance.
1437
+
1438
+ - **Full-span floored open channels are recognised and dimensioned as first-class features**
1439
+ (#917). Automatic and declared drawings state the independent overall extent, one wall and
1440
+ channel width, suppress the derived opposite wall, and report any missing member of that
1441
+ defining chain.
1442
+
1443
+ ### Fixed
1444
+
1445
+ - **Square and near-square parts retain both independent planar extents** (#997). A plain
1446
+ square plate could previously lose both plan dimensions, while parts up to five percent off
1447
+ square could lose one and be silently represented as square. The unsafe suppression rule is
1448
+ removed; explicit square notation remains tracked separately in #918.
1449
+
1450
+ - **Machined flats retain their physical stock identity from recognition through placement
1451
+ and completeness lint** (#1013/#1015/#1034/#1036). Flats on separate parallel, coaxial or
1452
+ slanted stock no longer collapse into one definition or borrow an opposite face from another
1453
+ stock region. Independent callouts receive clear-margin candidates, while double-D faces on
1454
+ one stock still form one A/F requirement.
1455
+
1456
+ - **Dense callout and step-detail cases keep their complete defining set** (#915). Hole
1457
+ callout batches are reconciled as a queue, detail views fit their actual aspect, step levels
1458
+ retain the supporting-face correspondence needed for truthful crops, and a detail redraws
1459
+ only rungs omitted from its parent view.
1460
+
1461
+ - **Pocket leaders start on the physical opening rim** (#916), removing the spurious inner
1462
+ silhouette crossing while preserving the solved label corridor and approved dimensions on
1463
+ X-, Y- and Z-depth pockets.
1464
+
1465
+ - **Wedge-mounted raised pads survive recognition** (#909). Lower ledges that touch only in
1466
+ plan no longer make the recogniser discard a valid upper pad; true staircase tiers remain
1467
+ excluded.
1468
+
1469
+ - **Annotation-dense detail placement no longer stalls the build** (#1065). Detail views
1470
+ avoid every decomposed annotation shaft, witness, label and item footprint; CTC-02
1471
+ supplies 571 such boxes. The free-rectangle search previously treated that set as tiny
1472
+ and admitted roughly 34 billion candidate rectangles, exhausting the slow tier's
1473
+ ten-minute test budget before export. A compressed-coordinate sweep preserves the same
1474
+ deterministic winning rectangle while completing the real search in seconds.
1475
+
1476
+ - **Through double-D bores are recognised and called out as first-class hole profiles**
1477
+ (#1061). Automatic and declared builds preserve the parent-circle diameter, A/F size,
1478
+ orientation and depth, and render one compound `DOUBLE-D ... A/F` bore callout. Physical
1479
+ critique now accepts that supported inner profile while retaining a separate warning for
1480
+ 13 evenly spaced common-circle arcs on the unresolved outer boundary; it does not infer a
1481
+ repeating full profile or a gear standard.
1482
+
1483
+ - **Unsupported internal profiles no longer pass physical-completeness lint silently**
1484
+ (#1058). Inner loops on principal boundary faces are checked against the current
1485
+ circular-hole, rectangular-opening, true-obround, and proven through-double-D vocabulary.
1486
+ Profiles outside it now produce
1487
+ `unrecognised_defining_geometry` and reduce the lint score instead of allowing an
1488
+ envelope-only drawing to score 1.0.
1489
+
1490
+ ### Changed
1491
+
1492
+ - **Crowded prismatic step dimensions recover into an enlarged detail view by default**
1493
+ (#909). The automatic `build_drawing` / `make_drawing` / CLI paths and generated
1494
+ `Sheet` scripts now preserve the omitted dimensions instead of returning
1495
+ `step_dim_dropped` unless the caller remembered `detail_view=True`. Pass
1496
+ `detail_view=False` to retain the parent-view-only behavior and its lint warning.
1497
+
1498
+ ## v0.4.0 — 2026-08-02
1499
+
1500
+ **The compat-exit release.** Every surface whose documented removal target was 0.4.0 is gone,
1501
+ and every deprecation that remains now says when it goes — in the message the caller sees, not
1502
+ only in a release note. ADR 0005 §4's rule is that an alias carries a tracking issue *and* a
1503
+ removal date, because "a facade with no exit date is a failure mode"; this release is that rule
1504
+ being kept rather than restated. `docs/deprecations.md` is the new index, and
1505
+ `tests/test_deprecation_dates.py` fails any deprecation that names no removal.
1506
+
1507
+ Also lands ADR 0016's declared-dimensioning work (#867): a `dimension(feature, role)` is
1508
+ referential — it names a measurement and carries no number — and an authored set means omission
1509
+ is suppression, enforced at the compiled-plan boundary rather than by a flag renderers check.
1510
+
1511
+ **Read this before upgrading:** two removals break *without a release that warned you*, because
1512
+ their deprecation never appeared in a shipped version. See *Removed (breaking)* below and
1513
+ `docs/deprecations.md`.
1514
+
1515
+ ### Removed (breaking)
1516
+
1517
+ Compat surfaces whose documented removal target was 0.4.0 (#720, ADR 0005 §4 — every alias
1518
+ carries a tracking issue *and* a removal date, or the facade is permanent by accident):
1519
+
1520
+ - **The seven `Drawing` compat aliases** — `_named` / `_anno_view` / `_pinned` /
1521
+ `_build_issues`, and `_pattern_callouts` / `_patterned_holes` / `_dropped_callout_diams`.
1522
+ These were private, and the public reads have existed since #699: use `dwg.registry`
1523
+ (`in reg`, `names()`, `issues`, `pinned_names()`) and `dwg.coverage`, or the `Drawing` verbs
1524
+ `annotations()` / `iter_annotations()` / `get_annotation()` / `view_of()`.
1525
+ **Note the asymmetry.** A *read* — `dwg._named` — now raises `AttributeError`. A *write* —
1526
+ `dwg._pinned = {...}` — does **not**: these were properties with setters, and `Drawing` has
1527
+ no `__slots__`, so assignment now quietly creates an unrelated instance attribute that no
1528
+ longer reaches the registry or coverage owner. Writes therefore fail silently rather than
1529
+ loudly. Grep for `\._(named|anno_view|pinned|build_issues|pattern_callouts|patterned_holes|dropped_callout_diams)\b`
1530
+ before upgrading; mutate through `registry.add()` / `pin()` / `record_issue()` /
1531
+ `restore_issues()` and the `CoverageState` methods instead.
1532
+ - **The `draftwright.sheet_dsl` module** — an import alias for `draftwright.sheet` since the
1533
+ #640 rename. Import `Sheet` from `draftwright` or `draftwright.sheet`.
1534
+ - **`generate_script`**, including its `draftwright.__all__` entry. It has raised since #940
1535
+ retired the imperative emitter; it is now simply absent, so the failure is an `ImportError`
1536
+ at the top of a script rather than a `RuntimeError` part-way through one. Use
1537
+ `--script` / `emit_sheet_script`.
1538
+ - **The bespoke `--style imperative` error message.** `--style` itself is unchanged and still
1539
+ accepts its sole value `sheet`; `imperative` is now an unrecognised value like any typo,
1540
+ rather than one carrying its own explanation of the #940 retirement.
1541
+
1542
+ - **Bare dimension-role spellings** — `sheet.dimension(f, "width")`. Use the parameter id,
1543
+ `"width.length"`; `dimension_ids()` on a handle lists the valid ones. The bare role is the
1544
+ *family* spelling: it selects every parameter carrying it, which is how
1545
+ `dimension(step, "step")` quietly declared two measurements. In an authored set, where
1546
+ omission means suppression, silently declaring an extra one is the mirror image of the rule
1547
+ — so it now raises rather than resolving. (A *discriminated* bare role, used with `axis=`,
1548
+ is unaffected: that is how variants like `grid_pitch.length.row` are addressed, and it never
1549
+ warned.)
1550
+ - **The `Sheet.dimension(kind=…, value=…)` call shape** — use `Sheet.measured_dimension(...)`.
1551
+ `dimension` is now solely the ADR 0016 referential verb: it names a feature and a parameter
1552
+ id and reads the value off the geometry.
1553
+
1554
+ Those last two break **without a release that warns you**. They were deprecated after v0.3.9
1555
+ and removed in 0.4.0, so the `DeprecationWarning` never appeared in a released version —
1556
+ upgrading from v0.3.9 or earlier goes straight from working to a raise. That is deliberate
1557
+ (ADR 0016); this entry and `docs/deprecations.md` are the only notice you get, so both
1558
+ failures name their replacement rather than raising about argument counts.
1559
+
1560
+ ### Deprecated
1561
+
1562
+ - **The legacy `Drawing.export` shapes now emit `DeprecationWarning`** (#987) — they were
1563
+ announced as deprecated in v0.3.1 and then said nothing at runtime for four minor releases,
1564
+ which would have made their 0.5.0 removal a silent break. Three cases warn:
1565
+ - `export()` with `formats=` **omitted or `None`** — the legacy default, which writes
1566
+ SVG + DXF and returns an `(svg, dxf)` **tuple** rather than the `{format: path}` dict.
1567
+ - the **`svg=` / `dxf=` booleans**. The suggested replacement names the formats *that call*
1568
+ selected, so following it cannot change what gets written.
1569
+ - passing a boolean **alongside** `formats=`, where it is silently ignored — `formats` wins,
1570
+ and now says so.
1571
+
1572
+ **If you promote `DeprecationWarning` to an error, previously-passing exports will now
1573
+ raise.** Migration: `export(out, formats=("svg", "dxf"))` and read the dict. `make_drawing`
1574
+ is unaffected — it returns the same tuple and does not warn.
1575
+
1576
+ ### Added
1577
+
1578
+ - **`sheet.add_dimension(feature, role)` — ask the planner to carry one more
1579
+ measurement** (ADR 0016 / #872). Referential: it names a feature and a role and
1580
+ carries **no number**, so the value still comes from the geometry and a size lives in
1581
+ exactly one place. It changes *selection*, not derivation — a request can never
1582
+ introduce a number the part does not have. Requesting something the planner already
1583
+ emits is a deliberate no-op, so a script can ask without first knowing the rule set's
1584
+ mind. `sheet.auto_dimensions()` states the source explicitly (optional in this
1585
+ release; #874 makes it mandatory).
1586
+ - **Suppressing a dimension marks it; it no longer leaks into the callout** (ADR 0016 /
1587
+ #875). `PlannedDimension.suppressed` was honoured at thirteen render sites but not by
1588
+ the compound hole-callout path, so a suppressed counterbore still printed. The group
1589
+ keeps its engineering data either way — what changes is whether a value reaches the
1590
+ page. Suppressing the bore ⌀ while a counterbore, spotface or countersink segment
1591
+ remains now **raises** and names the orphan: `⌀20 THRU ⌴ ⌀32 ↓ 1.5` has no reading with
1592
+ its leading term removed, and silently dropping the segments would discard authored
1593
+ intent while silently restoring the head would make the drawing say something the
1594
+ script does not.
1595
+ - **Addressable dimension identity** (#869/#870/#871): every planned measurement now has
1596
+ a stable `DimensionId(feature, parameter)`, and correlated measurements that must be
1597
+ named as one — a grid pattern's row and column pitch, a step ladder — group into an
1598
+ `AddressableDimension`. This is what a `dimension(...)` line will name, and what
1599
+ suppression and provenance key on.
1600
+
1601
+ ### Changed
1602
+
1603
+ - **`Sheet.dimension(kind=…, value=…)` is now `Sheet.measured_dimension(...)`**
1604
+ (ADR 0016 / #873), and `model.declare.authored_dimension` is
1605
+ `model.declare.measured_dimension` to match. This **reserves** the name `dimension` for
1606
+ the referential verb ADR 0016 defines — name a feature and a role, carry no number, let
1607
+ the engine read the value off the geometry — which `Drawing.dimension` already is and
1608
+ `Sheet.dimension` will become. The verb that carries an explicit number needed a name
1609
+ saying so first. The old spelling was a transitional overload during development and is
1610
+ **removed in this same release** (#720) rather than shipping — `Sheet.dimension` is solely
1611
+ the referential verb, and the old keyword call raises with `measured_dimension` named. See
1612
+ *Removed (breaking)* above. Generated AP242 scripts emit `measured_dimension` and so arrive
1613
+ un-deprecated.
1614
+ - **`Sheet` handles address features by identity, not position** (#908/#910/#912). A
1615
+ handle, tolerance, GD&T origin, section request or dimension intent now follows *its*
1616
+ feature through a `features` reorder instead of naming whatever took the slot.
1617
+ Reordering with `reverse()`/`sort()` preserves every reference; deleting or replacing
1618
+ a referenced feature raises rather than silently retargeting a neighbour. `of()` now
1619
+ accepts a handle, and negative indices resolve uniformly across `of()`,
1620
+ `add_dimension()` and the GD&T verbs.
1621
+
1622
+ ### Fixed
1623
+
1624
+ - **`sheet.envelope()` measured the file, not the part** (#977). An AP242 STEP import is a
1625
+ compound of the solid *plus its PMI presentation geometry* — annotation planes, leader
1626
+ curves — and the declared verb measured all of it. On the NIST CTC-01 fixture it declared
1627
+ 1170 × 650 where the part is 800 × 450: an envelope 370 mm too wide, silently, in anything
1628
+ declared by hand. `step_level()` had the same exposure, offsetting every step position by
1629
+ the annotation overhang. Both now measure the solid body, sharing the helper the engine
1630
+ already used for exactly this (`_analyse` and `Sheet.model`).
1631
+ - **A declared envelope's frame origin sat a half-height below a detected one** (#977). It
1632
+ used `bbox_min.Z` where the detector uses the bbox centre. **Dimension output is unchanged** —
1633
+ envelope sizes derive from `bbox_min`/`bbox_max`, and the overall height is compiled from the
1634
+ model bbox — but `frame.origin` is the generic feature *site*, so **a GD&T control frame,
1635
+ surface finish or note targeting a hand-declared `sheet.envelope()` was anchored at the
1636
+ bottom face rather than the centre, and its leader now moves to the centre**. That is a
1637
+ visible change on drawings carrying such an annotation; drawings without one are unaffected.
1638
+ Scripts generated by `--script` were never affected either way: they bake the detected frame
1639
+ explicitly rather than calling the verb.
1640
+
1641
+
1642
+ - **Front-view dimensions join the placement solve instead of committing to the strip**
1643
+ (#894): they previously took strip space before the global solve ran, so a
1644
+ higher-ranked dimension could find its space already gone. Priority only ranks
1645
+ candidates that are *in* the solve.
1646
+ - **A hole callout reads the feature's own `through` fact** (#868) rather than inferring
1647
+ it from whether a depth parameter happens to be present — so a suppressed depth can no
1648
+ longer make a blind hole print `THRU`.
1649
+ - **The balloon ring clears the view it annotates after a hole-table escalation**
1650
+ (#901/#903).
1651
+
1652
+ ## v0.3.9 — 2026-07-26
1653
+
1654
+ ### Added
1655
+
1656
+ - **Repeated recesses are dimensioned as one pattern, not N competing callouts**
1657
+ (#841): identical blind pockets and identical through-slots now collapse to a single
1658
+ grouped `5× 7.9 × 13.6 × 19 DEEP` / `4× SLOT 8 × 30` leader plus `(n-1)× pitch`
1659
+ dim(s). Previously each member competed for its own size dims and some silently
1660
+ dropped for want of strip room — five declared slots rendered only three length dims.
1661
+ Both new kinds (`PocketPatternFeature`, `SlotPatternFeature`) round-trip every
1662
+ surface: declare (`sheet.pocket_pattern(...)` / `sheet.slot_pattern(...)`),
1663
+ recognition from a real solid (coplanar, same-facing members only), the emitted
1664
+ Sheet script, and the editable surface (`dwg.callout(feature)` + `finalize()`).
1665
+ - **`Sheet.section()` and `Sheet.detail()` — ask for the view you need** (#841/#847):
1666
+ a section A–A auto-fires only for a Z-axis hole with a counterbore, spotface, or
1667
+ blind bottom, so a blind pocket's floor and depth stayed hidden-line-only with no
1668
+ supported way to request a cut. `sheet.section(feature)` / `section(at=y)` /
1669
+ bare `section()` force one (the cut plane is validated to lie strictly inside the
1670
+ part), and `sheet.detail()` exposes the existing `detail_view=True` opt-in as a
1671
+ chainable verb.
1672
+ - **External threads are a first-class declared aspect** (#859): the turned analog of
1673
+ the #764 internal thread — `sheet.step(shaft).thread("M3x0.5")`,
1674
+ `sheet.diameter(...).thread(...)`, and `sheet.boss(...).thread(...)` append the
1675
+ spec to the ⌀ callout (`ø3 M3x0.5`) instead of needing a free-text `.note()`. It
1676
+ composes with `.finish()` for Ra-on-thread, keys the callout bucket on `(⌀, thread)`
1677
+ so a threaded and a plain ø6 stay distinct, and now round-trips through the Sheet
1678
+ emitter — including pattern-member holes, closing the symmetric latent #764 gap.
1679
+ Declaration-only: a plain cylinder is geometrically indistinguishable from a
1680
+ threaded one, so there is no recogniser. *(Known limitation: the incremental
1681
+ `callout`/`finalize(only=…)` paths still dedup thread-blind — #863.)*
1682
+ - **Blind obround pocket recognition, including imported STEP** (#837): the blind
1683
+ counterpart of the #816 through-slot work. A stubby floored obround has side walls
1684
+ too short to pair, so it is recovered from its semicircular end caps — clustering
1685
+ faces by axis proximity so the quarter-cylinder split a STEP importer commonly
1686
+ produces recognises as well as build123d's half-cylinder. A sealed internal void
1687
+ (capped at both ends) is correctly *not* a pocket. A real tuner-jig STEP with five
1688
+ blind pockets ships as a fixture.
1689
+ - **`sheet.slot(...).note(...)` / `sheet.pocket(...).note(...)`** (#841/#845): the
1690
+ slot/pocket handle now anchors a note to its own feature, with no `ref=` needed
1691
+ (an explicit `ref` still forwards, as before).
1692
+
1693
+ ### Changed
1694
+
1695
+ - **An anchored note, GD&T frame, or finish relaxes its side instead of silently
1696
+ vanishing** (#841/#855): an annotation declared with an explicit `view=`/`side=`
1697
+ used to drop with only a warning when that strip was full, while the export still
1698
+ reported success. The requested side is now a *preference* — when the strip has no
1699
+ room the placer tries the opposite side, then the two perpendicular sides, taking
1700
+ the first with room (via the same bounds and title-block checks, so it never
1701
+ overshoots). A relaxed placement records an INFO `gdt_side_relaxed` issue naming
1702
+ requested versus actual. Only when no strip fits does it drop.
1703
+
1704
+ ### Internal
1705
+
1706
+ - **The state-bus endgame is closed to its permanent seam** (#830/#840): the
1707
+ solve trace is filled into `BuildState` at the one construction site, the redundant
1708
+ part-model re-attach is gone, and the detail view is now *transactional* — geometry
1709
+ is projected at the detail scale and committed only if dimensions land, so
1710
+ `_drop_view_coordinates` has zero engine callers. With those removed, the #817
1711
+ decision-D method-call exemption narrows from a blanket pass to a named
1712
+ `_LAYOUT_SEAM` allowlist (`_add_view`, `_set_view_coordinates`), accepted as the
1713
+ permanent interactive-layout seam; every other private-method call on the drawing
1714
+ is now flagged.
1715
+ - **A draftwright logo set** (#843) — drafting-idiom mark, all text outlined so the
1716
+ SVGs render font-independently; the README now leads with the lockup.
1717
+ - **A worked multi-feature object-reference example** (#853) in
1718
+ `docs/multi-feature-object-reference-workflow.md`.
1719
+
1720
+ ## v0.3.8 — 2026-07-23
1721
+
1722
+ ### Added
1723
+
1724
+ - **`Drawing.note()` — a public free-text note verb** (#817/#820): place a free-form
1725
+ `Note` at a page point without reaching for the low-level placement primitive —
1726
+ `dwg.note("SEE NOTE 1", at=(x, y))`. This is the safe door for free text that the
1727
+ privatisation below needs.
1728
+ - **Obround (racetrack) through-slot recognition** (#816): a part whose only features
1729
+ are obround through-slots is now recognised, dimensioned, and referenceable for GD&T.
1730
+ Previously such slots were silently undetected — and lint falsely reported the drawing
1731
+ complete. Stubby obrounds, whose straight side walls are too short to pair, are recovered
1732
+ from their two semicircular end caps (a round hole is never mistaken for a slot end).
1733
+
1734
+ ### Changed
1735
+
1736
+ - **The generated Sheet script is now self-describing** (#833): `--script --style sheet`
1737
+ emits a feature-census header, section sub-headers with repeat tallies (`8× R50`), and a
1738
+ plain-language comment on each verb line (`# ⌀8 THRU ×4`, `# slot 40 × 120`, `# R50`).
1739
+ All additions are comment-only, so re-run fidelity is unchanged.
1740
+
1741
+ ### Deprecated
1742
+
1743
+ - **The low-level `Drawing` placement API is now private — the public surface is
1744
+ safe-by-default** (#817): `add`, `place_dim`, and the view-coordinate / build-state
1745
+ plumbing (`set_view_coordinates`, `drop_view_coordinates`, `attach_part_model`,
1746
+ `attach_solve_trace`, `add_view`, `clear_annotations`) are deprecated in favour of the
1747
+ feature-backed verbs (`callout` / `dimension` / `locate` / `note` / …), which route
1748
+ through the layout solve. Each keeps a `@deprecated` (PEP 702) shim for one release —
1749
+ so type-checkers and IDEs flag call sites, not just a runtime warning — and is slated
1750
+ for removal in ~0.5.0.
1751
+
1752
+ ### Internal
1753
+
1754
+ - **`AGENTS.md` — an AI-agent usage guide** (#818) with an executable check, steering
1755
+ agents toward the safe declarative surface rather than the low-level primitives.
1756
+ - **Coverage reporting is governed and a Codecov badge added** (#825/#832).
1757
+
1758
+ ## v0.3.7 — 2026-07-21
1759
+
1760
+ ### Fixed
1761
+
1762
+ - **Turned ⌀ leaders point at the feature, not a corner or across the body**
1763
+ (#794/#798): the diameter callouts on a turned part now centre on the step's
1764
+ mid-length (a boss keeps its frame origin; a ⌀ shared by disjoint steps centres
1765
+ on the longest run), so the arrow lands on the middle of the feature's edge
1766
+ rather than a step corner. A leader that would otherwise cut diagonally through
1767
+ the body — a nested or end feature such as a ⌀6 boss stub — is re-routed out to
1768
+ the nearest clear margin (axis-aware: X-turned → left/right, Z-turned → top/
1769
+ bottom), pointing straight at the feature instead of clipping the silhouette. A
1770
+ re-route that can't be placed clear-and-safe is restored and flagged, and a
1771
+ pinned leader is never moved.
1772
+
1773
+ ### Added
1774
+
1775
+ - **`leader_crosses_silhouette` lint notice** (#796): an info-level structural
1776
+ notice when a leader shaft passes *through* the projected part body (as opposed
1777
+ to a hole callout legitimately exiting an internal hole), so the gap is
1778
+ observable even where routing cannot clear it.
1779
+
1780
+ ## v0.3.6 — 2026-07-21
1781
+
1782
+ ### Fixed
1783
+
1784
+ - **`Sheet.step(boss)` on a prismatic part fails loudly instead of silently
1785
+ dropping the height** (#631): declaring a turned `.step()` at a cylinder that is
1786
+ really an external boss set `orientation="z"`, which made `render_height_ladder`
1787
+ suppress the overall-height dim on the assumption the OD/step chain conveyed it —
1788
+ but on a prismatic box that chain never renders, so the height vanished and
1789
+ nothing replaced it (11 → 10 annotations). The build now detects a `StepFeature`
1790
+ coincident with a `BossFeature` (same axis, origin, diameter) — a combination
1791
+ that can never be legitimate — and raises a clear `ValueError` pointing to
1792
+ `.boss()` (which renders its own ø and height per #632). Legitimate turned shafts
1793
+ have steps but no coincident boss, so they are unaffected.
1794
+ - **`detail_view=True` no longer a silent no-op when the detail can't be placed**
1795
+ (#630): a part whose lint recommends a detail view (`step_dim_dropped`) but whose
1796
+ crowded band is full-width (e.g. a shelled cover's stacked face levels) can't be
1797
+ enlarged legibly *and* still fit alongside the main views — so `detail_view=True`
1798
+ produced a drawing byte-identical to `False`, silently ignoring the opt-in. It
1799
+ now records a `detail_unplaceable` warning naming the reason and an actionable
1800
+ remedy (dimension manually / move onto its own sheet), so the opt-in is
1801
+ observable. Parts whose detail *does* fit are unchanged.
1802
+ - **Imperative `--script` round-trips the title-block fields + sheet furniture**
1803
+ (#775): the imperative flavour reproduced only title/number/tolerance/drawn_by/
1804
+ scale/page, dropping the `material`/`date`/`revision`/`company` fields (#766) and
1805
+ the `frame`/`zones`/`projection` furniture (#767/#768/#769) — so a regenerated
1806
+ imperative script didn't match the direct CLI. They now ride the emitted script's
1807
+ cog config block and its `build_drawing(...)` call (the Sheet flavour already
1808
+ round-tripped them), and the CLI forwards them to `generate_script`.
1809
+
1810
+ ### Internal
1811
+
1812
+ - **Fast-tier dense-sheet canary for the #733/#734 strip-pressure regression**
1813
+ (#737): a synthetic contended-front-strip fixture that drops a step-height
1814
+ principal dim on the pre-#734 commit and stays clean on `main` — closing the
1815
+ fast-tier gap the #733 regression fell through (only the CI-only slow CTC
1816
+ fixtures exercised real strip pressure before).
1817
+ - **Layout-hypothesis fuzz tier is now a reproducible PR gate** (#692):
1818
+ `derandomize=True` makes every run generate the same fixed example set (with
1819
+ `print_blob` for paste-able reproduction), so a failure recurs deterministically
1820
+ instead of surfacing on a seed-dependent minority of runs; exploratory fuzzing
1821
+ moves behind `DRAFTWRIGHT_FUZZ_EXPLORE=1`. The two `test_make_drawing.py` xdist
1822
+ observations (#669) were investigated and found non-reproducible — both tests are
1823
+ non-flaky by construction/measurement on current `main`.
1824
+
1825
+ ## v0.3.5 — 2026-07-20
1826
+
1827
+ ### Fixed
1828
+
1829
+ - **Generated `Sheet` script round-trips `--frame` / `--zones` / `--projection`**:
1830
+ the sheet-furniture flags added in #767/#768/#769 were not emitted into the
1831
+ generated `Sheet(...)` constructor (nor forwarded by the CLI's `--script` path),
1832
+ so a regenerated script dropped the border / zone ruler / projection symbol —
1833
+ the script no longer matched the direct CLI drawing. The emitter now emits
1834
+ `frame=True` / `zones=True` / `projection=…` when set (a plain drawing keeps a
1835
+ clean constructor), and the CLI forwards them to `generate_sheet_script`.
1836
+
1837
+ ### Added
1838
+
1839
+ - **ISO 5457 zone-grid border ruler** (#768): `build_drawing(zones=True)` /
1840
+ `Sheet(zones=True)` / `--zones` draws the grid reference system — numbers 1..
1841
+ along the top/bottom edges, letters A.. (skipping I/O) down the sides — in the
1842
+ band between the frame and the page edge, with the standard division count per
1843
+ A-series page (~50 mm zones). Implies a frame (the ticks sit on it). Off by
1844
+ default → byte-identical. The border ticks are lint-exempt like the frame; the
1845
+ labels are exempt only from the page-bounds check (they legitimately sit outside
1846
+ the drawable), still covered by overlap lint.
1847
+ - **Projection-method symbol** (#769): `build_drawing(projection="third"|"first")` /
1848
+ `Sheet(projection=…)` / `--projection` draws the ISO 5456-2 third-/first-angle
1849
+ glyph in the reserved title-block band (above the title block). Uses the new
1850
+ `ProjectionSymbol` primitive from build123d-drafting-helpers **0.14.1** (the pin
1851
+ is bumped). Off by default → byte-identical. Unlike the page-spanning frame, the
1852
+ small well-placed glyph is *not* lint-exempt — lint covers it so a future
1853
+ mispositioning is caught.
1854
+ - **Opt-in drawn sheet frame / border** (#767): `build_drawing(frame=True)` /
1855
+ `Sheet(frame=True)` / `--frame` draws a border rectangle at the page margin. It's
1856
+ the *content boundary* (Option B), not a rectangle over the drawing: turning it on
1857
+ raises the effective content margin, and because `compose._layout_geometry` is the
1858
+ single authority shared by scale/page selection **and** placement, the reservation
1859
+ flows through `choose_scale` — so a framed drawing may pick a smaller scale / larger
1860
+ page (ADR 0004), with content clearing the border. Default `frame=False` is
1861
+ byte-identical (guarded by the golden + layout-cleanliness suites). The page-spanning
1862
+ border carries an `is_sheet_frame` rider so lint skips it.
1863
+
1864
+ ## v0.3.4 — 2026-07-20
1865
+
1866
+ ### Changed
1867
+
1868
+ - **Imperative `--script` output defaults to PDF and the modern export form**
1869
+ (#709). **Breaking** for regenerated imperative scripts: without `-f`, a
1870
+ regenerated script now writes `<stem>.pdf` (was SVG+DXF via the deprecated
1871
+ legacy tuple), and the script-visible variables changed from
1872
+ `svg_path, dxf_path` to a `paths` dict printed in the requested-format order.
1873
+ This aligns the imperative flavour with the Sheet flavour and the direct CLI,
1874
+ which have defaulted to PDF since #702/#288. Migration: regenerate with
1875
+ `--format svg,dxf` to keep the old outputs. Previously-generated scripts are
1876
+ unaffected (they carry their own export lines).
1877
+ - **`finalize()` drains in the auto-pass's order** (#699 slice b): both build
1878
+ paths now execute the orchestrator's one canonical `_PASS_SEQUENCE` (via the
1879
+ shared `run_stages`), removing the hand-mirrored second orchestration in
1880
+ `Drawing._drain_intents`. For the deferred/finalize path this reorders three
1881
+ things toward auto-pass parity: the turned diameter/step-length set-solves now
1882
+ place *before* the corridor drain (matching the auto-pass's obstacle
1883
+ visibility); slots register after them; and the register-only height-ladder /
1884
+ step-position stages register *after* locations — observable too, since
1885
+ registration order decides corridor key-creation (= drain) order and
1886
+ same-priority tie-breaks (Codex review; the new order is exactly the
1887
+ auto-pass's). Recompose output may shift accordingly; the auto-pass order is
1888
+ unchanged.
1889
+ - **`Sheet.export` speaks the modern export API** (#702): it now takes
1890
+ `formats=` (any of svg/dxf/pdf/png, default **PDF** — matching the CLI
1891
+ default) and `dpi=`, and returns the `{format: path}` dict. Previously it
1892
+ rode the deprecated legacy path, could only write SVG+DXF — the flagship
1893
+ facade couldn't produce a PDF — and returned the old `(svg_path, dxf_path)`
1894
+ tuple. **Breaking** for callers unpacking that tuple: use
1895
+ `sheet.export(stem, formats=("svg", "dxf"))` and read the dict.
1896
+
1897
+ ### Added
1898
+
1899
+ - **Solve-trace / explain mode** (#736, from the #733 post-mortem): opt-in
1900
+ observability for strip placement. `build_drawing(trace=…)` or
1901
+ `DRAFTWRIGHT_TRACE=<path-or-dir>` writes ONE JSON file per build (schema v2)
1902
+ with two record types: `solves` — every corridor solve's key, strip bounds,
1903
+ candidate set, the obstacles that carved the strip (with owning annotation
1904
+ names), the free segments, and each candidate's outcome (placed /
1905
+ dropped-with-reason / deduped / promoted / deferred-to-post-drain) — and
1906
+ `pass_events` — the standalone strip passes plus the *immediate* placers
1907
+ (post-drain machined-feature leader callouts, the turned diameter row/column
1908
+ and step-length set-solves), each with per-item placed/dropped outcomes. So
1909
+ "why did X drop" is one `jq` query (`.solves[].outcomes[]` /
1910
+ `.pass_events[].items[]`). A `placement_unsatisfiable` strip-full drop now
1911
+ also names the top occupants in its lint message (`…strip full; occupied
1912
+ by: …`). Recording-only: an unwritable path logs a warning (never aborts a
1913
+ build), writes are atomic, and the recorder joins `finalize()`'s #647
1914
+ rollback. Default off; zero output change, nil cost when off.
1915
+
1916
+ - **`draftwright.model.authored_dimension`** (#704): the IR constructor behind
1917
+ `Sheet.dimension()`, extracted so `build_drawing(model=…)` callers can author
1918
+ a pre-measured drafting dimension without the `Sheet` façade.
1919
+
1920
+ ### Added
1921
+
1922
+ - **Object-reference script workflow: docs + an inline tip** (#770/#771): a
1923
+ generated `Sheet` script from a **live-source** input (a `module:attr` /
1924
+ `file.py:attr` object spec, or a build123d object) now carries an inline tip
1925
+ showing how to swap each detected-numbers line for a reference to your object
1926
+ (ADR 0011 declare — the size is read off the object). A STEP-sourced script is
1927
+ unchanged (byte-stable — no object to reference). The README documents the full
1928
+ from-a-part-to-an-object-referenced-script workflow.
1929
+ - **Knurl callout verb** (#765): `sheet.diameter(shaft).knurl("0.8")` →
1930
+ `KNURL 0.8 STRAIGHT` (or `.knurl("0.8", "DIAMOND")`). Named sugar over
1931
+ `.note()` with canonical formatting — a knurl is a text callout on a leader,
1932
+ not modelled geometry, so it flows through the existing note path (no new
1933
+ IR/render).
1934
+ - **Standing ISO 7200 title-block fields** (#766): `material`, `date`, `revision`,
1935
+ and `company` (legal owner) are now first-class — settable on `build_drawing` /
1936
+ `make_drawing`, the `Sheet(...)` constructor, and the CLI (`--material` /
1937
+ `--date` / `--revision` / `--company`), and emitted into the generated `Sheet`
1938
+ script when non-default so a re-run reproduces them. Defaults preserve the prior
1939
+ output (revision `A`, the rest blank). The `revision`/`legal_owner` cells the
1940
+ renderer previously hardcoded now flow from these. (The imperative `--script`
1941
+ flavour's title-block round-trip of these fields is a follow-up.)
1942
+ - **First-class thread/tap callout on a hole** (#764): ``sheet.hole(...).thread("M3x0.5")``
1943
+ (and ``declare.hole(..., thread=)``) folds a tap/thread spec onto the hole's existing
1944
+ compound leader (e.g. ``ø2.5 THRU M3x0.5``) — a structured aspect that round-trips, so
1945
+ ``.thread(...).finish(...)`` yields Ra-on-thread. A declaration-only aspect (ADR 0011
1946
+ side-layer: threads are cosmetic, not modelled geometry — no recogniser). The layout
1947
+ width estimator (``_est_planned_bore_callout_width``) now accounts for the thread text so
1948
+ the wider callout is reserved room and not dropped (the #261 estimator/render agreement).
1949
+
1950
+ ### Fixed
1951
+
1952
+ - **Generated imperative script reconstructs side-drilled hole locations**
1953
+ (#426/#133): an X/Y-axis (side-drilled) bore gets `dim_loc_*` position dims in
1954
+ the direct build, but the `--script` emitter emitted a gap comment instead of a
1955
+ `locate()` for non-Z holes (because `locate()`/`render_locations` are Z-plan
1956
+ only, #133). The emitter now emits `dwg.locate(f)` for holes on any axis;
1957
+ `finalize()` routes by the feature's axis — Z-plan holes drain through
1958
+ `render_locations` as before, side-drilled bores through the whole-model
1959
+ `_locate_off_axis_holes` pass (registered at the auto-pass's own
1960
+ `off_axis_across`/`off_axis_along` stages, placed at the shared drain). The
1961
+ Z-only `locate()` live contract is untouched (side-drilled locates never reach
1962
+ it — they route to the off-axis stages). Closes the last direct-vs-generated
1963
+ script parity gap tracked by #426.
1964
+ - **Generated imperative script reconstructs rotational furniture** (#426/#424):
1965
+ a turned/cylindrical part's `dwg.model()` carries a `RotationalFeature`, but the
1966
+ `--script` emitter parked it in the gap-comment list, so an executed
1967
+ reconstruction of e.g. a plain cylinder was missing `dim_od` and the
1968
+ `centerline_front`/`centerline_side` marks the direct build draws. A new
1969
+ `Drawing.rotational(feature)` add-verb records the intent, and `finalize()`
1970
+ drains it through the shared whole-model `render_rotational` at the auto-pass's
1971
+ own `"rotational"` stage — byte-identical to the direct build (no `only=` subset,
1972
+ fixed literal names, so no naming-seam or `⊇`-vs-`==` gap). The emitter now emits
1973
+ `dwg.rotational(f)` for rotational features. Closes the rotational half of the
1974
+ direct-vs-generated-script convergence.
1975
+ - **Emitted `Sheet` script now faithfully reproduces the direct CLI drawing**
1976
+ (#707): the divergence reported against 0.3.3 on the Maquetto GRM-03 part was
1977
+ closed by #709 (script `--format` forwarding) + #661 (finalize detail drain).
1978
+ Locked with a vendored-fixture regression that asserts the full invariant the
1979
+ issue named — same views, annotation inventory, page, scale **and** lint
1980
+ between `build_drawing(...)` and the executed emitted `Sheet` — not just the
1981
+ annotation signature the synthetic parity cases check.
1982
+ - **`_largest_empty_rect` no longer blows up on crowded detail views** (#661):
1983
+ the largest-empty-rectangle placer enumerated every candidate rectangle
1984
+ (O(N⁴) in the coordinate-cut count) — fine for the iso view's handful of
1985
+ obstacles, but the detail-view placer feeds it *every* placed-annotation
1986
+ footprint, so a two-detail turned part reached ~70 obstacles and spent ~13 s
1987
+ in a single call (multi-minute on the slower Windows CI runners, which tipped
1988
+ some jobs over their wall-clock budget). It now skips candidates that provably
1989
+ can't beat the best found so far (a `bisect` past every too-narrow pair, an
1990
+ early `break` once even the widest is too small): the same exact result and
1991
+ tie-break — verified identical to the naïve search across 3 000 random cases —
1992
+ at ~85× the speed on the pathological input (12.6 s → 0.15 s).
1993
+ - **Detail-view height demotion is pin- and provenance-safe** (#661, user
1994
+ review): when a crowded prismatic detail needs the overall-height dim's room it
1995
+ is demoted and the detail retried, but (a) the canonical `dim_height` name was
1996
+ handed to that retry unconditionally — a *pinned* or user-replaced height could
1997
+ be removed; it now passes the same pin/label guards as the generalised
1998
+ `dim_length{n}` path (a pin, ADR 0012, is never demoted), and (b) if the retry
1999
+ also failed, the restore re-added only object/name/view, orphaning the dim from
2000
+ its envelope feature (lost `annotations_of`/`drop`/re-discovery); it now
2001
+ snapshots and restores the feature provenance and pin too.
2002
+ - **`finalize()` resolves queued detail requests — detail views now exist on the
2003
+ edit path** (#661): the finalize drain was missing the auto pass's
2004
+ `detail_request`/`details` stages, so a crowded turned head's queued
2005
+ `DetailRequest` died with the per-run context and a prismatic
2006
+ `detail_view=True` build never even queued one — generated scripts silently
2007
+ lost every DETAIL A/B view. `build_drawing(detail_view=…)` is now persisted on
2008
+ the build state so the drain gates the prismatic request exactly as the auto
2009
+ pass does; the details stage re-projects the iso at sheet scale for the
2010
+ free-rectangle search and refits it after (mirroring the auto pass's ordering,
2011
+ where details place before the iso is fitted); and the overall-height demotion
2012
+ retry now finds the height dim by its envelope attribution, not only the
2013
+ auto-pass `dim_height` name. Three script-parity characterisation tests
2014
+ (single detail, two details, turned head) flip from xfail to passing.
2015
+ - **Y-turned stepped shafts no longer crash** (#661): `render_step_lengths`
2016
+ projected every step span into the front view, where a Y-axis span is end-on
2017
+ (a point) — `Dimension` then raised "start and end points must be different"
2018
+ on BOTH the direct build and a generated script's finalize. Y-axis steps are
2019
+ now skipped (no step render pipeline consumes Y, #731 — mirroring
2020
+ `render_diameters`' x/z bucketing), the emitter flags the gap as a comment
2021
+ instead of emitting an unreplayable `dimension()` verb, and a hand-written
2022
+ Y step-length intent live-replays (surfacing the verb's own behaviour) rather
2023
+ than vanishing into the routed no-op. The Y-axis script-parity
2024
+ characterisation test flips from xfail to passing.
2025
+ - **Boss heights are modeled, rendered, and coverage-checked** (#632): detected
2026
+ and object-declared cylindrical bosses now carry their axial extent through
2027
+ `BossFeature` and the dimension planner. Prismatic bosses receive a linear
2028
+ height dimension in a profile view in addition to their end-on diameter
2029
+ leader. `Drawing.lint()` reconciles that modeled height against the live
2030
+ feature-owned `Dimension`, reporting `boss_height_missing` if it is removed
2031
+ or otherwise absent (demoted to `info` on assembly drawings). This closes the
2032
+ declarative false-negative where a boss's diameter and the overall envelope
2033
+ were documented but its own independent height was not.
2034
+
2035
+ - **`--format` now reaches `--script` output** (#709, from the #702 adversarial
2036
+ review): `--script -f svg` used to silently emit a PDF-producing script — the
2037
+ CLI parsed the flag but never forwarded it. Both emitters now thread it
2038
+ through: the Sheet script emits `sheet.export(stem, formats=(…,))` when
2039
+ non-default (bare call for the PDF default), the imperative template embeds
2040
+ the requested tuple and prints in the **requested** order, and
2041
+ `generate_script` / `generate_sheet_script` grow a `formats=` parameter. The
2042
+ dormant script-parity characterisation module
2043
+ (`test_script_detail_parity.py`) is also un-skipped so its strict xfails give
2044
+ the remaining gaps CI signal.
2045
+
2046
+ - **Authored fillet, flat, groove, pocket, plate and slot tolerances now render**
2047
+ (#725/#726/#727/#728/#729/#730 / #698): the four leader-callout kinds join
2048
+ `_CONVENTION` and their renderers (`render_fillets`/`render_flats`/
2049
+ `render_grooves`/`render_pockets`) — and the plate-thickness and slot
2050
+ width/length linear passes
2051
+ (`render_plates`/`render_slots`, explicit `"linear"` entries) — consume the
2052
+ planner's `DimensionGroup`s, binding each
2053
+ planned dim explicitly by `(role, kind)` — so a `decorations`-authored ± /
2054
+ limit tolerance reaches the placed callout (`R8 ±0.1`, `17 ±0.2 A/F`,
2055
+ `4 ±0.1 WIDE × ø16 ±0.5`, `18 ±0.2 × 30 ±0.2 × 5 ±0.2 DEEP`, a `10 ±0.1`
2056
+ thickness dim, an `8 ±0.1` slot width; on a
2057
+ multi-value label each suffix rides its own number). Previously all six
2058
+ passes formatted raw feature fields and silently dropped it — the latent #629
2059
+ bug class the ADR 0015 bypass list documents. The fillet `n× R` and flat
2060
+ double-D/hex collapses stay render-side (first-authored tolerance wins, the
2061
+ `render_diameters` precedent); a pocket's three values — and a slot's
2062
+ width + length — share one authored
2063
+ tolerance (all kind `"length"`). The slot's model-derived datum position dim
2064
+ and its corridor/immediate placement mechanics are untouched. Untolerated
2065
+ labels/views/placement are unchanged.
2066
+ - **Authored chamfer tolerances now render** (#724 / #698): `render_chamfers`
2067
+ consumes the planner's `DimensionGroup` (chamfer joins `_CONVENTION` as a
2068
+ leader), so a `decorations`-authored ± / limit tolerance on a chamfer leg
2069
+ reaches the placed callout (`C12 ±0.2`). Previously the pass formatted raw
2070
+ feature fields and silently dropped it — the latent #629 bug class the ADR
2071
+ 0015 bypass list documents. Untolerated chamfer labels/views are unchanged.
2072
+ - **`model.chamfer(face=…)` rejects a non-planar face** (#704): the declared
2073
+ front-end silently accepted a curved face (`normal_at()` does not fail on
2074
+ one) and read garbage legs off it — e.g. a fillet or countersink face became
2075
+ a bogus `ChamferFeature`. It now raises the documented `ValueError`, using
2076
+ the recogniser's own planarity gate (`classify_bevel`), so declared and
2077
+ detected chamfers classify identically by construction.
2078
+
2079
+ ### Fixed
2080
+
2081
+ - **CTC-02/04 no longer drop step-height / overall-height dims** (#733): when
2082
+ the height ladder joined the corridor solve (#689) it silently moved from
2083
+ "places early" to "registers early, places at the drain" — so the immediate
2084
+ machined-feature leader callouts (pockets on CTC-04) filled the front-right
2085
+ strip first and the FORCED principal dims hard-dropped
2086
+ (`placement_unsatisfiable`), a priority inversion the old stage ordering had
2087
+ prevented implicitly. The leader-callout stages (chamfers/fillets/flats/
2088
+ pockets) now sit **after the corridor drain** in `_PASS_SEQUENCE` (joining
2089
+ grooves, which already placed post-drain for exactly this reason): principal
2090
+ dims win by construction, and a callout with no clear room yields with a
2091
+ warning, never a principal-dim error. Callout leaders may shift (they now
2092
+ route around the drained dims); on the `hex_bar` golden this *recovers* a
2093
+ silently starved `m_env_width` envelope dim.
2094
+
2095
+ ### Removed
2096
+
2097
+ - **Three orphaned root exports** (#704): `draftwright.recognise_face_levels`
2098
+ (one recogniser of ~12 — incoherent as a top-level surface; import it from
2099
+ `draftwright.recognition`), `draftwright.dedup_diams` (an analysis internal),
2100
+ and `draftwright.fix_svg_page_size` (still available from `draftwright.export`
2101
+ and the `draftwright.make_drawing` facade). None had any internal or test
2102
+ consumer via the root API.
2103
+
2104
+ ### Deprecated
2105
+
2106
+ - **The seven `Drawing` compat aliases** (`_named`/`_anno_view`/`_pinned`/
2107
+ `_build_issues`/`_pattern_callouts`/`_patterned_holes`/
2108
+ `_dropped_callout_diams`) are formally on notice (#699 slice c, tracked by
2109
+ #720): removal target **0.4.0**, per ADR 0005 §4. Read through the public
2110
+ surface instead — `annotations()`, `iter_annotations()`, `get_annotation()`,
2111
+ `view_of()`, `registry.pinned_names()`/`is_pinned()`, and the new
2112
+ `registry.issues`. All external production and test call sites in this repo
2113
+ are already redirected (`drawing.py` internals ride the aliases until the
2114
+ #720 deletion).
2115
+ - **`draftwright.sheet_dsl`** (belated announcement — the alias shim shipped
2116
+ in 0.3.1 when #640 renamed the module to `draftwright.sheet`): importing it
2117
+ warns; it will be removed in **0.4.0**. Import from `draftwright.sheet`, or
2118
+ just `from draftwright import Sheet`.
2119
+
2120
+ ## v0.3.3 — 2026-07-18
2121
+
2122
+ The **layout-quality release**: the occupancy model gets honest about ink shape,
2123
+ the last solver-invisible pass joins the corridor solve, and crossed labels shift
2124
+ themselves clear. Also completes the #635 consolidation epic (ADR 0005 fully
2125
+ executed; ADR 0009's no-invisible-occupant guarantee now holds by construction
2126
+ for every auto-pass).
2127
+
2128
+ ### Added
2129
+
2130
+ - **Witness-crossing label shifts** (#690): a dimension label crossed by another
2131
+ dimension's witness stroke now shifts along its own line to clear it — a
2132
+ deterministic post-drain pass using the repair machinery (destination-aware,
2133
+ axis-aligned only, pinned dims exempt).
2134
+
2135
+ ### Changed
2136
+
2137
+ - **L-shaped occupancy** (#685): placement obstacles decompose into per-stroke
2138
+ boxes + label box (helpers ≥0.14 `segments`) instead of one hull, with
2139
+ preset-aware arrowhead pads. View-corner contention between perpendicular
2140
+ strips disappears by construction (a plate-thickness dim returns to its
2141
+ natural strip); packing tightens. The cleanliness ratchet mirrors the model —
2142
+ only transverse stroke crossings are exempt, so parallel overprints and label
2143
+ contacts are detected honestly (twelve hull-artifact allowlist entries burned
2144
+ down; the legitimate shared-datum running-dimension pairs are documented).
2145
+ - **Height ladder joins the corridor solve** (#636): step heights + overall
2146
+ height are corridor candidates (the leapfrog witness cursor survives as a
2147
+ deterministic build-time chain; the overall height stacks outermost by
2148
+ construction and packs tighter). An explicitly-requested crowded-step detail
2149
+ view now *deliberately* demotes the overall-height dim when the sheet cannot
2150
+ hold both (previously the carve dropped it silently); a Y-drilled hole's Z
2151
+ location joins the front running ladder per the documented routing.
2152
+ - **One typed `BuildState`** (#639): the drawing's build context (analysis,
2153
+ part model, lint geometry caches) consolidates into a single object filled at
2154
+ one builder site, single-writer-guarded; the render passes make zero private
2155
+ `Drawing` reads (fail-closed empty allowlist).
2156
+
2157
+ ### Fixed
2158
+
2159
+ - A prismatic detail view no longer loses both the detail *and* the height dim
2160
+ when space is tight (transactional demotion).
2161
+ - The GD&T alternate-strip fallback defers until every corridor has drained, so
2162
+ it can never preempt a sibling strip's reserved corner.
2163
+
2164
+ ## v0.3.2 — 2026-07-18
2165
+
2166
+ The **performance release**: the #602 perf loop (six PRs) plus the
2167
+ build123d-drafting-helpers 0.14 upgrade. A dimension-dense drawing now builds
2168
+ roughly **14× faster** than v0.3.1 (grid-plate benchmark 40 s → 2.8 s; NIST
2169
+ CTC-02 ~107 s → well under half); the full test tier dropped ~14 min → ~3.5 min.
2170
+
2171
+ ### Performance
2172
+
2173
+ - **Placement measures, then builds** (#678, #679): corridor/strip candidates are
2174
+ evaluated on analytical footprints (`dim_footprint`) or a single probe box; an
2175
+ accepted candidate builds its OCC geometry exactly once and re-validates the
2176
+ real box. Dimension constructions on the scattered-plate benchmark: 59 → 21
2177
+ (= the placed count).
2178
+ - **Detect once** (#682): the sizing model `_analyse` builds pre-scale is stored
2179
+ on `Analysis.model` and reused as the render model — restoring ADR 0008
2180
+ Amendment 5 ("one inventory, detected once"). ~13 s off CTC-02.
2181
+ - **Fillet recognition de-quadratised** (#683): edge→faces adjacency map replaces
2182
+ the O(faces²×edges²) `IsSame` sweep (3.7 M calls on CTC-02); 10.8 s → 4.6 s,
2183
+ byte-identical records.
2184
+ - **Lint bounding boxes memoised** (#681): `Drawing.lint()` 0.88 s → 0.07 s per
2185
+ call (identity+location-token-checked cache beside the #143 view-edge cache).
2186
+ - **DXF viewport from the known page window** (#680): skips `ezdxf.zoom.extents`'
2187
+ per-entity walk; ~2.4 s per DXF export.
2188
+ - **helpers 0.14** (#684): boolean-free dimension ink (upstream #177, filed and
2189
+ fixed same-day) — ~25 ms per `Dimension`, was ~234 ms.
2190
+
2191
+ ### Changed
2192
+
2193
+ - **Tight-span dimensions now render honestly** (helpers 0.14): ISO outside
2194
+ arrows where 0.13 silently degraded to a bare line. Ink extents grow on such
2195
+ dims; placement models the flip analytically. Two placement-golden fixtures
2196
+ re-blessed (ink extent only).
2197
+ - Diameter-row leader labels space by **measured text width** and repair
2198
+ helpers' shelf-flip collisions (crowded rows only; ordinary rows unchanged).
2199
+ - A plate-thickness dim whose home strip corner is structurally contested falls
2200
+ through to the opposite strip / side view instead of dropping (#684; the
2201
+ underlying one-box-occupancy artifact is tracked as #685).
2202
+
2203
+ ### Fixed
2204
+
2205
+ - `recognise_fillets` neighbour tie-break is order-independent (#683 review).
2206
+ - Lint's annotation-box cache prunes departed objects and detects in-place
2207
+ relocation (#681 review).
2208
+
2209
+ ## v0.3.1 — 2026-07-17
2210
+
2211
+ Also lands the **#635 consolidation epic** (P1–P6, all six children closed): the render
2212
+ layer's state bus is retired (`annotations/` reads zero `Drawing` privates,
2213
+ machine-enforced), the three complexity hotspots are split (nesting ≤2), the whole-package
2214
+ import DAG + a test-import ratchet are enforced, and the test suite gains a Hypothesis fuzz
2215
+ tier + relational cross-kernel placement gates. All internal / behaviour-preserving
2216
+ (byte-identical golden) — no output change.
2217
+
2218
+ ### Added
2219
+
2220
+ - **PNG raster export** — `--format png` on the CLI, or `Drawing.export(formats=("png",))`.
2221
+ Rendered SVG → PDF → PNG via **pypdfium2** (Apache-2.0, Google PDFium BSD-3) + **Pillow**
2222
+ (HPND) — both permissively licensed and pure-wheel, so PNG works cross-platform with **no
2223
+ native cairo** (ADR 0006) and keeps the render path dual-license-friendly. `dpi=` sets the
2224
+ raster resolution.
2225
+
2226
+ ### Changed
2227
+
2228
+ - **Unified export API** — `Drawing.export(out, *, formats=("pdf",)) → {format: path}` is now
2229
+ the single entry point over `svg`/`dxf`/`pdf`/`png`; it returns a dict and internally handles
2230
+ the SVG→PDF→PNG intermediate chain (writing/cleaning up intermediates it wasn't asked to keep).
2231
+ The CLI `--format` gains `png`.
2232
+
2233
+ ### Deprecated
2234
+
2235
+ - `Drawing.export_pdf()` — use `export(formats=("pdf",))["pdf"]`; the method now warns.
2236
+ - The legacy `export(svg=, dxf=)` boolean keywords and their `(svg_path, dxf_path)` tuple return
2237
+ — kept for back-compat; prefer `formats=[...]` (the dict API).
2238
+
2239
+ ## v0.3.0 — 2026-07-14
2240
+
2241
+ **Breaking — feature-recogniser API renamed** (ADR 0013 / #568): *deliberate* breaks with
2242
+ **no compatibility aliases**, hence the minor bump. Only import paths and callable
2243
+ signatures change — but note this release also **changes drawing output**: new machined-
2244
+ feature callouts, corrected leader anchors, and a reoriented isometric (see Added / Fixed).
2245
+
2246
+ ### Added
2247
+
2248
+ - **Machined-feature recognition — the #148 epic.** #135 deliberately handled only enclosed
2249
+ rectangular through-slots; this broadens recognition, and each feature is recognised **and**
2250
+ dimensioned **and** authorable (recognise + emit + declare, ADR 0011):
2251
+ - **Blind slots + pockets** — floored recesses, `W × L × D DEEP` (#603).
2252
+ - **Cross / intersecting slots** collapse into single continuous channels (#604).
2253
+ - **Machined flats** on round stock — `{across} A/F` (#605).
2254
+ - **Turned / circlip grooves** — `{width} WIDE × ø{floor}` (#606).
2255
+ - **Slots / pockets in curved stock** — arc-bounded walls (#607).
2256
+ - **Radiused-end (obround) slots** now report their **overall** length (#613).
2257
+ - **Chamfer callouts** — `C{leg}` / `{leg}×{angle}°` off each bevel (#560).
2258
+ - **Fillet radius callouts** — `R{radius}` off each rounded edge (#561).
2259
+ - **Plate / wall thickness** dimensions on multi-plate prismatics (#559).
2260
+ - **Prismatic step-position** dimensions locating each shoulder (#555).
2261
+ - **Countersink callouts** — `ø6 THRU ⌵ ø14 × 90°` (major-ø + included angle), like the
2262
+ counterbore `⊔ ø.. ↧..` (#558). The countersink rides on `HoleRecord`/`HoleFeature`, so
2263
+ grouping and the callout-width layout estimate account for it.
2264
+ - **Every recognised feature round-trips all three ADR-0011 surfaces (#574).** Beyond
2265
+ detection, each feature now **emits** into the generated `--script` Sheet and is
2266
+ **declarable** on the fluent `Sheet` surface — authored from scratch or read off a
2267
+ build123d object: chamfer (#576), countersink (#575), plate (#577), step-position (#585),
2268
+ fillet (#561), plus all the #148 features above.
2269
+ - **`draftwright.score.feature_census(part)`** — a per-kind recognised-feature census, a
2270
+ measurement tool for tracking recognition coverage (#608).
2271
+
2272
+ ### Changed
2273
+
2274
+ - **Feature recognisers renamed `find_`/`analyse_` → `recognise_`** and their
2275
+ tuning/dependencies made keyword-only: `find_holes`→`recognise_holes`,
2276
+ `find_bosses`→`recognise_bosses`, `find_hole_patterns`→`recognise_hole_patterns`,
2277
+ `find_slots`→`recognise_slots`, `find_plates`→`recognise_plates`,
2278
+ `find_chamfers`→`recognise_chamfers`, `find_turned_steps`→`recognise_turned_steps`,
2279
+ `find_step_shoulders`→`recognise_step_shoulders`, `analyse_face_levels`→
2280
+ `recognise_face_levels` (the last was also re-exported at top level as
2281
+ `draftwright.analyse_face_levels` → now `draftwright.recognise_face_levels`).
2282
+ Cylinder-analysis substrate (`analyse_cylinders`, `full_cylinders`,
2283
+ `feature_diameters`) keeps its names.
2284
+ - **`recognise_step_shoulders` now returns `list[StepShoulder]`** (a frozen dataclass)
2285
+ instead of raw `(axis, position)` tuples; `levels` is keyword-only.
2286
+ - **`recognise_turned_steps` now returns `list[TurnedStep]`** (empty for a non-turned
2287
+ part) instead of `TurnedProfile | None`. `TurnedStep` gained an `axis` field (each step
2288
+ is now a self-contained record); `TurnedProfile` remains only as a pipeline aggregate
2289
+ (`TurnedProfile.from_steps(steps)`), no longer a recogniser return.
2290
+ - **`recognise_face_levels` now returns `list[FaceLevel]`** (a frozen `FaceLevel(z)`
2291
+ record) instead of `list[float]`.
2292
+ - **Recognition record classes renamed to avoid clashing with the IR `Feature` types:**
2293
+ `draftwright.recognition.HoleFeature` → `HoleRecord`, `BossFeature` → `BossRecord`
2294
+ (the IR `draftwright.model.ir.HoleFeature`/`BossFeature` are unchanged).
2295
+ - **Page/scale sizing now reads the IR, not recognition records (ADR 0008 / #588).** The
2296
+ part model is built up front and drives sheet sizing, so detected and declared parts share
2297
+ one path and no recogniser record reaches the sheet estimators. Byte-identical to the old
2298
+ estimators except a hole pattern that shares a machining spec with loose holes now sizes
2299
+ separately (no phantom merged `N×`), which can shift a tightly-packed such part's layout.
2300
+
2301
+ ### Fixed
2302
+
2303
+ - **Isometric view orientation (#620).** The iso camera viewed the *rear* (+Y) side against a
2304
+ front-view orthographic set, mirroring asymmetric features; it now views from the same
2305
+ front (−Y) / plan (+Z) / right (+X) combination.
2306
+ - **Chamfer leaders (#621)** anchor on the bevel-face centroid (the diagonal middle), not the
2307
+ supporting plane's parametric origin (which projected to a corner/endpoint).
2308
+ - **Fillet leaders (#622)** anchor on the radius surface, not the face bounding-box centre
2309
+ (which sat off the round, near the virtual sharp corner).
2310
+ - **Authored `ref_pts`-only dimensions** now render (with Z-strip sizing) (#562).
2311
+ - **Pitch-dim labels** are cleared off centrelines / bolt circles at creation time, not via
2312
+ the repair loop (#129).
2313
+
2314
+ ### Removed
2315
+
2316
+ - **`draftwright.features` compatibility module** — deleted. Import `recognise_slots`/
2317
+ `Slot` from `draftwright.recognition` instead.
2318
+
2319
+ ## v0.2.13 — 2026-07-09
2320
+
2321
+ **Compose-then-pack is now the layout authority.** This patch finishes the
2322
+ ViewBlock footprint migration: estimated and measured layout paths now use the
2323
+ same composed block model, furniture declarations reserve their real footprint,
2324
+ and measure/repack iterates until stable instead of trusting one measured pass.
2325
+
2326
+ ### Changed
2327
+
2328
+ - **ViewBlock footprints now drive the orthographic stack.** The old
2329
+ estimator-only plan-view balloon lift is gone; plan balloon headroom is part
2330
+ of the plan block in both estimated and measured/repack layout. (#112)
2331
+ - **Measure/repack iterates to a fixed point.** Repack now keeps measuring and
2332
+ repacking while view-owned footprints continue to grow, with bounded
2333
+ convergence and tests for multi-pass growth. (#302)
2334
+ - **Estimated view-block composition is explicit.** Scale/page fitness, repack,
2335
+ and furniture placement share the same composed block semantics instead of
2336
+ rebuilding corridor estimates independently.
2337
+
2338
+ ### Fixed
2339
+
2340
+ - **Declared callout furniture reserves layout footprint.** User-declared
2341
+ furniture is now measured and fed into the layout model, reducing post-hoc
2342
+ collisions from declarations that previously existed only at render time.
2343
+ - **Annotation-box composition owns strip sizing.** Strip depths reduce from
2344
+ composed annotation boxes, making footprint sizing less dependent on divergent
2345
+ scalar estimates.
2346
+
2347
+ ### Documentation
2348
+
2349
+ - **Layout architecture docs reflect the current state.** ADR 0004 and the
2350
+ layout primer now mark the core layout-authority tranche complete and separate
2351
+ remaining work into hardening, coverage, detail-view, and manual-intent phases.
2352
+
2353
+ ## v0.2.12 — 2026-07-09
2354
+
2355
+ **Sheet scripts now round-trip authored layout intent.** This patch finishes the next
2356
+ slice of the layout-authority work: below/right ladders, pinned edits, height ladders,
2357
+ detected envelopes, and AP242 authored dimensions now survive the declarative Sheet path
2358
+ without being invalidated by post-hoc placement or raw imported metadata.
2359
+
2360
+ ### Added
2361
+
2362
+ - **Pinned edit intents route through the corridor solve.** `locate(..., pin=True)` and
2363
+ pinned dimensions now become priority-ranked candidates in the shared corridor instead
2364
+ of fixed post-hoc edits, so user intent participates in the same ordering/spacing model
2365
+ as automatic dimensions. (#511)
2366
+ - **AP242 dimensional PMI lowers to authored drafting dimensions.** Imported AP242
2367
+ size/location dimensions now become `AuthoredDimension` IR and generated Sheet scripts
2368
+ emit `sheet.dimension(...)`; unsupported PMI records remain explicit raw fallbacks.
2369
+ (#503, #422, #62)
2370
+
2371
+ ### Changed
2372
+
2373
+ - **`place_dim` is deprecated.** Manual dimensions should use pinned candidate/dimension
2374
+ intent instead of the old incremental edit path.
2375
+ - **Prismatic height ladders and detected envelopes round-trip through Sheet scripts.**
2376
+ Generated scripts now preserve `StepLevelFeature` and detected `EnvelopeFeature`
2377
+ fallbacks, preventing CTC01-style ladder swaps and raw STEP-envelope remeasurement
2378
+ drift.
2379
+
2380
+ ### Fixed
2381
+
2382
+ - **Below/right corridors now share one solve.** Side-hole locations, height ladders,
2383
+ PMI/GD&T, envelope dims, and related below/right strip occupants negotiate in the shared
2384
+ corridor rather than competing through separate late passes. (#477)
2385
+ - **Sheet script parity is tighter.** Generated scripts preserve member positions, step
2386
+ levels, AP242 authored dimensions, raw PMI fallbacks, and detected envelopes so
2387
+ direct-vs-script CTC01 output has matching annotation names.
2388
+
2389
+ ## v0.2.11 — 2026-07-08
2390
+
2391
+ **Automatic layout now has one authority.** Page/scale selection, section placement,
2392
+ furniture footprints, and table/balloon escalation now negotiate through the same layout
2393
+ fitness model instead of using separate fixed offsets or first-fit policies after the main
2394
+ solve.
2395
+
2396
+ ### Fixed
2397
+
2398
+ - **Solver placement paths are more consistent.** STEP PMI dimensions now queue into
2399
+ the shared ADR 0009 corridor solve instead of carving after the drain; front-view
2400
+ hole callouts use the strip solver instead of fixed row stepping; pitch-dimension
2401
+ fallback searches bounded, obstacle-aware positions; repair no longer hides
2402
+ `annotation_overlap` with a fixed-step nudge; and step-count sizing now handles
2403
+ non-convergence conservatively. (#524)
2404
+ - **Page/scale fitness is shared across initial selection and repack.** Later layout
2405
+ passes now compare candidates with the same model as the page chooser, preventing a
2406
+ nominally "better" repack from invalidating the original layout decision. (#519)
2407
+ - **Section A-A participates in layout selection.** Section placement is measured as
2408
+ part of page/scale fitness, so a section view is no longer a fixed-offset afterthought
2409
+ that can disappear on dense sheets. (#515)
2410
+ - **Furniture placement reserves full rendered footprints.** Section arrows, detail
2411
+ views, balloons, hole/data tables, and other furniture now reserve/check their true
2412
+ rendered footprint instead of just their labels. (#518)
2413
+ - **Hole/data tables and balloon rings escalate through the layout model.** Dense table
2414
+ and balloon outputs can move or negotiate for available room rather than dropping from
2415
+ greedy first-fit placement when a small adjustment would fit. (#516, #517)
2416
+
2417
+ ## v0.2.10 — 2026-07-07
2418
+
2419
+ **Declarative-surface fidelity and layout-engine unification.** Editing and re-running a
2420
+ declarative `Sheet` script now reproduces more of the drawing (title block, layout, PMI on the
2421
+ declared path), a new lint catches stale declarations, and an intentional scale is respected.
2422
+ Internally the annotation placer moves further onto the single collect-then-solve (ADR 0009),
2423
+ and `kiwisolver` is dropped as a dependency.
2424
+
2425
+ ### Added
2426
+
2427
+ - **Declaration-vs-geometry reconciliation lint** — `lint()` now flags a declared cylindrical
2428
+ feature (hole/boss/step) with **no matching geometry** in the part (`declared_feature_absent`):
2429
+ a callout drawn over empty space because the part was edited but the declaration went stale.
2430
+ Closes the last gap in the "did my edit break the drawing?" loop (over-declaration; coverage
2431
+ lint already caught under-declaration). Matches on axis + ⌀ + in-plane position + bore/boss
2432
+ polarity. Gated on a caller-supplied `model=` (detection can't over-declare). (#487)
2433
+ - **Title-block + layout aspects on the `Sheet` DSL** — `Sheet(drawn_by=…, tolerance=…, scale=…,
2434
+ page=…)`; the generated `--script` reproduces them, and the CLI forwards
2435
+ `--drawn-by`/`--tolerance`/`--scale`/`--page` on the sheet path (no more inert-flag warning). (#474)
2436
+ - **PMI reproduced on the declared-model path** — `build_drawing(path, model=…, pmi="annotate")`
2437
+ synthesises the STEP AP242 PMI into the declared model, so a declared build draws the same PMI
2438
+ dimensions as the detection path. (#472)
2439
+
2440
+ ### Changed
2441
+
2442
+ - **An intentional explicit `scale=` below the legibility floor is honoured with a warning**
2443
+ rather than rejected. A part deliberately drawn at 1:1 (or `Sheet(scale="1:10")`) whose smallest
2444
+ feature falls under the 10 mm legibility floor now renders (annotations may crowd) instead of
2445
+ raising `ValueError`; only a genuinely degenerate scale (< 0.1 mm projected, where OCCT arcs
2446
+ collapse) is rejected. The floor now binds only the *automatic* scale. (#489)
2447
+ - **`kiwisolver` is no longer a dependency.** The 1-D strip solve delegates to the built-in
2448
+ deterministic minimum-total-leader-length PAVA algorithm, which supersedes the Cassowary
2449
+ solver — same placement contract, one fewer third-party dependency. (#507)
2450
+
2451
+ ### Fixed
2452
+
2453
+ - **Dimension placement is more robust under contention.** Candidate **priority** is plumbed
2454
+ through the shared corridor solve, so an authored GD&T frame is kept over a lower-value
2455
+ automatic dimension when a strip is over capacity (#357). Rotational concentric-bore leaders
2456
+ are now bounded to the front-view height with ranked drops, and pitch dimensions are placed
2457
+ onto the obstacle-aware per-side zone-strip solve — retiring the last fixed-offset,
2458
+ count-based stacking placers (the shape behind earlier dense-sheet overruns). (#374)
2459
+
2460
+ ## v0.2.9 — 2026-07-06
2461
+
2462
+ **Declarative GD&T, datums, and surface finish (ADR 0011 Phase 2b/2c).** The `Sheet` DSL can
2463
+ now author the drawing information geometry can't carry — geometric tolerances, datum feature
2464
+ symbols, and surface-finish marks — by pointing at a build123d feature or face. They render
2465
+ through the auto-layout engine as first-class ADR 0009 corridor candidates, placed and spaced
2466
+ crossing-free alongside the automatic dimensions (not a post-hoc overlay).
2467
+
2468
+ ```python
2469
+ sheet.datum("A", base_face)
2470
+ sheet.control(bore).position(0.1, to="A", diameter=True).perpendicularity(0.05, to="A")
2471
+ sheet.diameter(journal).finish("0.8")
2472
+ ```
2473
+
2474
+ ### Added
2475
+
2476
+ - **Feature control frames (ISO 1101)** — `sheet.control(ref)` returns a chainable builder with
2477
+ one method per **all 14** characteristics (`position`, `flatness`, `perpendicularity`,
2478
+ `cylindricity`, `circular_runout`, `total_runout`, …); `to=` names the referenced datum(s),
2479
+ `diameter=` prefixes the ⌀ tolerance zone, `modifier=` adds a material-condition symbol (Ⓜ/Ⓛ/Ⓟ).
2480
+ - **Datum feature symbols (ISO 5459)** — `sheet.datum("A", face_or_feature)`.
2481
+ - **Surface finish (ISO 1302, Ra)** — `.finish("1.6")` on a hole / diameter / step handle, or
2482
+ `sheet.finish(ra, face)`.
2483
+ - The target view + strip side are **derived from the referenced geometry** (a feature's axis →
2484
+ face-on view, a planar face's normal → edge-on view); `view=`/`side=` override.
2485
+ - Render core: `ControlFrame` / `DatumRef` / `Finish` IR features placed by `render_gdt` as
2486
+ first-class corridor candidates, with the real-footprint plumbing needed to space wide frames.
2487
+
2488
+ ### Fixed
2489
+
2490
+ - A control frame referencing an **undeclared datum letter** now warns at build.
2491
+ - GD&T placement is **title-block aware** and **falls through to the opposite strip side** before
2492
+ dropping a frame, so stacked control frames place robustly rather than vanishing or overlapping
2493
+ the title block.
2494
+ - An invalid glyph spec / degenerate leader in a caller-supplied model **drops the one item with a
2495
+ warning** instead of crashing the whole build (the IR is public input).
2496
+ - Turned-shaft rotational furniture now reproduces correctly through the declared-model path
2497
+ (`model=` / `Sheet`) (#476).
2498
+
2499
+ ## v0.2.8 — 2026-07-06
2500
+
2501
+ **`--script` now defaults to the declarative `Sheet` DSL.** The editable script the CLI
2502
+ writes is the beautiful-Python surface — one commentable line per feature — for both STEP and
2503
+ build123d-object input.
2504
+
2505
+ ### Changed
2506
+
2507
+ - **`draftwright … --script` now emits the declarative `Sheet` script by default** (was the
2508
+ imperative edit-verb reconstruction). The imperative reconstruction is still available via
2509
+ `--script --style imperative`. For prismatic parts the generated script reproduces the same
2510
+ drawing as a direct build; a turned shaft's centre lines and base-diameter style are not yet
2511
+ identical (the remaining validity gap, tracked separately). `--style imperative` now errors
2512
+ clearly on a `module:attr` object spec (it reads a STEP file; use the default `sheet`).
2513
+ The Sheet DSL doesn't yet model the title-block / layout aspects the imperative script
2514
+ embeds, so `--script --style sheet` **warns** when `--drawn-by`, `--tolerance`, `--scale`
2515
+ or `--page` is set rather than silently ignoring it (use `--style imperative` to embed them).
2516
+
2517
+ ## v0.2.7 — 2026-07-05
2518
+
2519
+ **The three authoring modes (ADR 0011 Amendment 1).** Naming how you drive draftwright —
2520
+ *just do it* / *auto then tweak* / *generate an editable script* — and filling in the third:
2521
+ a declarative `Sheet`-DSL generator, and number-free authoring against the build123d objects
2522
+ you built.
2523
+
2524
+ ### Added
2525
+
2526
+ - **`draftwright part.step --script --style sheet`** — generate an **editable declarative
2527
+ `Sheet` script** from a detected part: one commentable line per feature (hole / diameter /
2528
+ step / slot / pattern / envelope), views noted, the auto-section flagged. Comment a line out
2529
+ to drop that feature; the script re-runs to the drawing. The existing `--script` (imperative
2530
+ edit-verb reconstruction) stays the default (#461).
2531
+ - **Object-reading `.cbore(tool)` / `.spotface(tool)`** on a declared hole — read the
2532
+ counterbore/spotface's ⌀ **and** depth off the tool object + part (depth measured from the
2533
+ local open face, so a rib/wall elsewhere doesn't skew it). A `Sheet`-declared drawing can now
2534
+ carry **zero magic numbers** and track the geometry parametrically (#462).
2535
+ - **`Sheet.of(feature)`** — a fluent handle onto an **existing / detection-seeded** feature (by
2536
+ the build123d object, an index, or the `Feature` itself), so you can `.fit(...)` /
2537
+ `.tolerance(...)` a feature from `Sheet.from_part()` without re-declaring it (#463).
2538
+
2539
+ ### Docs
2540
+
2541
+ - **ADR 0011 Amendment 1** — the three authoring modes and the mode-3 generation surface;
2542
+ records the decision to emit **honest detected numbers** for a detected part rather than
2543
+ fabricate a build123d reconstruction that misrepresents the geometry (#464).
2544
+
2545
+ ## v0.2.6 — 2026-07-05
2546
+
2547
+ **ADR 0011 — the IR as a public input: declare features, don't only detect them.**
2548
+ Detection stays the default, but the caller can now *supply* the feature model, so a
2549
+ part you built parametrically reads as its own drawing and misdetection becomes
2550
+ recoverable by construction. Plus the first Phase-2 aspect layer (tolerances + fits).
2551
+
2552
+ ### Added
2553
+
2554
+ - **`build_drawing(part, model=…)`** accepts a caller-supplied `PartModel` (or a
2555
+ `Sequence[Feature]`); when given, **detection is skipped** and the auto-pass dimensions
2556
+ the declared features. Detection and declaration are two producers of the same IR (#447,
2557
+ ADR 0011 Phase 0).
2558
+ - **Object → feature constructors** `draftwright.model.hole` / `boss` / `step` / `slot` /
2559
+ `pattern` / `envelope` — read a feature's geometry off the build123d object you built (⌀
2560
+ from the cylindrical face, axis/location from the bbox), or supply explicit values (#447).
2561
+ - **The fluent `Sheet` façade** (`draftwright.Sheet`) — reference the objects you built,
2562
+ declare their drawing aspects, `.build()` / `.export()`. `Sheet.from_part()` seeds the
2563
+ hybrid override mode from detection (#447, Phase 1).
2564
+ - **Toleranced dimensions** — a `±` / limit tolerance on a diameter, step, or hole bore,
2565
+ rendered on both the linear and ⌀-callout paths (`Sheet.tolerance(...)`, or a
2566
+ `decorations=` side-layer) (#28, Phase 2 P2a).
2567
+ - **Fit-class deviation** — `Sheet.fit("H7")` resolves an ISO 286 fit class to its limit
2568
+ deviation for the feature's nominal ⌀, rendered as the class code (`ø20 H7`, default) or
2569
+ the signed deviations (`show="deviation"` → `ø20 +0.021/0`). Common classes; fails loud
2570
+ outside its table (#29, Phase 2 P2a.2).
2571
+
2572
+ ### Changed
2573
+
2574
+ - **Object constructors honour explicit overrides** — a passed object supplies *defaults*;
2575
+ each explicit keyword overrides that field independently, and invalid public input fails
2576
+ at declaration with a clear `ValueError` (#451, #452).
2577
+ - **A declared hole/pattern renders at its declared position** even where detection missed
2578
+ it — the callout membership is sourced from the declared model, not only detection (#448).
2579
+ - **`Sheet.model()` / `Sheet.from_part()`** no longer build a full drawing just to return
2580
+ the IR — feature inspection/seeding is now a cheap, no-render path (#453).
2581
+
2582
+ ### Fixed
2583
+
2584
+ - A narrow diameter band hidden under a larger OD silhouette is no longer silently
2585
+ undimensioned (the two feature inventories agreed) (#298).
2586
+
2587
+ ## v0.2.5 — 2026-07-04
2588
+
2589
+ **The editable write API and record-then-finalize.** A detected drawing became an editable
2590
+ object, and a generated `--script` became a runnable reconstruction that reaches auto-pass
2591
+ quality.
2592
+
2593
+ ### Added
2594
+
2595
+ - **`dwg.model()`** exposes the detected `PartModel` as a read surface, plus feature-
2596
+ referenced add verbs `dimension()` / `callout()` / `locate()` / `furniture()` /
2597
+ `section()` and `drop(feature)`, with a machine-checked completeness audit (#400).
2598
+ - **Record-then-finalize** (#426): the verbs record intents in deferred mode and
2599
+ `dwg.finalize()` (auto-run by export) drains them through the auto-pass's own solvers, so
2600
+ a reconstruction reaches auto-pass quality; `--script` now emits a runnable detect-only
2601
+ reconstruction.
2602
+
2603
+ ### Changed
2604
+
2605
+ - **Dimension-line spacing now follows ISO 129-1 / ASME Y14.5 convention** (#347): a wider
2606
+ first-line gap (8 → 10 mm) and tighter, uniform parallel stacking (between-line clear gap
2607
+ 4 → 2.5 mm), with the inter-view corridor widened in step. Re-drifts every drawing.
2608
+
2609
+ ## v0.2.4 — 2026-07-03
2610
+
2611
+ A follow-up patch on the ADR 0009 placement rebuild in 0.2.3: it finishes unifying
2612
+ the shared "above-view" dimension corridor, adds a layout-overflow safety net, and
2613
+ makes two more drop paths non-silent.
2614
+
2615
+ ### Changed
2616
+
2617
+ - **Plan-view X location dimensions, side-view Y location dimensions, and a
2618
+ coincident slot-position dimension now share one collect-then-solve pass** (ADR
2619
+ 0009 Amendment 6, #345/#346). Previously each pass carved the strip independently,
2620
+ so a hole location and a slot position measuring the same datum span could both be
2621
+ drawn, and the location ladder could come out non-monotonic. One solve now dedups
2622
+ the coincident span (keeping the higher-priority location dimension) and orders the
2623
+ whole ladder as segregated, monotonic runs — feature-size dimensions nearest the
2624
+ view, datum locations nesting outward by distance.
2625
+
2626
+ ### Fixed
2627
+
2628
+ - **`choose_scale` never returns an overflowing layout** (#350). Scale selection
2629
+ could pick a scale whose composed block layout exceeded the drawable area; it now
2630
+ rejects any overflowing candidate.
2631
+ - **A hole location and a coincident slot position are no longer drawn twice** (#345),
2632
+ including at fractional datum distances where a display-value snap gap previously let
2633
+ the duplicate escape deduplication.
2634
+ - **The plan-view location ladder is monotonic** (#346) — running dimensions off a
2635
+ shared datum stack outward in ascending order instead of interleaving.
2636
+ - **A dropped balloon is non-silent** (#387). A balloon that cannot be placed now
2637
+ reports the drop and clears its `callout_dropped` state precisely, instead of
2638
+ vanishing with no on-sheet signal.
2639
+
2640
+ ## v0.2.3 — 2026-07-03
2641
+
2642
+ A large patch release: the **annotation-placement engine was rebuilt** as a
2643
+ collect-then-solve *boundary-labeling* stage (ADR 0009). Placement is now
2644
+ deterministic and minimises total leader length, and the recurring class of
2645
+ overlaps where a label was drawn on top of an "invisible" occupant — a leader
2646
+ shaft, a witness/extension line, the section hatch — is removed by construction.
2647
+ Drawing output changes for many turned, cross-drilled, and multi-feature parts.
2648
+
2649
+ ### Changed
2650
+
2651
+ - **Every annotation in a view's margin is now placed by one collect-then-solve
2652
+ pass** (ADR 0009, #317–#323). Dimensions, hole callouts, turned-diameter
2653
+ leaders, and the section hatch share a single occupancy model instead of several
2654
+ independent passes each blind to the others. When a strip is over capacity it now
2655
+ drops the *lowest-priority* annotation (smallest bore first) rather than whichever
2656
+ pass happened to run last. The legacy strip cursor is retired.
2657
+ - **Leader placement minimises total leader length, deterministically** (P4,
2658
+ #318). A per-strip solve places each label at the shortest-leader position that
2659
+ keeps the labels in order and clear of keep-out rows (a view centre-line, a
2660
+ dimension's extension line); central/coaxial callouts are anchored to the
2661
+ view-centre row. Output is reproducible across platforms and Python versions.
2662
+ - **`scipy` is no longer a dependency** — the leader solve is a small deterministic
2663
+ algorithm (weighted-median isotonic regression), not a linear program.
2664
+ - **Output changes** for turned, cross-drilled, and multi-feature parts whose
2665
+ margin annotations are now positioned by the unified solver.
2666
+
2667
+ ### Fixed
2668
+
2669
+ - **A PMI bore-diameter dimension spans the bore radius, not the full diameter**
2670
+ (#360). A `pmi="annotate"` diameter callout drew its witness lines at ±diameter
2671
+ from the centre — twice too wide, missing the bore edges.
2672
+ - **A bore coaxial with a rotational part's turning axis is no longer
2673
+ over-dimensioned** (#309). It carried a redundant offset *and* height location
2674
+ dimension even though its centre mark already locates it.
2675
+ - **A dropped turned step-length chain is no longer silent** (#362). When a turned
2676
+ head's shoulders are too crowded to dimension, the drop is now reported
2677
+ (`step_dim_dropped`) instead of vanishing with no lint or on-sheet signal.
2678
+ - **A diameter callout can no longer overprint a bore callout's leader shaft**
2679
+ (#358). The turned-diameter column now avoids the *full* footprint of existing
2680
+ annotations, not just their text boxes.
2681
+ - **The balloon ring hugs its dimensions on a cramped sheet** (#349) — its band
2682
+ depth is clamped to the drawable area.
2683
+ - **Dimension detection is robust to `SafeDimension`** (#335/#349) — the corridor
2684
+ and balloon-ring filters test the dimension *type*, not a class-name string, so a
2685
+ future dimension subclass can't slip through.
2686
+
2687
+ ## v0.2.2 — 2026-06-30
2688
+
2689
+ A patch release of turned-part dimension-placement fixes and a CLI start-up
2690
+ speed-up. Drawing output changes for the affected turned/cross-drilled parts.
2691
+
2692
+ ### Fixed
2693
+
2694
+ - **A coaxial bore callout on a *stepped* turned shaft is now lifted off the round
2695
+ view's centre axis** (#305). The earlier fix only triggered for a uniform
2696
+ (`is_rotational`) cylinder; a stepped shaft (e.g. the gramel GRM-03 drive screw)
2697
+ has a turned step profile but isn't classified rotational, so its `⌀… ↓…` bore
2698
+ callout was still leadered straight along the centreline, with the centre mark
2699
+ running through the text. The lift now also fires for a turned-profile part.
2700
+ - **A side-drilled hole's location dimension now stacks *inside* the overall
2701
+ envelope dimension** (ISO order — overall dim outermost, feature/location dims
2702
+ nearer the view). It was placed *outside* the envelope, which forced the shorter
2703
+ location dim's arrowheads to flip outward and clash (seen on GRM-01 and GRM-02).
2704
+ The mandatory overall dimension is still guaranteed placement.
2705
+
2706
+ ### Changed
2707
+
2708
+ - **CLI shell completion and `--help` are fast again** (#313). The Typer CLI and
2709
+ the heavy CAD engine are now imported lazily, so tab-completion and `--help` no
2710
+ longer pay a ~6 s engine-import cost; a real drawing run is unaffected.
2711
+
2712
+ ## v0.2.1 — 2026-06-30
2713
+
2714
+ A patch release focused on **turned-part dimensioning legibility**: crowded
2715
+ step-length chains and fine turned heads are now drawn legibly instead of crammed.
2716
+ Drawing output changes for affected turned parts.
2717
+
2718
+ ### Added
2719
+
2720
+ - **Automatic enlarged detail view for a crowded turned head** (#304). A turned
2721
+ part with a fine cluster of steps near one end and a long shaft (e.g. a thumbwheel
2722
+ drive screw) cannot have its head dimensioned legibly in line at any sensible
2723
+ scale. The head is now located as one block on the main view and broken out into
2724
+ an enlarged **DETAIL A — SCALE n:1** — the textbook treatment — firing
2725
+ automatically when a head's shoulders fall below the page legibility floor.
2726
+
2727
+ ### Changed
2728
+
2729
+ - **Crowded turned step-length chains stagger across two tiers** (#293) instead of
2730
+ cramming or being skipped. When the labels would collide on one line, the ISO
2731
+ 129-1 staggered convention alternates them between a near and a far tier so every
2732
+ step length stays legible at the drawing's own scale — no rescale needed. A roomy
2733
+ chain stays on a single tier.
2734
+ - **Detail views are now one unified pipeline** (#307). The prismatic step-height
2735
+ detail and the new turned-head detail flow through a single
2736
+ detect → request → render path; several crowded regions become DETAIL A/B/…
2737
+ - **Output changes** for turned parts whose step chains were previously crammed, or
2738
+ whose fine heads are now broken out into a detail view.
2739
+
2740
+ ### Fixed
2741
+
2742
+ - **A coaxial bore callout no longer overlaps the round view's centreline** (#305):
2743
+ its leader is angled off the centre axis so the callout text sits in clear space.
2744
+
2745
+ ### Internal
2746
+
2747
+ - A new **layout-cleanliness invariant test** asserts that finished drawings have no
2748
+ view/annotation collisions across representative part archetypes, and the
2749
+ measure-and-repack pass gained a trigger for an annotation growing into a
2750
+ neighbouring view's line-work (so the views spread to make room).
2751
+
2752
+ ## v0.2.0 — 2026-06-30
2753
+
2754
+ A major release. draftwright took ownership of feature recognition and linting
2755
+ (ADR 0007) and was re-architected onto a feature-IR + dimensioning-planner
2756
+ "compiler" (ADR 0008), gaining a Typer CLI and a portable, pure-Python PDF path
2757
+ along the way. **Generated drawings change** versus v0.1.13: the new pipeline
2758
+ dimensions parts more completely and consistently, so placement and the set of
2759
+ dimensions can differ — output is not byte-compatible with prior releases.
2760
+
2761
+ ### Changed
2762
+
2763
+ - **Re-architected onto a feature-IR + dimensioning planner** (ADR 0008). The
2764
+ engine is now a compiler: detectors build one feature inventory → a typed IR /
2765
+ `PartModel` → a dimensioning planner emits render-intents → shared
2766
+ layout/projection/export. Orientation and feature-kind are *data in the IR*,
2767
+ not code branches, and every feature class (holes, patterns, counterbores,
2768
+ slots, turned diameters/steps, centre marks, location dims, envelope/OD,
2769
+ section A–A, PMI/GD&T) was migrated onto this one path; the old parallel
2770
+ recognisers and placement passes were deleted as each was replaced. Net effect
2771
+ for users: more complete, more consistent drawings — but output differs from
2772
+ v0.1.13.
2773
+ - **draftwright owns feature recognition and linting** (ADR 0007). The hole/
2774
+ boss/cylinder/pattern recognisers, the slot/turned-step recognisers, and the
2775
+ feature-coverage lint engine are vendored into `recognition/` and `linting.py`;
2776
+ `build123d-drafting-helpers` is now purely the rendering library.
2777
+ - **The CLI writes a PDF by default** and takes a `--format` selector (#288).
2778
+ Previously `draftwright part.step` emitted SVG + DXF; it now emits a single PDF.
2779
+ Choose outputs with `--format` (a comma-list, with an `all` alias) —
2780
+ `--format pdf,dxf`, `--format svg`, `--format all`. The library API is
2781
+ unchanged: `make_drawing(...)` / `Drawing.export()` still write SVG + DXF.
2782
+ - **PDF export is now pure-Python and a core capability** (#288). The renderer
2783
+ moved from `cairosvg` (which `dlopen`s the native `libcairo` system library —
2784
+ absent on stock macOS/Windows, so PDF-by-default would have crashed there) to
2785
+ `svglib` + `reportlab`, both pip-installable wheels with no system dependency.
2786
+ PDF therefore works out of the box on every platform; output is visually
2787
+ identical to the cairo renderer.
2788
+
2789
+ ### Added
2790
+
2791
+ - **A Typer command-line interface** (#289/#291): shell completion
2792
+ (`--install-completion` / `--show-completion`), rich `--help`, and `--version`
2793
+ (reports the installed distribution version). All existing flags are preserved.
2794
+ - **`--format` output selector** (#288) — `pdf` (default), `svg`, `dxf`, or `all`,
2795
+ as a comma-list.
2796
+ - **Turned-part dimensioning**: axial step-length recognition and chains
2797
+ (#188/#189/#231), step-diameter callouts, collapse of a uniform step
2798
+ staircase to an "N× length" note (#290), and OD of a horizontal (X/Y) round
2799
+ body dimensioned on the profile view (#292).
2800
+ - **Slot recognition and dimensioning** converged onto the IR as `SlotFeature`
2801
+ (#242), and **section A–A** is now triggered by the planner (#271).
2802
+ - **A Contributor License Agreement** (#183).
2803
+
2804
+ ### Removed
2805
+
2806
+ - **The `--pdf` flag** (use `--format pdf`, the new default), the **`[pdf]`
2807
+ install extra**, and the **`cairosvg` dependency** (#288).
2808
+ - The byte-exact golden-output test harness (#190) — regression coverage rests on
2809
+ the geometry-level and standards suites (ADR 0005 §3 / ADR 0007).
2810
+
2811
+ ### Fixed
2812
+
2813
+ - Locate **every** side-drilled (off-axis) hole, not just the first (#225/#286).
2814
+ - Don't mis-detect a prismatic part with incidental cylinders as a turned part
2815
+ (#293/#294); drop phantom zero-diameter turned steps (#279/#284); skip an
2816
+ illegibly-dense step-length chain rather than cram it (#293/#296).
2817
+ - Degraded hole-pattern callout consistency (#262/#274).
2818
+ - Windows `python -m draftwright.make_drawing` CLI smoke / entrypoint (#181/#182).
2819
+
2820
+ ## v0.1.13 — 2026-06-27
2821
+
2822
+ ### Changed
2823
+
2824
+ - **Requires `build123d-drafting-helpers>=0.13.0`; text pinned to bundled fonts**
2825
+ (#149, ADR 0006). draftwright now vendors IBM Plex (OFL-1.1) and renders and
2826
+ measures all text via `font_path` — IBM Plex Mono for dimensions/callouts/notes,
2827
+ IBM Plex Sans Condensed for the title block — instead of resolving the system
2828
+ font name `"Arial"`. Resolving a name substitutes a different font on Linux,
2829
+ which shifted the whole sheet ~1 mm; pinning a bundled font file makes generated
2830
+ layout **deterministic across Linux/macOS/Windows** and gives a consistent
2831
+ typeface. **Drawing output changes**: positions shift slightly from prior
2832
+ releases and labels render in IBM Plex (helpers #172).
2833
+
2834
+ ### Internal
2835
+
2836
+ - **Compiler-pipeline module split** (#138, ADR 0005). The two large modules
2837
+ `make_drawing.py` (3,907 lines) and `annotate.py` (2,587) were decomposed into a
2838
+ DAG of focused stage modules — `projection`, `sheet`, `analysis`, `drawing`,
2839
+ `builder`, the `annotations/` subpackage (sections/turned/pmi/holes/orchestrator),
2840
+ alongside the existing `registry`/`linting`/`repair`/`export`/`fonts`. Annotation
2841
+ identity, the lint coverage signal, and the deterministic repair loop each gained a
2842
+ single owner; `make_drawing.py` / `annotate.py` are now thin compat facades, so all
2843
+ existing imports and the `draftwright` CLI entry point keep working. A golden-output
2844
+ regression gate verified every step is behaviour-preserving (output byte-identical),
2845
+ and mypy was tightened on the settled contracts. No public API or drawing-output
2846
+ change. (Phases #160–#166.)
2847
+
2848
+ ## v0.1.12 — 2026-06-21
2849
+
2850
+ ### Changed
2851
+
2852
+ - **Requires `build123d-drafting-helpers>=0.12.0`** (#92, #122). draftwright now
2853
+ consumes the new sub-clustered hole-pattern recognition, the
2854
+ `feature_diameters()` coverage inventory, the persistent `view_edge_cache`,
2855
+ and the `ViewCoordinates.from_viewport()` ISO projection basis.
2856
+ - **Grouped hole-pattern callouts** (#92, #111, #114). A recognised perimeter,
2857
+ grid, or bolt circle collapses to a single `n× ⌀ …` callout plus its pattern
2858
+ dimensions instead of a balloon on every hole. A spec group now sub-clusters
2859
+ into multiple patterns (a perimeter → its edge `LinearArray` rows, a filled
2860
+ lattice → one `RectGrid` with a `(rows×cols)` callout and both pitch
2861
+ dimensions); only genuinely unpatterned holes fall back to the per-hole table.
2862
+ On NIST CTC-02 the table shrinks from 61 rows to the unpatterned remainder.
2863
+ - **Layout overhaul — compose-then-pack** (#121, #112, ADR 0004). Each view owns
2864
+ the annotations created against it, and the resulting view blocks are packed
2865
+ disjoint with automatic page/scale escalation. This eliminates cross-view
2866
+ overlap — most visibly, plan-view balloons landing on front-view dimensions.
2867
+ - **Drawing attribution** (#120). The title block records the author, the SVG
2868
+ and PDF carry a clickable draftwright hyperlink, and a "generated by
2869
+ draftwright" note is written to the SVG/DXF/PDF file metadata.
2870
+ - **Gap between wrapped hole-table column blocks** (#123) so a chart that wraps
2871
+ into several blocks reads as distinct columns.
2872
+
2873
+ ### Fixed
2874
+
2875
+ - **Plan-view top balloon ring no longer floats over a phantom corridor** (#125).
2876
+ The hole-table escalation deletes the X-location dimensions but left their
2877
+ stale depth in the strip cursor, so the top balloons were parked far above the
2878
+ view. The ring is now sized to the real dimension stack, so the top-side
2879
+ leaders are short like the other three sides.
2880
+ - **No more phantom `feature_not_dimensioned` warnings** on slot-ends and shallow
2881
+ recesses, via the helpers 0.12.0 `feature_diameters()` coverage inventory
2882
+ (#92).
2883
+
2884
+ ### Internal
2885
+
2886
+ - `AnnoBox` box-model footprint foundation and the four-side balloon ring placed
2887
+ in a reserved view halo (#111, #112); the title block is pinned as a
2888
+ first-class layout block (#112).
2889
+
2890
+ ## v0.1.11 — 2026-06-19
2891
+
2892
+ ### Changed
2893
+
2894
+ - **Feature-coverage lint is assembly-aware.** A general-arrangement drawing of
2895
+ a multi-solid part deliberately omits each part's bores (they belong on detail
2896
+ sheets), so `feature_not_dimensioned` / `feature_count_mismatch` are now
2897
+ emitted at `info` rather than `warning` when the part is multi-solid — out of
2898
+ the warning count and quality score, but still queryable. Auto-detected;
2899
+ override with `build_drawing(..., assembly=True/False)` or
2900
+ `lint_feature_coverage(..., assembly=...)` (#69).
2901
+
2902
+ ### Fixed
2903
+
2904
+ - **`place_dim` now labels the real-world length, not the page distance**, at
2905
+ non-1:1 scale. Previously a dimension placed at a scale other than 1:1 showed
2906
+ the on-page millimetre span instead of the true model dimension (#104).
2907
+
2908
+ ### Internal
2909
+
2910
+ - **`make_drawing.py` decomposed (#98).** The per-view projection math and the
2911
+ analysis namespace were deduplicated and typed (the namespace is now a frozen
2912
+ `Analysis` dataclass), and the annotation passes were extracted into a new
2913
+ `draftwright.annotate` module on top of a shared `draftwright._core`. The
2914
+ module graph is a DAG (`layout → _core → {make_drawing, annotate}`) and
2915
+ `make_drawing.py` shrank from ~5,270 to ~2,930 lines. No public API or
2916
+ behaviour change.
2917
+
2918
+ ## v0.1.10 — 2026-06-18
2919
+
2920
+ ### Added
2921
+
2922
+ - **Constraint-based layout engine (ADR 0003).** A new `draftwright.layout`
2923
+ module with a `Placeable` protocol and a `LayoutSolver`: a 1D Cassowary strip
2924
+ solver (`solve_strip`, with per-pair gaps) and a 2D free-rectangle placer
2925
+ (`place_box` / `fit_box`) that positions a box in a free part of the page
2926
+ clear of the views, title block, and existing annotations. Hole-callout and
2927
+ turned-diameter placement now run on the solver. The engine grows per real
2928
+ consumer; a monolithic global 2D solve is deferred (see the ADR).
2929
+ - **Hole table + balloons (#93).** `dwg.add_table(rows)` places a generic data
2930
+ table (gear data, BOM, revision block, …) in a free corner via `place_box`;
2931
+ `dwg.add_hole_table(view)` builds a hole chart from the detected holes with a
2932
+ circled balloon tag at each hole. A **too-dense plan view now auto-escalates**:
2933
+ a part the layout cannot legibly dimension hole-by-hole is replaced by a
2934
+ complete per-instance hole chart (`TAG | ⌀ | X | Y`, datum-relative) plus
2935
+ balloons, instead of silently dropping callouts and location dims. The chart
2936
+ wraps into multiple column-blocks to fit the page.
2937
+ - **External turned diameters (#77).** A turned part lying along the X axis now
2938
+ gets ø leader-callouts for its external stepped diameters, with thread/worm
2939
+ patches collapsed into a single boss.
2940
+ - **Pin / manual override (#89).** `dwg.pin(name)` / `dwg.unpin(name)` fix an
2941
+ annotation's position so `repair()` — and the layout engine — never move it; a
2942
+ deliberate (human or AI) placement wins over automatic layout.
2943
+
2944
+ ### Changed
2945
+
2946
+ - Hole-callout and turned-diameter placement is deconflicted through the shared
2947
+ `LayoutSolver` instead of ad-hoc per-pass logic (no output change).
2948
+
2949
+ ### Fixed
2950
+
2951
+ - **Exact circles recovered for revolution silhouettes.** `project_to_viewport`'s
2952
+ HLR returns the on-axis silhouette of a turned feature (or a concentric
2953
+ gear-tooth-tip arc) as an approximating spline, not a true circle — splines in
2954
+ the DXF where CAM expects `CIRCLE`/`ARC`, and fitted rather than exact radii.
2955
+ `add_view` now refits any silhouette whose samples are equidistant from a
2956
+ recognised revolution axis back to an exact circle/arc (#67).
2957
+ - **Blind-hole depth no longer measured across solid boundaries.** On a
2958
+ multi-solid assembly, coaxial bores in different bodies were merged into one
2959
+ hole, reporting a depth spanning the inter-body gap (the ⌀9.8 ↓111.4 symptom).
2960
+ Fixed upstream in `build123d-drafting-helpers` 0.10.1; the dependency pin is
2961
+ bumped to `>=0.10.1` to pick it up (#68).
2962
+
2963
+ ### Docs
2964
+
2965
+ - The skill and generated-script header now lead with the domain API
2966
+ (`features` / `place_dim` / `repair` / `lint_summary`) and the
2967
+ build → critique → fix loop. ADR 0003 records the layout architecture; ADRs
2968
+ 0001/0002 remain the editing-model and lint→repair foundations.
2969
+
2970
+ ## v0.1.9 — 2026-06-16
2971
+
2972
+ ### Added
2973
+
2974
+ - **Domain-semantic editing API.** `dwg.features(view)` returns detected holes
2975
+ and features grouped by machining spec in page coordinates, and
2976
+ `dwg.place_dim(p1, p2, side, view, draft, name=…)` places a dimension from
2977
+ domain inputs — the vocabulary a script (or an AI assistant) needs to edit a
2978
+ drawing without hand-computing page geometry (#25, #26).
2979
+ - **`dwg.annotations()` and `dwg.get_annotation(name)`.** Introspect what is
2980
+ already on the drawing — a `{name: type}` map and a name lookup — so a script
2981
+ can make incremental edits without risking a silent name-collision replace
2982
+ (#27).
2983
+ - **`dwg.view_bounds(view)`.** Returns `(x_min, y_min, x_max, y_max)`, the page
2984
+ bounding box of a view's projected geometry (or `None` for an unknown view),
2985
+ so free-form notes and leader elbows can be placed just outside a view without
2986
+ guessing offsets from `dwg.at()` (#28).
2987
+ - **Lint findings carry a suggested fix.** Each repairable lint issue now
2988
+ includes a ready-to-run domain-API call snippet, so acting on a finding is one
2989
+ copy-paste away (#29).
2990
+ - **Lint→repair loop.** `Drawing.repair()` — run by default in `build_drawing` —
2991
+ mechanically resolves the lint codes that have a deterministic placement fix:
2992
+ overlapping labels are pushed apart and wrong-side dimensions are flipped. A
2993
+ pass that would net-increase the issue count is rolled back, so repair never
2994
+ makes a drawing worse (#30).
2995
+ - **TYP / representative dimensioning for uniform step patterns.** A run of
2996
+ equal-rise, equal-going steps is dimensioned once and labelled representative
2997
+ (TYP) instead of repeating identical dimensions down the ladder (#45).
2998
+ - **Enlarged detail view for crowded step clusters (MVP).** When shoulders are
2999
+ too closely spaced to dimension legibly at sheet scale, an opt-in
3000
+ (`detail_view=True`) detail view re-draws them at a larger scale (#42).
3001
+
3002
+ ### Changed
3003
+
3004
+ - **BREAKING: the annotation list `dwg.annotations` is renamed to `dwg.items`.**
3005
+ `dwg.annotations` is now a method (see Added); the ordered, mutable list of
3006
+ annotation objects it used to be is now `dwg.items`. Pre-1.0 with no published
3007
+ consumers, so the clearer name was taken now rather than spelling the new query
3008
+ method awkwardly (#27).
3009
+
3010
+ ### Documentation
3011
+
3012
+ - ADRs 0001 (editing model) and 0002 (iteration loop) record the design
3013
+ direction behind the domain API and the lint→repair loop (#51).
3014
+
3015
+ ## v0.1.8 — 2026-06-16
3016
+
3017
+ ### Changed
3018
+
3019
+ - **Automatic scale selection now minimises the sheet size.** The preference
3020
+ ladder is page-major: every standard scale on the smallest sheet is tried
3021
+ before the next sheet up, so a part lands on the smallest sheet it fits at the
3022
+ largest scale that sheet allows. A 20 × 15 × 10 mm part is now drawn 2:1 on A4
3023
+ instead of 5:1 on A3 — a smaller sheet is preferred over a larger enlargement
3024
+ scale. Reductions keep their legibility-first balance, so a too-big part is
3025
+ not over-reduced onto a small sheet.
3026
+ - **A specified page now enlarges to the best fitting scale.** When the caller
3027
+ fixes the page (`--page A3`) or scale, scale selection packs the isometric
3028
+ view into the largest empty rectangle the placement engine actually uses (it
3029
+ may sit in vertical headroom above the views), instead of charging it a column
3030
+ in the view row. A long, short part — e.g. a 100 × 10 × 11 mm staircase — now
3031
+ fills a requested A3 at 2:1 where it was previously under-scaled to 1:1.
3032
+ Automatic selection (no page/scale given) keeps the conservative row model,
3033
+ which reserves enough room to place every annotation rather than dropping some
3034
+ onto a tighter sheet (staircase review).
3035
+ - **Isometric view growth is capped.** The iso is fitted to fill its zone but no
3036
+ longer grows past 1.3× sheet scale; on an oversized sheet it could previously
3037
+ balloon to ~8× and dwarf the dimensioned orthographic views. Shrinking to fit
3038
+ a small zone is unchanged.
3039
+ - **Step heights are dimensioned only where legibly separable.** After the
3040
+ adaptive cap (#36), a part with many closely-spaced shoulders (e.g. NIST
3041
+ CTC-02 at 1:5) tried to dimension faces only ~1 mm apart on the page. A step
3042
+ is now dimensioned only if it is both tall enough from the base *and* at least
3043
+ one legible step-height above the previously dimensioned one; the rest surface
3044
+ as `step_dim_dropped` (use a detail view). "Fits" is not the same as
3045
+ "legible" (#41).
3046
+ - **Hole-location dimensions are gated for legibility.** A hole-dense part (e.g.
3047
+ NIST CTC-02, ~38 distinct hole locations) previously stacked every location
3048
+ reference into a tall, busy tower above the views — "fits" is not "legible".
3049
+ Each axis's references are now gated by inter-dimension page spacing
3050
+ (`_legible_locations`, analogous to the step-height gate #41): only locations
3051
+ at least one value-label footprint apart on the page are dimensioned; the rest
3052
+ surface as `location_ref_dropped` (full fidelity belongs in a detail view,
3053
+ #42). Sparse parts are unchanged (#43).
3054
+ - **Tighter location-dimension tier pitch.** The vertical pitch between stacked
3055
+ X/Y location dimensions is now derived from the label footprint
3056
+ (`font_size + 2·pad_around_text`, ≈7 mm) instead of a looser `font_size·3`,
3057
+ so location stacks pack closer (#41).
3058
+
3059
+ ### Fixed
3060
+
3061
+ - **Phantom step corridor no longer blocks a larger scale.** Page/scale
3062
+ selection reserved a step-ladder corridor sized for *every* candidate
3063
+ horizontal face, including ones the legibility gate would never dimension. A
3064
+ part with many sub-legible faces (e.g. a staircase with 15 tiny treads) was
3065
+ forced onto an oversized sheet at 1:1. Scale selection now iterates so the
3066
+ reserved corridor matches the step count actually placed, freeing the part to
3067
+ pick a tighter sheet (staircase.step review).
3068
+ - **Engraved-text faces are no longer dimensioned as steps.** `analyse_face_levels`
3069
+ gained a `min_area_frac` filter; a horizontal face counts as a step only if
3070
+ its area is at least 1% of the part's plan footprint. This drops sub-feature
3071
+ faces (fragments of engraved numbers/text) that were surfacing as phantom
3072
+ shoulders — e.g. a 0.57 mm² digit face dimensioned as z=6.4 on staircase.step.
3073
+ - **Overall-height dimension nests outside the step dims.** The overall height
3074
+ is now placed last on the front view's right ladder so it sits outermost, with
3075
+ the step-height dims inside it; extension lines nest instead of leapfrogging
3076
+ (staircase.step review).
3077
+
3078
+ ## v0.1.7 — 2026-06-15
3079
+
3080
+ ### Added
3081
+
3082
+ - `Drawing.lint_summary()` — a JSON-friendly aggregate of `lint()` for
3083
+ non-interactive callers (scripts, or an LLM via the API): severity counts,
3084
+ per-code counts, a `geometry_issues` tally (standards/geometry checks vs pure
3085
+ layout), a `passed` flag, a coarse 0–1 `score`, and the full issue list. Gives
3086
+ a single signal to gate and optimise on without rendering the SVG (#32).
3087
+
3088
+ ### Changed
3089
+
3090
+ - **Adaptive annotation placement.** The three hard-coded cardinality caps —
3091
+ four hole callouts per view, four hole location references per part, and three
3092
+ step-height dimensions — are removed. The engine now places as many as the
3093
+ available strip/corridor space allows (callouts largest-first, locations
3094
+ nearest-datum-first, every legible step), so a part with room is dimensioned
3095
+ completely instead of dropped to an arbitrary count. An annotation that
3096
+ genuinely doesn't fit is never force-placed; it surfaces via lint
3097
+ (`callout_dropped` / `location_ref_dropped`, warning severity). On the NIST
3098
+ CTC parts this raises coverage substantially (e.g. CTC-02: 4 → 36 location
3099
+ dimensions, 4 → 9 callouts) with no error-severity lint (#36).
3100
+ - **No silent annotation drops.** Every place the layout has to drop an
3101
+ annotation now records a machine-readable lint issue, surfaced by `lint()`,
3102
+ so a short drawing always carries a reason. A dropped callout names its
3103
+ diameter and is excluded from `feature_not_dimensioned` (no double-report).
3104
+ `placement_unsatisfiable` (error severity) is reserved for the degenerate
3105
+ case where space was reserved but an annotation still could not be placed
3106
+ (#32).
3107
+ - **Layout constants derived from first principles.** Bare, fixture-tuned
3108
+ constants (strip slot widths, callout label widths, isometric fit factor) are
3109
+ now computed from text metrics and page size rather than hard-coded, so the
3110
+ layout generalises to unseen geometry instead of fitting the test cases (#31).
3111
+ - `_auto_annotate` clears its build-time lint records on re-entry, and repeated
3112
+ `lint()` calls are stable (#32).
3113
+
3114
+ ### Fixed
3115
+
3116
+ - AP242 / PMI STEP import segfault: STEP geometry is now read directly via
3117
+ `STEPControl_Reader`, avoiding the XCAF/PMI read that crashed (SIGSEGV) on
3118
+ with-PMI files such as NIST CTC-02 (#20).
3119
+
3120
+ ### Tests
3121
+
3122
+ - Overfitting guards pin the general layout behaviour on turned/hybrid parts
3123
+ (flange OD + bolt circle), multi-bore parts, and the step-legibility boundary
3124
+ (#13).
3125
+ - The full NIST CTC set (AP203 and AP242) builds and is covered by the slow
3126
+ end-to-end tier.
3127
+
3128
+ ## v0.1.6 — 2026-06-15
3129
+
3130
+ ### Fixed
3131
+
3132
+ - Section-view boolean cut on cast geometry: the exact `body - Box(...)` boolean
3133
+ raised an uncatchable `Standard_DomainError` (C++ abort, SIGABRT) on some parts
3134
+ (NIST CTC-04), crashing the whole drawing. `_fuzzy_cut()` now runs
3135
+ `BRepAlgoAPI_Cut` with a small fuzzy tolerance and keeps solids-only, making
3136
+ the section cut robust (#20, #22).
3137
+
3138
+ ### Tests
3139
+
3140
+ - NIST CTC-04 (both AP203 and AP242) now build with a clean section view and are
3141
+ covered by the CTC build tests.
3142
+ - Known: CTC-02 AP242 still segfaults inside OCCT's AP242/PMI STEP read (#20),
3143
+ excluded from build tests.
3144
+
3145
+ ## v0.1.5 — 2026-06-15
3146
+
3147
+ ### Fixed
3148
+
3149
+ - CTC-02 spurious full-page line: build123d's `ExportSVG` projected
3150
+ circle-edge-on edges (hole/fillet rims seen edge-on) as elliptical arcs with
3151
+ a near-zero minor radius, which renderers blow up into full-page lines.
3152
+ `sanitize_svg_arcs()` rewrites any arc with a sub-1e-3 mm radius into the
3153
+ straight line it actually is, leaving real arcs untouched (#19). Not a PMI
3154
+ issue — the file is AP203 geometry-only.
3155
+
3156
+ ### Tests
3157
+
3158
+ - Added the full NIST CTC set (01–05) as fixtures, both AP203 geometry-only and
3159
+ AP242 (with-PMI) variants.
3160
+ - Heavy end-to-end CTC fixture builds are marked `slow` and deselected from the
3161
+ default `pytest` run (fast normal run, ~4.5 min); CI runs the fast tier across
3162
+ the OS/Python matrix and the slow tier once.
3163
+ - Known: AP242 CTC-02 and both CTC-04 variants crash OCCT on import (#20); their
3164
+ fixtures are excluded from build tests.
3165
+
3166
+ ## v0.1.4 — 2026-06-15
3167
+
3168
+ ### Changed
3169
+
3170
+ - Feature annotations (hole callouts, location dimensions, section view) now
3171
+ fire on feature presence independent of the turned/prismatic classification,
3172
+ so turned-and-drilled parts (e.g. flanges) get both the OD/centreline base
3173
+ set and per-hole callouts plus bolt-circle furniture (#10).
3174
+ - Isometric view placement now uses a general largest-empty-rectangle search in
3175
+ place of the wide/flat-on-A3 special case (#11).
3176
+ - Concentric bore-leader stacking is generalised beyond three, and the
3177
+ step-height dimension gate is now a single derived constant (#10, #12).
3178
+
3179
+ ### Internal
3180
+
3181
+ - Single-sourced duplicated geometry constants from the draft preset (#12).
3182
+ - Minor comment and logging cleanups.
3183
+
3184
+ ## v0.1.0 — 2026-06-14
3185
+
3186
+ Initial release — spun out of `build123d-drafting-helpers` v0.9.1.
3187
+
3188
+ The automated drawing engine (`make_drawing`, `build_drawing`, `Drawing`)
3189
+ was previously part of `build123d-drafting-helpers`. It is now a separate
3190
+ AGPL-licensed package that depends on `build123d-drafting-helpers>=0.9.1`
3191
+ for annotation primitives.
3192
+
3193
+ ### Migration from build123d-drafting-helpers
3194
+
3195
+ ```python
3196
+ # Before
3197
+ from build123d_drafting import make_drawing, Drawing, build_drawing
3198
+
3199
+ # After
3200
+ from draftwright import make_drawing, Drawing, build_drawing
3201
+ ```
3202
+
3203
+ ### Features (carried over from build123d-drafting-helpers)
3204
+
3205
+ - **`make_drawing`** / **`build_drawing`** — automatic multi-view technical
3206
+ drawing from a build123d solid: view layout, scale selection, orthographic
3207
+ projection, dimension placement, title block.
3208
+ - **`Drawing`** — composable drawing object with `.lint()`, `.add()`,
3209
+ `.export_svg()`, `.export_dxf()`.
3210
+ - **`choose_scale`** — ISO/ASME standard scale selection.
3211
+ - **`lint_feature_coverage`** — checks annotation coverage against detected
3212
+ part features (holes, bosses, bolt circles).
3213
+ - **Section A–A views** — automatic section view for blind/stepped holes,
3214
+ with ISO 128-44 solid filled cutting-plane arrows and ISO 128-50 45°
3215
+ hatching on the cut face.
3216
+ - **`generate_script`** — generates a standalone drawing script from a STEP
3217
+ file.