pytest-selective 1.1.1__tar.gz → 1.2.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 (197) hide show
  1. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/ci.yml +4 -3
  2. pytest_selective-1.2.0/.github/workflows/dry-lint-audit.yml +23 -0
  3. pytest_selective-1.2.0/.release-please-manifest.json +3 -0
  4. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/CHANGELOG.md +14 -0
  5. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/CONTRIBUTING.md +5 -3
  6. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/PKG-INFO +5 -5
  7. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/README.md +4 -4
  8. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/plan-lanes/action.yml +4 -2
  9. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/conftest.py +2 -1
  10. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/runtime_deps.py +4 -0
  11. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/actions.md +4 -0
  12. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/ci.md +3 -4
  13. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/configuration.md +53 -5
  14. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/selection-health.md +2 -1
  15. pytest_selective-1.2.0/docs/shared-plan-execution.md +43 -0
  16. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/pyproject.toml +1 -1
  17. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/ci_file_config.py +1 -8
  18. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/measure_ci_runs.py +4 -0
  19. pytest_selective-1.2.0/scripts/run_ci_tests.py +8 -0
  20. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/skills/pytest-selective/SKILL.md +137 -81
  21. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/ci.py +8 -0
  22. pytest_selective-1.1.1/scripts/run_ci_tests.py → pytest_selective-1.2.0/src/pytest_selective/execution.py +18 -3
  23. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_audit.py +9 -4
  24. pytest_selective-1.2.0/src/pytest_selective/impact_audit_declarations.py +148 -0
  25. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_cli.py +58 -3
  26. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_graph.py +148 -22
  27. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_models.py +21 -0
  28. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_ci_admission.py +1 -0
  29. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_ci_test_execution.py +5 -5
  30. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_public_actions.py +8 -0
  31. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_repository_ci.py +28 -3
  32. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_audit.py +34 -0
  33. pytest_selective-1.2.0/tests/testing/test_impact_audit_declarations.py +114 -0
  34. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_graph.py +100 -0
  35. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/uv.lock +1 -1
  36. pytest_selective-1.1.1/.github/workflows/dry-lint-audit.yml +0 -91
  37. pytest_selective-1.1.1/.release-please-manifest.json +0 -3
  38. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.dry-lint/baseline.json +0 -0
  39. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.dry-lint/progress.json +0 -0
  40. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.dry-lint.toml +0 -0
  41. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.gitattributes +0 -0
  42. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  43. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  44. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/dependabot.yml +0 -0
  45. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/test-lanes.json +0 -0
  46. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/validation-inputs.json +0 -0
  47. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/docs.yml +0 -0
  48. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/dry-lint-judge.yml +0 -0
  49. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/dry-lint.yml +0 -0
  50. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/observe-ci.yml +0 -0
  51. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/pr-title.yml +0 -0
  52. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/public-actions.yml +0 -0
  53. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/release-please.yml +0 -0
  54. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/release.yml +0 -0
  55. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/windows-performance.yml +0 -0
  56. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.gitignore +0 -0
  57. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.pre-commit-config.yaml +0 -0
  58. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.readthedocs.yaml +0 -0
  59. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/AGENTS.md +0 -0
  60. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/CLAUDE.md +0 -0
  61. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/LICENSE +0 -0
  62. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/PROVENANCE.md +0 -0
  63. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/RELEASING.md +0 -0
  64. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/action.yml +0 -0
  65. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/README.md +0 -0
  66. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/pytest-expansion/action.yml +0 -0
  67. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/test-impact/action.yml +0 -0
  68. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/validation-gate/action.yml +0 -0
  69. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/agentsync.toml +0 -0
  70. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/README.md +0 -0
  71. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/quint-connect.json +0 -0
  72. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/quint.ini +0 -0
  73. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/selection.qnt +0 -0
  74. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/selection_driver.py +0 -0
  75. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/test_selection_contract.py +0 -0
  76. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/README.md +0 -0
  77. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/agents.md +0 -0
  78. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/banner.png +0 -0
  79. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/banner.svg +0 -0
  80. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/brand.json +0 -0
  81. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/icon.png +0 -0
  82. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/logo.svg +0 -0
  83. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/raster-sources.json +0 -0
  84. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/social-preview.png +0 -0
  85. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/social-preview.svg +0 -0
  86. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
  87. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
  88. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
  89. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
  90. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
  91. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
  92. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
  93. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
  94. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
  95. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22.md +0 -0
  96. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
  97. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
  98. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
  99. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
  100. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
  101. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
  102. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
  103. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
  104. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
  105. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/windows-runtime.md +0 -0
  106. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/ci-toolkit.md +0 -0
  107. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/comparison-pytest-impacted.md +0 -0
  108. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/evidence.md +0 -0
  109. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/local-development.md +0 -0
  110. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/pytest-hygiene.md +0 -0
  111. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/tutorial.md +0 -0
  112. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/tutorials/adoption-checklist.md +0 -0
  113. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/tutorials/sidecar-dependencies.md +0 -0
  114. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/tutorials/skip-unrelated-ci.md +0 -0
  115. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
  116. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
  117. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
  118. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/graph-based-inputs-plan.md +0 -0
  119. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
  120. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
  121. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
  122. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/ratchet-duplicates-zero-test-plan.md +0 -0
  123. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/rerun-selection-test-plan.md +0 -0
  124. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/assets/routing-comparison.svg +0 -0
  125. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/check_site.py +0 -0
  126. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/ecosystem.md +0 -0
  127. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/hooks.py +0 -0
  128. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/index.md +0 -0
  129. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/llms.txt +0 -0
  130. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/overrides/main.html +0 -0
  131. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/publishing.md +0 -0
  132. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/requirements.in +0 -0
  133. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/requirements.txt +0 -0
  134. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/mkdocs.yml +0 -0
  135. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/release-please-config.json +0 -0
  136. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/benchmark_graph.py +0 -0
  137. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/benchmark_parsing.py +0 -0
  138. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/benchmark_rust.py +0 -0
  139. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/check_docs.py +0 -0
  140. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/check_metadata.py +0 -0
  141. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/compare_impacted.py +0 -0
  142. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/public_actions.py +0 -0
  143. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/render_brand.py +0 -0
  144. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/smoke_distribution.py +0 -0
  145. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/tutorial.py +0 -0
  146. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/skills/pytest-selective/agents/openai.yaml +0 -0
  147. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/__init__.py +0 -0
  148. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/__main__.py +0 -0
  149. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/ast_imports.py +0 -0
  150. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/checkpoint.py +0 -0
  151. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/gate.py +0 -0
  152. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/git_operations.py +0 -0
  153. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/hygiene.py +0 -0
  154. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
  155. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_declarations.py +0 -0
  156. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_explanation.py +0 -0
  157. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_first_party.py +0 -0
  158. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_name_scope.py +0 -0
  159. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_selection.py +0 -0
  160. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/input_policy.py +0 -0
  161. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/local.py +0 -0
  162. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/parse_batch.py +0 -0
  163. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/plugin.py +0 -0
  164. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/py.typed +0 -0
  165. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/pytest_expansion.py +0 -0
  166. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/pytest_plugins.py +0 -0
  167. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/pytest_sharding.py +0 -0
  168. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/test_inventory.py +0 -0
  169. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/typecheck_scope.py +0 -0
  170. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/working_tree.py +0 -0
  171. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/__init__.py +0 -0
  172. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_checkpoint_policy.py +0 -0
  173. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_ci_measurements.py +0 -0
  174. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_distribution_metadata.py +0 -0
  175. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_hygiene.py +0 -0
  176. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_pytest_expansion.py +0 -0
  177. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_release_workflow.py +0 -0
  178. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_repository_hygiene.py +0 -0
  179. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
  180. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_tutorial.py +0 -0
  181. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/__init__.py +0 -0
  182. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/git_repo.py +0 -0
  183. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_ast_imports.py +0 -0
  184. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_ci.py +0 -0
  185. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_gate.py +0 -0
  186. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_git_repo.py +0 -0
  187. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_global_inputs.py +0 -0
  188. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_explanation.py +0 -0
  189. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_first_party.py +0 -0
  190. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_name_scope.py +0 -0
  191. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_parse_batch.py +0 -0
  192. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_portability.py +0 -0
  193. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_pytest_plugin.py +0 -0
  194. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_pytest_plugins.py +0 -0
  195. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_pytest_sharding.py +0 -0
  196. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_typecheck_scope.py +0 -0
  197. {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_working_tree.py +0 -0
@@ -149,12 +149,13 @@ jobs:
149
149
  - name: Record coverage runtime
150
150
  run: uv run --locked python -m coverage --version
151
151
  - name: Execute the shared affected-file plan
152
- run: uv run --locked python scripts/run_ci_tests.py -q -n 4 --durations=20 --junitxml=test-timings.xml
152
+ run: uv run --locked pytest-selective execute-plan -- -q -n 4 --dist=worksteal --durations=20 --junitxml=test-timings.xml
153
153
  env:
154
154
  CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
155
155
  CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
156
156
  CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
157
- CI_COVERAGE: 'true'
157
+ # Full coverage on each OS; 3.11 retains the same compatibility tests without duplicate instrumentation.
158
+ CI_COVERAGE: ${{ matrix.python == '3.14' }}
158
159
  # Keep temporary Git repositories beside the checkout on the work disk.
159
160
  TMP: ${{ runner.temp }}
160
161
  TEMP: ${{ runner.temp }}
@@ -186,7 +187,7 @@ jobs:
186
187
  python-version: '3.11'
187
188
  - run: uv pip install 'pytest==8.0.0'
188
189
  - name: Execute the same file plan on minimum pytest
189
- run: uv run --no-sync python scripts/run_ci_tests.py -q -n 4 --durations=20
190
+ run: uv run --no-sync pytest-selective execute-plan -- -q -n 4 --dist=worksteal --durations=20
190
191
  env:
191
192
  CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
192
193
  CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
@@ -0,0 +1,23 @@
1
+ name: dry-lint audit
2
+ # Weekly, or dispatched after merging a cleanup: dry-lint's own audit workflow sweeps the repository against the
3
+ # accepted backlog and opens or updates one dry-lint/ratchet pull request recording what was cleaned. Its steps ship
4
+ # with dry-lint releases; this file only says when it runs and with which credentials.
5
+ on:
6
+ schedule:
7
+ - cron: '23 6 * * 1'
8
+ workflow_dispatch:
9
+ # dry-lint.yml calls this file on a manual dispatch, to exercise the audit with the rest of the checks.
10
+ workflow_call:
11
+ permissions:
12
+ contents: read
13
+ jobs:
14
+ audit:
15
+ uses: gigaverse-app/dry-lint/.github/workflows/audit.yml@v1
16
+ with:
17
+ python-version: '3.13'
18
+ budget-usd: '5'
19
+ plugins: anthropic==2.4.0
20
+ secrets:
21
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
22
+ # The organization credential makes the ratchet pull request run ordinary CI.
23
+ DRY_LINT_RATCHET_TOKEN: ${{ secrets.GV_CI_CROSS_REPO_TOKEN }}
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.2.0"
3
+ }
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.1.2...v1.2.0) (2026-11-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * **ci:** share plan execution and reduce matrix instrumentation ([1fe0f36](https://github.com/gigaverse-app/pytest-selective/commit/1fe0f36463d4354a99ea3f45406825fa728a12c2))
9
+
10
+ ## [1.1.2](https://github.com/gigaverse-app/pytest-selective/compare/v1.1.1...v1.1.2) (2026-11-09)
11
+
12
+
13
+ ### Bug fixes
14
+
15
+ * agent setup round 2, from a second adoption trial ([#114](https://github.com/gigaverse-app/pytest-selective/issues/114)) ([45d4afe](https://github.com/gigaverse-app/pytest-selective/commit/45d4afe76cde9f6093519d97000244c6c06b157d))
16
+
3
17
  ## [1.1.1](https://github.com/gigaverse-app/pytest-selective/compare/v1.1.0...v1.1.1) (2026-11-09)
4
18
 
5
19
 
@@ -79,7 +79,9 @@ split indexes and linked worktrees without modifying the user's index or HEAD.
79
79
  CI tests Python 3.11 and 3.14 on Linux, Windows, and macOS. A separate job checks
80
80
  pytest 8.0.0 on Python 3.11, the plugin's lower bound. It also checks hooks, builds both distribution
81
81
  formats, validates PyPI metadata, and installs each distribution in an isolated
82
- environment on both Python versions. Evidence artifacts carry coverage and tutorial output on every platform and Python version.
82
+ environment on both Python versions. JUnit retains results and timings on every platform and Python version.
83
+ Full runs collect coverage on Python 3.14 on all three operating systems; Python 3.11 runs the same compatibility
84
+ tests without a duplicate instrumentation pass. Each runner uses four work-stealing workers.
83
85
 
84
86
  Use `uv lock --upgrade-package NAME` for a targeted dependency update and run the
85
87
  affected checks. Dependabot proposes weekly uv and GitHub Actions updates.
@@ -116,8 +118,8 @@ File planning derives its graph from that same lane config and merges its declar
116
118
  global execution inputs with the graph's environment inputs. Declare subprocess
117
119
  and non-import dependencies at their readers. Full plans collect whole-suite
118
120
  coverage; scoped plans produce JUnit timing evidence without presenting partial
119
- coverage as a whole-suite result. The small execution wrapper only consumes the
120
- public action's JSON file list: it does not implement another selector.
121
+ coverage as a whole-suite result. The public [shared-plan executor](docs/shared-plan-execution.md)
122
+ consumes the action's JSON file list without implementing another selector.
121
123
  The outer pytest process disables the selective plugin to avoid a second planner
122
124
  and a hidden dependency of every test on plugin startup. Plugin integration tests
123
125
  still load it normally in subprocesses and declare that dependency explicitly.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pytest-selective
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: Explainable Python test impact analysis: select affected pytest files from Git changes
5
5
  Project-URL: Homepage, https://github.com/gigaverse-app/pytest-selective
6
6
  Project-URL: Documentation, https://pytest-selective.readthedocs.io/en/latest/
@@ -60,9 +60,9 @@ the affected test files, and explains the result. Use it directly as a pytest
60
60
  plugin, or plan before collection and service startup with the CLI and CI actions.
61
61
 
62
62
  Built from the selector used in Gigaverse's backend, AI, and shared Python
63
- infrastructure. Pin the actions and the package to the same release: `@vX.Y.Z`
64
- (or its commit SHA) and `pytest-selective==X.Y.Z`. The moving `@v1` tag follows
65
- the newest release.
63
+ infrastructure. Use the actions at the moving `@v1` tag, which follows the newest
64
+ release, and the package at `pytest-selective>=X.Y.Z,<2`. Where your tooling rejects
65
+ tags, pin both to one release instead: `@<sha> # vX.Y.Z` and `pytest-selective==X.Y.Z`.
66
66
  [Provenance and extraction scope](https://github.com/gigaverse-app/pytest-selective/blob/main/PROVENANCE.md).
67
67
 
68
68
  [Quick tutorial](https://github.com/gigaverse-app/pytest-selective/blob/main/docs/tutorial.md) ·
@@ -237,7 +237,7 @@ skip pytest, because invoking pytest without paths runs the whole suite.
237
237
  | --- | --- |
238
238
  | A transitive dependency | Import chains and package initialization |
239
239
  | A deleted or renamed module | Dependencies in both the base and head trees |
240
- | Fixtures or startup code | Ancestor conftests, plugins named by `-p` in `addopts`, and configured global modules |
240
+ | Fixtures or startup code | Ancestor conftests, plugins named by `-p` in `addopts`, the project's own `pytest11` entry points, and configured global modules |
241
241
  | Templates, schemas, or data files | [Reader-owned declarations and YAML sidecars](docs/tutorials/sidecar-dependencies.md) |
242
242
  | A surprisingly large selection | Dependency paths, startup-floor and blast-radius explanations |
243
243
  | A large CI suite | Cached parsing, graph reuse, and duration-based shard planning |
@@ -29,9 +29,9 @@ the affected test files, and explains the result. Use it directly as a pytest
29
29
  plugin, or plan before collection and service startup with the CLI and CI actions.
30
30
 
31
31
  Built from the selector used in Gigaverse's backend, AI, and shared Python
32
- infrastructure. Pin the actions and the package to the same release: `@vX.Y.Z`
33
- (or its commit SHA) and `pytest-selective==X.Y.Z`. The moving `@v1` tag follows
34
- the newest release.
32
+ infrastructure. Use the actions at the moving `@v1` tag, which follows the newest
33
+ release, and the package at `pytest-selective>=X.Y.Z,<2`. Where your tooling rejects
34
+ tags, pin both to one release instead: `@<sha> # vX.Y.Z` and `pytest-selective==X.Y.Z`.
35
35
  [Provenance and extraction scope](https://github.com/gigaverse-app/pytest-selective/blob/main/PROVENANCE.md).
36
36
 
37
37
  [Quick tutorial](https://github.com/gigaverse-app/pytest-selective/blob/main/docs/tutorial.md) ·
@@ -206,7 +206,7 @@ skip pytest, because invoking pytest without paths runs the whole suite.
206
206
  | --- | --- |
207
207
  | A transitive dependency | Import chains and package initialization |
208
208
  | A deleted or renamed module | Dependencies in both the base and head trees |
209
- | Fixtures or startup code | Ancestor conftests, plugins named by `-p` in `addopts`, and configured global modules |
209
+ | Fixtures or startup code | Ancestor conftests, plugins named by `-p` in `addopts`, the project's own `pytest11` entry points, and configured global modules |
210
210
  | Templates, schemas, or data files | [Reader-owned declarations and YAML sidecars](docs/tutorials/sidecar-dependencies.md) |
211
211
  | A surprisingly large selection | Dependency paths, startup-floor and blast-radius explanations |
212
212
  | A large CI suite | Cached parsing, graph reuse, and duration-based shard planning |
@@ -12,8 +12,10 @@ inputs:
12
12
  default: auto
13
13
  artifact-name:
14
14
  description: >
15
- Upload the lane plan under this name, where CI-esta's report renders it as
16
- the sticky's lane section. Empty skips the upload.
15
+ Upload the lane plan (test-lanes.json) as an artifact under this name, where
16
+ CI-esta's report renders it as the sticky's lane section. Give each planner job
17
+ in one workflow run its own name. Empty skips the upload; planning and the
18
+ outputs are unchanged.
17
19
  default: test-lane-selection
18
20
  outputs:
19
21
  lanes:
@@ -305,7 +305,8 @@ class RepositoryDriver(SelectionDriver):
305
305
  changed = set(git_text(self.root, "diff", "--name-only", "--no-renames", base, self.head).splitlines())
306
306
  tests = self._tests(self.head)
307
307
  at_head = self._runtime(self.head, tests)
308
- at_base = self._runtime(base, [test for test in tests if test in set(self._tests(base))])
308
+ base_tests = set(self._tests(base))
309
+ at_base = self._runtime(base, [test for test in tests if test in base_tests])
309
310
  needed = {test for test in tests if (at_head[test] | at_base.get(test, frozenset())) & changed}
310
311
  return {NAMES[test] for test in needed} - set(selection["selected"])
311
312
 
@@ -9,12 +9,16 @@ Modules and sys.path are restored between test files, so each file's dependencie
9
9
  import json
10
10
  import os
11
11
  import sys
12
+ from functools import cache
12
13
  from pathlib import Path
13
14
 
14
15
  import pytest
15
16
 
16
17
 
18
+ @cache
17
19
  def _under(root: Path, path: str | None) -> str | None:
20
+ # Each process inspects one immutable exported tree. Cache path resolution,
21
+ # never the per-test import/open observations, which remain isolated below.
18
22
  if not path:
19
23
  return None
20
24
  try:
@@ -28,6 +28,10 @@ supported on GitHub.com with runner 2.336.0 or newer. Individual `actions/NAME`
28
28
  entry points call the implementations directly and do not require that syntax.
29
29
  [GitHub reference documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax).
30
30
 
31
+ `plan-lanes` uploads its plan as the `test-lane-selection` artifact by default. Set
32
+ `artifact-name` to rename it, which a second planner job in the same workflow run needs, or to
33
+ `''` to skip the upload.
34
+
31
35
  ## Action reference
32
36
 
33
37
  Each link below is the public action's complete input/output contract.
@@ -253,11 +253,10 @@ job per workflow the required check and run
253
253
  gated job to its lanes. It fails unless each prerequisite succeeded or was skipped while
254
254
  the planner succeeded and every mapped lane is `false`; a failed planner fails it.
255
255
 
256
- Do not gate a job that a publish or deploy job `needs:` on pull-request-only logic:
257
- skipping it skips publishing. Keep it running on other events
258
- (`github.event_name != 'pull_request' || ...`), or plan inside the job with
256
+ Never gate a job that a publish or deploy job `needs:`, directly or transitively:
257
+ skipping it skips publishing. Leave it ungated, or plan inside the job with
259
258
  `continue-on-error: true` and admit only its expensive steps, as the `distributions`
260
- job in this repository's CI does. A branch's first push has no previous commit, so
259
+ job in this repository's CI does. Gate every other job that installs, builds or tests. A branch's first push has no previous commit, so
261
260
  `on: push` for every branch runs every lane once per new branch; prefer pull-request
262
261
  triggers plus pushes to the default branch.
263
262
  The default event policy handles manual/release checkpoints, schedules and
@@ -24,9 +24,10 @@ select. The default filenames are `test_*.py`, `*_test.py`, `*_tests.py`, and
24
24
 
25
25
  ## Review and record collection policy
26
26
 
27
- If your repository has `pyproject.toml`, `pytest.ini`, `tox.ini`, or `setup.cfg`,
28
- record its committed pytest-policy fingerprints. Even a discovered configuration
29
- file with no pytest settings can have a fingerprint. This review prevents a new
27
+ If your repository has `pyproject.toml`, `pytest.ini`, a `tox.ini` with a `[pytest]`
28
+ section, or a `setup.cfg` with a `[tool:pytest]` section, record its committed
29
+ pytest-policy fingerprints. A `pyproject.toml` or `pytest.ini` with no pytest settings
30
+ still has a fingerprint, because pytest can still choose it. This review prevents a new
30
31
  collection rule from silently invalidating the inventory.
31
32
 
32
33
  ```bash
@@ -96,7 +97,7 @@ patterns to your actual inventory. Central-rule reader patterns use shell-style
96
97
  matching: here `*` also covers nested paths under `tests/`. Common lockfiles, dependency manifests and pytest configuration already belong
97
98
  to the default `global_inputs`. Keep those defaults unless you have reviewed a
98
99
  narrower environment contract; these reader-specific rules are for additional
99
- inputs. Other non-Python files are not automatically global inputs. Plugins named by `-p NAME` in a pytest configuration's `addopts` are global modules automatically; plugins passed only on the command line or in `PYTEST_ADDOPTS` are not visible to the planner. Use `global_modules` for those and other modules loaded by every test and
100
+ inputs. Other non-Python files are not automatically global inputs. Plugins named by `-p NAME` in a pytest configuration's `addopts` are global modules automatically, and so are the project's own `pytest11` entry points (`[project.entry-points.pytest11]` or `[tool.poetry.plugins.pytest11]` in any tracked `pyproject.toml`) whose module is a tracked file. An entry point stays global unless every pytest configuration disables it, with `-p no:NAME` (`NAME` is the entry point's name) or `--disable-plugin-autoload` in `addopts`. Plugins passed only on the command line or in `PYTEST_ADDOPTS`, and `PYTEST_DISABLE_PLUGIN_AUTOLOAD`, are not visible to the planner. Use `global_modules` for those and other modules loaded by every test and
100
101
  `always_run` for explicit canary test paths. Retain full-suite checkpoints.
101
102
 
102
103
  ## Explain and audit
@@ -120,6 +121,49 @@ pytest-selective audit --config .github/test-impact.json --records impact-audit
120
121
  Audit results cover executed paths, not every possible execution. Declare missing
121
122
  edges at their readers. See [evidence and limitations](evidence.md).
122
123
 
124
+ ### Write the missing declarations
125
+
126
+ `audit --write-declarations` writes a rule for every undeclared read into the
127
+ reading test's sidecar, `<test>.gv-dependency.yaml`, creating it or appending to
128
+ the one it already has:
129
+
130
+ ```bash
131
+ pytest-selective audit --config .github/test-impact.json --records impact-audit --write-declarations
132
+ pytest-selective audit --config .github/test-impact.json --records impact-audit --working-tree # confirms nothing is left before you commit
133
+ ```
134
+
135
+ ```yaml
136
+ dependencies:
137
+ - inputs:
138
+ - "//examples/backgrounds/**"
139
+ - "//examples/gold.toml"
140
+ kind: content
141
+ reason: "Read at runtime; recorded by pytest-selective audit --write-declarations."
142
+ - inputs:
143
+ - "//scripts/replay_coverage.py"
144
+ kind: import
145
+ reason: "Loaded or run outside the test's imports; recorded by pytest-selective audit --write-declarations."
146
+ ```
147
+
148
+ - A Python input becomes an `import` rule: a test that runs a script in a
149
+ subprocess also depends on what the script imports. Any other input becomes a
150
+ `content` rule. Review the kind where the test only reads Python as text.
151
+ - Several reads in one directory become `<directory>/**` only when the test read
152
+ every tracked file under it, so the glob claims nothing the run did not see.
153
+ It also covers files added there later.
154
+ - Every pattern must match a tracked file at the audited revision. A path the
155
+ pattern language cannot express (for example one containing `[`) is printed as
156
+ `not declared` and the command exits 2.
157
+ - Appending keeps an existing sidecar's text and comments. When the result would
158
+ not parse to exactly the old rules plus the new ones, the file is left alone
159
+ and the rules are printed for you to add by hand.
160
+
161
+ The command prints each sidecar and pattern it wrote and exits 0 when every
162
+ undeclared read was declared. The files are in the working tree, so the plain
163
+ audit (which reads `--head`) still reports them until you commit; use
164
+ `--working-tree` to check first. Prefer a hand-written comment or directory
165
+ manifest where it reads better; the writer only removes the transcription step.
166
+
123
167
  Audit discovery uses the requested repository even when a calling Git hook exports
124
168
  location variables for another checkout. It shares repository isolation with the
125
169
  planner and expansion guard.
@@ -135,7 +179,11 @@ Unsupported checker patterns retain the full-check fallback.
135
179
 
136
180
  The policy fingerprints cover conventional pytest configuration names, including
137
181
  `pytest.toml`, `.pytest.toml`, `.pytest.ini` and nested candidates. Empty candidates
138
- are included because their presence can change pytest's root and precedence.
182
+ are included because their presence can change pytest's root and precedence. A
183
+ `tox.ini` without `[pytest]` and a `setup.cfg` without `[tool:pytest]` are not: pytest
184
+ skips them. Adding that section later changes the fingerprints and needs a review.
185
+ Releases up to 1.1.1 recorded such a file as the hash of an empty policy; that entry
186
+ is still accepted and needs no refresh.
139
187
  Changes require reviewing and refreshing `pytest_policy`; until then planning
140
188
  fails safely and the CI action runs the complete configured roots.
141
189
 
@@ -30,7 +30,8 @@ uv run pytest-selective audit --config .github/test-impact.json --records impact
30
30
  The recorder observes real file reads in the tests you execute. The audit can
31
31
  expose a JSON fixture, template or schema that the graph did not declare. Add an
32
32
  edge at the reader using a dependency comment or YAML sidecar, then recheck the
33
- route. [Work through the sidecar tutorial](tutorials/sidecar-dependencies.md), or consult
33
+ route. `audit --write-declarations` writes those sidecars for you; see
34
+ [write the missing declarations](configuration.md#write-the-missing-declarations). [Work through the sidecar tutorial](tutorials/sidecar-dependencies.md), or consult
34
35
  [declaration syntax](configuration.md#declare-inputs-that-imports-cannot-describe).
35
36
  The audit covers observed paths; keep representative full runs for paths it has
36
37
  not seen, and explicitly model dynamic imports and external inputs.
@@ -0,0 +1,43 @@
1
+ # Plan once, execute across compatibility lanes
2
+
3
+ Use `pytest-selective execute-plan` to run a file plan emitted by the public
4
+ test-impact action. Every matrix job consumes the same plan; it does not need
5
+ Git history or a second dependency analysis.
6
+
7
+ ```yaml
8
+ - run: uv run --locked pytest-selective execute-plan -- -q -n 4 --dist=worksteal
9
+ env:
10
+ CI_PLANNER_RESULT: ${{ needs.plan.result }}
11
+ CI_TEST_MODE: ${{ needs.plan.outputs.mode }}
12
+ CI_TEST_FILES: ${{ needs.plan.outputs.files }}
13
+ CI_COVERAGE: 'true'
14
+ ```
15
+
16
+ `files` must be the action's `test_roots_json` output. A successful `none` plan
17
+ does not start pytest. Failed planning, malformed JSON, unknown modes or an
18
+ empty scoped selection execute the complete roots. Roots default to `tests`;
19
+ override with `--full-roots tests/unit tests/integration -- ...`. Paths with
20
+ spaces remain single arguments, and pytest receives its normal exit status.
21
+
22
+ `CI_COVERAGE=true` adds `--cov --cov-report=term-missing --cov-report=xml` only
23
+ for full execution. The environment must have pytest-cov installed. Keep
24
+ coverage thresholds in your project configuration; do not enforce a full-suite
25
+ threshold on selected files. The outer execution disables the selective plugin
26
+ because planning already happened. Integration tests can still load it explicitly.
27
+
28
+ When lane and file selection share a graph, derive the file config:
29
+
30
+ ```sh
31
+ pytest-selective file-config --lanes .github/test-lanes.json --output /tmp/test-impact.json
32
+ ```
33
+
34
+ This combines the graph's global inputs with the lane config's explicit global
35
+ inputs. Declare `global_inputs` before using it. Lane-specific `inputs` remain
36
+ lane admission rules; they do not automatically make every test file affected.
37
+ Declare non-import dependencies in the graph as usual.
38
+
39
+ pytest-selective's own CI uses four work-stealing workers on each existing
40
+ runner. Its full checkpoints retain all Python 3.11 and 3.14 compatibility tests
41
+ on Linux, Windows and macOS. Coverage runs on Python 3.14 on all three systems,
42
+ avoiding a second instrumentation pass on Python 3.11. The minimum-pytest lane,
43
+ Quint contract and installed-distribution smoke remain independent checks.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pytest-selective"
7
- version = "1.1.1"
7
+ version = "1.2.0"
8
8
  description = "Explainable Python test impact analysis: select affected pytest files from Git changes"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -4,15 +4,8 @@ import sys
4
4
  from pathlib import Path
5
5
 
6
6
  from pytest_selective.ci import LaneConfig
7
- from pytest_selective.impact_models import ImpactConfig
8
7
 
9
-
10
- def file_config(lanes: LaneConfig) -> ImpactConfig:
11
- if lanes.global_inputs is None:
12
- raise ValueError("This repository must declare its shared CI execution inputs")
13
- return lanes.graph.model_copy(
14
- update={"global_inputs": sorted(set(lanes.graph.global_inputs) | set(lanes.global_inputs))}
15
- )
8
+ file_config = LaneConfig.file_config
16
9
 
17
10
 
18
11
  if __name__ == "__main__":
@@ -5,6 +5,10 @@ head. A side may hold several workflow runs (for example CI and Docs triggered b
5
5
  push): runner-seconds add up and elapsed runs from the first run's creation to the last job's
6
6
  completion. `--expected-failure JOB` accepts that job's deliberate failure (a probe) as executed
7
7
  work instead of rejecting the run; any other failed, cancelled or unfinished job still does.
8
+
9
+ The sides may come from different events. A `pull_request` run records the branch head as its
10
+ `head_sha` (although it checks out the merge commit), so a `workflow_dispatch` or `push` run on
11
+ that branch is the same head: the usual full control beside a selected pull-request run.
8
12
  """
9
13
 
10
14
  import argparse
@@ -0,0 +1,8 @@
1
+ """Compatibility entry point for the public shared-plan executor."""
2
+
3
+ import sys
4
+
5
+ from pytest_selective.execution import main
6
+
7
+ if __name__ == "__main__":
8
+ raise SystemExit(main(["--", *sys.argv[1:]]))