draftwright 0.3.9__tar.gz → 0.4.1__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 (200) hide show
  1. {draftwright-0.3.9 → draftwright-0.4.1}/CHANGELOG.md +247 -0
  2. {draftwright-0.3.9 → draftwright-0.4.1}/PKG-INFO +26 -3
  3. {draftwright-0.3.9 → draftwright-0.4.1}/README.md +25 -2
  4. {draftwright-0.3.9 → draftwright-0.4.1}/docs/adr/README.md +4 -0
  5. {draftwright-0.3.9 → draftwright-0.4.1}/pyproject.toml +21 -3
  6. {draftwright-0.3.9 → draftwright-0.4.1}/skills/SKILL.md +99 -43
  7. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/__init__.py +8 -3
  8. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/_core.py +311 -53
  9. draftwright-0.4.1/src/draftwright/_geometry.py +295 -0
  10. draftwright-0.4.1/src/draftwright/_warnings.py +39 -0
  11. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/analysis.py +124 -42
  12. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/annotate.py +1 -1
  13. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/annotations/_common.py +107 -17
  14. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/annotations/balloons.py +125 -25
  15. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/annotations/from_model.py +1602 -642
  16. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/annotations/holes.py +567 -157
  17. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/annotations/orchestrator.py +99 -31
  18. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/annotations/sections.py +209 -67
  19. draftwright-0.4.1/src/draftwright/audit.py +249 -0
  20. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/builder.py +190 -431
  21. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/cli.py +36 -63
  22. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/compose.py +61 -54
  23. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/drawing.py +700 -167
  24. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/layout.py +81 -6
  25. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/linting/__init__.py +12 -0
  26. draftwright-0.4.1/src/draftwright/linting/channel_coverage.py +190 -0
  27. draftwright-0.4.1/src/draftwright/linting/coverage.py +1320 -0
  28. draftwright-0.4.1/src/draftwright/linting/flat_coverage.py +207 -0
  29. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/linting/issues.py +4 -0
  30. draftwright-0.4.1/src/draftwright/linting/profiled_bore_coverage.py +122 -0
  31. draftwright-0.4.1/src/draftwright/linting/slot_coverage.py +315 -0
  32. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/make_drawing.py +1 -2
  33. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/model/__init__.py +24 -2
  34. draftwright-0.4.1/src/draftwright/model/callout.py +366 -0
  35. draftwright-0.4.1/src/draftwright/model/compiled.py +1183 -0
  36. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/model/declare.py +393 -37
  37. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/model/detect.py +183 -36
  38. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/model/ir.py +408 -10
  39. draftwright-0.4.1/src/draftwright/model/planner.py +867 -0
  40. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/projection.py +9 -1
  41. draftwright-0.4.1/src/draftwright/py.typed +0 -0
  42. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/__init__.py +33 -6
  43. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/_features.py +89 -24
  44. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/flats.py +103 -3
  45. draftwright-0.4.1/src/draftwright/recognition/levels.py +346 -0
  46. draftwright-0.4.1/src/draftwright/recognition/pads.py +138 -0
  47. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/plates.py +5 -0
  48. draftwright-0.4.1/src/draftwright/recognition/profiled_bores.py +368 -0
  49. draftwright-0.4.1/src/draftwright/recognition/result.py +277 -0
  50. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/slots.py +226 -21
  51. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/registry.py +120 -4
  52. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/repair.py +11 -3
  53. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/score.py +2 -0
  54. draftwright-0.4.1/src/draftwright/sheet.py +1825 -0
  55. draftwright-0.4.1/src/draftwright/sheet_emit.py +1306 -0
  56. draftwright-0.4.1/tests/conftest.py +84 -0
  57. draftwright-0.4.1/tests/fixtures/if_step_flat_across_cylinder.step +189 -0
  58. draftwright-0.4.1/tests/fixtures/issue_1058_wheel_rh.step +64624 -0
  59. draftwright-0.4.1/tests/fixtures/issue_909_basic_part_design_017_body.step +465 -0
  60. draftwright-0.4.1/tests/fixtures/issue_915_case_study_2.step +3527 -0
  61. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/pocketed.json +39 -3
  62. draftwright-0.4.1/tests/test_add_dimension.py +1358 -0
  63. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_agents_guide.py +2 -2
  64. draftwright-0.4.1/tests/test_audit_differential.py +768 -0
  65. draftwright-0.4.1/tests/test_balloon_ring_standoff.py +78 -0
  66. draftwright-0.4.1/tests/test_compiled_plan_boundary.py +1216 -0
  67. draftwright-0.4.1/tests/test_counting_calls.py +117 -0
  68. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_declare.py +492 -65
  69. draftwright-0.4.1/tests/test_declared_recognition_gate.py +542 -0
  70. draftwright-0.4.1/tests/test_deprecation_dates.py +153 -0
  71. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_detect_once.py +40 -38
  72. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_detect_registry.py +12 -3
  73. draftwright-0.4.1/tests/test_dimension_role_vocabulary.py +378 -0
  74. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_drawing_encapsulation.py +105 -7
  75. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_e2e_slice.py +3 -1
  76. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_e2e_standards.py +70 -16
  77. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_fits.py +3 -3
  78. draftwright-0.4.1/tests/test_flat_completeness.py +330 -0
  79. draftwright-0.4.1/tests/test_flat_stock_identity.py +240 -0
  80. draftwright-0.4.1/tests/test_flat_stock_opposition.py +63 -0
  81. draftwright-0.4.1/tests/test_grid_lattice_convention.py +327 -0
  82. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_import_boundaries.py +9 -1
  83. draftwright-0.4.1/tests/test_issue_1058_wheel_profile.py +676 -0
  84. draftwright-0.4.1/tests/test_issue_885_prismatic_coverage.py +126 -0
  85. draftwright-0.4.1/tests/test_issue_909_sloped_profile.py +150 -0
  86. draftwright-0.4.1/tests/test_issue_915_dense_case.py +108 -0
  87. draftwright-0.4.1/tests/test_issue_916_pocket_leader.py +102 -0
  88. draftwright-0.4.1/tests/test_issue_917_open_channel.py +189 -0
  89. draftwright-0.4.1/tests/test_label_provenance.py +206 -0
  90. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_layout.py +80 -0
  91. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_lint_reconciliation.py +3 -3
  92. draftwright-0.4.1/tests/test_location_vocabulary.py +400 -0
  93. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_make_drawing.py +1214 -324
  94. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_object_aspects.py +6 -6
  95. draftwright-0.4.1/tests/test_parameter_id.py +537 -0
  96. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_part_model.py +2 -2
  97. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_pmi.py +3 -1
  98. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_pocket_pattern.py +8 -7
  99. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_pocket_pattern_recognition.py +29 -1
  100. draftwright-0.4.1/tests/test_principal_profile_classifier.py +122 -0
  101. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_private_test_attr_reads.py +0 -7
  102. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_recogniser_contract.py +35 -4
  103. draftwright-0.4.1/tests/test_recognition_manifest.py +567 -0
  104. draftwright-0.4.1/tests/test_recognition_result.py +340 -0
  105. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_registry.py +93 -0
  106. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_render_seam.py +37 -1
  107. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_script_detail_parity.py +121 -54
  108. draftwright-0.4.1/tests/test_sheet_emit.py +3426 -0
  109. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_sheet_gdt.py +12 -12
  110. draftwright-0.4.1/tests/test_sheet_identity_invariant.py +807 -0
  111. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_sheet_notes.py +14 -14
  112. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_sheet_of.py +3 -3
  113. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_sheet_section.py +1 -1
  114. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_sheet_tables.py +3 -1
  115. draftwright-0.4.1/tests/test_slanted_blind_step.py +291 -0
  116. draftwright-0.4.1/tests/test_slot_completeness.py +595 -0
  117. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_slot_pattern.py +10 -8
  118. draftwright-0.4.1/tests/test_soft_deprecation.py +221 -0
  119. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_solve_trace.py +5 -2
  120. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_strip_layout.py +69 -5
  121. draftwright-0.4.1/tests/test_suppression_ledger.py +380 -0
  122. draftwright-0.4.1/tests/test_suppression_marks.py +553 -0
  123. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_tolerances.py +64 -40
  124. draftwright-0.3.9/src/draftwright/_geometry.py +0 -142
  125. draftwright-0.3.9/src/draftwright/linting/coverage.py +0 -581
  126. draftwright-0.3.9/src/draftwright/model/planner.py +0 -320
  127. draftwright-0.3.9/src/draftwright/recognition/levels.py +0 -175
  128. draftwright-0.3.9/src/draftwright/sheet.py +0 -1016
  129. draftwright-0.3.9/src/draftwright/sheet_dsl.py +0 -35
  130. draftwright-0.3.9/src/draftwright/sheet_emit.py +0 -766
  131. draftwright-0.3.9/tests/test_sheet_emit.py +0 -1280
  132. {draftwright-0.3.9 → draftwright-0.4.1}/.gitignore +0 -0
  133. {draftwright-0.3.9 → draftwright-0.4.1}/LICENSE +0 -0
  134. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/annotations/__init__.py +0 -0
  135. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/export.py +0 -0
  136. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/fits.py +0 -0
  137. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
  138. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
  139. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
  140. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
  141. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/fonts/__init__.py +0 -0
  142. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/intents.py +0 -0
  143. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/linting/structural.py +0 -0
  144. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/linting/suggest.py +0 -0
  145. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/pmi.py +0 -0
  146. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/_record.py +0 -0
  147. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/chamfers.py +0 -0
  148. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/countersinks.py +0 -0
  149. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/fillets.py +0 -0
  150. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/grooves.py +0 -0
  151. {draftwright-0.3.9 → draftwright-0.4.1}/src/draftwright/recognition/turned.py +0 -0
  152. {draftwright-0.3.9 → draftwright-0.4.1}/tests/_kernel.py +0 -0
  153. {draftwright-0.3.9 → draftwright-0.4.1}/tests/_layout_sig.py +0 -0
  154. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
  155. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
  156. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
  157. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
  158. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
  159. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
  160. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
  161. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
  162. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
  163. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
  164. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
  165. {draftwright-0.3.9 → draftwright-0.4.1}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
  166. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/bracket_section.json +0 -0
  167. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/centered_rebate.json +0 -0
  168. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/chamfered.json +0 -0
  169. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/filleted.json +0 -0
  170. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/flange_dense.json +0 -0
  171. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/grid_plate.json +0 -0
  172. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/grooved_shaft.json +0 -0
  173. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/hex_bar.json +0 -0
  174. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/holed_slot.json +0 -0
  175. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/prismatic_ladder.json +0 -0
  176. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/scattered_plate.json +0 -0
  177. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/side_drilled.json +0 -0
  178. {draftwright-0.3.9 → draftwright-0.4.1}/tests/refactor_golden/turned_stepped.json +0 -0
  179. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_architecture_docs.py +0 -0
  180. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_carve_free_position_callers.py +0 -0
  181. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_dense_sheet_canary.py +0 -0
  182. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_export_dxf_zoom.py +0 -0
  183. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_fillets_adjacency.py +0 -0
  184. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_gdt_placement.py +0 -0
  185. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_layout_cleanliness.py +0 -0
  186. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_layout_hypothesis.py +0 -0
  187. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_layout_property.py +0 -0
  188. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_lint_box_cache.py +0 -0
  189. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_lint_structural.py +0 -0
  190. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_linting.py +0 -0
  191. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_note_verb.py +0 -0
  192. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_occupancy_boxes.py +0 -0
  193. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_pitch_dim_footprint.py +0 -0
  194. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_private_test_imports.py +0 -0
  195. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_recognition.py +0 -0
  196. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_refactor_golden.py +0 -0
  197. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_score.py +0 -0
  198. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_slot_pattern_recognition.py +0 -0
  199. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_turned_steps.py +0 -0
  200. {draftwright-0.3.9 → draftwright-0.4.1}/tests/test_witness_label_reconciliation.py +0 -0
@@ -1,5 +1,252 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## v0.4.1 — 2026-08-07
6
+
7
+ **The recognition trust release.** Automatic drawings now fail closed on unsupported inner
8
+ profiles, prove semantic coverage for flats, slots and patterns, and retain the measurement
9
+ identity needed to explain placed, suppressed, dropped and missing definitions. Several real
10
+ STEP case studies also move from clean-looking but incomplete output to complete drawings or
11
+ actionable lint.
12
+
13
+ ### Discouraged (supported)
14
+
15
+ - **`Sheet.auto_dimensions()` and `Sheet.add_dimension()` now emit
16
+ `SoftDeprecationWarning`** (#1043). They remain supported with no removal planned; the
17
+ warning steers declared scripts toward `authored_dimensions()` plus explicit
18
+ `dimension(feature, parameter_id)` lines, where omission can mean suppression. Automatic
19
+ `build_drawing(part)` and the `Sheet.from_part(part)` on-ramp remain silent. See
20
+ `docs/deprecations.md` for filtering and migration guidance.
21
+
22
+ ### Added
23
+
24
+ - **Finished drawings expose why measurements were omitted and, on covered placement paths,
25
+ what an annotation measures** (#996/#1002). `Drawing.suppressions()` returns the compiler's
26
+ omission ledger, `Drawing.measurement_keys(name)` exposes recorded measurement provenance, and
27
+ `draftwright.audit.diff_builds(before, after)` reports losses, substitutions and candidate
28
+ suppression explanations. The differential is deliberately a triage aid, not a proof where
29
+ renderer identity remains incomplete.
30
+
31
+ - **Physical-completeness lint now follows semantic correspondence for flats, lone slots and
32
+ slot patterns** (#1018). Coverage is joined through recognition, IR and compiler identities
33
+ rather than inferred from label text or page geometry, and distinguishes authored
34
+ suppression, placement drops, missing definitions and unverifiable provenance.
35
+
36
+ - **Full-span floored open channels are recognised and dimensioned as first-class features**
37
+ (#917). Automatic and declared drawings state the independent overall extent, one wall and
38
+ channel width, suppress the derived opposite wall, and report any missing member of that
39
+ defining chain.
40
+
41
+ ### Fixed
42
+
43
+ - **Square and near-square parts retain both independent planar extents** (#997). A plain
44
+ square plate could previously lose both plan dimensions, while parts up to five percent off
45
+ square could lose one and be silently represented as square. The unsafe suppression rule is
46
+ removed; explicit square notation remains tracked separately in #918.
47
+
48
+ - **Machined flats retain their physical stock identity from recognition through placement
49
+ and completeness lint** (#1013/#1015/#1034/#1036). Flats on separate parallel, coaxial or
50
+ slanted stock no longer collapse into one definition or borrow an opposite face from another
51
+ stock region. Independent callouts receive clear-margin candidates, while double-D faces on
52
+ one stock still form one A/F requirement.
53
+
54
+ - **Dense callout and step-detail cases keep their complete defining set** (#915). Hole
55
+ callout batches are reconciled as a queue, detail views fit their actual aspect, step levels
56
+ retain the supporting-face correspondence needed for truthful crops, and a detail redraws
57
+ only rungs omitted from its parent view.
58
+
59
+ - **Pocket leaders start on the physical opening rim** (#916), removing the spurious inner
60
+ silhouette crossing while preserving the solved label corridor and approved dimensions on
61
+ X-, Y- and Z-depth pockets.
62
+
63
+ - **Wedge-mounted raised pads survive recognition** (#909). Lower ledges that touch only in
64
+ plan no longer make the recogniser discard a valid upper pad; true staircase tiers remain
65
+ excluded.
66
+
67
+ - **Annotation-dense detail placement no longer stalls the build** (#1065). Detail views
68
+ avoid every decomposed annotation shaft, witness, label and item footprint; CTC-02
69
+ supplies 571 such boxes. The free-rectangle search previously treated that set as tiny
70
+ and admitted roughly 34 billion candidate rectangles, exhausting the slow tier's
71
+ ten-minute test budget before export. A compressed-coordinate sweep preserves the same
72
+ deterministic winning rectangle while completing the real search in seconds.
73
+
74
+ - **Through double-D bores are recognised and called out as first-class hole profiles**
75
+ (#1061). Automatic and declared builds preserve the parent-circle diameter, A/F size,
76
+ orientation and depth, and render one compound `DOUBLE-D ... A/F` bore callout. Physical
77
+ critique now accepts that supported inner profile while retaining a separate warning for
78
+ 13 evenly spaced common-circle arcs on the unresolved outer boundary; it does not infer a
79
+ repeating full profile or a gear standard.
80
+
81
+ - **Unsupported internal profiles no longer pass physical-completeness lint silently**
82
+ (#1058). Inner loops on principal boundary faces are checked against the current
83
+ circular-hole, rectangular-opening, true-obround, and proven through-double-D vocabulary.
84
+ Profiles outside it now produce
85
+ `unrecognised_defining_geometry` and reduce the lint score instead of allowing an
86
+ envelope-only drawing to score 1.0.
87
+
88
+ ### Changed
89
+
90
+ - **Crowded prismatic step dimensions recover into an enlarged detail view by default**
91
+ (#909). The automatic `build_drawing` / `make_drawing` / CLI paths and generated
92
+ `Sheet` scripts now preserve the omitted dimensions instead of returning
93
+ `step_dim_dropped` unless the caller remembered `detail_view=True`. Pass
94
+ `detail_view=False` to retain the parent-view-only behavior and its lint warning.
95
+
96
+ ## v0.4.0 — 2026-08-02
97
+
98
+ **The compat-exit release.** Every surface whose documented removal target was 0.4.0 is gone,
99
+ and every deprecation that remains now says when it goes — in the message the caller sees, not
100
+ only in a release note. ADR 0005 §4's rule is that an alias carries a tracking issue *and* a
101
+ removal date, because "a facade with no exit date is a failure mode"; this release is that rule
102
+ being kept rather than restated. `docs/deprecations.md` is the new index, and
103
+ `tests/test_deprecation_dates.py` fails any deprecation that names no removal.
104
+
105
+ Also lands ADR 0016's declared-dimensioning work (#867): a `dimension(feature, role)` is
106
+ referential — it names a measurement and carries no number — and an authored set means omission
107
+ is suppression, enforced at the compiled-plan boundary rather than by a flag renderers check.
108
+
109
+ **Read this before upgrading:** two removals break *without a release that warned you*, because
110
+ their deprecation never appeared in a shipped version. See *Removed (breaking)* below and
111
+ `docs/deprecations.md`.
112
+
113
+ ### Removed (breaking)
114
+
115
+ Compat surfaces whose documented removal target was 0.4.0 (#720, ADR 0005 §4 — every alias
116
+ carries a tracking issue *and* a removal date, or the facade is permanent by accident):
117
+
118
+ - **The seven `Drawing` compat aliases** — `_named` / `_anno_view` / `_pinned` /
119
+ `_build_issues`, and `_pattern_callouts` / `_patterned_holes` / `_dropped_callout_diams`.
120
+ These were private, and the public reads have existed since #699: use `dwg.registry`
121
+ (`in reg`, `names()`, `issues`, `pinned_names()`) and `dwg.coverage`, or the `Drawing` verbs
122
+ `annotations()` / `iter_annotations()` / `get_annotation()` / `view_of()`.
123
+ **Note the asymmetry.** A *read* — `dwg._named` — now raises `AttributeError`. A *write* —
124
+ `dwg._pinned = {...}` — does **not**: these were properties with setters, and `Drawing` has
125
+ no `__slots__`, so assignment now quietly creates an unrelated instance attribute that no
126
+ longer reaches the registry or coverage owner. Writes therefore fail silently rather than
127
+ loudly. Grep for `\._(named|anno_view|pinned|build_issues|pattern_callouts|patterned_holes|dropped_callout_diams)\b`
128
+ before upgrading; mutate through `registry.add()` / `pin()` / `record_issue()` /
129
+ `restore_issues()` and the `CoverageState` methods instead.
130
+ - **The `draftwright.sheet_dsl` module** — an import alias for `draftwright.sheet` since the
131
+ #640 rename. Import `Sheet` from `draftwright` or `draftwright.sheet`.
132
+ - **`generate_script`**, including its `draftwright.__all__` entry. It has raised since #940
133
+ retired the imperative emitter; it is now simply absent, so the failure is an `ImportError`
134
+ at the top of a script rather than a `RuntimeError` part-way through one. Use
135
+ `--script` / `emit_sheet_script`.
136
+ - **The bespoke `--style imperative` error message.** `--style` itself is unchanged and still
137
+ accepts its sole value `sheet`; `imperative` is now an unrecognised value like any typo,
138
+ rather than one carrying its own explanation of the #940 retirement.
139
+
140
+ - **Bare dimension-role spellings** — `sheet.dimension(f, "width")`. Use the parameter id,
141
+ `"width.length"`; `dimension_ids()` on a handle lists the valid ones. The bare role is the
142
+ *family* spelling: it selects every parameter carrying it, which is how
143
+ `dimension(step, "step")` quietly declared two measurements. In an authored set, where
144
+ omission means suppression, silently declaring an extra one is the mirror image of the rule
145
+ — so it now raises rather than resolving. (A *discriminated* bare role, used with `axis=`,
146
+ is unaffected: that is how variants like `grid_pitch.length.row` are addressed, and it never
147
+ warned.)
148
+ - **The `Sheet.dimension(kind=…, value=…)` call shape** — use `Sheet.measured_dimension(...)`.
149
+ `dimension` is now solely the ADR 0016 referential verb: it names a feature and a parameter
150
+ id and reads the value off the geometry.
151
+
152
+ Those last two break **without a release that warns you**. They were deprecated after v0.3.9
153
+ and removed in 0.4.0, so the `DeprecationWarning` never appeared in a released version —
154
+ upgrading from v0.3.9 or earlier goes straight from working to a raise. That is deliberate
155
+ (ADR 0016); this entry and `docs/deprecations.md` are the only notice you get, so both
156
+ failures name their replacement rather than raising about argument counts.
157
+
158
+ ### Deprecated
159
+
160
+ - **The legacy `Drawing.export` shapes now emit `DeprecationWarning`** (#987) — they were
161
+ announced as deprecated in v0.3.1 and then said nothing at runtime for four minor releases,
162
+ which would have made their 0.5.0 removal a silent break. Three cases warn:
163
+ - `export()` with `formats=` **omitted or `None`** — the legacy default, which writes
164
+ SVG + DXF and returns an `(svg, dxf)` **tuple** rather than the `{format: path}` dict.
165
+ - the **`svg=` / `dxf=` booleans**. The suggested replacement names the formats *that call*
166
+ selected, so following it cannot change what gets written.
167
+ - passing a boolean **alongside** `formats=`, where it is silently ignored — `formats` wins,
168
+ and now says so.
169
+
170
+ **If you promote `DeprecationWarning` to an error, previously-passing exports will now
171
+ raise.** Migration: `export(out, formats=("svg", "dxf"))` and read the dict. `make_drawing`
172
+ is unaffected — it returns the same tuple and does not warn.
173
+
174
+ ### Added
175
+
176
+ - **`sheet.add_dimension(feature, role)` — ask the planner to carry one more
177
+ measurement** (ADR 0016 / #872). Referential: it names a feature and a role and
178
+ carries **no number**, so the value still comes from the geometry and a size lives in
179
+ exactly one place. It changes *selection*, not derivation — a request can never
180
+ introduce a number the part does not have. Requesting something the planner already
181
+ emits is a deliberate no-op, so a script can ask without first knowing the rule set's
182
+ mind. `sheet.auto_dimensions()` states the source explicitly (optional in this
183
+ release; #874 makes it mandatory).
184
+ - **Suppressing a dimension marks it; it no longer leaks into the callout** (ADR 0016 /
185
+ #875). `PlannedDimension.suppressed` was honoured at thirteen render sites but not by
186
+ the compound hole-callout path, so a suppressed counterbore still printed. The group
187
+ keeps its engineering data either way — what changes is whether a value reaches the
188
+ page. Suppressing the bore ⌀ while a counterbore, spotface or countersink segment
189
+ remains now **raises** and names the orphan: `⌀20 THRU ⌴ ⌀32 ↓ 1.5` has no reading with
190
+ its leading term removed, and silently dropping the segments would discard authored
191
+ intent while silently restoring the head would make the drawing say something the
192
+ script does not.
193
+ - **Addressable dimension identity** (#869/#870/#871): every planned measurement now has
194
+ a stable `DimensionId(feature, parameter)`, and correlated measurements that must be
195
+ named as one — a grid pattern's row and column pitch, a step ladder — group into an
196
+ `AddressableDimension`. This is what a `dimension(...)` line will name, and what
197
+ suppression and provenance key on.
198
+
199
+ ### Changed
200
+
201
+ - **`Sheet.dimension(kind=…, value=…)` is now `Sheet.measured_dimension(...)`**
202
+ (ADR 0016 / #873), and `model.declare.authored_dimension` is
203
+ `model.declare.measured_dimension` to match. This **reserves** the name `dimension` for
204
+ the referential verb ADR 0016 defines — name a feature and a role, carry no number, let
205
+ the engine read the value off the geometry — which `Drawing.dimension` already is and
206
+ `Sheet.dimension` will become. The verb that carries an explicit number needed a name
207
+ saying so first. The old spelling was a transitional overload during development and is
208
+ **removed in this same release** (#720) rather than shipping — `Sheet.dimension` is solely
209
+ the referential verb, and the old keyword call raises with `measured_dimension` named. See
210
+ *Removed (breaking)* above. Generated AP242 scripts emit `measured_dimension` and so arrive
211
+ un-deprecated.
212
+ - **`Sheet` handles address features by identity, not position** (#908/#910/#912). A
213
+ handle, tolerance, GD&T origin, section request or dimension intent now follows *its*
214
+ feature through a `features` reorder instead of naming whatever took the slot.
215
+ Reordering with `reverse()`/`sort()` preserves every reference; deleting or replacing
216
+ a referenced feature raises rather than silently retargeting a neighbour. `of()` now
217
+ accepts a handle, and negative indices resolve uniformly across `of()`,
218
+ `add_dimension()` and the GD&T verbs.
219
+
220
+ ### Fixed
221
+
222
+ - **`sheet.envelope()` measured the file, not the part** (#977). An AP242 STEP import is a
223
+ compound of the solid *plus its PMI presentation geometry* — annotation planes, leader
224
+ curves — and the declared verb measured all of it. On the NIST CTC-01 fixture it declared
225
+ 1170 × 650 where the part is 800 × 450: an envelope 370 mm too wide, silently, in anything
226
+ declared by hand. `step_level()` had the same exposure, offsetting every step position by
227
+ the annotation overhang. Both now measure the solid body, sharing the helper the engine
228
+ already used for exactly this (`_analyse` and `Sheet.model`).
229
+ - **A declared envelope's frame origin sat a half-height below a detected one** (#977). It
230
+ used `bbox_min.Z` where the detector uses the bbox centre. **Dimension output is unchanged** —
231
+ envelope sizes derive from `bbox_min`/`bbox_max`, and the overall height is compiled from the
232
+ model bbox — but `frame.origin` is the generic feature *site*, so **a GD&T control frame,
233
+ surface finish or note targeting a hand-declared `sheet.envelope()` was anchored at the
234
+ bottom face rather than the centre, and its leader now moves to the centre**. That is a
235
+ visible change on drawings carrying such an annotation; drawings without one are unaffected.
236
+ Scripts generated by `--script` were never affected either way: they bake the detected frame
237
+ explicitly rather than calling the verb.
238
+
239
+
240
+ - **Front-view dimensions join the placement solve instead of committing to the strip**
241
+ (#894): they previously took strip space before the global solve ran, so a
242
+ higher-ranked dimension could find its space already gone. Priority only ranks
243
+ candidates that are *in* the solve.
244
+ - **A hole callout reads the feature's own `through` fact** (#868) rather than inferring
245
+ it from whether a depth parameter happens to be present — so a suppressed depth can no
246
+ longer make a blind hole print `THRU`.
247
+ - **The balloon ring clears the view it annotates after a hole-table escalation**
248
+ (#901/#903).
249
+
3
250
  ## v0.3.9 — 2026-07-26
4
251
 
5
252
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: draftwright
3
- Version: 0.3.9
3
+ Version: 0.4.1
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
@@ -771,9 +771,12 @@ from draftwright import build_drawing
771
771
 
772
772
  dwg = build_drawing(part, title="Mounting Plate")
773
773
  issues = dwg.lint() # list[LintIssue] — coverage, page bounds, ISO
774
- svg_path, dxf_path = dwg.export("my_part")
774
+ paths = dwg.export("my_part", formats=("svg", "dxf")) # {"svg": ..., "dxf": ...}
775
775
  ```
776
776
 
777
+ `export()` with no `formats=` returns the old `(svg, dxf)` tuple; that shape is deprecated
778
+ and removed in 0.5.0 (see [docs/deprecations.md](docs/deprecations.md)).
779
+
777
780
  ### Declarative drawings — reference features, declare intent
778
781
 
779
782
  Instead of relying on detection, **reference the build123d objects you built** and declare
@@ -789,10 +792,16 @@ plate = Box(120, 80, 20)
789
792
  bore = Pos(0, 0, 0) * Cylinder(4, 20)
790
793
 
791
794
  sheet = Sheet(plate - bore, title="Plate", number="DWG-002")
792
- sheet.envelope()
795
+ env = sheet.envelope()
793
796
  sheet.datum("A", plate.faces().sort_by()[-1]) # datum A on the top face
794
797
  hole = sheet.hole(bore).finish("1.6").note("M3x0.5 TAP") # ⌀8 bore, Ra 1.6, tapped
795
798
  sheet.control(hole).position(0.1, to="A", diameter=True) # ⌀0.1 position wrt A
799
+
800
+ sheet.authored_dimensions() # THIS is the complete set
801
+ sheet.dimension(env, "width.length") # (ADR 0016: a build says where
802
+ sheet.dimension(env, "height.length") # its dimensions come from, and
803
+ sheet.dimension(hole, "bore.diameter") # omitting one means suppress it)
804
+
796
805
  sheet.export("plate") # writes plate.pdf
797
806
  ```
798
807
 
@@ -834,6 +843,20 @@ sheet.hole(features.m3_bore).thread("M3x0.5").finish("1.6") # tapped + Ra on t
834
843
  An object-sourced script carries an inline tip pointing at exactly this edit; a STEP-sourced
835
844
  script keeps the detected numbers (there's no object to reference).
836
845
 
846
+ Every generated script ends by naming the drawing it built, so you can critique or inspect it
847
+ before (or instead of) exporting — one build, not two:
848
+
849
+ ```python
850
+ drawing = sheet.build()
851
+
852
+ for issue in drawing.lint(): # ISO/coverage critique of THIS drawing
853
+ print(issue.severity, issue.code, issue.message)
854
+
855
+ drawing.export("part", formats=("pdf",))
856
+ ```
857
+
858
+ `sheet.export(...)` remains the one-liner for handwritten scripts that just want the file.
859
+
837
860
  See [`docs/multi-feature-object-reference-workflow.md`](docs/multi-feature-object-reference-workflow.md)
838
861
  for a complete, runnable walkthrough of this pattern on a multi-feature turned part —
839
862
  including the backward-compatible refactor, external threads (no `.thread()` on a step —
@@ -75,9 +75,12 @@ from draftwright import build_drawing
75
75
 
76
76
  dwg = build_drawing(part, title="Mounting Plate")
77
77
  issues = dwg.lint() # list[LintIssue] — coverage, page bounds, ISO
78
- svg_path, dxf_path = dwg.export("my_part")
78
+ paths = dwg.export("my_part", formats=("svg", "dxf")) # {"svg": ..., "dxf": ...}
79
79
  ```
80
80
 
81
+ `export()` with no `formats=` returns the old `(svg, dxf)` tuple; that shape is deprecated
82
+ and removed in 0.5.0 (see [docs/deprecations.md](docs/deprecations.md)).
83
+
81
84
  ### Declarative drawings — reference features, declare intent
82
85
 
83
86
  Instead of relying on detection, **reference the build123d objects you built** and declare
@@ -93,10 +96,16 @@ plate = Box(120, 80, 20)
93
96
  bore = Pos(0, 0, 0) * Cylinder(4, 20)
94
97
 
95
98
  sheet = Sheet(plate - bore, title="Plate", number="DWG-002")
96
- sheet.envelope()
99
+ env = sheet.envelope()
97
100
  sheet.datum("A", plate.faces().sort_by()[-1]) # datum A on the top face
98
101
  hole = sheet.hole(bore).finish("1.6").note("M3x0.5 TAP") # ⌀8 bore, Ra 1.6, tapped
99
102
  sheet.control(hole).position(0.1, to="A", diameter=True) # ⌀0.1 position wrt A
103
+
104
+ sheet.authored_dimensions() # THIS is the complete set
105
+ sheet.dimension(env, "width.length") # (ADR 0016: a build says where
106
+ sheet.dimension(env, "height.length") # its dimensions come from, and
107
+ sheet.dimension(hole, "bore.diameter") # omitting one means suppress it)
108
+
100
109
  sheet.export("plate") # writes plate.pdf
101
110
  ```
102
111
 
@@ -138,6 +147,20 @@ sheet.hole(features.m3_bore).thread("M3x0.5").finish("1.6") # tapped + Ra on t
138
147
  An object-sourced script carries an inline tip pointing at exactly this edit; a STEP-sourced
139
148
  script keeps the detected numbers (there's no object to reference).
140
149
 
150
+ Every generated script ends by naming the drawing it built, so you can critique or inspect it
151
+ before (or instead of) exporting — one build, not two:
152
+
153
+ ```python
154
+ drawing = sheet.build()
155
+
156
+ for issue in drawing.lint(): # ISO/coverage critique of THIS drawing
157
+ print(issue.severity, issue.code, issue.message)
158
+
159
+ drawing.export("part", formats=("pdf",))
160
+ ```
161
+
162
+ `sheet.export(...)` remains the one-liner for handwritten scripts that just want the file.
163
+
141
164
  See [`docs/multi-feature-object-reference-workflow.md`](docs/multi-feature-object-reference-workflow.md)
142
165
  for a complete, runnable walkthrough of this pattern on a multi-feature turned part —
143
166
  including the backward-compatible refactor, external threads (no `.thread()` on a step —
@@ -32,6 +32,8 @@ 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 (with `b123d-recognisers` as its deferred shared deployment) | Enforce a uniform geometry-only recogniser contract internally; defer package extraction until there is a second consumer. | Accepted; Phase 1 in progress; extraction deferred | `test_recogniser_contract.py`, `test_import_boundaries.py` |
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. | Accepted; supersedes 0009 | `test_carve_free_position_callers.py`, `test_strip_layout.py`, `test_layout_property.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` |
36
+ | [0017](0017-recognition-inventory-correspondence-and-measurement-provenance.md) | One recognition result per run; correspondence is evidence-gated | Produce one explicit recognition result owned by `BuildState`; require vertical-slice evidence before generalising correspondence, identity, requirements, outcomes, or reconciliation. | Accepted; ownership phase landed, extensions gated by #1018 | `test_recognition_manifest.py`, `test_recognition_result.py`, `test_declared_recognition_gate.py` |
35
37
 
36
38
  ## Historical records
37
39
 
@@ -46,6 +48,8 @@ architecture** table; open retired or superseded records only for design history
46
48
  - Compiler and state ownership: 0001 → 0005 → 0015.
47
49
  - Recognition and public declaration: 0007 → 0013 → 0011 → 0015.
48
50
  - Layout and placement: 0004 → 0014 → 0012.
51
+ - Declared intent and the editable surface: 0001 → 0011 → 0012 → 0016.
49
52
  - Quality and correction: 0002, with provenance from 0010.
53
+ - Recognition correspondence and completeness: 0007 → 0013 → 0015 → 0017.
50
54
 
51
55
  Tracking issue: #745.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "draftwright"
7
- version = "0.3.9"
7
+ version = "0.4.1"
8
8
  description = "Automated technical-drawing generation for build123d"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -80,6 +80,18 @@ markers = [
80
80
  "slow: heavy end-to-end CTC fixture builds (deselected by default)",
81
81
  "smoke: curated build-light subset for a fast local check (~30 s)",
82
82
  ]
83
+ # `SoftDeprecationWarning` marks a surface that is discouraged but SUPPORTED and not going
84
+ # away (#1043). The suite exercises `Sheet.auto_dimensions()` in ~170 places, deliberately —
85
+ # it is still a supported path and its behaviour must keep being tested. Migrating those to
86
+ # authored dimensions would be churn that tests less, so the category is silenced here rather
87
+ # than at ~170 call sites.
88
+ #
89
+ # This does NOT weaken the guard: `tests/test_soft_deprecation.py` asserts the warning with
90
+ # `pytest.warns`, which fires regardless of this filter. Real `DeprecationWarning`s are
91
+ # untouched and still carry removal dates (`tests/test_deprecation_dates.py`).
92
+ filterwarnings = [
93
+ "ignore::draftwright.SoftDeprecationWarning",
94
+ ]
83
95
  # Coverage is a CI concern (it adds ~13% locally and instruments every line); the
84
96
  # CI workflow passes the --cov flags explicitly. Keep the local default lean.
85
97
  addopts = "--tb=short -m 'not slow'"
@@ -87,6 +99,9 @@ addopts = "--tb=short -m 'not slow'"
87
99
  [tool.coverage.run]
88
100
  source = ["src/draftwright"]
89
101
  omit = ["src/draftwright/__init__.py"]
102
+ # Keep XML paths anchored at the repository root so local diff coverage and Codecov
103
+ # measure the same changed source lines rather than relying on runner-specific paths.
104
+ relative_files = true
90
105
  # Branch coverage makes the regression gate sensitive to untested decision paths,
91
106
  # which matter more than raw statement execution in the recognition/layout code.
92
107
  branch = true
@@ -94,13 +109,16 @@ branch = true
94
109
  [tool.coverage.report]
95
110
  # Baseline measured on the full fast tier on Linux/Python 3.13 (#825):
96
111
  # 92.05% combined line+branch coverage (93.90% statements, 86.89% branches).
97
- # Keep two points of cross-platform/kernel headroom; ratchet upward only after the
98
- # lowest CI matrix result has stayed above the proposed floor.
112
+ # Keep two points of headroom; ratchet upward only after the canonical coverage job
113
+ # has stayed above the proposed floor.
99
114
  fail_under = 90
100
115
  precision = 1
101
116
  exclude_lines = [
102
117
  "pragma: no cover",
103
118
  "if TYPE_CHECKING:",
119
+ # `@overload` stubs are typing declarations: Python discards the bodies, so they can
120
+ # never execute and counting them as missed coverage measures nothing (#963).
121
+ "@overload",
104
122
  ]
105
123
 
106
124
  [tool.mypy]