draftwright 0.4.7__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.7 → draftwright-0.4.9}/CHANGELOG.md +162 -1
  3. {draftwright-0.4.7 → draftwright-0.4.9}/PKG-INFO +8 -2
  4. {draftwright-0.4.7 → draftwright-0.4.9}/README.md +6 -0
  5. {draftwright-0.4.7 → draftwright-0.4.9}/docs/adr/README.md +5 -4
  6. {draftwright-0.4.7 → draftwright-0.4.9}/docs/reference/recogniser-capabilities.md +10 -3
  7. {draftwright-0.4.7 → draftwright-0.4.9}/pyproject.toml +4 -2
  8. {draftwright-0.4.7 → draftwright-0.4.9}/skills/SKILL.md +15 -2
  9. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/_core.py +37 -12
  10. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/_geometry.py +384 -42
  11. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotations/_common.py +264 -44
  12. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotations/balloons.py +83 -52
  13. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotations/from_model.py +779 -112
  14. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotations/holes.py +341 -15
  15. draftwright-0.4.9/src/draftwright/annotations/leaders.py +2243 -0
  16. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotations/orchestrator.py +135 -2
  17. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotations/sections.py +258 -58
  18. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/builder.py +44 -4
  19. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/compose.py +19 -5
  20. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/drawing.py +364 -94
  21. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/evaluation/step_analysis.py +227 -6
  22. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/layout.py +96 -17
  23. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/__init__.py +12 -1
  24. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/coverage.py +44 -2
  25. draftwright-0.4.9/src/draftwright/linting/evidence.py +423 -0
  26. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/hole_coverage.py +93 -19
  27. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/issues.py +9 -0
  28. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/pmi_coverage.py +18 -1
  29. draftwright-0.4.9/src/draftwright/linting/quality.py +672 -0
  30. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/structural.py +246 -111
  31. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/model/callout.py +26 -0
  32. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/model/compiled.py +157 -7
  33. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/model/detect.py +26 -0
  34. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/model/planner.py +251 -28
  35. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/projection.py +222 -28
  36. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/recogniser_contract.py +103 -7
  37. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/repair.py +7 -0
  38. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/sheet_emit.py +15 -1
  39. {draftwright-0.4.7 → draftwright-0.4.9}/tests/conftest.py +73 -0
  40. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/bracket_section.json +26 -20
  41. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/grooved_shaft.json +14 -14
  42. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/pocketed.json +14 -8
  43. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/scattered_plate.json +42 -32
  44. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/side_drilled.json +8 -8
  45. draftwright-0.4.9/tests/test_architecture_docs.py +25 -0
  46. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_audit_differential.py +11 -3
  47. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_detect_registry.py +36 -2
  48. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_drawing_encapsulation.py +6 -0
  49. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_e2e_standards.py +4 -1
  50. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_flat_completeness.py +14 -8
  51. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_flat_stock_identity.py +10 -16
  52. draftwright-0.4.9/tests/test_iso_nts_caption_placement.py +611 -0
  53. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1073_cross_body_patterns.py +19 -7
  54. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1143_hole_completeness.py +40 -1
  55. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1144_transactional_hole_table.py +34 -11
  56. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1146_scale_completeness.py +3 -0
  57. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1147_legibility_pair_aggregation.py +15 -2
  58. draftwright-0.4.9/tests/test_issue_1153_contradictory_dimensions.py +292 -0
  59. draftwright-0.4.9/tests/test_issue_1154_one_owner_per_measurement.py +584 -0
  60. draftwright-0.4.9/tests/test_issue_1166_cross_pass_feature_leaders.py +2356 -0
  61. draftwright-0.4.9/tests/test_issue_1176_quality_fidelity.py +988 -0
  62. draftwright-0.4.9/tests/test_issue_1177_angular_dimensions.py +518 -0
  63. draftwright-0.4.9/tests/test_issue_1187_unroutable_leaders.py +111 -0
  64. draftwright-0.4.9/tests/test_issue_1188_per_view_assignment.py +101 -0
  65. draftwright-0.4.9/tests/test_issue_1190_section_decision.py +253 -0
  66. draftwright-0.4.9/tests/test_issue_1196_deterministic_view_names.py +422 -0
  67. draftwright-0.4.9/tests/test_issue_1202_observed_drawing_consumer.py +625 -0
  68. draftwright-0.4.9/tests/test_issue_1204_multiscale_view_issues.py +335 -0
  69. draftwright-0.4.9/tests/test_issue_1215_envelope_tolerance.py +326 -0
  70. draftwright-0.4.9/tests/test_issue_1215_no_approved_tolerance_is_dropped.py +913 -0
  71. draftwright-0.4.9/tests/test_issue_1216_callout_reservation_measures_ink.py +167 -0
  72. draftwright-0.4.9/tests/test_issue_1216_pairwise_across_scale_groups.py +125 -0
  73. draftwright-0.4.9/tests/test_issue_1217_claimed_representations.py +490 -0
  74. draftwright-0.4.9/tests/test_issue_1217_the_facility_is_shared.py +422 -0
  75. draftwright-0.4.9/tests/test_issue_1219_location_claims_its_own_axis.py +268 -0
  76. draftwright-0.4.9/tests/test_issue_1229_ledger_member_keying.py +177 -0
  77. draftwright-0.4.9/tests/test_issue_1240_iso_above_strip_visibility.py +405 -0
  78. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_676_polygonal_boss.py +11 -7
  79. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_740_leader_assignment.py +139 -30
  80. draftwright-0.4.9/tests/test_issue_798_floor_cardinality.py +202 -0
  81. draftwright-0.4.9/tests/test_issue_798_material_field.py +495 -0
  82. draftwright-0.4.9/tests/test_issue_798_silhouette_lint.py +308 -0
  83. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_916_pocket_leader.py +5 -1
  84. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_917_open_channel.py +4 -1
  85. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_958_cross_solid_recognition.py +7 -1
  86. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_label_provenance.py +9 -4
  87. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_layout_cleanliness.py +4 -7
  88. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_layout_hypothesis.py +1 -0
  89. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_leader_footprint.py +10 -1
  90. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_lint_structural.py +20 -81
  91. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_make_drawing.py +164 -83
  92. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_pmi.py +23 -4
  93. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_private_test_attr_reads.py +5 -0
  94. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_private_test_imports.py +32 -2
  95. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_quality_components.py +41 -1
  96. draftwright-0.4.9/tests/test_recogniser_adoption.py +43 -0
  97. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_recogniser_capabilities.py +138 -24
  98. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_recognition.py +20 -4
  99. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_recognition_manifest.py +135 -6
  100. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_recognition_result.py +13 -136
  101. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_render_seam.py +136 -17
  102. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_sheet_emit.py +1 -0
  103. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_slanted_blind_step.py +8 -1
  104. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_slot_completeness.py +12 -6
  105. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_strip_layout.py +19 -10
  106. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_suppression_ledger.py +11 -1
  107. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_tolerances.py +244 -18
  108. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_two_repository_workflow.py +14 -3
  109. draftwright-0.4.7/.gitignore +0 -16
  110. draftwright-0.4.7/src/draftwright/linting/quality.py +0 -352
  111. draftwright-0.4.7/tests/test_architecture_docs.py +0 -43
  112. {draftwright-0.4.7 → draftwright-0.4.9}/LICENSE +0 -0
  113. {draftwright-0.4.7 → draftwright-0.4.9}/docs/reference/sheet.md +0 -0
  114. {draftwright-0.4.7 → draftwright-0.4.9}/docs/research/1062-repeating-radial-profile-evidence.md +0 -0
  115. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/__init__.py +0 -0
  116. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/_pmi_part21.py +0 -0
  117. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/_warnings.py +0 -0
  118. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/analysis.py +0 -0
  119. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotate.py +0 -0
  120. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotations/__init__.py +0 -0
  121. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/annotations/gears.py +0 -0
  122. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/audit.py +0 -0
  123. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/cli.py +0 -0
  124. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/evaluation/__init__.py +0 -0
  125. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/export.py +0 -0
  126. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/fits.py +0 -0
  127. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
  128. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
  129. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
  130. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
  131. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/fonts/__init__.py +0 -0
  132. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/intents.py +0 -0
  133. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/channel_coverage.py +0 -0
  134. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/flat_coverage.py +0 -0
  135. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/gear_coverage.py +0 -0
  136. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/polygonal_stock_coverage.py +0 -0
  137. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/profiled_bore_coverage.py +0 -0
  138. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/slot_coverage.py +0 -0
  139. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/linting/suggest.py +0 -0
  140. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/make_drawing.py +0 -0
  141. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/model/__init__.py +0 -0
  142. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/model/declare.py +0 -0
  143. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/model/ir.py +0 -0
  144. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/pmi.py +0 -0
  145. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/py.typed +0 -0
  146. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/recognition/__init__.py +0 -0
  147. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/recognition_cache.py +0 -0
  148. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/registry.py +0 -0
  149. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/score.py +0 -0
  150. {draftwright-0.4.7 → draftwright-0.4.9}/src/draftwright/sheet.py +0 -0
  151. {draftwright-0.4.7 → draftwright-0.4.9}/tests/_kernel.py +0 -0
  152. {draftwright-0.4.7 → draftwright-0.4.9}/tests/_layout_sig.py +0 -0
  153. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/evaluation/README.md +0 -0
  154. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/evaluation/ambiguous-open-semicircle.step +0 -0
  155. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/evaluation/blind-hole.step +0 -0
  156. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/evaluation/corpus-v1.json +0 -0
  157. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/evaluation/plain-block.step +0 -0
  158. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/evaluation/topology-a.step +0 -0
  159. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/evaluation/topology-b.step +0 -0
  160. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
  161. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
  162. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
  163. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
  164. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/issue_915_case_study_2.step +0 -0
  165. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
  166. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
  167. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
  168. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
  169. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
  170. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
  171. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
  172. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
  173. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
  174. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
  175. {draftwright-0.4.7 → draftwright-0.4.9}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
  176. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/centered_rebate.json +0 -0
  177. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/chamfered.json +0 -0
  178. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/filleted.json +0 -0
  179. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/flange_dense.json +0 -0
  180. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/grid_plate.json +0 -0
  181. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/hex_bar.json +0 -0
  182. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/holed_slot.json +0 -0
  183. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/prismatic_ladder.json +0 -0
  184. {draftwright-0.4.7 → draftwright-0.4.9}/tests/refactor_golden/turned_stepped.json +0 -0
  185. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_add_dimension.py +0 -0
  186. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_agents_guide.py +0 -0
  187. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_api_docs.py +0 -0
  188. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_balloon_ring_standoff.py +0 -0
  189. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_carve_free_position_callers.py +0 -0
  190. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_compiled_plan_boundary.py +0 -0
  191. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_counting_calls.py +0 -0
  192. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_cross_repository_delivery_protocol.py +0 -0
  193. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_declare.py +0 -0
  194. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_declared_recognition_gate.py +0 -0
  195. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_dense_sheet_canary.py +0 -0
  196. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_deprecation_dates.py +0 -0
  197. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_detect_once.py +0 -0
  198. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_dimension_role_vocabulary.py +0 -0
  199. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_e2e_slice.py +0 -0
  200. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_export_dxf_curves.py +0 -0
  201. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_export_dxf_zoom.py +0 -0
  202. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_external_recognition_boundary.py +0 -0
  203. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_fillets_adjacency.py +0 -0
  204. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_fits.py +0 -0
  205. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_flat_stock_opposition.py +0 -0
  206. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_gdt_placement.py +0 -0
  207. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_grid_lattice_convention.py +0 -0
  208. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_import_boundaries.py +0 -0
  209. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1000_envelope_reuse.py +0 -0
  210. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1058_wheel_profile.py +0 -0
  211. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1082_polygonal_stock.py +0 -0
  212. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1086_declared_gears.py +0 -0
  213. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_1142_hole_leader_labels.py +0 -0
  214. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_367_leader_ink.py +0 -0
  215. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
  216. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_885_prismatic_coverage.py +0 -0
  217. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_909_sloped_profile.py +0 -0
  218. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_915_dense_case.py +0 -0
  219. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_issue_955_height_contingency.py +0 -0
  220. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_layout.py +0 -0
  221. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_layout_property.py +0 -0
  222. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_lint_box_cache.py +0 -0
  223. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_lint_reconciliation.py +0 -0
  224. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_linting.py +0 -0
  225. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_location_vocabulary.py +0 -0
  226. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_note_verb.py +0 -0
  227. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_object_aspects.py +0 -0
  228. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_occupancy_boxes.py +0 -0
  229. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_parameter_id.py +0 -0
  230. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_part_model.py +0 -0
  231. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_pitch_dim_footprint.py +0 -0
  232. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_pmi_datum_lowering.py +0 -0
  233. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_pmi_gtol_lowering.py +0 -0
  234. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_pmi_part21.py +0 -0
  235. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_pocket_pattern.py +0 -0
  236. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_pocket_pattern_recognition.py +0 -0
  237. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_principal_profile_classifier.py +0 -0
  238. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_recogniser_contract.py +0 -0
  239. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_refactor_golden.py +0 -0
  240. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_registry.py +0 -0
  241. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_repack_geometry_seam.py +0 -0
  242. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_score.py +0 -0
  243. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_script_detail_parity.py +0 -0
  244. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_shared_box_memo.py +0 -0
  245. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_sheet_gdt.py +0 -0
  246. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_sheet_identity_invariant.py +0 -0
  247. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_sheet_notes.py +0 -0
  248. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_sheet_of.py +0 -0
  249. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_sheet_section.py +0 -0
  250. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_sheet_tables.py +0 -0
  251. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_slot_pattern.py +0 -0
  252. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_slot_pattern_recognition.py +0 -0
  253. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_soft_deprecation.py +0 -0
  254. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_solve_trace.py +0 -0
  255. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_step_analysis_evaluation.py +0 -0
  256. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_suppression_marks.py +0 -0
  257. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_turned_steps.py +0 -0
  258. {draftwright-0.4.7 → draftwright-0.4.9}/tests/test_witness_label_reconciliation.py +0 -0
  259. {draftwright-0.4.7 → 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,6 +1,167 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## v0.4.9 — 2026-08-21
4
+
5
+ ### Added
6
+
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).
31
+
32
+ ### Changed
33
+
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
62
+ recorded in `.github/recogniser-release.json`; focused compatibility evidence and the
63
+ normal consumer gates run on the generated PR.
64
+
65
+ ### Fixed
66
+
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
+
123
+ - Compatible automatic/deferred sparse ordinary side/plan hole and post-drain
124
+ machined-feature callouts now share one bounded same-view leader inventory
125
+ (#1166). Pattern, profiled-bore, and dense table-eligible hole queues retain
126
+ their specialised established paths because their winners affect downstream
127
+ furniture/table semantics or await #798 silhouette-aware routing. The solve
128
+ preserves required/priority-ranked evidence, rejects new-leader crossings,
129
+ checks complete leader ink against actual-width fixed dimension/witness,
130
+ arrow, label, and centre-furniture components (including rendered arrows on
131
+ shifted dimensions and component-local dashed circular centre furniture);
132
+ fixed residual faces and the selected survivor both require each tessellation
133
+ triangle to fit one same analytical component rather than merely the union,
134
+ plus exact continuous OCC containment for curved faces; residual lowering
135
+ also retains filled datum/GD&T faces absent from segment metadata; if candidate
136
+ or selected-survivor construction raises, or a survivor fails validation, the
137
+ bad alternative fails closed and the canonical lazy producer tail is replayed
138
+ instead of falsely dropping an otherwise valid callout; an all-invalid tail is
139
+ reported as validation-stage geometry failure rather than no clear room (so
140
+ scale fallback does not retry a non-spatial helper defect), while compiler
141
+ invariant violations such as a missing semantic callout label remain loud;
142
+ only explicitly marked global turning axes permit an arrow-local leader-tip
143
+ attachment; near-collinear shaft travel and ownerless section/cutting lines
144
+ remain fixed ink. The solve prefers
145
+ clear routes over explicit Policy-B fallbacks, and then minimises real leader
146
+ length. Provisional sections cannot veto that primary result: one separately
147
+ bounded refinement may choose an equally complete leader inventory that clears
148
+ their exact components, after which the final section performs one bounded
149
+ end-symbol repair and yields if exact ink still conflicts. The selected OCC survivor is validated against the same analytical
150
+ ink contract. A retained fixed-ink crossing now emits the machine-readable
151
+ `feature_leader_crossing` info finding instead of appearing lint-clean. Candidate,
152
+ fixed-component lowering, fixed-obstacle-probe, pair, and exact-search work is
153
+ bounded; the fixed inventory is lowered once and reused with the producer's
154
+ legacy lazy result as the floor, including state-cap exhaustion; page,
155
+ own-silhouette, and title-reservation constraints remain hard in every fallback.
156
+ A replay that exhausts exact fixed-probe classification is retained visibly as
157
+ `feature_leader_fixed_ink_unverified` rather than rescanning past the cap, and
158
+ that uncertainty participates in the legibility quality inventory.
159
+ Trace output preserves an abandoned fully admitted inventory when state search
160
+ exhausts, records the producer-floor replay separately, and names exact committed/provisional components,
161
+ refinement status, and competing blockers; live
162
+ single-callout edits remain immediate.
163
+
164
+ ## v0.4.7 — 2026-08-16
4
165
 
5
166
  ### Added
6
167
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: draftwright
3
- Version: 0.4.7
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
@@ -30,11 +30,12 @@ architecture** table; open retired or superseded records only for design history
30
30
  | [0011](0011-ir-as-public-input.md) | The IR as a public input: declare features, don't only detect them | Accept the feature IR as public input through declarations and the `Sheet` façade. | Accepted; core landed; #62/#462/#495 remain | `test_declare.py`, `test_object_aspects.py`, `test_sheet_gdt.py` |
31
31
  | [0012](0012-edits-as-pinned-priority-candidates-in-the-global-solve.md) | User annotation edits are pinned, priority-ranked corridor candidates | Drain recorded semantic edits through corridor placement with pin and priority. | Accepted; partially landed; full recomposition/parity remains #426/#707 (#661 detail views landed) | `test_make_drawing.py`, `test_sheet_emit.py` |
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
- | [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; post-drain feature leaders also assign jointly within each pass. | Accepted; supersedes 0009; #740 Amendment 1 | `test_carve_free_position_callers.py`, `test_strip_layout.py`, `test_layout_property.py`, `test_issue_740_leader_assignment.py`, `test_import_boundaries.py` |
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.7"
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):