pytest-selective 0.4.1__tar.gz → 1.0.0__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 (189) hide show
  1. pytest_selective-1.0.0/.dry-lint/baseline.json +80 -0
  2. pytest_selective-1.0.0/.dry-lint/progress.json +7 -0
  3. pytest_selective-1.0.0/.dry-lint.toml +6 -0
  4. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/test-lanes.json +28 -2
  5. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/workflows/ci.yml +55 -15
  6. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/workflows/docs.yml +3 -3
  7. pytest_selective-1.0.0/.github/workflows/dry-lint-audit.yml +91 -0
  8. pytest_selective-1.0.0/.github/workflows/dry-lint-judge.yml +30 -0
  9. pytest_selective-1.0.0/.github/workflows/dry-lint.yml +41 -0
  10. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/workflows/pr-title.yml +1 -1
  11. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/workflows/public-actions.yml +6 -2
  12. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/workflows/release-please.yml +2 -2
  13. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/workflows/release.yml +8 -4
  14. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/workflows/windows-performance.yml +11 -7
  15. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.gitignore +6 -0
  16. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.pre-commit-config.yaml +12 -0
  17. pytest_selective-1.0.0/.release-please-manifest.json +3 -0
  18. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/AGENTS.md +10 -0
  19. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/CHANGELOG.md +22 -0
  20. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/CONTRIBUTING.md +103 -3
  21. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/PKG-INFO +47 -2
  22. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/README.md +46 -1
  23. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/RELEASING.md +23 -1
  24. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/actions/plan-lanes/action.yml +2 -2
  25. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/actions/test-impact/action.yml +4 -4
  26. pytest_selective-1.0.0/contracts/selection/README.md +33 -0
  27. pytest_selective-1.0.0/contracts/selection/conftest.py +297 -0
  28. pytest_selective-1.0.0/contracts/selection/quint-connect.json +107 -0
  29. pytest_selective-1.0.0/contracts/selection/quint.ini +8 -0
  30. pytest_selective-1.0.0/contracts/selection/selection.qnt +336 -0
  31. pytest_selective-1.0.0/contracts/selection/selection_driver.py +112 -0
  32. pytest_selective-1.0.0/contracts/selection/test_selection_contract.py +4 -0
  33. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/ci-toolkit.md +5 -1
  34. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/ci.md +12 -1
  35. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/configuration.md +12 -1
  36. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/local-development.md +5 -0
  37. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/tutorials/adoption-checklist.md +5 -0
  38. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/tutorials/skip-unrelated-ci.md +33 -0
  39. pytest_selective-1.0.0/docs/work-in-progress/tickets/graph-based-inputs-plan.md +58 -0
  40. pytest_selective-1.0.0/docs/work-in-progress/tickets/ratchet-duplicates-zero-test-plan.md +18 -0
  41. pytest_selective-1.0.0/documentation/requirements.in +2 -0
  42. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/documentation/requirements.txt +5 -5
  43. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/pyproject.toml +3 -1
  44. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/skills/pytest-selective/SKILL.md +8 -0
  45. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/skills/pytest-selective/references/adoption.md +3 -0
  46. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/ast_imports.py +26 -2
  47. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/ci.py +9 -4
  48. pytest_selective-1.0.0/src/pytest_selective/git_operations.py +49 -0
  49. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/hygiene.py +3 -10
  50. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_audit_plugin.py +1 -6
  51. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_first_party.py +4 -11
  52. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_graph.py +134 -46
  53. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_models.py +3 -0
  54. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_name_scope.py +1 -5
  55. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/pytest_expansion.py +21 -70
  56. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/typecheck_scope.py +4 -7
  57. pytest_selective-1.0.0/src/pytest_selective/working_tree.py +41 -0
  58. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_hygiene.py +8 -0
  59. pytest_selective-1.0.0/tests/test_release_workflow.py +36 -0
  60. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_repository_ci.py +90 -29
  61. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_ci.py +53 -0
  62. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_impact_graph.py +69 -0
  63. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_portability.py +3 -0
  64. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_typecheck_scope.py +20 -0
  65. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_working_tree.py +78 -0
  66. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/uv.lock +170 -150
  67. pytest_selective-0.4.1/.release-please-manifest.json +0 -3
  68. pytest_selective-0.4.1/documentation/requirements.in +0 -2
  69. pytest_selective-0.4.1/src/pytest_selective/working_tree.py +0 -43
  70. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.gitattributes +0 -0
  71. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  72. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  73. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/dependabot.yml +0 -0
  74. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.github/workflows/observe-ci.yml +0 -0
  75. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/.readthedocs.yaml +0 -0
  76. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/CLAUDE.md +0 -0
  77. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/LICENSE +0 -0
  78. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/PROVENANCE.md +0 -0
  79. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/action.yml +0 -0
  80. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/actions/README.md +0 -0
  81. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/actions/pytest-expansion/action.yml +0 -0
  82. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/agentsync.toml +0 -0
  83. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/README.md +0 -0
  84. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/actions.md +0 -0
  85. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/agents.md +0 -0
  86. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/assets/banner.png +0 -0
  87. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/assets/banner.svg +0 -0
  88. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/assets/brand.json +0 -0
  89. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/assets/icon.png +0 -0
  90. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/assets/logo.svg +0 -0
  91. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/assets/raster-sources.json +0 -0
  92. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/assets/social-preview.png +0 -0
  93. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/assets/social-preview.svg +0 -0
  94. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
  95. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
  96. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
  97. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
  98. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
  99. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
  100. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
  101. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
  102. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
  103. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-22.md +0 -0
  104. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
  105. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
  106. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
  107. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
  108. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
  109. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
  110. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
  111. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
  112. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
  113. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/benchmarks/windows-runtime.md +0 -0
  114. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/comparison-pytest-impacted.md +0 -0
  115. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/evidence.md +0 -0
  116. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/pytest-hygiene.md +0 -0
  117. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/selection-health.md +0 -0
  118. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/tutorial.md +0 -0
  119. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/tutorials/sidecar-dependencies.md +0 -0
  120. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
  121. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
  122. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
  123. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
  124. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
  125. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
  126. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/docs/work-in-progress/tickets/rerun-selection-test-plan.md +0 -0
  127. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/documentation/assets/routing-comparison.svg +0 -0
  128. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/documentation/check_site.py +0 -0
  129. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/documentation/ecosystem.md +0 -0
  130. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/documentation/hooks.py +0 -0
  131. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/documentation/index.md +0 -0
  132. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/documentation/overrides/main.html +0 -0
  133. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/documentation/publishing.md +0 -0
  134. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/mkdocs.yml +0 -0
  135. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/release-please-config.json +0 -0
  136. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/benchmark_graph.py +0 -0
  137. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/benchmark_parsing.py +0 -0
  138. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/benchmark_rust.py +0 -0
  139. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/check_docs.py +0 -0
  140. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/check_metadata.py +0 -0
  141. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/ci_file_config.py +0 -0
  142. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/compare_impacted.py +0 -0
  143. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/measure_ci_runs.py +0 -0
  144. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/public_actions.py +0 -0
  145. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/render_brand.py +0 -0
  146. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/run_ci_tests.py +0 -0
  147. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/smoke_distribution.py +0 -0
  148. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/scripts/tutorial.py +0 -0
  149. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/skills/pytest-selective/agents/openai.yaml +0 -0
  150. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/__init__.py +0 -0
  151. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/__main__.py +0 -0
  152. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/checkpoint.py +0 -0
  153. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_audit.py +0 -0
  154. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_cli.py +0 -0
  155. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_declarations.py +0 -0
  156. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_explanation.py +0 -0
  157. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/impact_selection.py +0 -0
  158. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/input_policy.py +0 -0
  159. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/local.py +0 -0
  160. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/parse_batch.py +0 -0
  161. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/plugin.py +0 -0
  162. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/py.typed +0 -0
  163. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/pytest_plugins.py +0 -0
  164. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/pytest_sharding.py +0 -0
  165. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/src/pytest_selective/test_inventory.py +0 -0
  166. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/__init__.py +0 -0
  167. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_checkpoint_policy.py +0 -0
  168. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_ci_admission.py +0 -0
  169. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_ci_measurements.py +0 -0
  170. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_ci_test_execution.py +0 -0
  171. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_distribution_metadata.py +0 -0
  172. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_public_actions.py +0 -0
  173. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_pytest_expansion.py +0 -0
  174. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_repository_hygiene.py +0 -0
  175. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
  176. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/test_tutorial.py +0 -0
  177. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/__init__.py +0 -0
  178. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/git_repo.py +0 -0
  179. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_ast_imports.py +0 -0
  180. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_git_repo.py +0 -0
  181. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_global_inputs.py +0 -0
  182. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_impact_audit.py +0 -0
  183. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_impact_explanation.py +0 -0
  184. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_impact_first_party.py +0 -0
  185. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_impact_name_scope.py +0 -0
  186. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_parse_batch.py +0 -0
  187. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_pytest_plugin.py +0 -0
  188. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_pytest_plugins.py +0 -0
  189. {pytest_selective-0.4.1 → pytest_selective-1.0.0}/tests/testing/test_pytest_sharding.py +0 -0
@@ -0,0 +1,80 @@
1
+ {
2
+ "schema": 1,
3
+ "contract": "fbf56a4a852e47539b956057d11e9f3ee56a986bbb3b26ac209dc491d4b45f6e",
4
+ "started_at": "fee809549a83ddfb432cb6599ea57ae73efbe66a",
5
+ "checked_at": "031cafd1748babfecff2ef2629a0320715d8fe9c",
6
+ "entries": [
7
+ {
8
+ "hashes": {
9
+ "src/pytest_selective/hygiene.py::discover_test_modules": "49e17f87bfe24aeb",
10
+ "src/pytest_selective/test_inventory.py::discover_test_files": "567e0cacf263d340"
11
+ },
12
+ "context_hash": "",
13
+ "state": "cleaned"
14
+ },
15
+ {
16
+ "hashes": {
17
+ "src/pytest_selective/impact_graph.py::within": "5417d89a2cedc5ea",
18
+ "src/pytest_selective/typecheck_scope.py::_under": "221f42a33c38887c"
19
+ },
20
+ "context_hash": "",
21
+ "state": "cleaned"
22
+ },
23
+ {
24
+ "hashes": {
25
+ "src/pytest_selective/impact_audit_plugin.py::_git": "be715e103f83b281",
26
+ "src/pytest_selective/impact_graph.py::git": "a53bf9a83105b354"
27
+ },
28
+ "context_hash": "",
29
+ "state": "cleaned"
30
+ },
31
+ {
32
+ "hashes": {
33
+ "src/pytest_selective/impact_audit_plugin.py::_git": "be715e103f83b281",
34
+ "src/pytest_selective/pytest_expansion.py::git": "42cac77499ad42b1"
35
+ },
36
+ "context_hash": "",
37
+ "state": "cleaned"
38
+ },
39
+ {
40
+ "hashes": {
41
+ "src/pytest_selective/impact_first_party.py::_module_name": "c214530cc8698c18",
42
+ "src/pytest_selective/pytest_expansion.py::SourceTree.module_name": "633b2e80dfaac9e6"
43
+ },
44
+ "context_hash": "",
45
+ "state": "cleaned"
46
+ },
47
+ {
48
+ "hashes": {
49
+ "src/pytest_selective/impact_graph.py::git": "a53bf9a83105b354",
50
+ "src/pytest_selective/pytest_expansion.py::git": "42cac77499ad42b1"
51
+ },
52
+ "context_hash": "",
53
+ "state": "cleaned"
54
+ },
55
+ {
56
+ "hashes": {
57
+ "src/pytest_selective/impact_graph.py::git_environment": "1b32a738ac740022",
58
+ "src/pytest_selective/pytest_expansion.py::git_environment": "df7139fadaa213a9"
59
+ },
60
+ "context_hash": "",
61
+ "state": "cleaned"
62
+ },
63
+ {
64
+ "hashes": {
65
+ "src/pytest_selective/impact_name_scope.py::_casefold": "9ecabff1de943de2",
66
+ "src/pytest_selective/impact_graph.py::ModuleIndex.resolve": "a1089dfe99f751cd"
67
+ },
68
+ "context_hash": "",
69
+ "state": "cleaned"
70
+ },
71
+ {
72
+ "hashes": {
73
+ "src/pytest_selective/impact_name_scope.py::_casefold": "9ecabff1de943de2",
74
+ "src/pytest_selective/impact_graph.py::ModuleIndex.__init__": "84c3976fe798fb7e"
75
+ },
76
+ "context_hash": "",
77
+ "state": "cleaned"
78
+ }
79
+ ]
80
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "label": "dry-lint",
4
+ "color": "7E4FC9",
5
+ "labelColor": "24292F",
6
+ "message": "9 cleaned \u00b7 0 remaining"
7
+ }
@@ -0,0 +1,6 @@
1
+ # Uses dry-lint's default tracked-file scope; review include/exclude before changing it.
2
+ # Strict CI refuses incomplete judging; sweep can use a larger one-off budget.
3
+ preset = "sonnet"
4
+
5
+ [budget]
6
+ usd = 0.25
@@ -7,6 +7,11 @@
7
7
  "test_roots": [
8
8
  "tests"
9
9
  ],
10
+ "global_inputs": [
11
+ "pyproject.toml",
12
+ "uv.lock",
13
+ ".python-version"
14
+ ],
10
15
  "pytest_policy": {
11
16
  "pyproject.toml": "7a78d68735c0e3b091962244620d3a17557951123c645970638f15d3a737d987"
12
17
  }
@@ -20,6 +25,27 @@
20
25
  "src/**",
21
26
  "tests/**"
22
27
  ]
28
+ },
29
+ "contracts": {
30
+ "roots": [
31
+ "contracts"
32
+ ],
33
+ "inputs": [
34
+ "src/**",
35
+ "contracts/**",
36
+ "tests/testing/git_repo.py"
37
+ ]
38
+ },
39
+ "package": {
40
+ "inputs": [
41
+ "src/**",
42
+ "pyproject.toml",
43
+ "uv.lock",
44
+ "README.md",
45
+ "LICENSE",
46
+ "scripts/check_metadata.py",
47
+ "scripts/smoke_distribution.py"
48
+ ]
23
49
  }
24
50
  },
25
51
  "global_inputs": [
@@ -33,9 +59,9 @@
33
59
  ".github/test-lanes.json",
34
60
  ".github/workflows/ci.yml",
35
61
  ".github/workflows/release.yml",
36
- "actions/**",
62
+ "actions/*/*",
63
+ "actions/*.py",
37
64
  "action.yml",
38
- "examples/**",
39
65
  ".github/workflows/public-actions.yml"
40
66
  ]
41
67
  }
@@ -6,6 +6,8 @@ on:
6
6
  types: [opened, synchronize, reopened]
7
7
  workflow_dispatch:
8
8
  workflow_call:
9
+ schedule:
10
+ - cron: '13 3 * * 1'
9
11
  permissions:
10
12
  contents: read
11
13
  concurrency:
@@ -20,7 +22,7 @@ jobs:
20
22
  mode: ${{ steps.files.outputs.mode }}
21
23
  files: ${{ steps.files.outputs.test_roots_json }}
22
24
  steps:
23
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
25
+ - uses: actions/checkout@v7
24
26
  with:
25
27
  fetch-depth: 0
26
28
  - id: plan
@@ -41,11 +43,14 @@ jobs:
41
43
  quality:
42
44
  runs-on: ubuntu-latest
43
45
  steps:
44
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
46
+ - uses: actions/checkout@v7
45
47
  - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
46
48
  with:
47
49
  python-version: '3.14'
50
+ # Semantic judging owns its credentials and fork boundary in the required dry-lint job.
48
51
  - run: uv run --locked pre-commit run --all-files
52
+ env:
53
+ SKIP: dry-lint
49
54
  - name: Check aggregate gate against the execution lanes
50
55
  run: >-
51
56
  uv run --no-project --python 3.14
@@ -65,7 +70,7 @@ jobs:
65
70
  os: [ubuntu-latest, windows-latest, macos-latest]
66
71
  runs-on: ${{ matrix.os }}
67
72
  steps:
68
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
73
+ - uses: actions/checkout@v7
69
74
  - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
70
75
  with:
71
76
  python-version: ${{ matrix.python }}
@@ -89,13 +94,12 @@ jobs:
89
94
  with:
90
95
  junit-pattern: test-timings.xml
91
96
  artifact-name: test-timings-${{ matrix.python }}-${{ matrix.os }}
92
- - run: uv run --locked python scripts/tutorial.py --output tutorial-report.json
93
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
97
+ # tests/test_tutorial.py owns the end-to-end tutorial and follows the file plan.
98
+ - uses: actions/upload-artifact@v7
99
+ if: ${{ hashFiles('coverage.xml') != '' }}
94
100
  with:
95
101
  name: evidence-${{ matrix.os }}-${{ matrix.python }}
96
- path: |
97
- coverage.xml
98
- tutorial-report.json
102
+ path: coverage.xml
99
103
  pytest-minimum:
100
104
  needs: select-tests
101
105
  if: >-
@@ -103,7 +107,7 @@ jobs:
103
107
  fromJSON(needs.select-tests.outputs.lanes).tests) }}
104
108
  runs-on: ubuntu-latest
105
109
  steps:
106
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
110
+ - uses: actions/checkout@v7
107
111
  - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
108
112
  with:
109
113
  python-version: '3.11'
@@ -116,11 +120,13 @@ jobs:
116
120
  CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
117
121
  distributions:
118
122
  needs: select-tests
119
- # README changes still need build/metadata checks; planner failure must not skip them.
120
- if: ${{ !cancelled() }}
123
+ # The package lane includes the embedded README and LICENSE; planner failure must not skip it.
124
+ if: >-
125
+ ${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
126
+ fromJSON(needs.select-tests.outputs.lanes).package) }}
121
127
  runs-on: ubuntu-latest
122
128
  steps:
123
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
129
+ - uses: actions/checkout@v7
124
130
  - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
125
131
  with:
126
132
  python-version: '3.14'
@@ -132,15 +138,49 @@ jobs:
132
138
  ${{ needs.select-tests.result != 'success' ||
133
139
  fromJSON(needs.select-tests.outputs.lanes).tests }}
134
140
  run: uv run --locked python scripts/smoke_distribution.py
135
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
141
+ - uses: actions/upload-artifact@v7
136
142
  with:
137
143
  name: distributions
138
144
  path: dist/*
139
145
  if-no-files-found: error
140
146
 
147
+ selection-contract:
148
+ name: Selection contract (Quint)
149
+ needs: select-tests
150
+ permissions:
151
+ contents: read
152
+ # The contract report's pull-request comment.
153
+ pull-requests: write
154
+ if: >-
155
+ ${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
156
+ fromJSON(needs.select-tests.outputs.lanes).contracts) }}
157
+ runs-on: ubuntu-latest
158
+ steps:
159
+ - uses: actions/checkout@v7
160
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
161
+ with:
162
+ python-version: '3.14'
163
+ # pytest-quint-connect installs its locked Quint with npm on first use.
164
+ - uses: actions/setup-node@v7
165
+ with:
166
+ node-version: '24'
167
+ - name: Check the generated driver against the model
168
+ run: uv run --locked --group quint pytest-quint-connect codegen --check contracts/selection/quint-connect.json
169
+ - name: Generate histories from the model and replay them against the planner
170
+ run: uv run --locked --group quint pytest -c contracts/selection/quint.ini contracts/selection --quint-require-complete -q
171
+ # Match the README with the same locked renderer that produced these receipts.
172
+ # A presentation update to the floating report action must not break an immutable release.
173
+ - name: Check the README against the locked contract renderer
174
+ run: uv run --locked --group quint pytest-quint-connect report '**/.artifacts/quint-connect/results.json' --readme README.md --check
175
+ # The current action owns the sticky and artifacts, independently of README formatting.
176
+ - uses: gigaverse-app/pytest-quint-connect/report@v1
177
+ if: ${{ always() }}
178
+ with:
179
+ readme-mode: 'off'
180
+
141
181
  validation-gate:
142
182
  name: Validation gate
143
- needs: [select-tests, quality, tests, pytest-minimum, distributions]
183
+ needs: [select-tests, quality, tests, pytest-minimum, distributions, selection-contract]
144
184
  if: ${{ !cancelled() }}
145
185
  runs-on: ubuntu-latest
146
186
  steps:
@@ -148,4 +188,4 @@ jobs:
148
188
  with:
149
189
  needs: ${{ toJSON(needs) }}
150
190
  lanes: ${{ needs.select-tests.outputs.lanes || '{}' }}
151
- lane-jobs: '{"tests":["tests"],"pytest-minimum":["tests"]}'
191
+ lane-jobs: '{"tests":["tests"],"pytest-minimum":["tests"],"distributions":["package"],"selection-contract":["contracts"]}'
@@ -13,8 +13,8 @@ jobs:
13
13
  docs:
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
- - uses: actions/setup-python@v6
16
+ - uses: actions/checkout@v7
17
+ - uses: actions/setup-python@v7
18
18
  with:
19
19
  python-version: '3.13'
20
20
  cache: pip
@@ -23,7 +23,7 @@ jobs:
23
23
  - run: python scripts/render_brand.py --check
24
24
  - run: python -m mkdocs build --strict
25
25
  - run: python documentation/check_site.py
26
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
26
+ - uses: actions/upload-artifact@v7
27
27
  with:
28
28
  name: documentation-site
29
29
  path: site/
@@ -0,0 +1,91 @@
1
+ name: dry-lint audit
2
+ # Audits the whole repository against the accepted backlog, then ratchets it: pairs that were refactored away or
3
+ # judged separate are recorded as cleaned in .dry-lint/baseline.json, progress.json and the README block, through a
4
+ # pull request that runs the ordinary checks. The baseline only ever shrinks; a new blocking pair fails the audit
5
+ # and no ratchet pull request is opened. Run it by hand after merging a cleanup to record the progress at once.
6
+ on:
7
+ schedule:
8
+ - cron: '23 6 * * 1'
9
+ workflow_dispatch:
10
+ workflow_call:
11
+ permissions:
12
+ contents: read
13
+ concurrency:
14
+ group: dry-lint-audit
15
+ cancel-in-progress: false
16
+ jobs:
17
+ audit:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+ with:
22
+ persist-credentials: false
23
+ - uses: gigaverse-app/dry-lint@v1
24
+ with:
25
+ mode: sweep
26
+ base: HEAD
27
+ python-version: '3.13'
28
+ anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
29
+ plugins: anthropic==2.4.0
30
+ baseline: .dry-lint/baseline.json
31
+ require-complete: 'true'
32
+ budget-usd: '5'
33
+ - name: Retain the audit for human or agent review
34
+ if: always()
35
+ uses: actions/upload-artifact@v7
36
+ with:
37
+ name: dry-lint-audit
38
+ path: |
39
+ ${{ runner.temp }}/dry-lint/report.json
40
+ ${{ runner.temp }}/dry-lint/comment.md
41
+ retention-days: 30
42
+ if-no-files-found: warn
43
+ - name: Require ratchet PR authentication
44
+ env:
45
+ HAS_RATCHET_TOKEN: ${{ secrets.GV_CI_CROSS_REPO_TOKEN != '' }}
46
+ run: |
47
+ if [[ "$HAS_RATCHET_TOKEN" != "true" ]]; then
48
+ echo "::error::Configure the adopter-owned GV_CI_CROSS_REPO_TOKEN with contents and pull-request write access."
49
+ exit 1
50
+ fi
51
+ # The Action's sweep mode only audits. The CLI records the ratchet, reusing the verdicts the audit just stored,
52
+ # and the exact CLI environment installed by the Action. No second install or judge-version drift.
53
+ - name: Record cleaned pairs
54
+ env:
55
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
56
+ run: |
57
+ dry-lint sweep --update --store "$RUNNER_TEMP/dry-lint/verdicts" --budget-usd 5 \
58
+ --readme README.md --repository "$GITHUB_REPOSITORY" --branch main
59
+ # One standing branch: a later run updates the open ratchet PR instead of stacking another. The adopter-owned
60
+ # credential makes the PR run ordinary CI, including the required dry-lint check that refuses any widening.
61
+ - uses: peter-evans/create-pull-request@v8
62
+ with:
63
+ token: ${{ secrets.GV_CI_CROSS_REPO_TOKEN }}
64
+ branch: dry-lint/ratchet
65
+ delete-branch: true
66
+ add-paths: |
67
+ .dry-lint/baseline.json
68
+ .dry-lint/progress.json
69
+ README.md
70
+ commit-message: 'chore(dry-lint): record cleaned duplicate pairs in the baseline'
71
+ title: 'chore(dry-lint): record cleaned duplicate pairs in the baseline'
72
+ body: |
73
+ ## TLDR
74
+
75
+ The weekly dry-lint audit found accepted duplicate pairs that no longer exist or were judged separate,
76
+ and shrinks `.dry-lint/baseline.json`, the progress badge and the README block to match.
77
+
78
+ The baseline can only shrink: the required `dry-lint` check rejects any pull request that widens it.
79
+ The full audit is in the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).
80
+
81
+ ```text
82
+ BEFORE: merged cleanup -> accepted baseline and README still show old debt
83
+ AFTER: complete audit -> reviewed metadata PR -> baseline and README record cleanup
84
+ ```
85
+
86
+ | Policy | Before | After |
87
+ | --- | --- | --- |
88
+ | Accepted pairs | Previously recorded states | Removed/separate pairs marked cleaned; explicit exclusions dismissed |
89
+ | New debt | Rejected | Still rejected; no added allowances |
90
+
91
+ Pair-count changes appear in the baseline/progress diff and the linked audit. No application code changes.
@@ -0,0 +1,30 @@
1
+ name: dry-lint judge
2
+ on:
3
+ workflow_run:
4
+ workflows: [dry-lint]
5
+ types: [completed]
6
+ permissions:
7
+ actions: read
8
+ checks: write
9
+ contents: read
10
+ pull-requests: write
11
+ concurrency:
12
+ group: dry-lint-judge-${{ github.event.workflow_run.head_branch }}
13
+ cancel-in-progress: true
14
+ jobs:
15
+ judge:
16
+ if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.head_repository.full_name != github.repository
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ # Only trusted default-branch settings and baseline; fork code is artifact data.
20
+ - uses: actions/checkout@v7
21
+ with:
22
+ persist-credentials: false
23
+ - uses: gigaverse-app/dry-lint@v1
24
+ with:
25
+ anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
26
+ plugins: anthropic==2.4.0
27
+ baseline: .dry-lint/baseline.json
28
+ require-complete: 'true'
29
+ comment: 'true'
30
+ comment-on-clean: 'true'
@@ -0,0 +1,41 @@
1
+ name: dry-lint
2
+ on:
3
+ pull_request:
4
+ types: [opened, synchronize, reopened]
5
+ push:
6
+ branches: [main]
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ pull-requests: write
12
+
13
+ concurrency:
14
+ group: dry-lint-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }}
15
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
16
+
17
+ jobs:
18
+ check:
19
+ # A detection-only fork job must never satisfy the required judged check.
20
+ name: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'dry-lint detect' || 'dry-lint' }}
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+ with:
25
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
26
+ fetch-depth: 1
27
+ persist-credentials: false
28
+ - uses: gigaverse-app/dry-lint@v1
29
+ with:
30
+ anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
31
+ plugins: anthropic==2.4.0
32
+ baseline: .dry-lint/baseline.json
33
+ require-complete: 'true'
34
+ comment: 'true'
35
+ comment-on-clean: 'true'
36
+
37
+ audit:
38
+ # Manual full validation also exercises the scheduled ratchet before its first merge.
39
+ if: github.event_name == 'workflow_dispatch'
40
+ uses: ./.github/workflows/dry-lint-audit.yml
41
+ secrets: inherit
@@ -14,7 +14,7 @@ jobs:
14
14
  timeout-minutes: 5
15
15
  steps:
16
16
  # Only reads PR metadata. Never check out or execute PR code in this workflow.
17
- - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
17
+ - uses: amannn/action-semantic-pull-request@v6
18
18
  with:
19
19
  types: |
20
20
  feat
@@ -4,6 +4,8 @@ on:
4
4
  push:
5
5
  branches: [main]
6
6
  workflow_dispatch:
7
+ schedule:
8
+ - cron: '33 3 * * 1'
7
9
  permissions:
8
10
  contents: read
9
11
  jobs:
@@ -27,7 +29,7 @@ jobs:
27
29
  fromJSON(needs.select-tests.outputs.lanes).tests) }}
28
30
  runs-on: ubuntu-latest
29
31
  steps:
30
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
32
+ - uses: actions/checkout@v7
31
33
  with:
32
34
  fetch-depth: 0
33
35
  - name: Use the reviewed repository graph
@@ -67,7 +69,9 @@ jobs:
67
69
  run: |
68
70
  python - <<'PY'
69
71
  import json, os
70
- assert json.loads(os.environ['LANES']) == {'tests': True}
72
+ # A full checkpoint admits every configured lane.
73
+ configured = json.load(open('.github/test-lanes.json', encoding='utf-8'))['lanes']
74
+ assert json.loads(os.environ['LANES']) == dict.fromkeys(configured, True)
71
75
  PY
72
76
  - name: Require successful planning after fallback execution
73
77
  if: ${{ !cancelled() && needs.select-tests.result != 'success' }}
@@ -28,7 +28,7 @@ jobs:
28
28
  steps:
29
29
  - name: Only process the tested main head
30
30
  id: current
31
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
31
+ uses: actions/github-script@v9
32
32
  with:
33
33
  script: |
34
34
  const {data: branch} = await github.rest.repos.getBranch({...context.repo, branch: 'main'});
@@ -44,7 +44,7 @@ jobs:
44
44
  fi
45
45
  - name: Prepare release PR or publish its GitHub release
46
46
  if: steps.current.outputs.matches == 'true'
47
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
47
+ uses: googleapis/release-please-action@v5
48
48
  with:
49
49
  # This credential triggers CI on release PRs and Publish on releases.
50
50
  # GITHUB_TOKEN would suppress those follow-up workflow events.
@@ -10,8 +10,8 @@ jobs:
10
10
  validate-tag:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
14
- - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
13
+ - uses: actions/checkout@v7
14
+ - uses: astral-sh/setup-uv@v10.3.3
15
15
  with:
16
16
  python-version: '3.14'
17
17
  - name: Require a version-matching tag
@@ -21,6 +21,10 @@ jobs:
21
21
  uv run --no-project python -c 'import os, pathlib, tomllib; version = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]; assert os.environ["RELEASE_REF"] == f"refs/tags/v{version}", "Run publishing from the version-matching v tag"'
22
22
  checks:
23
23
  needs: validate-tag
24
+ # Reusable CI's Quint report requests this grant, even on a tag with no PR.
25
+ permissions:
26
+ contents: read
27
+ pull-requests: write
24
28
  uses: ./.github/workflows/ci.yml
25
29
  publish:
26
30
  needs: checks
@@ -31,12 +35,12 @@ jobs:
31
35
  permissions:
32
36
  id-token: write
33
37
  steps:
34
- - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
38
+ - uses: actions/download-artifact@v8
35
39
  with:
36
40
  name: distributions
37
41
  path: dist
38
42
  - name: Publish tested distributions with attestations
39
- uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # release/v1
43
+ uses: pypa/gh-action-pypi-publish@release/v1
40
44
 
41
45
  action-version:
42
46
  needs: publish
@@ -21,34 +21,38 @@ jobs:
21
21
  matrix:
22
22
  python: ['3.11', '3.14']
23
23
  steps:
24
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
24
+ - uses: actions/checkout@v7
25
25
  with:
26
26
  ref: ${{ inputs.baseline || github.event.pull_request.base.sha }}
27
27
  path: baseline
28
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
28
+ - uses: actions/checkout@v7
29
29
  with:
30
30
  ref: ${{ github.event.pull_request.head.sha || github.sha }}
31
31
  path: candidate
32
- - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e
32
+ - uses: astral-sh/setup-uv@v10.3.3
33
33
  with:
34
34
  python-version: ${{ matrix.python }}
35
35
  cache-suffix: matched-windows-${{ matrix.python }}
36
36
  - run: uv sync --directory baseline --locked
37
37
  - run: uv sync --directory candidate --locked
38
+ - name: Preserve the baseline Git search path
39
+ shell: pwsh
40
+ run: '"BASELINE_GIT_PATH=$env:PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append'
41
+ - uses: gigaverse-app/ci-esta/actions/native-windows-git@v1
38
42
  - name: Run complete suites in both orders on the same worker
39
43
  shell: pwsh
40
44
  run: |
41
45
  $results = Join-Path $env:GITHUB_WORKSPACE 'measurements'
42
46
  New-Item -ItemType Directory -Path $results | Out-Null
43
47
  $measurements = @()
44
- $originalPath = $env:PATH
48
+ $originalPath = $env:BASELINE_GIT_PATH
49
+ $nativeGitPath = $env:PATH
45
50
  foreach ($sample in @('baseline-1', 'candidate-1', 'candidate-2', 'baseline-2')) {
46
51
  $arm = $sample.Split('-')[0]
47
52
  Push-Location (Join-Path $env:GITHUB_WORKSPACE $arm)
48
53
  try {
49
54
  # Start every arm with the same PATH; only the candidate opts in.
50
- $env:PATH = $originalPath
51
- if ($arm -eq 'candidate') { ./scripts/use_native_windows_git.ps1 }
55
+ $env:PATH = if ($arm -eq 'candidate') { $nativeGitPath } else { $originalPath }
52
56
  $sha = git rev-parse HEAD
53
57
  # Keep repeated arms from timing cleanup of an earlier arm's Git fixtures.
54
58
  $temp = Join-Path $env:RUNNER_TEMP "matched-pytest-$sample"
@@ -63,7 +67,7 @@ jobs:
63
67
  if ($result -ne 0) { throw "${sample} failed: $result" }
64
68
  } finally { Pop-Location }
65
69
  }
66
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
70
+ - uses: actions/upload-artifact@v7
67
71
  if: always()
68
72
  with:
69
73
  name: matched-windows-${{ matrix.python }}
@@ -15,3 +15,9 @@ tutorial-report.json
15
15
  # Built documentation
16
16
  /site/
17
17
  /.venv-docs/
18
+
19
+ # Local judge credentials
20
+ .env
21
+
22
+ # pytest-quint-connect receipts, traces and logs
23
+ .artifacts/
@@ -1,3 +1,6 @@
1
+ minimum_pre_commit_version: '4.3.0'
2
+ default_install_hook_types: [pre-commit, prepare-commit-msg]
3
+ default_stages: [pre-commit]
1
4
  repos:
2
5
  - repo: local
3
6
  hooks:
@@ -17,3 +20,12 @@ repos:
17
20
  language: system
18
21
  pass_filenames: false
19
22
  types: [markdown]
23
+
24
+ - repo: https://github.com/gigaverse-app/dry-lint
25
+ rev: v1
26
+ hooks:
27
+ - id: dry-lint
28
+ args: [--preset, sonnet, --baseline, .dry-lint/baseline.json]
29
+ language_version: python3.13
30
+ - id: dry-lint-trailers
31
+ language_version: python3.13
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.0.0"
3
+ }