draftwright 0.4.21__tar.gz → 0.4.22__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 (550) hide show
  1. {draftwright-0.4.21 → draftwright-0.4.22}/CHANGELOG.md +3 -0
  2. {draftwright-0.4.21 → draftwright-0.4.22}/PKG-INFO +2 -2
  3. {draftwright-0.4.21 → draftwright-0.4.22}/docs/reference/recogniser-capabilities.md +3 -3
  4. {draftwright-0.4.21 → draftwright-0.4.22}/pyproject.toml +6 -2
  5. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/inspection_contract.py +1 -1
  6. draftwright-0.4.22/tests/_evidence_contract.py +61 -0
  7. draftwright-0.4.22/tests/_mutation_corpus.py +127 -0
  8. {draftwright-0.4.21 → draftwright-0.4.22}/tests/conftest.py +2 -0
  9. draftwright-0.4.22/tests/test_clone_budget.py +149 -0
  10. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_inspection_contract.py +1 -1
  11. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1369_hole_completeness_evidence.py +9 -5
  12. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1370_countersink_completeness_evidence.py +30 -20
  13. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1370_double_d_completeness_evidence.py +21 -17
  14. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1370_hole_pattern_completeness_evidence.py +21 -15
  15. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1371_flat_completeness_evidence.py +31 -33
  16. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1371_polygonal_stock_completeness_evidence.py +23 -27
  17. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1372_groove_completeness_evidence.py +43 -37
  18. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1372_pad_completeness_evidence.py +31 -36
  19. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1372_pocket_completeness_evidence.py +31 -33
  20. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1372_pocket_pattern_completeness_evidence.py +23 -29
  21. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1372_polygonal_boss_completeness_evidence.py +28 -31
  22. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1373_plate_completeness_evidence.py +37 -40
  23. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1374_chamfer_completeness_evidence.py +32 -33
  24. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1374_fillet_completeness_evidence.py +30 -33
  25. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1374_turned_step_completeness_evidence.py +32 -33
  26. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_recogniser_capabilities.py +1 -1
  27. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_step_analysis_evaluation.py +13 -0
  28. draftwright-0.4.22/tests/test_version_bump_ci.py +250 -0
  29. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_workflows.py +176 -17
  30. draftwright-0.4.21/tests/test_recognition.py +0 -861
  31. {draftwright-0.4.21 → draftwright-0.4.22}/.gitignore +0 -0
  32. {draftwright-0.4.21 → draftwright-0.4.22}/LICENSE +0 -0
  33. {draftwright-0.4.21 → draftwright-0.4.22}/README.md +0 -0
  34. {draftwright-0.4.21 → draftwright-0.4.22}/docs/adr/README.md +0 -0
  35. {draftwright-0.4.21 → draftwright-0.4.22}/docs/reference/sheet.md +0 -0
  36. {draftwright-0.4.21 → draftwright-0.4.22}/docs/research/1062-repeating-radial-profile-evidence.md +0 -0
  37. {draftwright-0.4.21 → draftwright-0.4.22}/skills/SKILL.md +0 -0
  38. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/__init__.py +0 -0
  39. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/_build_profile.py +0 -0
  40. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/_core.py +0 -0
  41. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/_geometry.py +0 -0
  42. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/_pmi_part21.py +0 -0
  43. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/_warnings.py +0 -0
  44. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/analysis.py +0 -0
  45. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotate.py +0 -0
  46. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/__init__.py +0 -0
  47. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/_common.py +0 -0
  48. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/balloons.py +0 -0
  49. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/from_model.py +0 -0
  50. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/gears.py +0 -0
  51. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/holes.py +0 -0
  52. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/leaders.py +0 -0
  53. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/orchestrator.py +0 -0
  54. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/annotations/sections.py +0 -0
  55. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/audit.py +0 -0
  56. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/blend_contract.py +0 -0
  57. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/builder.py +0 -0
  58. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/cli.py +0 -0
  59. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/compose.py +0 -0
  60. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/drawing.py +0 -0
  61. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/evaluation/__init__.py +0 -0
  62. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/evaluation/step_analysis.py +0 -0
  63. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/export.py +0 -0
  64. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/feature_identity.py +0 -0
  65. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/fits.py +0 -0
  66. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
  67. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
  68. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
  69. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
  70. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/fonts/__init__.py +0 -0
  71. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/inspection.py +0 -0
  72. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/intents.py +0 -0
  73. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/layout.py +0 -0
  74. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/__init__.py +0 -0
  75. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/_registry.py +0 -0
  76. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/angled_step_coverage.py +0 -0
  77. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/blend_coverage.py +0 -0
  78. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/chamfer_coverage.py +0 -0
  79. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/channel_coverage.py +0 -0
  80. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/circular_blind_step_coverage.py +0 -0
  81. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/coverage.py +0 -0
  82. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/evidence.py +0 -0
  83. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/fillet_coverage.py +0 -0
  84. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/flat_coverage.py +0 -0
  85. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/gear_coverage.py +0 -0
  86. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/groove_coverage.py +0 -0
  87. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/hole_coverage.py +0 -0
  88. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/ink_overlap.py +0 -0
  89. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/issues.py +0 -0
  90. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/oriented_slot_coverage.py +0 -0
  91. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/pad_coverage.py +0 -0
  92. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/paired_ramp_step_coverage.py +0 -0
  93. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/passage_coverage.py +0 -0
  94. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/plate_coverage.py +0 -0
  95. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/pmi_coverage.py +0 -0
  96. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/pocket_coverage.py +0 -0
  97. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/pocket_pattern_coverage.py +0 -0
  98. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/polygonal_boss_coverage.py +0 -0
  99. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/polygonal_stock_coverage.py +0 -0
  100. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/prismatic_pocket_coverage.py +0 -0
  101. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/profiled_bore_coverage.py +0 -0
  102. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/quality.py +0 -0
  103. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/rectangular_blind_slot_coverage.py +0 -0
  104. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/requirements.py +0 -0
  105. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/round_bottom_blind_slot_coverage.py +0 -0
  106. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/section_recess_coverage.py +0 -0
  107. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/slot_coverage.py +0 -0
  108. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/structural.py +0 -0
  109. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/suggest.py +0 -0
  110. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/through_step_coverage.py +0 -0
  111. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/linting/turned_step_coverage.py +0 -0
  112. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/make_drawing.py +0 -0
  113. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/model/__init__.py +0 -0
  114. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/model/callout.py +0 -0
  115. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/model/compiled.py +0 -0
  116. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/model/declare.py +0 -0
  117. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/model/detect.py +0 -0
  118. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/model/ir.py +0 -0
  119. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/model/planner.py +0 -0
  120. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/model/pmi_lowering.py +0 -0
  121. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/oriented_slot_contract.py +0 -0
  122. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/plate_correspondence.py +0 -0
  123. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/pmi.py +0 -0
  124. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/projection.py +0 -0
  125. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/py.typed +0 -0
  126. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/recogniser_contract.py +0 -0
  127. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/recogniser_policy.py +0 -0
  128. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/recogniser_schema.py +0 -0
  129. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/recognition/__init__.py +0 -0
  130. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/recognition_cache.py +0 -0
  131. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/recognition_frame.py +0 -0
  132. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/recognition_ownership.py +0 -0
  133. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/registry.py +0 -0
  134. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/repair.py +0 -0
  135. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/reporting.py +0 -0
  136. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/score.py +0 -0
  137. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/section_recess_contract.py +0 -0
  138. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/sheet.py +0 -0
  139. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/sheet_emit.py +0 -0
  140. {draftwright-0.4.21 → draftwright-0.4.22}/src/draftwright/view_plan.py +0 -0
  141. {draftwright-0.4.21 → draftwright-0.4.22}/tests/_kernel.py +0 -0
  142. {draftwright-0.4.21 → draftwright-0.4.22}/tests/_layout_sig.py +0 -0
  143. {draftwright-0.4.21 → draftwright-0.4.22}/tests/_recogniser_public_contract.py +0 -0
  144. {draftwright-0.4.21 → draftwright-0.4.22}/tests/_section_recess_cases.py +0 -0
  145. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/ap242_single_cylinder_diameter.step +0 -0
  146. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/README.md +0 -0
  147. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/ambiguous-open-semicircle.step +0 -0
  148. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/blind-hole.step +0 -0
  149. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-asymmetric.step +0 -0
  150. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-compound.step +0 -0
  151. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-overlap.step +0 -0
  152. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-plain.step +0 -0
  153. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-planar-x.step +0 -0
  154. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-planar-y.step +0 -0
  155. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-planar-z.step +0 -0
  156. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-topology-a.step +0 -0
  157. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-topology-b.step +0 -0
  158. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/chamfer-turned.step +0 -0
  159. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-chamfers-v1.json +0 -0
  160. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-countersinks-v1.json +0 -0
  161. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-double-d-bores-v1.json +0 -0
  162. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-fillets-v1.json +0 -0
  163. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-flats-v1.json +0 -0
  164. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-grooves-v1.json +0 -0
  165. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-hole-patterns-v1.json +0 -0
  166. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-plates-v1.json +0 -0
  167. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-pocket-patterns-v1.json +0 -0
  168. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-pockets-v1.json +0 -0
  169. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-polygonal-bosses-v1.json +0 -0
  170. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-polygonal-stock-v1.json +0 -0
  171. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-rectangular-pads-v1.json +0 -0
  172. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-turned-steps-v1.json +0 -0
  173. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/corpus-v1.json +0 -0
  174. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-deburr.step +0 -0
  175. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-external-cone.step +0 -0
  176. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-mixed-pair.step +0 -0
  177. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-single.step +0 -0
  178. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-topology-a.step +0 -0
  179. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/countersink-topology-b.step +0 -0
  180. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-axis-x.step +0 -0
  181. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-axis-y.step +0 -0
  182. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-blind.step +0 -0
  183. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-coaxial-compound.step +0 -0
  184. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-opposed-blind.step +0 -0
  185. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-roll-30.step +0 -0
  186. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-round-bore.step +0 -0
  187. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-single-z.step +0 -0
  188. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-topology-a.step +0 -0
  189. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/double-d-topology-b.step +0 -0
  190. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-compound.step +0 -0
  191. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-overlap.step +0 -0
  192. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-plain.step +0 -0
  193. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-planar-x.step +0 -0
  194. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-planar-y.step +0 -0
  195. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-planar-z.step +0 -0
  196. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-repeated.step +0 -0
  197. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-topology-a.step +0 -0
  198. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-topology-b.step +0 -0
  199. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/fillet-turned.step +0 -0
  200. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-coaxial.step +0 -0
  201. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-double-d.step +0 -0
  202. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-lone-d.step +0 -0
  203. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-nonround.step +0 -0
  204. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-slanted-double-d.step +0 -0
  205. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-topology-a.step +0 -0
  206. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/flat-topology-b.step +0 -0
  207. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-compound.step +0 -0
  208. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-lone-x.step +0 -0
  209. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-lone-y.step +0 -0
  210. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-lone-z.step +0 -0
  211. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-monotonic-negative.step +0 -0
  212. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-narrow.step +0 -0
  213. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-topology-a.step +0 -0
  214. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/groove-topology-b.step +0 -0
  215. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-compound-equal.step +0 -0
  216. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-detached-body-negative.step +0 -0
  217. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-full-span-ledge-negative.step +0 -0
  218. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-nested-staircase-negative.step +0 -0
  219. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-topology-a.step +0 -0
  220. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-topology-b.step +0 -0
  221. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-x-negative.step +0 -0
  222. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-x-positive.step +0 -0
  223. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-y-negative.step +0 -0
  224. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-y-positive.step +0 -0
  225. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-z-negative.step +0 -0
  226. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pad-z-positive.step +0 -0
  227. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pattern-ambiguous.step +0 -0
  228. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pattern-grid.step +0 -0
  229. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pattern-topology-a.step +0 -0
  230. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pattern-topology-b.step +0 -0
  231. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plain-block.step +0 -0
  232. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-compound-equal.step +0 -0
  233. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-cross-topology-a.step +0 -0
  234. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-cross-topology-b.step +0 -0
  235. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-detached-negative.step +0 -0
  236. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-rotational-negative.step +0 -0
  237. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-single-negative.step +0 -0
  238. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-t-xz.step +0 -0
  239. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-t-yz.step +0 -0
  240. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-thick-negative.step +0 -0
  241. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-u-additive.step +0 -0
  242. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/plate-u-cut.step +0 -0
  243. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-compound.step +0 -0
  244. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-edge-anchored.step +0 -0
  245. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-lone.step +0 -0
  246. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-opposed.step +0 -0
  247. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-ambiguous.step +0 -0
  248. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-grid.step +0 -0
  249. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-linear.step +0 -0
  250. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-pair.step +0 -0
  251. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-topology-a.step +0 -0
  252. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-pattern-topology-b.step +0 -0
  253. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-prismatic-negative.step +0 -0
  254. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-side.step +0 -0
  255. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-through-negative.step +0 -0
  256. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-topology-a.step +0 -0
  257. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-topology-b.step +0 -0
  258. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/pocket-two-equal.step +0 -0
  259. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-compound-equal.step +0 -0
  260. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-detached-negative.step +0 -0
  261. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-in-plane-rotated.step +0 -0
  262. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-other-protrusions-negative.step +0 -0
  263. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-recess-negative.step +0 -0
  264. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-stock-negative.step +0 -0
  265. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-topology-a.step +0 -0
  266. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-topology-b.step +0 -0
  267. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-x.step +0 -0
  268. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-y.step +0 -0
  269. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-boss-z.step +0 -0
  270. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-compound-negative.step +0 -0
  271. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-cylinder-negative.step +0 -0
  272. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-irregular-negative.step +0 -0
  273. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-octagon-negative.step +0 -0
  274. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-recess-negative.step +0 -0
  275. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-roll17.step +0 -0
  276. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-topology-a.step +0 -0
  277. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-topology-b.step +0 -0
  278. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-translated.step +0 -0
  279. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-x.step +0 -0
  280. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/polygonal-stock-y.step +0 -0
  281. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/topology-a.step +0 -0
  282. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/topology-b.step +0 -0
  283. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-axis-x.step +0 -0
  284. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-axis-y.step +0 -0
  285. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-axis-z.step +0 -0
  286. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-compound.step +0 -0
  287. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-repeated-lengths.step +0 -0
  288. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-through-bore.step +0 -0
  289. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-topology-a.step +0 -0
  290. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-topology-b.step +0 -0
  291. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/evaluation/turned-step-translated-blind-bore.step +0 -0
  292. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
  293. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/grm03_thumbwheel_drive_screw_ap242_pmi.step +0 -0
  294. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/grm04_drive_plate.step +0 -0
  295. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
  296. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
  297. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/issue_1247_angled_blind_step.step +0 -0
  298. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
  299. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/issue_915_case_study_2.step +0 -0
  300. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
  301. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
  302. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
  303. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
  304. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
  305. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
  306. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
  307. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
  308. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
  309. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
  310. {draftwright-0.4.21 → draftwright-0.4.22}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
  311. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/bracket_section.json +0 -0
  312. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/centered_rebate.json +0 -0
  313. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/chamfered.json +0 -0
  314. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/filleted.json +0 -0
  315. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/flange_dense.json +0 -0
  316. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/grid_plate.json +0 -0
  317. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/grooved_shaft.json +0 -0
  318. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/hex_bar.json +0 -0
  319. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/holed_slot.json +0 -0
  320. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/pocketed.json +0 -0
  321. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/prismatic_ladder.json +0 -0
  322. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/scattered_plate.json +0 -0
  323. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/side_drilled.json +0 -0
  324. {draftwright-0.4.21 → draftwright-0.4.22}/tests/refactor_golden/turned_stepped.json +0 -0
  325. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_add_dimension.py +0 -0
  326. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_adr0018_arrangement_gate.py +0 -0
  327. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_adr0018_view_routing.py +0 -0
  328. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_adr0018_view_selection.py +0 -0
  329. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_adr_corpus.py +0 -0
  330. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_agents_guide.py +0 -0
  331. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_api_docs.py +0 -0
  332. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_architecture_docs.py +0 -0
  333. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_audit_differential.py +0 -0
  334. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_balloon_ring_standoff.py +0 -0
  335. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_build_profile_harness.py +0 -0
  336. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_carve_free_position_callers.py +0 -0
  337. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_compiled_plan_boundary.py +0 -0
  338. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_counting_calls.py +0 -0
  339. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_declare.py +0 -0
  340. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_declared_recognition_gate.py +0 -0
  341. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_dense_sheet_canary.py +0 -0
  342. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_deprecation_dates.py +0 -0
  343. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_detect_once.py +0 -0
  344. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_detect_registry.py +0 -0
  345. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_dimension_role_vocabulary.py +0 -0
  346. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_drawing_encapsulation.py +0 -0
  347. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_e2e_slice.py +0 -0
  348. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_e2e_standards.py +0 -0
  349. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_export_dxf_curves.py +0 -0
  350. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_export_dxf_zoom.py +0 -0
  351. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_export_reproducible.py +0 -0
  352. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_external_recognition_boundary.py +0 -0
  353. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_fillets_adjacency.py +0 -0
  354. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_fits.py +0 -0
  355. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_flat_completeness.py +0 -0
  356. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_flat_stock_identity.py +0 -0
  357. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_flat_stock_opposition.py +0 -0
  358. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_gdt_placement.py +0 -0
  359. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_geometry_graph_spike.py +0 -0
  360. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_grid_lattice_convention.py +0 -0
  361. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_import_boundaries.py +0 -0
  362. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_iso_nts_caption_placement.py +0 -0
  363. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1000_envelope_reuse.py +0 -0
  364. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1006_measurement_comparison.py +0 -0
  365. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1058_wheel_profile.py +0 -0
  366. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1073_cross_body_patterns.py +0 -0
  367. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1082_polygonal_stock.py +0 -0
  368. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1086_declared_gears.py +0 -0
  369. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1116_ap242_hole_tolerance_lowering.py +0 -0
  370. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1130_view_planning_evidence.py +0 -0
  371. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1142_hole_leader_labels.py +0 -0
  372. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1143_hole_completeness.py +0 -0
  373. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1144_transactional_hole_table.py +0 -0
  374. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1146_scale_completeness.py +0 -0
  375. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1147_legibility_pair_aggregation.py +0 -0
  376. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1153_contradictory_dimensions.py +0 -0
  377. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1154_one_owner_per_measurement.py +0 -0
  378. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1155_grm04_sheet_use.py +0 -0
  379. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1166_cross_pass_feature_leaders.py +0 -0
  380. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1176_quality_fidelity.py +0 -0
  381. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1177_angular_dimensions.py +0 -0
  382. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1187_unroutable_leaders.py +0 -0
  383. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1188_per_view_assignment.py +0 -0
  384. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1190_section_decision.py +0 -0
  385. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1196_deterministic_view_names.py +0 -0
  386. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1202_observed_drawing_consumer.py +0 -0
  387. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1204_multiscale_view_issues.py +0 -0
  388. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1209_linear_pmi_witnesses.py +0 -0
  389. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1215_envelope_tolerance.py +0 -0
  390. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1215_no_approved_tolerance_is_dropped.py +0 -0
  391. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1216_callout_reservation_measures_ink.py +0 -0
  392. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1216_pairwise_across_scale_groups.py +0 -0
  393. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1217_claimed_representations.py +0 -0
  394. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1217_the_facility_is_shared.py +0 -0
  395. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1219_location_claims_its_own_axis.py +0 -0
  396. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1229_ledger_member_keying.py +0 -0
  397. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1240_iso_above_strip_visibility.py +0 -0
  398. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1245_passage_disposition.py +0 -0
  399. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1246_prismatic_pocket_disposition.py +0 -0
  400. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1247_angled_step_disposition.py +0 -0
  401. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1254_turned_chamfers.py +0 -0
  402. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1260_view_constraints.py +0 -0
  403. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1262_automatic_turned_views.py +0 -0
  404. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1276_turned_leader_targets.py +0 -0
  405. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1281_turned_fillets.py +0 -0
  406. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1296_cylindrical_diameter_pmi.py +0 -0
  407. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1298_manufacturing_requirements.py +0 -0
  408. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1299_page_escalation.py +0 -0
  409. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1308_machined_leader_analytics.py +0 -0
  410. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1312_engine_costs.py +0 -0
  411. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1325_nominal_agreement.py +0 -0
  412. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1332_overlap_remedy.py +0 -0
  413. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1332_zone_labels.py +0 -0
  414. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1333_bounded_ink_repair.py +0 -0
  415. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1334_prevent_ink.py +0 -0
  416. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1336_grm03_ap242_pmi_fixture.py +0 -0
  417. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1338_scale_before_page_escalation.py +0 -0
  418. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1349_precision_display.py +0 -0
  419. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1350_detected_takeover.py +0 -0
  420. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1351_structured_note_coverage.py +0 -0
  421. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1352_searchable_pdf_text.py +0 -0
  422. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1353_cnc_authoritative_workflow.py +0 -0
  423. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1357_framed_activation.py +0 -0
  424. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1357_framed_boundary.py +0 -0
  425. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1357_plural_turned_profiles.py +0 -0
  426. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1357_pmi_frame.py +0 -0
  427. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1360_thread_depth.py +0 -0
  428. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1378_diameter_leader_targets.py +0 -0
  429. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1382_046_step_inventory.py +0 -0
  430. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1382_circular_blind_step_semantics.py +0 -0
  431. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1382_framed_step_family_evidence.py +0 -0
  432. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1382_paired_ramp_semantics.py +0 -0
  433. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1382_through_step_semantics.py +0 -0
  434. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1390_sheet_registration.py +0 -0
  435. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1392_recognisers_048.py +0 -0
  436. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1395_degenerate_iso_region.py +0 -0
  437. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1396_layout_advisories.py +0 -0
  438. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1397_unverifiable_requirement_message.py +0 -0
  439. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1421_rectangular_blind_slot_completeness.py +0 -0
  440. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1421_rectangular_blind_slot_semantics.py +0 -0
  441. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1421_round_bottom_blind_slot_completeness.py +0 -0
  442. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1421_round_bottom_blind_slot_semantics.py +0 -0
  443. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1432_adoption.py +0 -0
  444. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1432_boundary_failures.py +0 -0
  445. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1432_oriented_slot_semantics.py +0 -0
  446. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1433_blend_semantics.py +0 -0
  447. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_boss_ownership.py +0 -0
  448. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_channel_ownership.py +0 -0
  449. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_cli_report_sidecar.py +0 -0
  450. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_generation_snapshot.py +0 -0
  451. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_nested_ownership.py +0 -0
  452. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_occurrence_ownership.py +0 -0
  453. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_pattern_ownership.py +0 -0
  454. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_plate_ownership.py +0 -0
  455. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_policy_dispositions.py +0 -0
  456. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_report_projection.py +0 -0
  457. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_report_writer.py +0 -0
  458. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_through_step_ownership.py +0 -0
  459. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1438_turned_step_ownership.py +0 -0
  460. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1450_boss_blend_ownership.py +0 -0
  461. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1460_step_inspection.py +0 -0
  462. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1466_dimension_options.py +0 -0
  463. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1466_semantic_sides.py +0 -0
  464. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1471_boundary_failures.py +0 -0
  465. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1471_evidence_schema.py +0 -0
  466. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1471_groove_profile.py +0 -0
  467. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1471_section_recess_contract.py +0 -0
  468. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1471_section_recess_pockets.py +0 -0
  469. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1479_radius_leader_targets.py +0 -0
  470. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1485_curved_pockets.py +0 -0
  471. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1485_quiddity_024_boundary.py +0 -0
  472. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_1485_rounded_pockets.py +0 -0
  473. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_367_leader_ink.py +0 -0
  474. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_443_grm03_axial_coverage.py +0 -0
  475. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_563_placement_intent.py +0 -0
  476. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_676_polygonal_boss.py +0 -0
  477. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
  478. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_740_leader_assignment.py +0 -0
  479. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_798_floor_cardinality.py +0 -0
  480. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_798_material_field.py +0 -0
  481. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_798_silhouette_lint.py +0 -0
  482. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_827_real_part_canary.py +0 -0
  483. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_883_location_identity.py +0 -0
  484. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_885_prismatic_coverage.py +0 -0
  485. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_909_sloped_profile.py +0 -0
  486. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_915_dense_case.py +0 -0
  487. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_916_pocket_leader.py +0 -0
  488. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_917_open_channel.py +0 -0
  489. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_924_zero_length_shoulders.py +0 -0
  490. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_955_height_contingency.py +0 -0
  491. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_issue_958_cross_solid_recognition.py +0 -0
  492. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_label_provenance.py +0 -0
  493. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_layout.py +0 -0
  494. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_layout_cleanliness.py +0 -0
  495. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_layout_hypothesis.py +0 -0
  496. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_layout_property.py +0 -0
  497. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_leader_footprint.py +0 -0
  498. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_lint_box_cache.py +0 -0
  499. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_lint_ink_overlap.py +0 -0
  500. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_lint_reconciliation.py +0 -0
  501. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_lint_structural.py +0 -0
  502. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_linting.py +0 -0
  503. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_location_vocabulary.py +0 -0
  504. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_make_drawing.py +0 -0
  505. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_note_verb.py +0 -0
  506. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_object_aspects.py +0 -0
  507. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_occupancy_boxes.py +0 -0
  508. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_parameter_id.py +0 -0
  509. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_part_model.py +0 -0
  510. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_pitch_dim_footprint.py +0 -0
  511. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_pmi.py +0 -0
  512. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_pmi_datum_lowering.py +0 -0
  513. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_pmi_gtol_lowering.py +0 -0
  514. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_pmi_part21.py +0 -0
  515. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_pocket_pattern.py +0 -0
  516. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_pocket_pattern_recognition.py +0 -0
  517. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_principal_profile_classifier.py +0 -0
  518. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_private_test_attr_reads.py +0 -0
  519. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_private_test_imports.py +0 -0
  520. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_quality_components.py +0 -0
  521. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_recogniser_adoption.py +0 -0
  522. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_recogniser_contract.py +0 -0
  523. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_recognition_result.py +0 -0
  524. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_refactor_golden.py +0 -0
  525. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_registry.py +0 -0
  526. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_render_seam.py +0 -0
  527. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_repack_geometry_seam.py +0 -0
  528. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_score.py +0 -0
  529. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_script_detail_parity.py +0 -0
  530. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_shared_box_memo.py +0 -0
  531. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_sheet_emit.py +0 -0
  532. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_sheet_gdt.py +0 -0
  533. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_sheet_identity_invariant.py +0 -0
  534. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_sheet_notes.py +0 -0
  535. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_sheet_of.py +0 -0
  536. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_sheet_section.py +0 -0
  537. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_sheet_tables.py +0 -0
  538. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_slanted_blind_step.py +0 -0
  539. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_slot_completeness.py +0 -0
  540. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_slot_pattern.py +0 -0
  541. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_slot_pattern_recognition.py +0 -0
  542. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_soft_deprecation.py +0 -0
  543. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_solve_trace.py +0 -0
  544. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_strip_layout.py +0 -0
  545. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_suppression_ledger.py +0 -0
  546. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_suppression_marks.py +0 -0
  547. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_tolerances.py +0 -0
  548. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_turned_steps.py +0 -0
  549. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_view_plan.py +0 -0
  550. {draftwright-0.4.21 → draftwright-0.4.22}/tests/test_witness_label_reconciliation.py +0 -0
@@ -154,6 +154,9 @@
154
154
 
155
155
  ### Changed
156
156
 
157
+ - Updated the exact Quiddity runtime and declared-inspection contract to 0.2.5 for the
158
+ provider's recognition performance improvements.
159
+
157
160
  - Production recognition now consumes published `quiddity==0.2.2`. Unified section-recess
158
161
  occurrences and their same-run patterns feed the existing pocket, channel and blind-slot
159
162
  drawing grammar. Original occurrence ownership, independent completeness and explicit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: draftwright
3
- Version: 0.4.21
3
+ Version: 0.4.22
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
@@ -690,7 +690,7 @@ Requires-Dist: build123d<0.12,>=0.11; python_full_version >= '3.13'
690
690
  Requires-Dist: numpy>=1.24
691
691
  Requires-Dist: pillow>=10
692
692
  Requires-Dist: pypdfium2>=4
693
- Requires-Dist: quiddity==0.2.4
693
+ Requires-Dist: quiddity==0.2.5
694
694
  Requires-Dist: reportlab>=4.0
695
695
  Requires-Dist: steputils==0.1
696
696
  Requires-Dist: svglib>=1.5
@@ -70,10 +70,10 @@ issue. The contract test derives package record outputs, converter registries, l
70
70
  and emitter branches independently, so copying a new name into the declaration alone cannot make CI
71
71
  pass.
72
72
 
73
- ### Quiddity 0.2.4 runtime contract
73
+ ### Quiddity 0.2.5 runtime contract
74
74
 
75
- [#1486](https://github.com/pzfreo/draftwright/pull/1486) adopts the published
76
- `quiddity==0.2.4` package after the migration in
75
+ The runtime pins the published `quiddity==0.2.5` package, following the 0.2.4 adoption in
76
+ [#1486](https://github.com/pzfreo/draftwright/pull/1486) and the migration in
77
77
  [#1471](https://github.com/pzfreo/draftwright/issues/1471). Its public `quiddity`, `quiddity.evidence` and
78
78
  `quiddity.inspection` surfaces supply recognition, exact occurrence evidence and declared geometry
79
79
  reads. There is one recognition aggregate per build; consumers project that result rather than
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "draftwright"
7
- version = "0.4.21"
7
+ version = "0.4.22"
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.2",
21
- "quiddity==0.2.4",
21
+ "quiddity==0.2.5",
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
@@ -167,6 +167,10 @@ dev = [
167
167
  "jsonschema>=4,<5",
168
168
  "mypy>=2.1.0",
169
169
  "pytest>=8",
170
+ # `coverage` is invoked directly by the CI `coverage-report` job (combine/xml/html/
171
+ # report); it arrives transitively via pytest-cov, but the job depends on the console
172
+ # script, so declare it.
173
+ "coverage>=7",
170
174
  "pytest-cov>=5",
171
175
  "pytest-split>=0.8",
172
176
  "pytest-timeout>=2",
@@ -16,7 +16,7 @@ from quiddity.inspection import inspection_api_manifest
16
16
  CONSUMER_INSPECTION_FORMAT = "draftwright-inspection-api"
17
17
  CONSUMER_INSPECTION_FORMAT_VERSION = 1
18
18
  SUPPORTED_INSPECTION_API_MAJOR = 1
19
- SUPPORTED_RECOGNISER_VERSION = "0.2.4"
19
+ SUPPORTED_RECOGNISER_VERSION = "0.2.5"
20
20
  _DISTRIBUTION = "quiddity"
21
21
  _PROVIDER_FORMAT = "quiddity-inspection-api"
22
22
  _NAMESPACE = "quiddity.inspection"
@@ -0,0 +1,61 @@
1
+ """Per-family evidence contracts that every `*_completeness_evidence` module asserts.
2
+
3
+ These bodies were written out once per feature family — thirteen copies of the
4
+ `_<family>_model_outcomes` check, eleven of the one-recognition check — differing only
5
+ in which family symbol they named. That is the growth pattern `test_clone_budget.py`
6
+ exists to stop: the same assertion re-paid on every CI run, and one more copy to write
7
+ (and to get subtly wrong) with each new recogniser family.
8
+
9
+ Each function here is the single body; the per-family test calls it with its own
10
+ symbols, so the assertion still runs once per family and the evidence is unchanged.
11
+ Adding a family should add a call, not a copy.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from collections.abc import Callable
17
+
18
+
19
+ def assert_missing_model_outcomes_fail_closed(
20
+ monkeypatch,
21
+ outcomes_attr: str,
22
+ states: Callable[..., object],
23
+ ) -> None:
24
+ """A family whose per-occurrence outcomes vanish scores `unknown`, never `supported`.
25
+
26
+ `outcomes_attr` is the family's `_<family>_model_outcomes` in
27
+ `draftwright.evaluation.step_analysis`; `states` is the module's own `_states`
28
+ reader, so each family still exercises its own observer and fixture part.
29
+ """
30
+ import draftwright.evaluation.step_analysis as step_analysis
31
+
32
+ monkeypatch.setattr(step_analysis, outcomes_attr, lambda *_args: [])
33
+ # `set(...)`: a few families' `_states` returns a list, the rest a set. Both mean
34
+ # "every observed boundary state", and both must be exactly `unknown` here.
35
+ assert set(states("ir_adapter")) == {"unknown"}
36
+
37
+
38
+ def assert_observer_uses_one_build_owned_recognition(
39
+ monkeypatch,
40
+ family: str,
41
+ part: object,
42
+ ) -> None:
43
+ """ADR 3: the observer scores the build's OWN aggregate, recognising exactly once.
44
+
45
+ A second `build_recognition_evidence` call would mean the facts being scored and the
46
+ features they are matched against came from different recognition runs.
47
+ """
48
+ import draftwright.analysis as analysis
49
+ from draftwright.evaluation.step_analysis import _default_observers
50
+
51
+ original = analysis.build_recognition_evidence
52
+ calls = 0
53
+
54
+ def counted(*args, **kwargs):
55
+ nonlocal calls
56
+ calls += 1
57
+ return original(*args, **kwargs)
58
+
59
+ monkeypatch.setattr(analysis, "build_recognition_evidence", counted)
60
+ assert _default_observers()[family](part)
61
+ assert calls == 1
@@ -0,0 +1,127 @@
1
+ """The smallest corpus subset that still exercises a provider mutation.
2
+
3
+ The `weakening_provider_*` / `deleting_provider_*` tests damage the provider and assert
4
+ that the benchmark's score falls. They read `detection` and `parameter_fidelity` only —
5
+ never `downstream_usefulness`, `complete_cases` or `conformant_cases` — yet they scored
6
+ every fixture in the family's corpus, and the drawing_consumer observer builds a full
7
+ drawing per case. Measured on the groove corpus: 4.77 s to re-prove one direction across
8
+ eight fixtures, of which 98% is `build_drawing`.
9
+
10
+ Scoring two fixtures proves the same direction. What it must NOT do is prove it vacuously,
11
+ so the subset is chosen rather than truncated, and chosen by TAG rather than by position:
12
+
13
+ * **one negative case**, always. Without a fixture the family must NOT match,
14
+ `false_positives == 0` is satisfied by a corpus that cannot produce a false positive at
15
+ all, and `detection.recall == 1.0` is trivially true of nothing.
16
+
17
+ Which negative matters. Taking the first in file order picked `chamfer-plain-negative`
18
+ and `fillet-plain-negative` — plain blocks carrying no bevel geometry whatsoever — so a
19
+ recogniser mutated to accept any `overlapping-family` shape would still have scored
20
+ `false_positives == 0`. That is exactly the vacuity the negative is here to prevent, so
21
+ an `ambiguous` or `overlapping-family` negative is preferred: one that the family could
22
+ plausibly claim and must not. `_NO_CONFUSABLE_NEGATIVE` records the two corpora that
23
+ offer none, rather than letting the weaker choice pass unremarked.
24
+
25
+ * **the positive expecting the most facts** (`BenchmarkCase.expected`), so the damaged
26
+ parameters are actually present to be scored. Ties are the norm — five positives tie in
27
+ pockets, six in polygonal-stock — so the tie is broken on `case_id`. Without that, the
28
+ exact ratios these tests assert would be pinned to JSON key order, and reformatting a
29
+ corpus file (touching no code) could silently re-point a test at different geometry.
30
+
31
+ `assert_reduced_corpus_is_sound` is the precondition that keeps this honest: the same
32
+ subset must score a clean 1.0. A subset that is already failing would let a damage
33
+ assertion pass for the wrong reason. `reduced_baseline_fixture` builds the per-module
34
+ fixture, so a new family adds a call rather than a tenth copy of the same body — the
35
+ growth pattern `test_clone_budget.py` exists to stop.
36
+
37
+ The full corpus is still scored in full by each module's `test_real_*_corpus_scores_*`,
38
+ which is where the absolute per-family counts live.
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ from dataclasses import replace
44
+
45
+ import pytest
46
+
47
+ #: Corpora offering no `ambiguous` / `overlapping-family` negative, so the plain-block
48
+ #: negative is the only one available. Their `false_positives == 0` assertions are
49
+ #: correspondingly weaker, and adding a confusable negative fixture would strengthen them.
50
+ _NO_CONFUSABLE_NEGATIVE = frozenset({"corpus-chamfers-v1.json", "corpus-turned-steps-v1.json"})
51
+
52
+ #: Tags marking a negative the family could plausibly claim, and must not.
53
+ _CONFUSABLE = ("ambiguous", "overlapping-family")
54
+
55
+
56
+ def reduced_corpus(corpus):
57
+ """One confusable negative plus the fact-richest positive, chosen deterministically."""
58
+ negatives = [case for case in corpus.cases if "negative" in _tags(case)]
59
+ positives = [
60
+ case
61
+ for case in corpus.cases
62
+ if "positive" in _tags(case) and "negative" not in _tags(case)
63
+ ]
64
+ assert negatives, (
65
+ "no negative case: a corpus that cannot produce a false positive makes "
66
+ "`false_positives == 0` vacuous, so it cannot be reduced this way"
67
+ )
68
+ assert positives, "no positive case: `detection.recall` would be vacuous"
69
+
70
+ confusable = sorted(
71
+ (case for case in negatives if any(tag in _tags(case) for tag in _CONFUSABLE)),
72
+ key=lambda case: case.case_id,
73
+ )
74
+ negative = confusable[0] if confusable else sorted(negatives, key=lambda c: c.case_id)[0]
75
+
76
+ # `case_id` breaks the tie: `len(expected)` alone leaves the choice to JSON key order.
77
+ richest = max(positives, key=lambda case: (len(case.expected), case.case_id))
78
+ assert richest.expected, (
79
+ f"richest positive {richest.case_id!r} expects no facts, so parameter fidelity "
80
+ "would be scored over an empty denominator"
81
+ )
82
+ return replace(corpus, cases=(negative, richest))
83
+
84
+
85
+ def _tags(case) -> tuple[str, ...]:
86
+ """The case's tags, which `load_corpus` joins into `classification`."""
87
+ return tuple(case.classification.split("+"))
88
+
89
+
90
+ def assert_reduced_corpus_is_sound(evaluate, corpus) -> object:
91
+ """Score the subset CLEAN and require a perfect result; return it for denominators.
92
+
93
+ Every damage assertion in the module is relative to this. If the subset were already
94
+ imperfect, `score == 0.5` after damage could hold because the fixture was broken
95
+ rather than because the damage was detected.
96
+ """
97
+ clean = evaluate(reduced_corpus(corpus))
98
+ assert clean.detection.recall == 1.0, (
99
+ f"reduced corpus does not detect cleanly (recall {clean.detection.recall}); "
100
+ "damage assertions built on it would pass for the wrong reason"
101
+ )
102
+ assert clean.detection.false_positives == 0, (
103
+ f"reduced corpus false-positives cleanly ({clean.detection.false_positives})"
104
+ )
105
+ assert clean.parameter_fidelity.score == 1.0, (
106
+ f"reduced corpus scores {clean.parameter_fidelity.score} clean, not 1.0"
107
+ )
108
+ assert clean.parameter_fidelity.total > 0, "reduced corpus scores no parameters at all"
109
+ return clean
110
+
111
+
112
+ def reduced_baseline_fixture(corpus_path):
113
+ """The per-module `reduced_baseline` fixture, as a call rather than a tenth copy.
114
+
115
+ Module scope, so the clean evaluation is paid once per module — note that under
116
+ `--dist worksteal` (which `scripts/pr-check --full` uses) xdist may distribute one
117
+ module across workers and set it up once per worker. CI runs `loadscope` and pays it
118
+ once.
119
+ """
120
+
121
+ @pytest.fixture(scope="module")
122
+ def reduced_baseline():
123
+ from draftwright.evaluation.step_analysis import evaluate_step_corpus, load_corpus
124
+
125
+ return assert_reduced_corpus_is_sound(evaluate_step_corpus, load_corpus(corpus_path))
126
+
127
+ return reduced_baseline
@@ -171,6 +171,7 @@ _UNIT_MODULES = frozenset(
171
171
  "test_api_docs.py",
172
172
  "test_architecture_docs.py",
173
173
  "test_carve_free_position_callers.py",
174
+ "test_clone_budget.py",
174
175
  "test_counting_calls.py",
175
176
  "test_deprecation_dates.py",
176
177
  "test_import_boundaries.py",
@@ -187,6 +188,7 @@ _UNIT_MODULES = frozenset(
187
188
  "test_recogniser_adoption.py",
188
189
  "test_registry.py",
189
190
  "test_workflows.py",
191
+ "test_version_bump_ci.py",
190
192
  }
191
193
  )
192
194
 
@@ -0,0 +1,149 @@
1
+ """The suite may not grow by copying a test body into another module.
2
+
3
+ Between 2026-08-21 and 2026-09-07 the corpus went from 3,347 to 4,934 test
4
+ functions and pull-request CI wall-clock went from ~31 to ~63 minutes. Sharding
5
+ absorbed that once; it cannot absorb it again, because sharding is a one-time
6
+ constant factor and cloning is not.
7
+
8
+ The growth is not people writing new assertions. It is one templated module
9
+ being copied per feature family: `test_missing_per_hole_boundary_outcomes_fail_closed`,
10
+ `..._per_pad_...`, `..._per_plate_...` — twelve hand-written copies of one three-line
11
+ body whose only difference is which `_<family>_model_outcomes` symbol it patches.
12
+ Each copy costs a real `build_drawing`, forever, on every CI run.
13
+
14
+ The two obvious ways to detect that both fail here, which is why it went unnoticed:
15
+ identical test NAMES miss it (every clone is renamed for its family) and identical
16
+ ASTs miss it (every clone substitutes its family's identifiers and literals). So this
17
+ guard compares test bodies with identifiers, attributes, literals and argument names
18
+ erased — the SHAPE — across modules, since same-module repetition is a much weaker smell
19
+ and often a deliberate readability choice, and only where a shape recurs in at least
20
+ `_MIN_GROUP_MEMBERS` places, since a bare pair is usually coincidence rather than a
21
+ template.
22
+
23
+ The fix when this fails is not to raise the budget. It is to parametrize over the
24
+ one thing that varies, which keeps every assertion and deletes the copies. Lower the
25
+ budget whenever you do. Raising it needs a reason in the pull request body, the same
26
+ way `fail_under` in `[tool.coverage.report]` does.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import ast
32
+ import collections
33
+ import hashlib
34
+ from pathlib import Path
35
+
36
+ TESTS = Path(__file__).parent
37
+
38
+ #: Cross-module structurally identical test bodies, counted as `group size - 1`, over
39
+ #: groups of at least `_MIN_GROUP_MEMBERS`. Measured at 33 on 2026-09-07.
40
+ #: RATCHET DOWN ONLY — see the module docstring.
41
+ CLONE_BUDGET = 33
42
+
43
+ #: A shape must recur in at least this many modules to count as templated cloning.
44
+ #:
45
+ #: At two, the guard was measuring coincidence. 61 of its 70 groups were pairs, and the
46
+ #: pairs are ordinary tests that happen to share a shape once identifiers and literals are
47
+ #: erased — `test_add_dimension.py::test_a_tuple_swap_invalidates_rather_than_retargets`
48
+ #: against `test_sheet_identity_invariant.py::test_a_tuple_swap_raises`. With the budget
49
+ #: sitting exactly on the measured count, the next PR adding any three-statement test that
50
+ #: collided by accident would have failed CI telling its author to "parametrize and delete
51
+ #: the copies", which would usually be wrong — and a gate that cries wolf gets raised
52
+ #: rather than obeyed. Templated cloning shows up as a family, not a pair.
53
+ _MIN_GROUP_MEMBERS = 3
54
+
55
+ #: A body must have at least this many statements, and this many AST nodes, before it
56
+ #: counts. Short bodies (`build; assert`) collide by chance and would make the guard
57
+ #: noise rather than signal.
58
+ #:
59
+ #: The size gate counts NODES rather than the length of `ast.dump`. CI runs Python
60
+ #: 3.10 through 3.14, and `ast.dump` text gains and loses optional fields between
61
+ #: versions; 22 bodies sit within 15% of the equivalent character threshold, so any of
62
+ #: them could be counted on one interpreter and skipped on another and fail the gate on
63
+ #: one matrix leg only. A node count moves only when the grammar does.
64
+ _MIN_STATEMENTS = 3
65
+ _MIN_NODES = 16
66
+
67
+
68
+ class _Shape(ast.NodeTransformer):
69
+ """Erase everything nameable, keep control flow and call structure."""
70
+
71
+ def visit_Name(self, node: ast.Name) -> ast.Name:
72
+ return ast.copy_location(ast.Name(id="_", ctx=node.ctx), node)
73
+
74
+ def visit_Attribute(self, node: ast.Attribute) -> ast.Attribute:
75
+ self.generic_visit(node)
76
+ return ast.copy_location(ast.Attribute(value=node.value, attr="_", ctx=node.ctx), node)
77
+
78
+ def visit_Constant(self, node: ast.Constant) -> ast.Constant:
79
+ return ast.copy_location(ast.Constant(value="_"), node)
80
+
81
+ def visit_arg(self, node: ast.arg) -> ast.arg:
82
+ return ast.copy_location(ast.arg(arg="_"), node)
83
+
84
+ def visit_keyword(self, node: ast.keyword) -> ast.keyword:
85
+ self.generic_visit(node)
86
+ return ast.copy_location(ast.keyword(arg="_", value=node.value), node)
87
+
88
+
89
+ def _clone_groups() -> dict[str, list[tuple[str, str]]]:
90
+ """Structurally identical test bodies that span more than one module."""
91
+ shapes: dict[str, list[tuple[str, str]]] = collections.defaultdict(list)
92
+ for path in sorted(TESTS.glob("test_*.py")):
93
+ tree = ast.parse(path.read_text(encoding="utf-8"))
94
+ for node in ast.walk(tree):
95
+ if not (isinstance(node, ast.FunctionDef) and node.name.startswith("test_")):
96
+ continue
97
+ body = [
98
+ statement
99
+ for statement in node.body
100
+ # Drop the docstring: prose is where a clone legitimately differs.
101
+ if not (
102
+ isinstance(statement, ast.Expr) and isinstance(statement.value, ast.Constant)
103
+ )
104
+ ]
105
+ if len(body) < _MIN_STATEMENTS:
106
+ continue
107
+ normalised = _Shape().visit(ast.Module(body=body, type_ignores=[]))
108
+ if sum(1 for _ in ast.walk(normalised)) < _MIN_NODES:
109
+ continue
110
+ digest = hashlib.md5(ast.dump(normalised).encode(), usedforsecurity=False).hexdigest()
111
+ shapes[digest].append((path.name, node.name))
112
+ return {
113
+ digest: members
114
+ for digest, members in shapes.items()
115
+ if len({module for module, _ in members}) > 1 and len(members) >= _MIN_GROUP_MEMBERS
116
+ }
117
+
118
+
119
+ def test_the_suite_does_not_grow_by_cloning_test_bodies_across_modules() -> None:
120
+ groups = _clone_groups()
121
+ redundant = sum(len(members) - 1 for members in groups.values())
122
+
123
+ worst = sorted(groups.values(), key=len, reverse=True)[:5]
124
+ report = "\n".join(
125
+ f" x{len(members)} {members[0][1]}\n"
126
+ + "\n".join(f" {module}" for module, _ in members[:4])
127
+ + ("\n ..." if len(members) > 4 else "")
128
+ for members in worst
129
+ )
130
+ assert redundant <= CLONE_BUDGET, (
131
+ f"{redundant} cross-module clones, budget {CLONE_BUDGET}. The largest groups:\n"
132
+ f"{report}\n\n"
133
+ "A test body copied into another module with only its family's identifiers "
134
+ "changed is not new evidence — it is the same assertion paid for again on every "
135
+ "CI run. Parametrize over the symbol that varies and delete the copies, then "
136
+ "lower CLONE_BUDGET. Raising it needs a reason in the PR body."
137
+ )
138
+
139
+
140
+ def test_the_budget_is_not_slack() -> None:
141
+ """A budget far above the real count silently permits the next wave of copies."""
142
+ redundant = sum(len(members) - 1 for members in _clone_groups().values())
143
+
144
+ assert redundant >= CLONE_BUDGET - 10, (
145
+ f"only {redundant} cross-module clones against a budget of {CLONE_BUDGET}: "
146
+ "consolidation happened without ratcheting the budget down, so the guard now "
147
+ f"permits {CLONE_BUDGET - redundant} new copies before it complains. "
148
+ f"Set CLONE_BUDGET = {redundant}."
149
+ )
@@ -26,7 +26,7 @@ def _manifest() -> dict:
26
26
  def test_installed_pypi_wheel_satisfies_the_inspection_contract() -> None:
27
27
  distribution = importlib.metadata.distribution("quiddity")
28
28
 
29
- assert distribution.version == "0.2.4"
29
+ assert distribution.version == "0.2.5"
30
30
  assert distribution.read_text("direct_url.json") is None
31
31
  assert (
32
32
  Path(inspect.getfile(inspection.inspection_api_manifest))
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
  from dataclasses import replace
10
10
  from pathlib import Path
11
11
 
12
+ from _evidence_contract import assert_missing_model_outcomes_fail_closed
12
13
  from build123d import Box, Cylinder, Pos
13
14
 
14
15
  from draftwright.evaluation.step_analysis import (
@@ -31,8 +32,14 @@ def _states(boundary: str) -> set[str]:
31
32
 
32
33
 
33
34
  def test_every_hole_boundary_is_observed_supported_on_the_real_public_path() -> None:
35
+ # One observation for all four boundaries. `_states` re-runs the observer —
36
+ # a full `build_drawing` — on every call, so the loop paid for four identical
37
+ # builds of the same part to read four keys off the same facts.
38
+ observed = _default_observers()["holes"](_part())
39
+ assert observed, "fixture produced no hole observations"
34
40
  for boundary in ("ir_adapter", "dsl_declaration", "generated_code", "drawing_consumer"):
35
- assert _states(boundary) == {"supported"}
41
+ states = {fact.downstream[boundary] for fact in observed}
42
+ assert states == {"supported"}
36
43
 
37
44
 
38
45
  def test_removing_holes_from_the_built_ir_loses_ir_adapter_credit(monkeypatch) -> None:
@@ -86,10 +93,7 @@ def test_deleting_generated_hole_lines_loses_generated_code_credit(monkeypatch)
86
93
 
87
94
 
88
95
  def test_a_boundary_with_missing_per_hole_outcomes_fails_closed(monkeypatch) -> None:
89
- import draftwright.evaluation.step_analysis as step_analysis
90
-
91
- monkeypatch.setattr(step_analysis, "_hole_model_outcomes", lambda *_args: [])
92
- assert _states("ir_adapter") == {"unknown"}
96
+ assert_missing_model_outcomes_fail_closed(monkeypatch, "_hole_model_outcomes", _states)
93
97
 
94
98
 
95
99
  def test_a_post_build_model_access_failure_is_scored_not_raised(monkeypatch) -> None:
@@ -7,6 +7,8 @@ from pathlib import Path
7
7
  from types import SimpleNamespace
8
8
 
9
9
  import pytest
10
+ from _evidence_contract import assert_missing_model_outcomes_fail_closed
11
+ from _mutation_corpus import reduced_baseline_fixture, reduced_corpus
10
12
  from build123d import Box, Compound, Cone, Cylinder, Pos, import_step
11
13
 
12
14
  from draftwright.evaluation.step_analysis import (
@@ -19,6 +21,9 @@ from draftwright.evaluation.step_analysis import (
19
21
 
20
22
  CORPUS = Path(__file__).parent / "fixtures" / "evaluation" / "corpus-countersinks-v1.json"
21
23
 
24
+ #: Clean score of the subset the damage tests below use, asserted perfect.
25
+ reduced_baseline = reduced_baseline_fixture(CORPUS)
26
+
22
27
 
23
28
  def _single_part():
24
29
  return import_step(CORPUS.parent / "countersink-single.step")
@@ -71,18 +76,18 @@ def test_versioned_countersink_corpus_covers_every_required_case_class() -> None
71
76
  def test_real_countersink_corpus_scores_all_layers_and_topology_variants() -> None:
72
77
  corpus = load_corpus(CORPUS)
73
78
 
74
- first = evaluate_step_corpus(corpus)
75
- second = evaluate_step_corpus(corpus)
76
-
77
- assert first == second
78
- assert first.detection.recall == 1.0
79
- assert first.detection.false_positive_rate == 0.0
80
- assert (first.parameter_fidelity.passed, first.parameter_fidelity.total) == (28, 28)
81
- assert first.parameter_fidelity.score == 1.0
82
- assert (first.downstream_usefulness.passed, first.downstream_usefulness.total) == (28, 28)
83
- assert first.downstream_usefulness.score == 1.0
84
- assert first.conformant_cases == first.complete_cases == len(corpus.cases)
85
- variants = [case for case in first.cases if "topology" in case.case_id]
79
+ evaluation = evaluate_step_corpus(corpus)
80
+ assert evaluation.detection.recall == 1.0
81
+ assert evaluation.detection.false_positive_rate == 0.0
82
+ assert (evaluation.parameter_fidelity.passed, evaluation.parameter_fidelity.total) == (28, 28)
83
+ assert evaluation.parameter_fidelity.score == 1.0
84
+ assert (evaluation.downstream_usefulness.passed, evaluation.downstream_usefulness.total) == (
85
+ 28,
86
+ 28,
87
+ )
88
+ assert evaluation.downstream_usefulness.score == 1.0
89
+ assert evaluation.conformant_cases == evaluation.complete_cases == len(corpus.cases)
90
+ variants = [case for case in evaluation.cases if "topology" in case.case_id]
86
91
  assert len(variants) == 2
87
92
  assert variants[0].detection == variants[1].detection
88
93
  assert variants[0].parameter_fidelity == variants[1].parameter_fidelity
@@ -214,8 +219,14 @@ def test_equal_seats_still_remain_two_physical_observations() -> None:
214
219
 
215
220
 
216
221
  def test_every_countersink_boundary_is_observed_supported_on_the_real_public_path() -> None:
222
+ # One observation for all four boundaries. `_states` re-runs the observer —
223
+ # a full `build_drawing` — on every call, so the loop paid for four identical
224
+ # builds of the same part to read four keys off the same facts.
225
+ observed = _default_observers()["countersinks"](_single_part())
226
+ assert len(observed) == 1, "fixture must produce one countersink observation"
217
227
  for boundary in ("ir_adapter", "dsl_declaration", "generated_code", "drawing_consumer"):
218
- assert _states(boundary) == {"supported"}
228
+ states = {fact.downstream[boundary] for fact in observed}
229
+ assert states == {"supported"}
219
230
 
220
231
 
221
232
  def test_removing_the_seat_from_built_ir_loses_ir_adapter_credit(monkeypatch) -> None:
@@ -276,10 +287,7 @@ def test_deleting_generated_countersink_argument_loses_generated_code_credit(
276
287
 
277
288
 
278
289
  def test_a_boundary_with_missing_per_seat_outcomes_fails_closed(monkeypatch) -> None:
279
- import draftwright.evaluation.step_analysis as step_analysis
280
-
281
- monkeypatch.setattr(step_analysis, "_countersink_model_outcomes", lambda *_args: [])
282
- assert _states("ir_adapter") == {"unknown"}
290
+ assert_missing_model_outcomes_fail_closed(monkeypatch, "_countersink_model_outcomes", _states)
283
291
 
284
292
 
285
293
  def test_removing_placed_countersink_ink_loses_drawing_credit(monkeypatch) -> None:
@@ -468,7 +476,7 @@ def test_countersink_observer_fails_closed_when_build_or_recognition_is_unavaila
468
476
 
469
477
  @pytest.mark.parametrize("corruption", ["nonnumeric-location", "missing-axis"])
470
478
  def test_malformed_public_countersink_records_become_missed_instead_of_aborting(
471
- monkeypatch, corruption: str
479
+ monkeypatch, corruption: str, reduced_baseline
472
480
  ) -> None:
473
481
  from draftwright.drawing import Drawing
474
482
 
@@ -496,9 +504,11 @@ def test_malformed_public_countersink_records_become_missed_instead_of_aborting(
496
504
  monkeypatch.setattr(Drawing, "recognition", with_malformed_record)
497
505
 
498
506
  assert _default_observers()["countersinks"](_single_part()) == ()
499
- damaged = evaluate_step_corpus(load_corpus(CORPUS))
507
+ damaged = evaluate_step_corpus(reduced_corpus(load_corpus(CORPUS)))
500
508
  assert damaged.detection.matched == 0
501
- assert damaged.detection.missed == 7
509
+ # Every seat that matched cleanly is now missed, stated against the clean subset
510
+ # rather than the full corpus's count of 7.
511
+ assert damaged.detection.missed == reduced_baseline.detection.matched
502
512
  assert damaged.detection.false_positives == 0
503
513
 
504
514
 
@@ -7,6 +7,7 @@ from pathlib import Path
7
7
  from types import SimpleNamespace
8
8
 
9
9
  import pytest
10
+ from _evidence_contract import assert_missing_model_outcomes_fail_closed
10
11
  from build123d import Align, Box, Compound, Cylinder, Pos, import_step
11
12
 
12
13
  from draftwright.evaluation.step_analysis import (
@@ -67,18 +68,18 @@ def test_versioned_double_d_corpus_covers_each_required_case_class() -> None:
67
68
  def test_real_double_d_corpus_scores_all_layers_and_topology_variants() -> None:
68
69
  corpus = load_corpus(CORPUS)
69
70
 
70
- first = evaluate_step_corpus(corpus)
71
- second = evaluate_step_corpus(corpus)
72
-
73
- assert first == second
74
- assert first.detection.recall == 1.0
75
- assert first.detection.false_positive_rate == 0.0
76
- assert (first.parameter_fidelity.passed, first.parameter_fidelity.total) == (50, 50)
77
- assert first.parameter_fidelity.score == 1.0
78
- assert (first.downstream_usefulness.passed, first.downstream_usefulness.total) == (40, 40)
79
- assert first.downstream_usefulness.score == 1.0
80
- assert first.conformant_cases == first.complete_cases == len(corpus.cases)
81
- variants = [case for case in first.cases if "topology" in case.case_id]
71
+ evaluation = evaluate_step_corpus(corpus)
72
+ assert evaluation.detection.recall == 1.0
73
+ assert evaluation.detection.false_positive_rate == 0.0
74
+ assert (evaluation.parameter_fidelity.passed, evaluation.parameter_fidelity.total) == (50, 50)
75
+ assert evaluation.parameter_fidelity.score == 1.0
76
+ assert (evaluation.downstream_usefulness.passed, evaluation.downstream_usefulness.total) == (
77
+ 40,
78
+ 40,
79
+ )
80
+ assert evaluation.downstream_usefulness.score == 1.0
81
+ assert evaluation.conformant_cases == evaluation.complete_cases == len(corpus.cases)
82
+ variants = [case for case in evaluation.cases if "topology" in case.case_id]
82
83
  assert len(variants) == 2
83
84
  assert variants[0].detection == variants[1].detection
84
85
  assert variants[0].parameter_fidelity == variants[1].parameter_fidelity
@@ -187,8 +188,14 @@ def test_public_depth_quantization_allowance_is_independently_bracketed() -> Non
187
188
 
188
189
 
189
190
  def test_every_double_d_boundary_is_supported_on_the_real_public_path() -> None:
191
+ # One observation for all four boundaries. `_states` re-runs the observer —
192
+ # a full `build_drawing` — on every call, so the loop paid for four identical
193
+ # builds of the same part to read four keys off the same facts.
194
+ observed = _default_observers()["double-d-bores"](_part())
195
+ assert len(observed) == 1, "fixture must produce one Double-D observation"
190
196
  for boundary in ("ir_adapter", "dsl_declaration", "generated_code", "drawing_consumer"):
191
- assert _states(boundary) == {"supported"}
197
+ states = {fact.downstream[boundary] for fact in observed}
198
+ assert states == {"supported"}
192
199
 
193
200
 
194
201
  @pytest.mark.parametrize(
@@ -629,10 +636,7 @@ def test_double_d_observer_fails_closed_when_build_or_recognition_is_unavailable
629
636
 
630
637
 
631
638
  def test_a_boundary_with_missing_per_bore_outcomes_fails_closed(monkeypatch) -> None:
632
- import draftwright.evaluation.step_analysis as step_analysis
633
-
634
- monkeypatch.setattr(step_analysis, "_double_d_model_outcomes", lambda *_args: [])
635
- assert _states("ir_adapter") == {"unknown"}
639
+ assert_missing_model_outcomes_fail_closed(monkeypatch, "_double_d_model_outcomes", _states)
636
640
 
637
641
 
638
642
  @pytest.mark.parametrize(