draftwright 0.4.8__tar.gz → 0.4.9__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 (259) hide show
  1. draftwright-0.4.9/.gitignore +28 -0
  2. {draftwright-0.4.8 → draftwright-0.4.9}/CHANGELOG.md +156 -35
  3. {draftwright-0.4.8 → draftwright-0.4.9}/PKG-INFO +8 -2
  4. {draftwright-0.4.8 → draftwright-0.4.9}/README.md +6 -0
  5. {draftwright-0.4.8 → draftwright-0.4.9}/docs/adr/README.md +4 -3
  6. {draftwright-0.4.8 → draftwright-0.4.9}/docs/reference/recogniser-capabilities.md +10 -3
  7. {draftwright-0.4.8 → draftwright-0.4.9}/pyproject.toml +4 -2
  8. {draftwright-0.4.8 → draftwright-0.4.9}/skills/SKILL.md +15 -2
  9. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/_core.py +37 -12
  10. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/_geometry.py +299 -0
  11. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/_common.py +208 -22
  12. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/balloons.py +83 -52
  13. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/from_model.py +614 -100
  14. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/holes.py +65 -7
  15. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/leaders.py +338 -23
  16. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/orchestrator.py +125 -2
  17. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/sections.py +150 -31
  18. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/builder.py +44 -4
  19. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/compose.py +19 -5
  20. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/drawing.py +356 -94
  21. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/evaluation/step_analysis.py +227 -6
  22. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/__init__.py +12 -1
  23. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/coverage.py +44 -2
  24. draftwright-0.4.9/src/draftwright/linting/evidence.py +423 -0
  25. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/hole_coverage.py +90 -18
  26. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/pmi_coverage.py +18 -1
  27. draftwright-0.4.9/src/draftwright/linting/quality.py +672 -0
  28. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/structural.py +246 -111
  29. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/model/callout.py +26 -0
  30. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/model/compiled.py +157 -7
  31. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/model/detect.py +26 -0
  32. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/model/planner.py +251 -28
  33. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/projection.py +222 -28
  34. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/recogniser_contract.py +103 -7
  35. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/repair.py +7 -0
  36. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/sheet_emit.py +15 -1
  37. {draftwright-0.4.8 → draftwright-0.4.9}/tests/conftest.py +73 -0
  38. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/bracket_section.json +7 -1
  39. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/pocketed.json +7 -1
  40. draftwright-0.4.9/tests/test_architecture_docs.py +25 -0
  41. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_audit_differential.py +6 -3
  42. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_detect_registry.py +36 -2
  43. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_drawing_encapsulation.py +6 -0
  44. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_e2e_standards.py +4 -1
  45. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_flat_completeness.py +14 -8
  46. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_flat_stock_identity.py +10 -16
  47. draftwright-0.4.9/tests/test_iso_nts_caption_placement.py +611 -0
  48. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1073_cross_body_patterns.py +19 -7
  49. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1143_hole_completeness.py +40 -1
  50. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1144_transactional_hole_table.py +34 -11
  51. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1147_legibility_pair_aggregation.py +15 -2
  52. draftwright-0.4.9/tests/test_issue_1153_contradictory_dimensions.py +292 -0
  53. draftwright-0.4.9/tests/test_issue_1154_one_owner_per_measurement.py +584 -0
  54. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1166_cross_pass_feature_leaders.py +33 -20
  55. draftwright-0.4.9/tests/test_issue_1176_quality_fidelity.py +988 -0
  56. draftwright-0.4.9/tests/test_issue_1177_angular_dimensions.py +518 -0
  57. draftwright-0.4.9/tests/test_issue_1187_unroutable_leaders.py +111 -0
  58. draftwright-0.4.9/tests/test_issue_1188_per_view_assignment.py +101 -0
  59. draftwright-0.4.9/tests/test_issue_1190_section_decision.py +253 -0
  60. draftwright-0.4.9/tests/test_issue_1196_deterministic_view_names.py +422 -0
  61. draftwright-0.4.9/tests/test_issue_1202_observed_drawing_consumer.py +625 -0
  62. draftwright-0.4.9/tests/test_issue_1204_multiscale_view_issues.py +335 -0
  63. draftwright-0.4.9/tests/test_issue_1215_envelope_tolerance.py +326 -0
  64. draftwright-0.4.9/tests/test_issue_1215_no_approved_tolerance_is_dropped.py +913 -0
  65. draftwright-0.4.9/tests/test_issue_1216_callout_reservation_measures_ink.py +167 -0
  66. draftwright-0.4.9/tests/test_issue_1216_pairwise_across_scale_groups.py +125 -0
  67. draftwright-0.4.9/tests/test_issue_1217_claimed_representations.py +490 -0
  68. draftwright-0.4.9/tests/test_issue_1217_the_facility_is_shared.py +422 -0
  69. draftwright-0.4.9/tests/test_issue_1219_location_claims_its_own_axis.py +268 -0
  70. draftwright-0.4.9/tests/test_issue_1229_ledger_member_keying.py +177 -0
  71. draftwright-0.4.9/tests/test_issue_1240_iso_above_strip_visibility.py +405 -0
  72. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_676_polygonal_boss.py +11 -7
  73. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_740_leader_assignment.py +6 -1
  74. draftwright-0.4.9/tests/test_issue_798_floor_cardinality.py +202 -0
  75. draftwright-0.4.9/tests/test_issue_798_material_field.py +495 -0
  76. draftwright-0.4.9/tests/test_issue_798_silhouette_lint.py +308 -0
  77. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_916_pocket_leader.py +5 -1
  78. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_917_open_channel.py +4 -1
  79. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_958_cross_solid_recognition.py +7 -1
  80. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_label_provenance.py +9 -4
  81. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_layout_hypothesis.py +1 -0
  82. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_lint_structural.py +20 -81
  83. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_make_drawing.py +147 -71
  84. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_pmi.py +23 -4
  85. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_private_test_attr_reads.py +5 -0
  86. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_private_test_imports.py +32 -2
  87. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_quality_components.py +41 -1
  88. draftwright-0.4.9/tests/test_recogniser_adoption.py +43 -0
  89. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_recogniser_capabilities.py +138 -24
  90. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_recognition.py +20 -4
  91. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_recognition_manifest.py +135 -6
  92. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_recognition_result.py +13 -136
  93. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_render_seam.py +136 -17
  94. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_sheet_emit.py +1 -0
  95. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_slanted_blind_step.py +8 -1
  96. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_slot_completeness.py +12 -6
  97. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_suppression_ledger.py +11 -1
  98. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_tolerances.py +244 -18
  99. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_two_repository_workflow.py +10 -3
  100. draftwright-0.4.8/.gitignore +0 -16
  101. draftwright-0.4.8/src/draftwright/linting/quality.py +0 -340
  102. draftwright-0.4.8/tests/test_architecture_docs.py +0 -43
  103. {draftwright-0.4.8 → draftwright-0.4.9}/LICENSE +0 -0
  104. {draftwright-0.4.8 → draftwright-0.4.9}/docs/reference/sheet.md +0 -0
  105. {draftwright-0.4.8 → draftwright-0.4.9}/docs/research/1062-repeating-radial-profile-evidence.md +0 -0
  106. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/__init__.py +0 -0
  107. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/_pmi_part21.py +0 -0
  108. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/_warnings.py +0 -0
  109. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/analysis.py +0 -0
  110. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotate.py +0 -0
  111. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/__init__.py +0 -0
  112. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/annotations/gears.py +0 -0
  113. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/audit.py +0 -0
  114. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/cli.py +0 -0
  115. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/evaluation/__init__.py +0 -0
  116. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/export.py +0 -0
  117. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/fits.py +0 -0
  118. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
  119. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
  120. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
  121. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
  122. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/fonts/__init__.py +0 -0
  123. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/intents.py +0 -0
  124. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/layout.py +0 -0
  125. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/channel_coverage.py +0 -0
  126. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/flat_coverage.py +0 -0
  127. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/gear_coverage.py +0 -0
  128. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/issues.py +0 -0
  129. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/polygonal_stock_coverage.py +0 -0
  130. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/profiled_bore_coverage.py +0 -0
  131. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/slot_coverage.py +0 -0
  132. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/linting/suggest.py +0 -0
  133. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/make_drawing.py +0 -0
  134. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/model/__init__.py +0 -0
  135. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/model/declare.py +0 -0
  136. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/model/ir.py +0 -0
  137. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/pmi.py +0 -0
  138. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/py.typed +0 -0
  139. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/recognition/__init__.py +0 -0
  140. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/recognition_cache.py +0 -0
  141. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/registry.py +0 -0
  142. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/score.py +0 -0
  143. {draftwright-0.4.8 → draftwright-0.4.9}/src/draftwright/sheet.py +0 -0
  144. {draftwright-0.4.8 → draftwright-0.4.9}/tests/_kernel.py +0 -0
  145. {draftwright-0.4.8 → draftwright-0.4.9}/tests/_layout_sig.py +0 -0
  146. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/evaluation/README.md +0 -0
  147. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/evaluation/ambiguous-open-semicircle.step +0 -0
  148. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/evaluation/blind-hole.step +0 -0
  149. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/evaluation/corpus-v1.json +0 -0
  150. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/evaluation/plain-block.step +0 -0
  151. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/evaluation/topology-a.step +0 -0
  152. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/evaluation/topology-b.step +0 -0
  153. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
  154. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
  155. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
  156. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
  157. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/issue_915_case_study_2.step +0 -0
  158. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
  159. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
  160. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
  161. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
  162. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
  163. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
  164. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
  165. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
  166. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
  167. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
  168. {draftwright-0.4.8 → draftwright-0.4.9}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
  169. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/centered_rebate.json +0 -0
  170. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/chamfered.json +0 -0
  171. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/filleted.json +0 -0
  172. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/flange_dense.json +0 -0
  173. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/grid_plate.json +0 -0
  174. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/grooved_shaft.json +0 -0
  175. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/hex_bar.json +0 -0
  176. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/holed_slot.json +0 -0
  177. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/prismatic_ladder.json +0 -0
  178. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/scattered_plate.json +0 -0
  179. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/side_drilled.json +0 -0
  180. {draftwright-0.4.8 → draftwright-0.4.9}/tests/refactor_golden/turned_stepped.json +0 -0
  181. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_add_dimension.py +0 -0
  182. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_agents_guide.py +0 -0
  183. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_api_docs.py +0 -0
  184. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_balloon_ring_standoff.py +0 -0
  185. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_carve_free_position_callers.py +0 -0
  186. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_compiled_plan_boundary.py +0 -0
  187. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_counting_calls.py +0 -0
  188. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_cross_repository_delivery_protocol.py +0 -0
  189. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_declare.py +0 -0
  190. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_declared_recognition_gate.py +0 -0
  191. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_dense_sheet_canary.py +0 -0
  192. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_deprecation_dates.py +0 -0
  193. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_detect_once.py +0 -0
  194. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_dimension_role_vocabulary.py +0 -0
  195. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_e2e_slice.py +0 -0
  196. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_export_dxf_curves.py +0 -0
  197. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_export_dxf_zoom.py +0 -0
  198. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_external_recognition_boundary.py +0 -0
  199. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_fillets_adjacency.py +0 -0
  200. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_fits.py +0 -0
  201. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_flat_stock_opposition.py +0 -0
  202. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_gdt_placement.py +0 -0
  203. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_grid_lattice_convention.py +0 -0
  204. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_import_boundaries.py +0 -0
  205. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1000_envelope_reuse.py +0 -0
  206. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1058_wheel_profile.py +0 -0
  207. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1082_polygonal_stock.py +0 -0
  208. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1086_declared_gears.py +0 -0
  209. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1142_hole_leader_labels.py +0 -0
  210. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_1146_scale_completeness.py +0 -0
  211. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_367_leader_ink.py +0 -0
  212. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
  213. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_885_prismatic_coverage.py +0 -0
  214. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_909_sloped_profile.py +0 -0
  215. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_915_dense_case.py +0 -0
  216. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_issue_955_height_contingency.py +0 -0
  217. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_layout.py +0 -0
  218. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_layout_cleanliness.py +0 -0
  219. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_layout_property.py +0 -0
  220. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_leader_footprint.py +0 -0
  221. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_lint_box_cache.py +0 -0
  222. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_lint_reconciliation.py +0 -0
  223. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_linting.py +0 -0
  224. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_location_vocabulary.py +0 -0
  225. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_note_verb.py +0 -0
  226. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_object_aspects.py +0 -0
  227. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_occupancy_boxes.py +0 -0
  228. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_parameter_id.py +0 -0
  229. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_part_model.py +0 -0
  230. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_pitch_dim_footprint.py +0 -0
  231. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_pmi_datum_lowering.py +0 -0
  232. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_pmi_gtol_lowering.py +0 -0
  233. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_pmi_part21.py +0 -0
  234. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_pocket_pattern.py +0 -0
  235. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_pocket_pattern_recognition.py +0 -0
  236. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_principal_profile_classifier.py +0 -0
  237. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_recogniser_contract.py +0 -0
  238. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_refactor_golden.py +0 -0
  239. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_registry.py +0 -0
  240. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_repack_geometry_seam.py +0 -0
  241. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_score.py +0 -0
  242. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_script_detail_parity.py +0 -0
  243. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_shared_box_memo.py +0 -0
  244. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_sheet_gdt.py +0 -0
  245. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_sheet_identity_invariant.py +0 -0
  246. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_sheet_notes.py +0 -0
  247. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_sheet_of.py +0 -0
  248. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_sheet_section.py +0 -0
  249. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_sheet_tables.py +0 -0
  250. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_slot_pattern.py +0 -0
  251. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_slot_pattern_recognition.py +0 -0
  252. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_soft_deprecation.py +0 -0
  253. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_solve_trace.py +0 -0
  254. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_step_analysis_evaluation.py +0 -0
  255. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_strip_layout.py +0 -0
  256. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_suppression_marks.py +0 -0
  257. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_turned_steps.py +0 -0
  258. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_witness_label_reconciliation.py +0 -0
  259. {draftwright-0.4.8 → draftwright-0.4.9}/tests/test_workflows.py +0 -0
@@ -0,0 +1,28 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ .venv/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .pytest_cache/
9
+ testenv/
10
+ .coverage
11
+ .coverage.*
12
+ coverage.xml
13
+ coverage*.json
14
+ htmlcov/
15
+ site/
16
+ .DS_Store
17
+
18
+ # Drawing exports. `Drawing.export()` writes wherever the calling script runs from, so
19
+ # ad-hoc scripts drop artefacts in the repo root — `bracket.pdf` and `grm01.pdf` were
20
+ # swept into a commit that way. These are build products, never sources.
21
+ #
22
+ # PDF and DXF have no tracked instances, so they are ignored anywhere. SVG and PNG DO
23
+ # (docs/images/), so only the repo root is ignored — the one place throwaway scripts
24
+ # write to — rather than hiding a legitimate new docs image.
25
+ *.pdf
26
+ *.dxf
27
+ /*.svg
28
+ /*.png
@@ -1,51 +1,125 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## v0.4.9 — 2026-08-21
4
4
 
5
5
  ### Added
6
6
 
7
- - Recogniser co-development now has a one-command, wheel-based two-checkout check; an exact
8
- registry-release evidence record; automated dependency PR preparation; and documented landing,
9
- compatibility, deprecation, rollback, ownership, and CI-budget rules. Dependency PRs retain the
10
- canonical coverage/Codecov gate while avoiding four duplicate platform suites and the slow tier
11
- (#1170).
12
- - A versioned, independently authored STEP-analysis corpus and evaluator now report detection
13
- recall/false positives, parameter fidelity, and downstream usefulness as separate evidence
14
- layers. Hash-pinned positive, negative, ambiguous, compound, and topology-order-variant fixtures,
15
- per-layer diagnostics, and an anti-self-validation mutation prevent recogniser output from
16
- defining its own denominator (#1169).
17
- - Draftwright now validates the released `b123d-recognisers` 0.2.0 capability manifest against
18
- an exhaustive consumer-owned declaration of IR, DSL, generated-code, drawing, completeness,
19
- and documentation states. Unknown families, stale implementations, record-schema drift, and
20
- unevidenced state transitions fail closed; intentionally geometry-only repeating-profile
21
- evidence remains usable without acquiring invented drafting semantics.
7
+ - Lint now verifies that an annotation claiming to carry a measurement actually renders it
8
+ (#1217). Coverage checks read provenance riders the annotations carry about themselves and
9
+ believed them; nothing checked the claim against what the annotation draws. Four new codes
10
+ `claimed_value_absent`, `claimed_measurement_not_compiled`, and two that report an
11
+ unverifiable claim rather than passing it — and `Drawing.add_table` now keeps the rows it
12
+ draws so a table's claims are readable back. Measured over all 21 STEP fixtures: 1,081
13
+ claims, 1,078 confirmed, and three findings all instances of one genuine provenance
14
+ defect, see #1219.
15
+
16
+ - `lint_summary()["quality"]` gains a fourth component, `fidelity`: whether what the drawing
17
+ says is TRUE. The existing three ask whether required content landed (completeness),
18
+ whether there is too much of it (restraint), and whether a reader can make it out
19
+ (legibility) none asked whether it is correct, so a drawing labelled 99 over a 16 mm
20
+ path reported a perfect score on every component available to it. Also covers a callout
21
+ drawn for a feature the part does not have, and a gear data table stating a tooth count
22
+ the geometry contradicts — the last of which reports `passed: True` and zero errors, so
23
+ the only existing channel that names it is `by_code`. Completeness, restraint and fidelity
24
+ report `available: False` with a `reason` rather than a flattering number when they have no
25
+ evidence, and every lint code the engine emits is classified onto exactly one component,
26
+ an explicit unscored register, or a register of codes whose component depends on the
27
+ issue's `outcome_stage`. `quality["unscored"]` reports the findings that reached no
28
+ component at all and flags any whose classification nobody made — `section_dropped` was
29
+ found scoring on nothing while a comment beside its emission said the opposite, and eleven
30
+ more `*_dropped` codes reach the engine as `drop_code` data with the same shape (#1176).
22
31
 
23
32
  ### Changed
24
33
 
25
- - Updated the exact `b123d-recognisers` production lock from 0.2.0 to 0.2.1.
26
- The immutable PyPI wheel/sdist hashes and capability-manifest digest
27
- `383a196329ed6a3d1bf88f18ae4ff75613c032f35d40f2b53302b6744da4d19e` are
34
+ - Updated the exact `b123d-recognisers` production lock from 0.2.4 to 0.2.6.
35
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest `aa52bc04216cd1eccc05796d2262547c7f476b80a3ec77334a77b9d8e3241a58` are
36
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
37
+ normal consumer gates run on the generated PR.
38
+ - `HoleRequirementOutcome` gains `members` and `features`, `ClaimOutcome` gains `measurement`,
39
+ and `linting.hole_coverage.canonical_hole_sites` is public (#1217). A consumer attributing a
40
+ requirement outcome to a specific recognised hole needs the evidence the outcome accounts
41
+ for, and needs to key into the canonical space the ledger publishes it in — a through hole's
42
+ own-axis coordinate is zeroed there.
43
+ - The STEP-analysis benchmark's `drawing_consumer` boundary now consumes
44
+ `hole_requirement_outcomes` instead of a second correspondence implementation of its own
45
+ (#1206), and follows the ledger's pointer through `linting.evidence` rather than trusting it.
46
+ Holes the ledger reports `unverifiable` now score `unknown` where the duplicate credited them
47
+ by guessing: 26 holes, five on each CTC-03 fixture and eight on each CTC-04 fixture. The
48
+ five-case evaluation corpus is unchanged, and one lint message on CTC-03 now reports its
49
+ coordinates in the ledger's own canonical space.
50
+
51
+ - `Drawing.suppressions()` rows gain a `conveyed_by` key: the dimension that states a
52
+ withheld measurement instead, or `None` when nothing takes the fact over (#1154). It is not
53
+ a synonym for the existing `authored` flag — that says whose decision it was, this says
54
+ where the measurement went.
55
+
56
+ - Updated the exact `b123d-recognisers` production lock from 0.2.2 to 0.2.4.
57
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest `d0023d30e583c03abc55f09dfeaaa56fddf56334afa0417818480b2fa2ce3f0f` are
58
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
59
+ normal consumer gates run on the generated PR.
60
+ - Updated the exact `b123d-recognisers` production lock from 0.2.1 to 0.2.2.
61
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest `7985befc8572bfe6d1c0805dfdf690d74a5a4a5f14d5988cc29c366315ff04ca` are
28
62
  recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
29
63
  normal consumer gates run on the generated PR.
30
64
 
31
65
  ### Fixed
32
66
 
33
- - Stable release and TestPyPI snapshot builds now update package metadata and the independently
34
- validated Draftwright consumer-contract identity atomically. This prevents stripped `.dev0`
35
- and numbered development artifacts from failing their own capability-contract validation.
36
- - Installed wheels validate every portable recogniser-contract property without requiring the
37
- repository-only behavior-test files whose existence remains a strict source/CI gate. This keeps
38
- the packaged STEP-analysis evaluator usable without weakening evidence checks in development.
39
- - Post-release development-version bumps now update the consumer-contract identity on a branch,
40
- open a protected PR, and dispatch the narrow maintenance gate instead of attempting a rejected
41
- direct push to protected `main` (#1170).
42
- - Machined-feature leader callouts now collect all clear alternatives within each
43
- post-drain pass and use a bounded maximum-cardinality, minimum-leader-length
44
- assignment instead of taking corners greedily in feature order (#740). If a
45
- deterministic alternative-count, pass-size, candidate-pair, or search budget is
46
- reached, placement retains the legacy greedy result or a result no worse than it
47
- rather than losing additional callouts. Pre-drain diameter clearance ranking and
48
- its Policy-B fallback remain unchanged.
67
+ - An authored tolerance the compiler approved now reaches the sheet wherever the measurement
68
+ is drawn (#1215, #1216). `build123d_drafting` resolves a dimension's text as
69
+ `label if label is not None else _number_with_units(measured, tolerance)`, and every
70
+ dimension this engine emits passes a label so a forwarded `tolerance=` rendered nothing
71
+ while type-checking and reading back correctly from `Dimension.label`. Ten render paths
72
+ discarded the band that way: envelope extents, the height ladder, the turned-step chain, two
73
+ public `Drawing` verbs, the deferred corridor route, the detail-view redraw, the short-rise
74
+ escape, step shoulders, pattern pitch, compound-callout recess terms, grid pitch, and both
75
+ hole tables. A general guard now decorates every parameter of every feature across a corpus,
76
+ through both `decorations=` key shapes, and joins what the compiler approved against what the
77
+ claiming annotation renders.
78
+
79
+ - Collapsed `N×` marks no longer state a tolerance their members do not share (#1216). A
80
+ pattern's pitch claimed the authored band of every gap although the recogniser admits 2%
81
+ jitter holes at −30, −10, 10.3, 30 printed `3× 20 ±0.1` over a 20.3 mm gap — and fillet and
82
+ flat collapses took the "first-authored" band, so one of four fillets at ±0.1 printed
83
+ `4× R5 ±0.1`. Equality is now judged at the drawn precision, and a withheld band is reported
84
+ (`pattern_pitch_tolerance_withheld`, `collapsed_tolerance_withheld`) rather than dropped
85
+ silently.
86
+
87
+ - An approved dimension that cannot be placed is now reported instead of vanishing (#1216).
88
+ The overall extents' corridor candidate carried a no-op drop handler — the only drop in the
89
+ engine that recorded nothing — and the step ladder's legibility floor skipped rungs without
90
+ incrementing any counter. Both now record against the measurement they concern, and a
91
+ withholding is withdrawn if a later pass draws the measurement after all.
92
+
93
+ - A feature leader filling a view's below corridor no longer costs the part its overall extent
94
+ (#1236). The mandatory width or depth now retries on the view's above strip after every
95
+ corridor has drained, the fallthrough starved slot and plate dimensions already use. Live on
96
+ three NIST CTC fixtures, which drew no overall width.
97
+
98
+ - The iso view and the above strips are no longer invisible to each other (#1240). Strip
99
+ placement never saw the iso, and the iso fit re-scaled after annotation without consulting
100
+ what had been placed — so growth could invade dimension ink, which it did on CTC-01. The fit
101
+ is now capped by measured re-projection rather than by a linear model of it: the projected
102
+ bbox translates as it scales, by up to 7.35 mm on a part carrying a Location, which no
103
+ prediction about the page centre can express.
104
+
105
+
106
+ - A part whose feature-local extent runs between the same two faces as an overall extent
107
+ is no longer dimensioned twice (#1154). GRM-04's drive plate printed `4.5` twice — once
108
+ as the hub's height and once as the overall thickness — because two detected records
109
+ claim one physical fact. The overall extent keeps it; the feature-local one is withheld
110
+ and records which dimension the reader finds it on. Reconciliation is by exact
111
+ support-plane coincidence, never by value: a square part's two equal extents run between
112
+ different faces and remain two facts (#997). A measurement is handed over only when the
113
+ extent receiving it is actually drawn — by the planner's rules, by the compiler's
114
+ overall-height rules, and by an authored set — and never when the yielding dimension
115
+ carries a tolerance, since a toleranced dimension and an untoleranced one are not the same
116
+ requirement. (A tolerance on the *receiving* extent is the same requirement on the same two
117
+ faces, and does not prevent the consolidation.)
118
+
119
+ ## v0.4.8 — 2026-08-16
120
+
121
+ ### Fixed
122
+
49
123
  - Compatible automatic/deferred sparse ordinary side/plan hole and post-drain
50
124
  machined-feature callouts now share one bounded same-view leader inventory
51
125
  (#1166). Pattern, profiled-bore, and dense table-eligible hole queues retain
@@ -87,6 +161,53 @@
87
161
  refinement status, and competing blockers; live
88
162
  single-callout edits remain immediate.
89
163
 
164
+ ## v0.4.7 — 2026-08-16
165
+
166
+ ### Added
167
+
168
+ - Recogniser co-development now has a one-command, wheel-based two-checkout check; an exact
169
+ registry-release evidence record; automated dependency PR preparation; and documented landing,
170
+ compatibility, deprecation, rollback, ownership, and CI-budget rules. Dependency PRs retain the
171
+ canonical coverage/Codecov gate while avoiding four duplicate platform suites and the slow tier
172
+ (#1170).
173
+ - A versioned, independently authored STEP-analysis corpus and evaluator now report detection
174
+ recall/false positives, parameter fidelity, and downstream usefulness as separate evidence
175
+ layers. Hash-pinned positive, negative, ambiguous, compound, and topology-order-variant fixtures,
176
+ per-layer diagnostics, and an anti-self-validation mutation prevent recogniser output from
177
+ defining its own denominator (#1169).
178
+ - Draftwright now validates the released `b123d-recognisers` 0.2.0 capability manifest against
179
+ an exhaustive consumer-owned declaration of IR, DSL, generated-code, drawing, completeness,
180
+ and documentation states. Unknown families, stale implementations, record-schema drift, and
181
+ unevidenced state transitions fail closed; intentionally geometry-only repeating-profile
182
+ evidence remains usable without acquiring invented drafting semantics.
183
+
184
+ ### Changed
185
+
186
+ - Updated the exact `b123d-recognisers` production lock from 0.2.0 to 0.2.1.
187
+ The immutable PyPI wheel/sdist hashes and capability-manifest digest
188
+ `383a196329ed6a3d1bf88f18ae4ff75613c032f35d40f2b53302b6744da4d19e` are
189
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
190
+ normal consumer gates run on the generated PR.
191
+
192
+ ### Fixed
193
+
194
+ - Stable release and TestPyPI snapshot builds now update package metadata and the independently
195
+ validated Draftwright consumer-contract identity atomically. This prevents stripped `.dev0`
196
+ and numbered development artifacts from failing their own capability-contract validation.
197
+ - Installed wheels validate every portable recogniser-contract property without requiring the
198
+ repository-only behavior-test files whose existence remains a strict source/CI gate. This keeps
199
+ the packaged STEP-analysis evaluator usable without weakening evidence checks in development.
200
+ - Post-release development-version bumps now update the consumer-contract identity on a branch,
201
+ open a protected PR, and dispatch the narrow maintenance gate instead of attempting a rejected
202
+ direct push to protected `main` (#1170).
203
+ - Machined-feature leader callouts now collect all clear alternatives within each
204
+ post-drain pass and use a bounded maximum-cardinality, minimum-leader-length
205
+ assignment instead of taking corners greedily in feature order (#740). If a
206
+ deterministic alternative-count, pass-size, candidate-pair, or search budget is
207
+ reached, placement retains the legacy greedy result or a result no worse than it
208
+ rather than losing additional callouts. Pre-drain diameter clearance ranking and
209
+ its Policy-B fallback remain unchanged.
210
+
90
211
  ## v0.4.6 — 2026-08-15
91
212
 
92
213
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: draftwright
3
- Version: 0.4.8
3
+ Version: 0.4.9
4
4
  Summary: Automated technical-drawing generation for build123d
5
5
  Project-URL: Homepage, https://github.com/pzfreo/draftwright
6
6
  Project-URL: Repository, https://github.com/pzfreo/draftwright
@@ -684,7 +684,7 @@ Classifier: Programming Language :: Python :: 3.14
684
684
  Classifier: Topic :: Scientific/Engineering
685
685
  Requires-Python: <3.15,>=3.10
686
686
  Requires-Dist: antlr4-python3-runtime<4.10
687
- Requires-Dist: b123d-recognisers==0.2.1
687
+ Requires-Dist: b123d-recognisers==0.2.6
688
688
  Requires-Dist: build123d-drafting-helpers>=0.14.1
689
689
  Requires-Dist: build123d<0.11,>=0.9.0; python_full_version < '3.13'
690
690
  Requires-Dist: build123d<0.12,>=0.11; python_full_version >= '3.13'
@@ -937,6 +937,12 @@ assert completeness["scope"] == "audited_recognized_requirements"
937
937
  completeness["excludes"] # what the denominator cannot see
938
938
  completeness["unrecognised_geometry_reports"] # a floor on that gap, never a measure
939
939
  legibility = crit["quality"]["legibility"]
940
+ # `quality` also carries `fidelity` (#1176): is what the drawing says TRUE? A drawing can
941
+ # be complete, restrained and legible and still assert a measurement the part lacks.
942
+ fidelity = crit["quality"]["fidelity"]
943
+ # …and `unscored`, the findings no component priced, so four components reading "fine"
944
+ # cannot conceal that some of the critique was scored by nothing.
945
+ unscored = crit["quality"]["unscored"]
940
946
  # Raw pair findings remain available and keep the original count semantics.
941
947
  legibility["warnings"], legibility["by_code"], crit["issues"]
942
948
  # The legibility scalar uses primary issues: one affected annotation/failure mechanism even
@@ -237,6 +237,12 @@ assert completeness["scope"] == "audited_recognized_requirements"
237
237
  completeness["excludes"] # what the denominator cannot see
238
238
  completeness["unrecognised_geometry_reports"] # a floor on that gap, never a measure
239
239
  legibility = crit["quality"]["legibility"]
240
+ # `quality` also carries `fidelity` (#1176): is what the drawing says TRUE? A drawing can
241
+ # be complete, restrained and legible and still assert a measurement the part lacks.
242
+ fidelity = crit["quality"]["fidelity"]
243
+ # …and `unscored`, the findings no component priced, so four components reading "fine"
244
+ # cannot conceal that some of the critique was scored by nothing.
245
+ unscored = crit["quality"]["unscored"]
240
246
  # Raw pair findings remain available and keep the original count semantics.
241
247
  legibility["warnings"], legibility["by_code"], crit["issues"]
242
248
  # The legibility scalar uses primary issues: one affected annotation/failure mechanism even
@@ -32,9 +32,10 @@ architecture** table; open retired or superseded records only for design history
32
32
  | [0013](0013-uniform-recognition-and-shared-package.md) | A uniform recogniser/feature contract and shared `b123d-recognisers` deployment | Enforce the geometry-only contract in the shared package and consume it through Draftwright's record→IR boundary. | Accepted; Phases 1–2 deployed | `test_external_recognition_boundary.py`, `test_detect_registry.py`, package semantic goldens |
33
33
  | [0014](0014-collect-then-solve-annotation-placement.md) | Collect-then-solve annotation placement (as built) | Collect, select, assign, and deterministically solve annotations per corridor before rendering; compatible sparse ordinary side/plan hole and post-drain machined leaders share one bounded late inventory. | Accepted; supersedes 0009; #740 Amendment 1; #1166 Amendment 2 | `test_carve_free_position_callers.py`, `test_strip_layout.py`, `test_layout_property.py`, `test_issue_740_leader_assignment.py`, `test_issue_1166_cross_pass_feature_leaders.py`, `test_import_boundaries.py` |
34
34
  | [0015](0015-part-drawing-compiler-as-built.md) | The part-drawing compiler, as built | Use one detected-or-declared feature IR and planner-fed dimension groups as the compiler waist. | Accepted; supersedes 0008 | `test_part_model.py`, `test_detect_once.py`, `test_import_boundaries.py` |
35
- | [0016](0016-declared-dimensioning-intent.md) | Declared dimensioning intent: capture what to measure, let the engine place it | Declare which measurements matter as scale-independent intent routed through the planner and corridor solve; never hardcode dimension geometry. | Accepted; epic #867 complete; phase 6 landed (#940) | `test_compiled_plan_boundary.py`, `test_label_provenance.py`, `test_sheet_emit.py`, `test_add_dimension.py` |
35
+ | [0016](0016-declared-dimensioning-intent.md) | Declared dimensioning intent: capture what to measure, let the engine place it | Declare which measurements matter as scale-independent intent routed through the planner and corridor solve; never hardcode dimension geometry. | Accepted; epic #867 complete; phase 6 landed (#940) | `test_compiled_plan_boundary.py`, `test_issue_1215_no_approved_tolerance_is_dropped.py`, `test_label_provenance.py`, `test_sheet_emit.py`, `test_add_dimension.py` |
36
36
  | [0017](0017-recognition-inventory-correspondence-and-measurement-provenance.md) | One recognition result per run; correspondence is evidence-gated | Produce one external immutable recognition result held by Draftwright's per-run cache; require vertical-slice evidence before generalising correspondence, identity, requirements, outcomes, or reconciliation. | Accepted; external cache ownership clarified, extensions gated by #1018 | `test_external_recognition_boundary.py`, `test_recognition_manifest.py`, `test_declared_recognition_gate.py` |
37
- | [0018](0018-requirement-driven-view-planning-and-editable-sheet-layout.md) | Requirement-driven view planning and editable sheet layout | Use one `ViewSpec` vocabulary and planner with distinct authored `ViewConstraints` and immutable `ResolvedViewPlan`; jointly validate views, typography, convention, scale, paper and layout against requirement survival. If accepted, supersedes ADR 0004's fixed-topology assumption while retaining compose-then-pack. | Proposed; tracked by #1130 | Required guards are listed in the ADR |
37
+ | [0018](0018-requirement-driven-view-planning-and-editable-sheet-layout.md) | Requirement-driven view planning and editable sheet layout | Use one `ViewSpec` vocabulary and planner with distinct authored `ViewConstraints` and immutable `ResolvedViewPlan`; jointly validate views, typography, convention, scale, paper and layout against requirement survival. Supersedes ADR 0004's fixed-topology assumption while retaining compose-then-pack. | Accepted 2026-08-16 (nothing implemented yet); tracked by #1130 | Required guards are listed in the ADR |
38
+ | [0019](0019-display-complete-labels-and-dimension-outcomes.md) | Display-complete labels and a dimension-outcome ledger | The compiled plan carries the full label text (tolerance and collapse wording included) so renderers render and never compose; dimension outcomes reconcile at one seam on both build routes; ladder rungs get per-mark identity. Finishes the ADR 0016 Amdt 1 boundary; supersedes Amdt 6's enforcement mechanism. | Proposed | `test_issue_1215_no_approved_tolerance_is_dropped.py` reduces to plan-equality when implemented |
38
39
 
39
40
  ## Historical records
40
41
 
@@ -49,7 +50,7 @@ architecture** table; open retired or superseded records only for design history
49
50
  - Compiler and state ownership: 0001 → 0005 → 0015.
50
51
  - Recognition and public declaration: 0007 → 0013 → 0011 → 0015.
51
52
  - Layout and placement: 0004 → 0014 → 0012 → 0018.
52
- - Declared intent and the editable surface: 0001 → 0011 → 0012 → 0016 → 0018.
53
+ - Declared intent and the editable surface: 0001 → 0011 → 0012 → 0016 → 0018 → 0019.
53
54
  - Quality and correction: 0002, with provenance from 0010.
54
55
  - Recognition correspondence and completeness: 0007 → 0013 → 0015 → 0017.
55
56
 
@@ -15,9 +15,16 @@ library.
15
15
 
16
16
  Validation fails closed and names the family and boundary whenever possible:
17
17
 
18
- - **family inventory mismatch** — the installed package added, removed, or renamed a family.
19
- Update the package pin deliberately, add or remove the Draftwright declaration, and provide the
20
- downstream behavior or an explicit non-supported state.
18
+ - **family inventory mismatch (`stale=`)** — Draftwright declares a family the installed package
19
+ no longer ships, so a declared adapter would call a recogniser that is gone. Remove or repoint
20
+ the declaration.
21
+
22
+ The opposite direction does **not** fail this validation. A family the package ships and
23
+ Draftwright has not declared cannot reach any Draftwright code path, and blocking on it made the
24
+ package unreleasable until its consumer caught up. It is reported by
25
+ `pending_family_declarations` and fails `tests/test_recogniser_adoption.py` in Draftwright's own
26
+ CI instead — adoption is still required, and is enforced where the decision is made. Provide the
27
+ downstream behavior or an explicit non-supported state as before.
21
28
  - **record schema mismatch** — a consumed record schema changed. Review the record fields and
22
29
  compatibility notes, update the relevant adapter and tests, then pin the accepted schema version.
23
30
  - **stale implementation** — a declared adapter, `Sheet` method, emitter, renderer, or completeness
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "draftwright"
7
- version = "0.4.8"
7
+ version = "0.4.9"
8
8
  description = "Automated technical-drawing generation for build123d"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -18,7 +18,7 @@ dependencies = [
18
18
  "build123d>=0.9.0,<0.11 ; python_full_version < '3.13'",
19
19
  "build123d>=0.11,<0.12 ; python_full_version >= '3.13'",
20
20
  "build123d-drafting-helpers>=0.14.1",
21
- "b123d-recognisers==0.2.1",
21
+ "b123d-recognisers==0.2.6",
22
22
  "numpy>=1.24",
23
23
  # PNG raster export (SVG→PDF→PNG). Both permissively licensed (Pillow HPND; pypdfium2
24
24
  # Apache-2.0 wrapping Google PDFium BSD-3) and ship pre-built wheels with NO native system
@@ -89,6 +89,7 @@ timeout = 300
89
89
  markers = [
90
90
  "slow: heavy end-to-end CTC fixture builds (deselected by default)",
91
91
  "smoke: curated build-light subset for a fast local check (~30 s)",
92
+ "unit: pure-logic inner-loop tier — constructs NO OCC geometry (enforced by conftest, #656)",
92
93
  ]
93
94
  # `SoftDeprecationWarning` marks a surface that is discouraged but SUPPORTED and not going
94
95
  # away (#1043). The suite exercises `Sheet.auto_dimensions()` in ~170 places, deliberately —
@@ -164,6 +165,7 @@ dev = [
164
165
  "mypy>=2.1.0",
165
166
  "pytest>=8",
166
167
  "pytest-cov>=5",
168
+ "pytest-split>=0.8",
167
169
  "pytest-timeout>=2",
168
170
  "pytest-xdist>=3",
169
171
  "ruff>=0.4",
@@ -281,9 +281,22 @@ dwg = build_drawing(part)
281
281
 
282
282
  # 1. Critique — the machine channel. JSON-friendly aggregate of lint().
283
283
  crit = dwg.lint_summary()
284
- # {"passed": bool, "score": 0..1, "errors": n, "warnings": n, "infos": n,
285
- # "by_code": {code: n}, "issues": [{code, severity, message, suggestion?}, ...]}
284
+ # {"passed": bool, "score": 0..1, "diagnostic_score": 0..1,
285
+ # "errors": n, "warnings": n, "infos": n, "by_code": {code: n},
286
+ # "issues": [{code, severity, message, suggestion?}, ...],
287
+ # "quality": {"completeness": …, "restraint": …, "legibility": …, "fidelity": …,
288
+ # "unscored": {"available": True, "score": None, "issues": n,
289
+ # "by_code": {…}, "unclassified": [...], "reason": …}}}
286
290
  # Gate on the severity/code COUNTS, not the scalar score.
291
+ #
292
+ # The four `quality` components answer four different questions and none substitutes for
293
+ # another: did required content land, is there too much of it, can a reader make it out,
294
+ # and — `fidelity` — is what it says TRUE. Completeness, restraint and fidelity report
295
+ # `available: False` with a `reason` rather than a flattering number when they have no
296
+ # evidence, so read `available` first. (Legibility is always available: an empty sheet
297
+ # genuinely IS legible, which is not the same kind of 1.0 as an empty sheet being truthful.)
298
+ # `quality["unscored"]` lists the findings none of the four priced; a non-empty
299
+ # `unclassified` there means a lint code reached no component and nobody decided it should.
287
300
 
288
301
  # 2. Each issue names the problem in DOMAIN terms and (when computable) carries a
289
302
  # ready-to-apply suggestion — a domain-API call you paste in, not page maths.
@@ -507,6 +507,36 @@ _MIN_STEP_SEP_MM = _FONT_SIZE + 2 * _PAD
507
507
  _MIN_LOC_SEP_MM = draft_preset(font_size=_FONT_SIZE, decimal_precision=1).arrow_length + _PAD
508
508
 
509
509
 
510
+ def _classify_steps(step_zs, bb_min_z, scale, *, allow_short=False):
511
+ """``(kept, too_close, too_short)`` — every step height, sorted into its outcome.
512
+
513
+ The one predicate. :func:`_legible_steps` is the historical two-value view of this and
514
+ delegates here, so the "which steps are dimensioned" rule cannot be stated twice and
515
+ drift; the third list exists because it was previously stated ZERO times.
516
+
517
+ ``too_short`` is the silent case (#1216 review r9). A step whose page distance from the
518
+ part's base cannot carry a dimension is skipped, and unlike ``too_close`` the skip
519
+ incremented no counter, raised no escalation and produced no lint — so a blind hole in a
520
+ plate had its ``step_height`` approved by the compiler, dropped here, and the drawing
521
+ linted perfectly clean with the measurement nowhere on it. Callers report it; the guard in
522
+ ``tests/test_issue_1215_no_approved_tolerance_is_dropped.py`` requires that they do.
523
+ """
524
+ kept: list[float] = []
525
+ too_close: list[float] = []
526
+ too_short: list[float] = []
527
+ last = None
528
+ for z in sorted(step_zs):
529
+ if not allow_short and (z - bb_min_z) * scale < _MIN_STEP_DIM_MM:
530
+ too_short.append(z)
531
+ continue
532
+ if last is not None and (z - last) * scale < _MIN_STEP_SEP_MM:
533
+ too_close.append(z)
534
+ continue
535
+ kept.append(z)
536
+ last = z
537
+ return kept, too_close, too_short
538
+
539
+
510
540
  def _legible_steps(step_zs, bb_min_z, scale, *, allow_short=False):
511
541
  """Step heights worth dimensioning at *scale*, and how many were too close.
512
542
 
@@ -518,19 +548,14 @@ def _legible_steps(step_zs, bb_min_z, scale, *, allow_short=False):
518
548
  when they do not fit inline (#565). Returns ``(kept_zs, n_too_close)``:
519
549
  the heights to dimension, and the count dropped for spacing (the caller
520
550
  surfaces these via lint; the full-fidelity answer is a detail view, #42).
551
+
552
+ The steps dropped as too SHORT are not in either return value — see
553
+ :func:`_classify_steps`, which is where this rule now lives.
521
554
  """
522
- kept: list[float] = []
523
- n_too_close = 0
524
- last = None
525
- for z in sorted(step_zs):
526
- if not allow_short and (z - bb_min_z) * scale < _MIN_STEP_DIM_MM:
527
- continue
528
- if last is not None and (z - last) * scale < _MIN_STEP_SEP_MM:
529
- n_too_close += 1
530
- continue
531
- kept.append(z)
532
- last = z
533
- return kept, n_too_close
555
+ kept, too_close, _too_short = _classify_steps(
556
+ step_zs, bb_min_z, scale, allow_short=allow_short
557
+ )
558
+ return kept, len(too_close)
534
559
 
535
560
 
536
561
  def _legible_locations(positions, scale):