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