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