pytest-selective 0.4.2__tar.gz → 1.1.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 (198) hide show
  1. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/test-lanes.json +28 -2
  2. pytest_selective-1.1.0/.github/validation-inputs.json +11 -0
  3. pytest_selective-1.1.0/.github/workflows/ci.yml +245 -0
  4. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/public-actions.yml +17 -1
  5. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/release.yml +7 -0
  6. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/windows-performance.yml +29 -14
  7. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.gitignore +3 -0
  8. pytest_selective-1.1.0/.release-please-manifest.json +3 -0
  9. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/AGENTS.md +5 -0
  10. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/CHANGELOG.md +28 -0
  11. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/CONTRIBUTING.md +34 -2
  12. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/PKG-INFO +58 -13
  13. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/README.md +57 -12
  14. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/RELEASING.md +23 -1
  15. pytest_selective-1.1.0/actions/validation-gate/action.yml +32 -0
  16. pytest_selective-1.1.0/contracts/selection/README.md +41 -0
  17. pytest_selective-1.1.0/contracts/selection/conftest.py +401 -0
  18. pytest_selective-1.1.0/contracts/selection/quint-connect.json +147 -0
  19. pytest_selective-1.1.0/contracts/selection/quint.ini +8 -0
  20. pytest_selective-1.1.0/contracts/selection/runtime_deps.py +54 -0
  21. pytest_selective-1.1.0/contracts/selection/selection.qnt +388 -0
  22. pytest_selective-1.1.0/contracts/selection/selection_driver.py +113 -0
  23. pytest_selective-1.1.0/contracts/selection/test_selection_contract.py +4 -0
  24. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/actions.md +1 -0
  25. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/agents.md +12 -9
  26. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/windows-runtime.md +61 -0
  27. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/ci-toolkit.md +17 -1
  28. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/ci.md +38 -3
  29. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/configuration.md +3 -2
  30. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/tutorials/adoption-checklist.md +9 -1
  31. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/tutorials/skip-unrelated-ci.md +43 -3
  32. pytest_selective-1.1.0/docs/work-in-progress/tickets/graph-based-inputs-plan.md +58 -0
  33. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/check_site.py +24 -1
  34. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/hooks.py +20 -3
  35. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/index.md +13 -0
  36. pytest_selective-1.1.0/documentation/llms.txt +31 -0
  37. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/mkdocs.yml +8 -1
  38. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/pyproject.toml +3 -1
  39. pytest_selective-1.1.0/scripts/measure_ci_runs.py +148 -0
  40. pytest_selective-1.1.0/skills/pytest-selective/SKILL.md +505 -0
  41. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/ci.py +21 -7
  42. pytest_selective-1.1.0/src/pytest_selective/gate.py +96 -0
  43. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_graph.py +158 -13
  44. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_models.py +3 -0
  45. pytest_selective-1.1.0/tests/test_ci_measurements.py +121 -0
  46. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_public_actions.py +10 -0
  47. pytest_selective-1.1.0/tests/test_release_workflow.py +75 -0
  48. pytest_selective-1.1.0/tests/test_repository_ci.py +217 -0
  49. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_ci.py +94 -0
  50. pytest_selective-1.1.0/tests/testing/test_gate.py +123 -0
  51. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_global_inputs.py +41 -0
  52. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_graph.py +101 -0
  53. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/uv.lock +18 -2
  54. pytest_selective-0.4.2/.github/workflows/ci.yml +0 -154
  55. pytest_selective-0.4.2/.release-please-manifest.json +0 -3
  56. pytest_selective-0.4.2/scripts/measure_ci_runs.py +0 -79
  57. pytest_selective-0.4.2/skills/pytest-selective/SKILL.md +0 -117
  58. pytest_selective-0.4.2/skills/pytest-selective/references/adoption.md +0 -112
  59. pytest_selective-0.4.2/tests/test_ci_measurements.py +0 -70
  60. pytest_selective-0.4.2/tests/test_repository_ci.py +0 -147
  61. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.dry-lint/baseline.json +0 -0
  62. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.dry-lint/progress.json +0 -0
  63. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.dry-lint.toml +0 -0
  64. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.gitattributes +0 -0
  65. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  66. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  67. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/dependabot.yml +0 -0
  68. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/docs.yml +0 -0
  69. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/dry-lint-audit.yml +0 -0
  70. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/dry-lint-judge.yml +0 -0
  71. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/dry-lint.yml +0 -0
  72. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/observe-ci.yml +0 -0
  73. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/pr-title.yml +0 -0
  74. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/release-please.yml +0 -0
  75. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.pre-commit-config.yaml +0 -0
  76. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.readthedocs.yaml +0 -0
  77. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/CLAUDE.md +0 -0
  78. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/LICENSE +0 -0
  79. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/PROVENANCE.md +0 -0
  80. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/action.yml +0 -0
  81. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/actions/README.md +0 -0
  82. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/actions/plan-lanes/action.yml +0 -0
  83. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/actions/pytest-expansion/action.yml +0 -0
  84. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/actions/test-impact/action.yml +0 -0
  85. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/agentsync.toml +0 -0
  86. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/README.md +0 -0
  87. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/banner.png +0 -0
  88. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/banner.svg +0 -0
  89. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/brand.json +0 -0
  90. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/icon.png +0 -0
  91. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/logo.svg +0 -0
  92. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/raster-sources.json +0 -0
  93. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/social-preview.png +0 -0
  94. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/social-preview.svg +0 -0
  95. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
  96. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
  97. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
  98. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
  99. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
  100. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
  101. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
  102. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
  103. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
  104. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22.md +0 -0
  105. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
  106. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
  107. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
  108. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
  109. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
  110. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
  111. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
  112. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
  113. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
  114. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/comparison-pytest-impacted.md +0 -0
  115. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/evidence.md +0 -0
  116. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/local-development.md +0 -0
  117. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/pytest-hygiene.md +0 -0
  118. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/selection-health.md +0 -0
  119. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/tutorial.md +0 -0
  120. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/tutorials/sidecar-dependencies.md +0 -0
  121. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
  122. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
  123. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
  124. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
  125. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
  126. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
  127. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/ratchet-duplicates-zero-test-plan.md +0 -0
  128. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/rerun-selection-test-plan.md +0 -0
  129. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/assets/routing-comparison.svg +0 -0
  130. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/ecosystem.md +0 -0
  131. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/overrides/main.html +0 -0
  132. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/publishing.md +0 -0
  133. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/requirements.in +0 -0
  134. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/requirements.txt +0 -0
  135. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/release-please-config.json +0 -0
  136. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/benchmark_graph.py +0 -0
  137. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/benchmark_parsing.py +0 -0
  138. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/benchmark_rust.py +0 -0
  139. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/check_docs.py +0 -0
  140. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/check_metadata.py +0 -0
  141. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/ci_file_config.py +0 -0
  142. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/compare_impacted.py +0 -0
  143. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/public_actions.py +0 -0
  144. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/render_brand.py +0 -0
  145. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/run_ci_tests.py +0 -0
  146. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/smoke_distribution.py +0 -0
  147. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/tutorial.py +0 -0
  148. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/skills/pytest-selective/agents/openai.yaml +0 -0
  149. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/__init__.py +0 -0
  150. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/__main__.py +0 -0
  151. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/ast_imports.py +0 -0
  152. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/checkpoint.py +0 -0
  153. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/git_operations.py +0 -0
  154. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/hygiene.py +0 -0
  155. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_audit.py +0 -0
  156. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
  157. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_cli.py +0 -0
  158. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_declarations.py +0 -0
  159. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_explanation.py +0 -0
  160. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_first_party.py +0 -0
  161. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_name_scope.py +0 -0
  162. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_selection.py +0 -0
  163. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/input_policy.py +0 -0
  164. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/local.py +0 -0
  165. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/parse_batch.py +0 -0
  166. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/plugin.py +0 -0
  167. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/py.typed +0 -0
  168. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/pytest_expansion.py +0 -0
  169. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/pytest_plugins.py +0 -0
  170. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/pytest_sharding.py +0 -0
  171. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/test_inventory.py +0 -0
  172. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/typecheck_scope.py +0 -0
  173. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/working_tree.py +0 -0
  174. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/__init__.py +0 -0
  175. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_checkpoint_policy.py +0 -0
  176. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_ci_admission.py +0 -0
  177. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_ci_test_execution.py +0 -0
  178. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_distribution_metadata.py +0 -0
  179. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_hygiene.py +0 -0
  180. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_pytest_expansion.py +0 -0
  181. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_repository_hygiene.py +0 -0
  182. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
  183. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_tutorial.py +0 -0
  184. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/__init__.py +0 -0
  185. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/git_repo.py +0 -0
  186. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_ast_imports.py +0 -0
  187. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_git_repo.py +0 -0
  188. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_audit.py +0 -0
  189. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_explanation.py +0 -0
  190. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_first_party.py +0 -0
  191. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_name_scope.py +0 -0
  192. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_parse_batch.py +0 -0
  193. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_portability.py +0 -0
  194. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_pytest_plugin.py +0 -0
  195. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_pytest_plugins.py +0 -0
  196. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_pytest_sharding.py +0 -0
  197. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_typecheck_scope.py +0 -0
  198. {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_working_tree.py +0 -0
@@ -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
  }
@@ -0,0 +1,11 @@
1
+ [
2
+ ".github",
3
+ "src",
4
+ "tests",
5
+ "scripts",
6
+ "actions",
7
+ "action.yml",
8
+ "pyproject.toml",
9
+ "uv.lock",
10
+ ".pre-commit-config.yaml"
11
+ ]
@@ -0,0 +1,245 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ types: [opened, synchronize, reopened]
7
+ workflow_dispatch:
8
+ workflow_call:
9
+ schedule:
10
+ - cron: '13 3 * * 1'
11
+ permissions:
12
+ contents: read
13
+ concurrency:
14
+ # Only newer revisions of the same PR supersede work; keep every main/manual run.
15
+ group: ci-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.number || github.run_id }}
16
+ cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == 1 }}
17
+ jobs:
18
+ select-tests:
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: read
22
+ actions: read # Receipts are completed steps of earlier runs' jobs.
23
+ checks: read
24
+ pull-requests: read
25
+ env:
26
+ # The jobs whose passing first attempts can prove the tests lane, by exact job name.
27
+ TEST_LANE_JOBS: >-
28
+ {"py311-ubuntu":"tests (3.11, ubuntu-latest)","py311-windows":"tests (3.11, windows-latest)",
29
+ "py311-macos":"tests (3.11, macos-latest)","py314-ubuntu":"tests (3.14, ubuntu-latest)",
30
+ "py314-windows":"tests (3.14, windows-latest)","py314-macos":"tests (3.14, macos-latest)",
31
+ "minimum":"pytest-minimum"}
32
+ outputs:
33
+ # Planned lanes after reuse: the tests lane is false only when every one of its jobs is proven by a receipt.
34
+ lanes: ${{ steps.admit.outputs.lanes }}
35
+ # The plan before reuse, for steps that follow changed runtime inputs rather than test execution.
36
+ planned: ${{ steps.plan.outputs.lanes }}
37
+ mode: ${{ steps.files.outputs.mode }}
38
+ files: ${{ steps.files.outputs.test_roots_json }}
39
+ receipt: ${{ steps.evidence.outputs.receipt }}
40
+ reused: ${{ steps.evidence.outputs.reused }}
41
+ steps:
42
+ - uses: actions/checkout@v7
43
+ with:
44
+ fetch-depth: 0
45
+ # GitHub often omits a run's pull_requests link. This marker binds the run to its PR and head, so the push
46
+ # after a squash merge can use this run's receipts. CI-esta matches the exact name.
47
+ - name: "PR evidence #${{ github.event.pull_request.number }} head ${{ github.event.pull_request.head.sha }}"
48
+ if: github.event_name == 'pull_request'
49
+ run: 'true'
50
+ - id: plan
51
+ uses: ./actions/plan-lanes
52
+ with:
53
+ config: .github/test-lanes.json
54
+ - name: Share the repository graph and execution inputs with file selection
55
+ env:
56
+ PYTHONPATH: src
57
+ run: |
58
+ uv run --no-project --python 3.14 --with pydantic==2.13.4 --with pyyaml==6.0.3 \
59
+ python scripts/ci_file_config.py "$RUNNER_TEMP/test-impact.json"
60
+ - id: files
61
+ uses: ./
62
+ with:
63
+ config: ${{ runner.temp }}/test-impact.json
64
+ full-roots: tests
65
+ # Everything the tests lane executes or reads, bound into each receipt.
66
+ - id: inputs
67
+ run: echo "paths=$(jq -c . .github/validation-inputs.json)" >> "$GITHUB_OUTPUT"
68
+ # CI-esta reuses a passing first attempt of the same job when its inputs and selected files are identical:
69
+ # after a squash merge, on a version-only release PR, and on a PR push that changes nothing the tests read.
70
+ # Release (workflow_call), schedules, manual runs and the test:full label always execute.
71
+ - id: evidence
72
+ if: >-
73
+ ${{ (github.event_name == 'pull_request' || github.event_name == 'push') &&
74
+ !contains(github.event.pull_request.labels.*.name, 'test:full') }}
75
+ uses: gigaverse-app/ci-esta/actions/resolve-lane-evidence@v1
76
+ with:
77
+ lanes: ${{ env.TEST_LANE_JOBS }}
78
+ execution-context: ${{ steps.files.outputs.mode }} ${{ steps.files.outputs.test_roots_json }}
79
+ input-paths: ${{ steps.inputs.outputs.paths }}
80
+ reuse-after-merge: 'true'
81
+ reuse-version-only-release: 'true'
82
+ - id: admit
83
+ name: Admit the tests lane unless every job is proven
84
+ env:
85
+ PLANNED_LANES: ${{ steps.plan.outputs.lanes }}
86
+ REUSED: ${{ steps.evidence.outputs.reused || '{}' }}
87
+ run: |
88
+ admitted=$(jq -c --argjson jobs "$TEST_LANE_JOBS" --argjson reused "$REUSED" 'if [$jobs | keys[] | $reused[.] == true] | all then .tests = false else . end' <<<"$PLANNED_LANES")
89
+ echo "reused=$REUSED"
90
+ echo "lanes=$admitted" | tee -a "$GITHUB_OUTPUT"
91
+ quality:
92
+ runs-on: ubuntu-latest
93
+ steps:
94
+ - uses: actions/checkout@v7
95
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
96
+ with:
97
+ python-version: '3.14'
98
+ # Semantic judging owns its credentials and fork boundary in the required dry-lint job.
99
+ - run: uv run --locked pre-commit run --all-files
100
+ env:
101
+ SKIP: dry-lint
102
+ - name: Check aggregate gate against the execution lanes
103
+ run: >-
104
+ uv run --no-project --python 3.14
105
+ --with "ci-esta>=1.1.0,<2.0.0"
106
+ ci-esta check-workflow --workflow .github/workflows/ci.yml --lanes .github/test-lanes.json
107
+ - name: Validate GitHub Actions
108
+ run: docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.7 -color
109
+ tests:
110
+ needs: select-tests
111
+ if: >-
112
+ ${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
113
+ fromJSON(needs.select-tests.outputs.lanes).tests) }}
114
+ strategy:
115
+ fail-fast: false
116
+ matrix:
117
+ python: ['3.11', '3.14']
118
+ os: [ubuntu-latest, windows-latest, macos-latest]
119
+ runs-on: ${{ matrix.os }}
120
+ steps:
121
+ - uses: actions/checkout@v7
122
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
123
+ with:
124
+ python-version: ${{ matrix.python }}
125
+ - name: Use native Git for Windows
126
+ uses: gigaverse-app/ci-esta/actions/native-windows-git@v1
127
+ - name: Record coverage runtime
128
+ run: uv run --locked python -m coverage --version
129
+ - name: Execute the shared affected-file plan
130
+ run: uv run --locked python scripts/run_ci_tests.py -q -n 4 --durations=20 --junitxml=test-timings.xml
131
+ env:
132
+ CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
133
+ CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
134
+ CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
135
+ CI_COVERAGE: 'true'
136
+ # Keep temporary Git repositories beside the checkout on the work disk.
137
+ TMP: ${{ runner.temp }}
138
+ TEMP: ${{ runner.temp }}
139
+ TMPDIR: ${{ runner.temp }}
140
+ - uses: gigaverse-app/ci-esta/actions/learn-test-timings@v1
141
+ if: ${{ hashFiles('test-timings.xml') != '' }}
142
+ with:
143
+ junit-pattern: test-timings.xml
144
+ artifact-name: test-timings-${{ matrix.python }}-${{ matrix.os }}
145
+ # tests/test_tutorial.py owns the end-to-end tutorial and follows the file plan.
146
+ - uses: actions/upload-artifact@v7
147
+ if: ${{ hashFiles('coverage.xml') != '' }}
148
+ with:
149
+ name: evidence-${{ matrix.os }}-${{ matrix.python }}
150
+ path: coverage.xml
151
+ - name: ${{ needs.select-tests.outputs.receipt || 'No execution receipt' }}
152
+ if: ${{ needs.select-tests.result == 'success' && needs.select-tests.outputs.receipt != '' }}
153
+ run: echo 'Recording a completed execution of this job for this tested tree and selection.'
154
+ pytest-minimum:
155
+ needs: select-tests
156
+ if: >-
157
+ ${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
158
+ fromJSON(needs.select-tests.outputs.lanes).tests) }}
159
+ runs-on: ubuntu-latest
160
+ steps:
161
+ - uses: actions/checkout@v7
162
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
163
+ with:
164
+ python-version: '3.11'
165
+ - run: uv pip install 'pytest==8.0.0'
166
+ - name: Execute the same file plan on minimum pytest
167
+ run: uv run --no-sync python scripts/run_ci_tests.py -q -n 4 --durations=20
168
+ env:
169
+ CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
170
+ CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
171
+ CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
172
+ - name: ${{ needs.select-tests.outputs.receipt || 'No execution receipt' }}
173
+ if: ${{ needs.select-tests.result == 'success' && needs.select-tests.outputs.receipt != '' }}
174
+ run: echo 'Recording a completed execution of this job for this tested tree and selection.'
175
+ distributions:
176
+ needs: select-tests
177
+ # The package lane includes the embedded README and LICENSE; planner failure must not skip it.
178
+ if: >-
179
+ ${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
180
+ fromJSON(needs.select-tests.outputs.lanes).package) }}
181
+ runs-on: ubuntu-latest
182
+ steps:
183
+ - uses: actions/checkout@v7
184
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
185
+ with:
186
+ python-version: '3.14'
187
+ - run: uv build
188
+ - run: uv run --locked twine check --strict dist/*
189
+ - run: uv run --locked python scripts/check_metadata.py
190
+ - name: Smoke installed distributions when runtime inputs change
191
+ if: >-
192
+ ${{ needs.select-tests.result != 'success' ||
193
+ fromJSON(needs.select-tests.outputs.planned).tests }}
194
+ run: uv run --locked python scripts/smoke_distribution.py
195
+ - uses: actions/upload-artifact@v7
196
+ with:
197
+ name: distributions
198
+ path: dist/*
199
+ if-no-files-found: error
200
+
201
+ selection-contract:
202
+ name: Selection contract (Quint)
203
+ needs: select-tests
204
+ permissions:
205
+ contents: read
206
+ # The contract report's pull-request comment.
207
+ pull-requests: write
208
+ if: >-
209
+ ${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
210
+ fromJSON(needs.select-tests.outputs.lanes).contracts) }}
211
+ runs-on: ubuntu-latest
212
+ steps:
213
+ - uses: actions/checkout@v7
214
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
215
+ with:
216
+ python-version: '3.14'
217
+ # pytest-quint-connect installs its locked Quint with npm on first use.
218
+ - uses: actions/setup-node@v7
219
+ with:
220
+ node-version: '24'
221
+ - name: Check the generated driver against the model
222
+ run: uv run --locked --group quint pytest-quint-connect codegen --check contracts/selection/quint-connect.json
223
+ - name: Generate histories from the model and replay them against the planner
224
+ run: uv run --locked --group quint pytest -c contracts/selection/quint.ini contracts/selection --quint-require-complete -q
225
+ # Match the README with the same locked renderer that produced these receipts.
226
+ # A presentation update to the floating report action must not break an immutable release.
227
+ - name: Check the README against the locked contract renderer
228
+ run: uv run --locked --group quint pytest-quint-connect report '**/.artifacts/quint-connect/results.json' --readme README.md --check
229
+ # The current action owns the sticky and artifacts, independently of README formatting.
230
+ - uses: gigaverse-app/pytest-quint-connect/report@v1
231
+ if: ${{ always() }}
232
+ with:
233
+ readme-mode: 'off'
234
+
235
+ validation-gate:
236
+ name: Validation gate
237
+ needs: [select-tests, quality, tests, pytest-minimum, distributions, selection-contract]
238
+ if: ${{ !cancelled() }}
239
+ runs-on: ubuntu-latest
240
+ steps:
241
+ - uses: gigaverse-app/ci-esta/actions/validation-gate@v1
242
+ with:
243
+ needs: ${{ toJSON(needs) }}
244
+ lanes: ${{ needs.select-tests.outputs.lanes || '{}' }}
245
+ lane-jobs: '{"tests":["tests"],"pytest-minimum":["tests"],"distributions":["package"],"selection-contract":["contracts"]}'
@@ -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:
@@ -67,8 +69,22 @@ 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' }}
74
78
  run: exit 1
79
+ gate:
80
+ name: Public action gate
81
+ needs: [select-tests, public-actions]
82
+ if: ${{ !cancelled() }}
83
+ runs-on: ubuntu-latest
84
+ steps:
85
+ - uses: actions/checkout@v7
86
+ # Dogfood the public gate: the smoke may be skipped only when the successful plan said so.
87
+ - uses: ./actions/validation-gate
88
+ with:
89
+ needs: ${{ toJSON(needs) }}
90
+ lane-jobs: '{"public-actions":["tests"]}'
@@ -21,6 +21,13 @@ 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
+ # A called workflow gets no more than this, and GitHub refuses to start it when a job asks for more: the Quint
25
+ # report needs pull-requests: write, and select-tests reads earlier runs' jobs and checks for lane receipts.
26
+ permissions:
27
+ contents: read
28
+ actions: read
29
+ checks: read
30
+ pull-requests: write
24
31
  uses: ./.github/workflows/ci.yml
25
32
  publish:
26
33
  needs: checks
@@ -1,17 +1,19 @@
1
1
  name: Matched Windows performance
2
2
  on:
3
- pull_request:
4
- paths: ['.github/workflows/windows-performance.yml']
5
3
  workflow_dispatch:
6
4
  inputs:
7
5
  baseline:
8
6
  description: Immutable baseline commit to compare with the selected ref
9
7
  required: true
8
+ candidate-workers:
9
+ description: Windows xdist worker count to compare with the existing four workers
10
+ type: number
11
+ default: 4
12
+ ci-esta-ref:
13
+ description: CI-esta ref containing the candidate native-Git action
14
+ default: v1
10
15
  permissions:
11
16
  contents: read
12
- concurrency:
13
- group: windows-performance-${{ github.event.pull_request.number || github.run_id }}
14
- cancel-in-progress: ${{ github.event_name == 'pull_request' }}
15
17
  jobs:
16
18
  compare:
17
19
  runs-on: windows-latest
@@ -23,11 +25,11 @@ jobs:
23
25
  steps:
24
26
  - uses: actions/checkout@v7
25
27
  with:
26
- ref: ${{ inputs.baseline || github.event.pull_request.base.sha }}
28
+ ref: ${{ inputs.baseline }}
27
29
  path: baseline
28
30
  - uses: actions/checkout@v7
29
31
  with:
30
- ref: ${{ github.event.pull_request.head.sha || github.sha }}
32
+ ref: ${{ github.sha }}
31
33
  path: candidate
32
34
  - uses: astral-sh/setup-uv@v10.3.3
33
35
  with:
@@ -35,34 +37,47 @@ jobs:
35
37
  cache-suffix: matched-windows-${{ matrix.python }}
36
38
  - run: uv sync --directory baseline --locked
37
39
  - run: uv sync --directory candidate --locked
38
- - name: Preserve the baseline Git search path
40
+ - uses: actions/checkout@v7
41
+ with:
42
+ repository: gigaverse-app/ci-esta
43
+ ref: ${{ inputs.ci-esta-ref }}
44
+ path: ci-esta-tools
45
+ - name: Preserve the launcher path for the control
39
46
  shell: pwsh
40
47
  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
48
+ - uses: ./ci-esta-tools/actions/native-windows-git
42
49
  - name: Run complete suites in both orders on the same worker
43
50
  shell: pwsh
51
+ env:
52
+ CANDIDATE_WORKERS: ${{ inputs.candidate-workers }}
53
+ TMP: ${{ runner.temp }}
54
+ TEMP: ${{ runner.temp }}
55
+ TMPDIR: ${{ runner.temp }}
44
56
  run: |
45
57
  $results = Join-Path $env:GITHUB_WORKSPACE 'measurements'
46
58
  New-Item -ItemType Directory -Path $results | Out-Null
47
59
  $measurements = @()
48
- $originalPath = $env:BASELINE_GIT_PATH
49
60
  $nativeGitPath = $env:PATH
50
61
  foreach ($sample in @('baseline-1', 'candidate-1', 'candidate-2', 'baseline-2')) {
51
62
  $arm = $sample.Split('-')[0]
52
63
  Push-Location (Join-Path $env:GITHUB_WORKSPACE $arm)
53
64
  try {
54
- # Start every arm with the same PATH; only the candidate opts in.
55
- $env:PATH = if ($arm -eq 'candidate') { $nativeGitPath } else { $originalPath }
65
+ # Keep the suite, coverage and disk identical; record the actual executable.
66
+ $env:PATH = if ($arm -eq 'candidate') { $nativeGitPath } else { $env:BASELINE_GIT_PATH }
67
+ $env:PYTHONPATH = Join-Path (Get-Location) 'src'
68
+ $git = (Get-Command git -CommandType Application | Select-Object -First 1).Source
69
+ Write-Host "$sample uses $git"
70
+ $workers = if ($arm -eq 'candidate') { [int]$env:CANDIDATE_WORKERS } else { 4 }
56
71
  $sha = git rev-parse HEAD
57
72
  # Keep repeated arms from timing cleanup of an earlier arm's Git fixtures.
58
73
  $temp = Join-Path $env:RUNNER_TEMP "matched-pytest-$sample"
59
74
  if (Test-Path -LiteralPath $temp) { throw "Temporary path already exists: $temp" }
60
75
  $clock = [System.Diagnostics.Stopwatch]::StartNew()
61
- uv run --locked pytest -q -n 4 --basetemp="$temp" --durations=20 --cov --cov-report="xml:$results/$sample-coverage.xml" --junitxml="$results/$sample-tests.xml" *> "$results/$sample.log"
76
+ uv run --locked pytest -q -p no:selective -n $workers --basetemp="$temp" --durations=20 --cov --cov-report="xml:$results/$sample-coverage.xml" --junitxml="$results/$sample-tests.xml" *> "$results/$sample.log"
62
77
  $result = $LASTEXITCODE
63
78
  $clock.Stop()
64
79
  Get-Content "$results/$sample.log" | Select-Object -Last 25
65
- $measurements += [pscustomobject]@{ sample=$sample; sha=$sha; seconds=$clock.Elapsed.TotalSeconds; exit_code=$result }
80
+ $measurements += [pscustomobject]@{ sample=$sample; sha=$sha; git=$git; workers=$workers; seconds=$clock.Elapsed.TotalSeconds; exit_code=$result }
66
81
  $measurements | ConvertTo-Json | Set-Content "$results/timings.json"
67
82
  if ($result -ne 0) { throw "${sample} failed: $result" }
68
83
  } finally { Pop-Location }
@@ -18,3 +18,6 @@ tutorial-report.json
18
18
 
19
19
  # Local judge credentials
20
20
  .env
21
+
22
+ # pytest-quint-connect receipts, traces and logs
23
+ .artifacts/
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.1.0"
3
+ }
@@ -11,6 +11,11 @@ on pytest-selective itself.
11
11
  - Reproduce selection bugs using real temporary Git repositories and existing
12
12
  `tests/testing/git_repo.py` helpers. Cover base and head trees, deleted inputs,
13
13
  explicit declarations and the relevant fallback. Do not mock away selection.
14
+ - The planner's import, conftest, declaration and content-read closure, its parse-error
15
+ stop, its selection against older bases and its graph cache's transparency are also a
16
+ Quint contract in
17
+ `contracts/selection`. After changing selection, run it (CONTRIBUTING.md, *The selection
18
+ contract*); extend the model rather than weakening it when the promise changes.
14
19
  - Planning supports committed revisions and explicit working-tree snapshots. Preserve full execution on
15
20
  operational uncertainty and diagnostics for invalid declarations. An empty
16
21
  selection is not an empty pytest argument list.
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.0.0...v1.1.0) (2026-11-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * one-prompt agent setup, and fixes from a real adoption trial ([#105](https://github.com/gigaverse-app/pytest-selective/issues/105)) ([27c4578](https://github.com/gigaverse-app/pytest-selective/commit/27c45786f1a6a349409eb2a083510e4e9a9180d3))
9
+
10
+
11
+ ### Bug fixes
12
+
13
+ * **lanes:** select only lanes holding graph tests when every graph test is affected ([#102](https://github.com/gigaverse-app/pytest-selective/issues/102)) ([d6f22b8](https://github.com/gigaverse-app/pytest-selective/commit/d6f22b8f93773a4b2e60b2b8ad9d4b26762b2d2f))
14
+ * **selection:** resolve imports from the directories pytest's default import mode adds to sys.path ([#96](https://github.com/gigaverse-app/pytest-selective/issues/96)) ([d825176](https://github.com/gigaverse-app/pytest-selective/commit/d825176eb6d259733ec8132ba98ac327be15eb72))
15
+
16
+ ## [1.0.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.4.3...v1.0.0) (2026-11-08)
17
+
18
+
19
+ ### Bug fixes
20
+
21
+ * **ci:** streamline dogfood checks and prepare the 1.0 release ([#97](https://github.com/gigaverse-app/pytest-selective/issues/97)) ([5c79edc](https://github.com/gigaverse-app/pytest-selective/commit/5c79edcc2e234ccd90902ef001d2852cf7ecfdbc))
22
+ * **selection:** follow star imports to the submodules a package lists in __all__ ([#94](https://github.com/gigaverse-app/pytest-selective/issues/94)) ([ea02fbf](https://github.com/gigaverse-app/pytest-selective/commit/ea02fbfe85c2d5b6e8685a4b159215d421f78653))
23
+
24
+ ## [0.4.3](https://github.com/gigaverse-app/pytest-selective/compare/v0.4.2...v0.4.3) (2026-11-08)
25
+
26
+
27
+ ### Bug fixes
28
+
29
+ * **lanes:** select lanes through the graph and match ** like gitignore ([#90](https://github.com/gigaverse-app/pytest-selective/issues/90)) ([107b021](https://github.com/gigaverse-app/pytest-selective/commit/107b0212ed741ac72c36a39cc864906eca75d1ab))
30
+
3
31
  ## [0.4.2](https://github.com/gigaverse-app/pytest-selective/compare/v0.4.1...v0.4.2) (2026-11-07)
4
32
 
5
33
 
@@ -32,6 +32,31 @@ Changes to collection policy, runtime auditing, or the composite action need tes
32
32
  at that boundary. Update the tutorial when the public command contract changes.
33
33
  Keep performance claims tied to reproducible measurements and their limitations.
34
34
 
35
+ ### The selection contract
36
+
37
+ [contracts/selection](contracts/selection/README.md) states the planner's core
38
+ promise as a [Quint](https://quint.sh) model and checks it with
39
+ [pytest-quint-connect](https://github.com/gigaverse-app/pytest-quint-connect):
40
+ generated histories of real commits, each selected by `affected()` and compared
41
+ with the model. It runs as its own CI lane, outside the xdist suite, and needs
42
+ Node.js 22+ for Quint:
43
+
44
+ ```bash
45
+ uv run --group quint pytest -c contracts/selection/quint.ini contracts/selection --quint-require-complete
46
+ ```
47
+
48
+ When selection semantics change on purpose, change `selection.qnt` with them and
49
+ regenerate the driver (`pytest-quint-connect codegen contracts/selection/quint-connect.json`).
50
+ Never drop a witness, mutant or observed field to make it pass.
51
+
52
+ CI reports each run in one pull-request comment and checks the README's contract block
53
+ against it. After a change to the contract's cases, refresh the block from a complete
54
+ local run and commit it:
55
+
56
+ ```bash
57
+ uv run --group quint pytest-quint-connect report '**/.artifacts/quint-connect/results.json' --readme README.md
58
+ ```
59
+
35
60
  ### Shared implementation owners
36
61
 
37
62
  | Contract | Owner |
@@ -72,8 +97,9 @@ limitations. The pull request template provides a short checklist. See
72
97
  ## CI lane selection
73
98
 
74
99
  This repository uses its own `plan-lanes` action. Documentation-only PRs keep
75
- quality checks (including Markdown links) and distribution build/metadata checks,
76
- while skipping the six-platform pytest matrix and minimum-pytest lane. Within an
100
+ quality checks (including Markdown links) and the documentation build, while
101
+ skipping every test lane and the distribution build. A README or LICENSE change
102
+ also runs the `package` lane, because both distributions embed them. Within an
77
103
  admitted lane, the repository's public file-selection action selects affected
78
104
  test files once; the platform matrix and minimum-pytest job execute that same
79
105
  plan. CI-esta receives both lane and file explanations, learns JUnit timings,
@@ -171,6 +197,12 @@ history or failed planning must execute the full affected lanes and fail the
171
197
  required planning gate. Ordinary PRs and pushes use selection; manual/release/scheduled/full overrides
172
198
  remain complete checkpoints.
173
199
 
200
+ CI and public-action smoke run full checkpoints every Monday. Ordinary runs keep
201
+ the shared affected-file plan across all six OS/Python combinations and minimum
202
+ pytest. `tests/test_tutorial.py` owns the end-to-end tutorial execution and follows
203
+ that plan; there is no second tutorial invocation in each matrix job. JUnit retains
204
+ its test result and timing, and full plans also upload whole-suite coverage.
205
+
174
206
  ## Duplication
175
207
 
176
208
  `uv run pre-commit install` installs both the staged dry-lint check and the