pytest-selective 0.3.0__tar.gz → 0.4.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 (169) hide show
  1. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/ci.yml +1 -17
  2. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/public-actions.yml +0 -2
  3. pytest_selective-0.4.0/.release-please-manifest.json +3 -0
  4. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/CHANGELOG.md +7 -0
  5. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/PKG-INFO +5 -2
  6. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/README.md +4 -1
  7. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/action.yml +7 -3
  8. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/actions/plan-lanes/action.yml +4 -4
  9. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/actions/test-impact/action.yml +8 -4
  10. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/README.md +1 -0
  11. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/ci.md +31 -13
  12. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/configuration.md +3 -0
  13. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/selection-health.md +2 -1
  14. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/tutorial.md +3 -0
  15. pytest_selective-0.4.0/docs/tutorials/adoption-checklist.md +244 -0
  16. pytest_selective-0.4.0/docs/tutorials/sidecar-dependencies.md +270 -0
  17. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/tutorials/skip-unrelated-ci.md +0 -2
  18. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/mkdocs.yml +6 -0
  19. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/pyproject.toml +1 -1
  20. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/skills/pytest-selective/SKILL.md +5 -0
  21. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/skills/pytest-selective/references/adoption.md +7 -3
  22. pytest_selective-0.4.0/src/pytest_selective/checkpoint.py +44 -0
  23. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/ci.py +2 -1
  24. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_cli.py +7 -1
  25. pytest_selective-0.4.0/tests/test_checkpoint_policy.py +106 -0
  26. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_ci_admission.py +2 -2
  27. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_repository_ci.py +1 -1
  28. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/uv.lock +1 -1
  29. pytest_selective-0.3.0/.release-please-manifest.json +0 -3
  30. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.gitattributes +0 -0
  31. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  32. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  33. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/dependabot.yml +0 -0
  34. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/test-lanes.json +0 -0
  35. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/docs.yml +0 -0
  36. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/observe-ci.yml +0 -0
  37. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/pr-title.yml +0 -0
  38. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/release-please.yml +0 -0
  39. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/release.yml +0 -0
  40. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/windows-performance.yml +0 -0
  41. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.gitignore +0 -0
  42. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.pre-commit-config.yaml +0 -0
  43. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.readthedocs.yaml +0 -0
  44. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/AGENTS.md +0 -0
  45. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/CLAUDE.md +0 -0
  46. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/CONTRIBUTING.md +0 -0
  47. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/LICENSE +0 -0
  48. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/PROVENANCE.md +0 -0
  49. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/RELEASING.md +0 -0
  50. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/actions/README.md +0 -0
  51. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/actions/pytest-expansion/action.yml +0 -0
  52. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/agentsync.toml +0 -0
  53. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/actions.md +0 -0
  54. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/agents.md +0 -0
  55. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/banner.png +0 -0
  56. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/banner.svg +0 -0
  57. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/brand.json +0 -0
  58. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/icon.png +0 -0
  59. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/logo.svg +0 -0
  60. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/raster-sources.json +0 -0
  61. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/social-preview.png +0 -0
  62. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/social-preview.svg +0 -0
  63. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
  64. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
  65. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
  66. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
  67. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
  68. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
  69. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
  70. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
  71. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
  72. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22.md +0 -0
  73. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
  74. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
  75. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
  76. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
  77. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
  78. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
  79. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
  80. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
  81. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
  82. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/windows-runtime.md +0 -0
  83. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/ci-toolkit.md +0 -0
  84. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/comparison-pytest-impacted.md +0 -0
  85. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/evidence.md +0 -0
  86. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/local-development.md +0 -0
  87. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/pytest-hygiene.md +0 -0
  88. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
  89. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
  90. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
  91. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
  92. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
  93. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
  94. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/assets/routing-comparison.svg +0 -0
  95. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/check_site.py +0 -0
  96. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/ecosystem.md +0 -0
  97. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/hooks.py +0 -0
  98. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/index.md +0 -0
  99. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/overrides/main.html +0 -0
  100. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/publishing.md +0 -0
  101. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/requirements.in +0 -0
  102. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/requirements.txt +0 -0
  103. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/release-please-config.json +0 -0
  104. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/benchmark_graph.py +0 -0
  105. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/benchmark_parsing.py +0 -0
  106. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/benchmark_rust.py +0 -0
  107. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/check_docs.py +0 -0
  108. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/check_metadata.py +0 -0
  109. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/ci_file_config.py +0 -0
  110. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/compare_impacted.py +0 -0
  111. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/measure_ci_runs.py +0 -0
  112. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/public_actions.py +0 -0
  113. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/render_brand.py +0 -0
  114. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/run_ci_tests.py +0 -0
  115. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/smoke_distribution.py +0 -0
  116. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/tutorial.py +0 -0
  117. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/skills/pytest-selective/agents/openai.yaml +0 -0
  118. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/__init__.py +0 -0
  119. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/__main__.py +0 -0
  120. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/ast_imports.py +0 -0
  121. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/hygiene.py +0 -0
  122. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_audit.py +0 -0
  123. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
  124. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_declarations.py +0 -0
  125. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_explanation.py +0 -0
  126. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_first_party.py +0 -0
  127. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_graph.py +0 -0
  128. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_models.py +0 -0
  129. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_name_scope.py +0 -0
  130. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_selection.py +0 -0
  131. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/input_policy.py +0 -0
  132. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/local.py +0 -0
  133. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/parse_batch.py +0 -0
  134. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/plugin.py +0 -0
  135. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/py.typed +0 -0
  136. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/pytest_expansion.py +0 -0
  137. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/pytest_plugins.py +0 -0
  138. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/pytest_sharding.py +0 -0
  139. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/test_inventory.py +0 -0
  140. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/typecheck_scope.py +0 -0
  141. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/working_tree.py +0 -0
  142. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/__init__.py +0 -0
  143. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_ci_measurements.py +0 -0
  144. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_ci_test_execution.py +0 -0
  145. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_distribution_metadata.py +0 -0
  146. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_hygiene.py +0 -0
  147. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_public_actions.py +0 -0
  148. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_pytest_expansion.py +0 -0
  149. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_repository_hygiene.py +0 -0
  150. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
  151. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_tutorial.py +0 -0
  152. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/__init__.py +0 -0
  153. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/git_repo.py +0 -0
  154. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_ast_imports.py +0 -0
  155. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_ci.py +0 -0
  156. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_git_repo.py +0 -0
  157. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_global_inputs.py +0 -0
  158. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_audit.py +0 -0
  159. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_explanation.py +0 -0
  160. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_first_party.py +0 -0
  161. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_graph.py +0 -0
  162. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_name_scope.py +0 -0
  163. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_parse_batch.py +0 -0
  164. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_portability.py +0 -0
  165. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_pytest_plugin.py +0 -0
  166. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_pytest_plugins.py +0 -0
  167. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_pytest_sharding.py +0 -0
  168. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_typecheck_scope.py +0 -0
  169. {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_working_tree.py +0 -0
@@ -18,7 +18,7 @@ jobs:
18
18
  outputs:
19
19
  lanes: ${{ steps.plan.outputs.lanes }}
20
20
  mode: ${{ steps.files.outputs.mode }}
21
- files: ${{ steps.paths.outputs.files }}
21
+ files: ${{ steps.files.outputs.test_roots_json }}
22
22
  steps:
23
23
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24
24
  with:
@@ -27,8 +27,6 @@ jobs:
27
27
  uses: ./actions/plan-lanes
28
28
  with:
29
29
  config: .github/test-lanes.json
30
- base: ${{ github.event.pull_request.base.sha || github.event.before }}
31
- full: ${{ github.run_attempt > 1 || contains(fromJSON('["workflow_dispatch","workflow_call","schedule","release"]'), github.event_name) || contains(github.event.pull_request.labels.*.name, 'test:full') }}
32
30
  - name: Share the repository graph and execution inputs with file selection
33
31
  env:
34
32
  PYTHONPATH: src
@@ -39,21 +37,7 @@ jobs:
39
37
  uses: ./
40
38
  with:
41
39
  config: ${{ runner.temp }}/test-impact.json
42
- base: ${{ github.event.pull_request.base.sha || github.event.before }}
43
40
  full-roots: tests
44
- full: ${{ github.run_attempt > 1 || contains(fromJSON('["workflow_dispatch","workflow_call","schedule","release"]'), github.event_name) || contains(github.event.pull_request.labels.*.name, 'test:full') }}
45
- - id: paths
46
- name: Forward exact selected paths without shell splitting
47
- env:
48
- MODE: ${{ steps.files.outputs.mode }}
49
- run: |
50
- python - <<'PY'
51
- import json, os
52
- from pathlib import Path
53
- files = json.loads(Path('test-impact-report.json').read_text())['selected'] if os.environ['MODE'] == 'scoped' else []
54
- with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
55
- output.write('files=' + json.dumps(files) + '\n')
56
- PY
57
41
  quality:
58
42
  runs-on: ubuntu-latest
59
43
  steps:
@@ -19,8 +19,6 @@ jobs:
19
19
  uses: ./actions/plan-lanes
20
20
  with:
21
21
  config: .github/test-lanes.json
22
- base: ${{ github.event.pull_request.base.sha || github.event.before }}
23
- full: ${{ github.run_attempt > 1 || contains(fromJSON('["workflow_dispatch","workflow_call","schedule","release"]'), github.event_name) || contains(github.event.pull_request.labels.*.name, 'test:full') }}
24
22
  artifact-name: public-action-selection
25
23
  public-actions:
26
24
  needs: select-tests
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.4.0"
3
+ }
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.3.0...v0.4.0) (2026-10-24)
4
+
5
+
6
+ ### Features
7
+
8
+ * **actions:** simplify selection adoption with shared defaults ([#63](https://github.com/gigaverse-app/pytest-selective/issues/63)) ([85e082d](https://github.com/gigaverse-app/pytest-selective/commit/85e082dfdbcf2f8c02ac3de38613c818cb749807))
9
+
3
10
  ## [0.3.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.2.0...v0.3.0) (2026-10-23)
4
11
 
5
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pytest-selective
3
- Version: 0.3.0
3
+ Version: 0.4.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/
@@ -71,6 +71,9 @@ and slack. [See the source runs and method](https://github.com/gigaverse-app/ci-
71
71
 
72
72
  ## Choose how to use it
73
73
 
74
+ Follow the [adoption tutorial and checklist](docs/tutorials/adoption-checklist.md)
75
+ for local execution, CI routing, dependency audits, timing feedback and measured rollout.
76
+
74
77
  | You want to… | Use | What it saves |
75
78
  | --- | --- | --- |
76
79
  | Test local edits with your existing pytest command | `uv add --dev pytest-selective`, then `uv run pytest --selective` | Unaffected test execution; staged, unstaged and untracked changes are included |
@@ -231,7 +234,7 @@ skip pytest, because invoking pytest without paths runs the whole suite.
231
234
  | A transitive dependency | Import chains and package initialization |
232
235
  | A deleted or renamed module | Dependencies in both the base and head trees |
233
236
  | Fixtures or startup code | Ancestor conftests and configured global modules |
234
- | Templates, schemas, or data files | Reader-owned declarations and YAML sidecars |
237
+ | Templates, schemas, or data files | [Reader-owned declarations and YAML sidecars](docs/tutorials/sidecar-dependencies.md) |
235
238
  | A surprisingly large selection | Dependency paths, startup-floor and blast-radius explanations |
236
239
  | A large CI suite | Cached parsing, graph reuse, and duration-based shard planning |
237
240
  | Undeclared runtime reads | An opt-in pytest recorder and audit command |
@@ -40,6 +40,9 @@ and slack. [See the source runs and method](https://github.com/gigaverse-app/ci-
40
40
 
41
41
  ## Choose how to use it
42
42
 
43
+ Follow the [adoption tutorial and checklist](docs/tutorials/adoption-checklist.md)
44
+ for local execution, CI routing, dependency audits, timing feedback and measured rollout.
45
+
43
46
  | You want to… | Use | What it saves |
44
47
  | --- | --- | --- |
45
48
  | Test local edits with your existing pytest command | `uv add --dev pytest-selective`, then `uv run pytest --selective` | Unaffected test execution; staged, unstaged and untracked changes are included |
@@ -200,7 +203,7 @@ skip pytest, because invoking pytest without paths runs the whole suite.
200
203
  | A transitive dependency | Import chains and package initialization |
201
204
  | A deleted or renamed module | Dependencies in both the base and head trees |
202
205
  | Fixtures or startup code | Ancestor conftests and configured global modules |
203
- | Templates, schemas, or data files | Reader-owned declarations and YAML sidecars |
206
+ | Templates, schemas, or data files | [Reader-owned declarations and YAML sidecars](docs/tutorials/sidecar-dependencies.md) |
204
207
  | A surprisingly large selection | Dependency paths, startup-floor and blast-radius explanations |
205
208
  | A large CI suite | Cached parsing, graph reuse, and duration-based shard planning |
206
209
  | Undeclared runtime reads | An opt-in pytest recorder and audit command |
@@ -10,7 +10,7 @@ inputs:
10
10
  required: true
11
11
  base:
12
12
  description: Complete change-set base (a validated ancestor, PR base, or push before SHA).
13
- required: true
13
+ default: ${{ github.event.pull_request.base.sha || github.event.before }}
14
14
  head:
15
15
  description: Exact checked-out commit.
16
16
  default: HEAD
@@ -18,8 +18,9 @@ inputs:
18
18
  description: Space-separated complete test roots used if planning cannot prove a narrower scope.
19
19
  required: true
20
20
  full:
21
- description: Force the complete suite for schedules, explicit labels, or coverage checkpoints.
22
- default: 'false'
21
+ description: auto selects on PRs/pushes and runs full on reruns, manual/scheduled/release events or test:full. Override
22
+ with true or false.
23
+ default: auto
23
24
  extra-files:
24
25
  description: Optional newline file containing prior failure canaries or additional changed paths.
25
26
  default: ''
@@ -68,6 +69,9 @@ outputs:
68
69
  test_roots:
69
70
  description: Exact selected files, or complete roots on fallback.
70
71
  value: ${{ steps.action.outputs.test_roots }}
72
+ test_roots_json:
73
+ description: JSON array of selected files or fallback roots; preserves spaces in paths. Empty array for none.
74
+ value: ${{ steps.action.outputs.test_roots_json }}
71
75
  shard_count:
72
76
  description: Number of shards.
73
77
  value: ${{ steps.action.outputs.shard_count }}
@@ -6,10 +6,10 @@ inputs:
6
6
  required: true
7
7
  base:
8
8
  description: Base commit containing the complete change set.
9
- required: true
9
+ default: ${{ github.event.pull_request.base.sha || github.event.before }}
10
10
  full:
11
- description: Run every lane for an explicit full-test checkpoint; ordinary PRs and pushes use selection.
12
- default: 'false'
11
+ description: 'auto selects on PRs/pushes and runs full on reruns, manual/scheduled/release events or test:full. Override with true or false.'
12
+ default: auto
13
13
  artifact-name:
14
14
  description: >
15
15
  Upload the lane plan under this name, where CI-esta's report renders it as
@@ -41,7 +41,7 @@ runs:
41
41
  run: |
42
42
  export PYTHONPATH="$SELECTIVE_ACTION/../../src${PYTHONPATH:+:$PYTHONPATH}"
43
43
  args=(--config "$SELECTIVE_CONFIG" --base "$SELECTIVE_BASE" --github-output "$GITHUB_OUTPUT")
44
- if [ "$SELECTIVE_FULL" = true ]; then args+=(--full); fi
44
+ args+=(--full "$SELECTIVE_FULL")
45
45
  uv run --no-project --python 3.14 --with pydantic==2.13.4 --with pyyaml==6.0.3 \
46
46
  python -m pytest_selective.ci "${args[@]}" | tee test-lanes.json
47
47
  {
@@ -9,7 +9,7 @@ inputs:
9
9
  required: true
10
10
  base:
11
11
  description: Complete change-set base (a validated ancestor, PR base, or push before SHA).
12
- required: true
12
+ default: ${{ github.event.pull_request.base.sha || github.event.before }}
13
13
  head:
14
14
  description: Exact checked-out commit.
15
15
  default: HEAD
@@ -17,8 +17,8 @@ inputs:
17
17
  description: Space-separated complete test roots used if planning cannot prove a narrower scope.
18
18
  required: true
19
19
  full:
20
- description: Force the complete suite for schedules, explicit labels, or coverage checkpoints.
21
- default: 'false'
20
+ description: 'auto selects on PRs/pushes and runs full on reruns, manual/scheduled/release events or test:full. Override with true or false.'
21
+ default: auto
22
22
  extra-files:
23
23
  description: Optional newline file containing prior failure canaries or additional changed paths.
24
24
  default: ''
@@ -70,6 +70,9 @@ outputs:
70
70
  test_roots:
71
71
  value: ${{ steps.select.outputs.test_roots }}
72
72
  description: Exact selected files, or complete roots on fallback.
73
+ test_roots_json:
74
+ value: ${{ steps.select.outputs.test_roots_json }}
75
+ description: JSON array of selected files or fallback roots; preserves spaces in paths. Empty array for none.
73
76
  shard_count:
74
77
  value: ${{ steps.select.outputs.shard_count }}
75
78
  description: Number of shards.
@@ -139,7 +142,7 @@ runs:
139
142
  --explanation-output test-impact-explanation.json)
140
143
  # A reused workspace must not upload a previous plan's explanation.
141
144
  rm -f test-impact-explanation.json
142
- if [ "$IMPACT_FULL" = true ]; then args+=(--full); fi
145
+ args+=(--full "$IMPACT_FULL")
143
146
  if [ -n "$IMPACT_EXTRA" ]; then args+=(--extra-files "$IMPACT_EXTRA"); fi
144
147
  if [ -n "${IMPACT_GRAPH_BASE:-}" ]; then args+=(--graph-base "$IMPACT_GRAPH_BASE"); fi
145
148
  # Optional: an unfetchable alternate only forgoes a smaller selection.
@@ -170,6 +173,7 @@ runs:
170
173
  echo "base=$IMPACT_BASE"
171
174
  echo "mode=full"
172
175
  echo "test_roots=$IMPACT_ROOTS"
176
+ echo "test_roots_json=$(python3 -c 'import json,os,shlex; print(json.dumps(shlex.split(os.environ["IMPACT_ROOTS"])))')"
173
177
  echo "shard_count=$IMPACT_SHARDS"
174
178
  echo "groups=$(python3 -c 'import json,os; print(json.dumps(list(range(1,int(os.environ["IMPACT_SHARDS"])+1))))')"
175
179
  echo "reason=Test impact proof unavailable; complete suite fallback"
@@ -4,6 +4,7 @@
4
4
  | --- | --- |
5
5
  | Have a coding agent optimize your test/CI workflow | [Agent prompt, skill and AGENTS.md instructions](agents.md) |
6
6
  | See a real regression caught by a smaller selection | [Runnable tutorial](tutorial.md) |
7
+ | Declare fixture, schema, scanner, or subprocess inputs | [Sidecar tutorial](tutorials/sidecar-dependencies.md) |
7
8
  | Configure an existing Python project | [Configuration and dependency declarations](configuration.md) |
8
9
  | Use `pytest --selective` or run local edits before collection | [Native plugin and local runner](local-development.md) |
9
10
  | Run the selected files safely in CI | [GitHub Actions and runner integration](ci.md) |
@@ -8,8 +8,8 @@ the base forward after a failed run.
8
8
 
9
9
  This job uses the repository's composite action and runs the plan on the checked-out
10
10
  head. Use the moving `v1` release tag and adapt dependency installation to
11
- your project. The example assumes a uv project
12
- and test paths without spaces (the action's `test_roots` output is space-separated).
11
+ your project. The example assumes a uv project and passes the planned paths as
12
+ JSON, so spaces in filenames survive execution.
13
13
 
14
14
  ```yaml
15
15
  name: Affected tests
@@ -28,26 +28,44 @@ jobs:
28
28
  uses: gigaverse-app/pytest-selective@v1
29
29
  with:
30
30
  config: .github/test-impact.json
31
- base: ${{ github.event.pull_request.base.sha }}
32
31
  full-roots: tests
33
32
  - if: steps.impact.outputs.mode != 'none'
34
33
  run: uv sync --locked
35
34
  - if: steps.impact.outputs.mode != 'none'
36
35
  env:
37
- TEST_ROOTS: ${{ steps.impact.outputs.test_roots }}
36
+ TEST_ROOTS: ${{ steps.impact.outputs.test_roots_json }}
38
37
  shell: bash
39
38
  run: |
40
- read -r -a roots <<< "$TEST_ROOTS"
41
- if [ "${#roots[@]}" -eq 0 ]; then
42
- echo "Expected test roots for a nonempty plan" >&2
43
- exit 1
44
- fi
45
- uv run --locked pytest "${roots[@]}"
39
+ uv run --locked python - <<'PY'
40
+ import json, os, subprocess, sys
41
+ roots = json.loads(os.environ['TEST_ROOTS'])
42
+ if not roots:
43
+ raise SystemExit('Expected test roots for a nonempty plan')
44
+ raise SystemExit(subprocess.call([sys.executable, '-m', 'pytest', '-p', 'no:selective', *roots]))
45
+ PY
46
46
  ```
47
47
 
48
48
  The action creates its own Python 3.14 planner environment. Your uv project
49
49
  controls the test interpreter. `full-roots` must contain the complete roots, not
50
- the previous selection. With `full: 'true'`, the action requests a complete run.
50
+ the previous selection. `base` defaults to the PR base or the push's previous
51
+ commit. Override it only when you have validated a different comparison base.
52
+
53
+ Both file and lane actions default to `full: auto`:
54
+
55
+ | Trigger | Default |
56
+ | --- | --- |
57
+ | First ordinary PR or push attempt | Select affected work |
58
+ | Rerun, manual, schedule or release | Full checkpoint |
59
+ | PR carrying `test:full` | Full checkpoint |
60
+ | Unknown event or unreadable required event metadata | Full checkpoint |
61
+
62
+ Use `full: 'true'` to always run everything, or `full: 'false'` to request
63
+ selection even for a manual experiment. Operational uncertainty still falls back
64
+ to full execution. Existing explicit expressions keep their behavior; remove a
65
+ copied standard expression to adopt the shared policy. Updating an action does
66
+ not add missing jobs, schedules, permissions or test dependency declarations.
67
+ GitHub reusable workflows retain the caller's event, so pass `full: 'true'` when
68
+ the reusable invocation itself must be a checkpoint.
51
69
 
52
70
  | Planner outcome | Action behavior |
53
71
  | --- | --- |
@@ -193,8 +211,8 @@ when the README is embedded in the built package.
193
211
  The action returns a `lanes` JSON object. Expose it as a planning-job output,
194
212
  then gate each expensive job with `always() && !cancelled()` and either a true
195
213
  lane value **or a failed planning job**. This preserves tests if planning fails.
196
- Run the action with `full: true` for manual/release checkpoints, schedules, and
197
- full-test overrides. Ordinary PRs and main pushes use dependency selection.
214
+ The default event policy handles manual/release checkpoints, schedules and
215
+ `test:full`. Ordinary PRs and main pushes use dependency selection.
198
216
  Selected jobs retain all of their existing tests and evidence checks.
199
217
 
200
218
 
@@ -54,6 +54,9 @@ runs the complete configured roots.
54
54
 
55
55
  ## Declare inputs that imports cannot describe
56
56
 
57
+ Start with the [sidecar tutorial](tutorials/sidecar-dependencies.md) for a runnable
58
+ fixture example, directory manifests, dependency comments, and dynamic Python inputs.
59
+
57
60
  For a test that reads a tracked JSON fixture, add a reader-owned comment:
58
61
 
59
62
  ```python
@@ -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. [Declaration syntax](configuration.md#declare-inputs-that-imports-cannot-describe).
33
+ route. [Work through the sidecar tutorial](tutorials/sidecar-dependencies.md), or consult
34
+ [declaration syntax](configuration.md#declare-inputs-that-imports-cannot-describe).
34
35
  The audit covers observed paths; keep representative full runs for paths it has
35
36
  not seen, and explicitly model dynamic imports and external inputs.
36
37
 
@@ -1,5 +1,8 @@
1
1
  # Start using pytest-selective
2
2
 
3
+ Use the [adoption checklist](tutorials/adoption-checklist.md) to work through
4
+ configuration, CI integration, selection health and measurement after this quickstart.
5
+
3
6
  ## Try your existing project
4
7
 
5
8
  Start in a Git repository whose full pytest suite passes. You need Python 3.11+,
@@ -0,0 +1,244 @@
1
+ ---
2
+ title: Get the most benefit from pytest-selective
3
+ description: A staged adoption tutorial and checklist for local pytest, pre-collection planning, CI lanes, dependency audits, hygiene, timing feedback and measured rollout.
4
+ ---
5
+
6
+ # Get the most benefit from pytest-selective
7
+
8
+ Choose the level that avoids your actual cost. The native plugin avoids
9
+ unaffected test execution. The pre-collection runner also avoids importing and
10
+ collecting unrelated files. Whole-lane CI planning can avoid starting runners,
11
+ services and application installation altogether.
12
+
13
+ Use this checklist in your adoption PR. Mark each stage **verified**, **deferred**
14
+ or **not applicable**, with a reason. Pair it with the
15
+ [CI-esta adoption checklist](https://github.com/gigaverse-app/ci-esta/blob/6a694957568c81929a2b3021064ad9674516fb7b/docs/tutorials/adoption-checklist.md)
16
+ for reporting, setup, sharding, history, evidence reuse and security integration.
17
+ Selection remains opt-in; installing the package alone does not narrow pytest.
18
+
19
+ ## 1. Run the disposable example
20
+
21
+ Use Python 3.11+ and Git 2.24+. Plan with an interpreter at least as new as your
22
+ source syntax; the planner need not install your application dependencies.
23
+
24
+ ```bash
25
+ git clone https://github.com/gigaverse-app/pytest-selective.git
26
+ cd pytest-selective
27
+ uv sync --locked
28
+ uv run python scripts/tutorial.py --output tutorial-report.json
29
+ ```
30
+
31
+ - [ ] The transitive regression selects its reader and catches the introduced failure.
32
+ - [ ] An unrelated README selects no tests and does not launch pytest accidentally.
33
+ - [ ] A shared conftest change retains the full configured inventory.
34
+ - [ ] The tutorial exits successfully despite its intentionally failing pytest controls.
35
+
36
+ **Done when:** you understand the difference between `scoped`, `none` and `full`
37
+ plans. This demonstrates behavior, not a wall-time speedup. The script uses a
38
+ temporary Git repository; it does not alter your project or staging area.
39
+
40
+ ## 2. Choose local collection or pre-collection execution
41
+
42
+ In your application repository, install the selector in the test environment:
43
+
44
+ ```bash
45
+ uv add --dev pytest-selective
46
+ uv run pytest --selective
47
+ uv run pytest --no-selective
48
+ ```
49
+
50
+ Commit the dependency and lockfile change through your normal workflow before
51
+ trying a narrow source edit. Uncommitted environment changes intentionally
52
+ select everything and would hide the benefit you are trying to verify.
53
+
54
+ The plugin collects normally and then deselects. Its default includes staged,
55
+ unstaged and nonignored untracked edits relative to HEAD. An explicit
56
+ `--selective-base origin/main` also includes changes since that exact ref.
57
+ Keep normal test paths, marker filters, fixtures and assertion plugins.
58
+
59
+ If collection or startup is expensive, create `.github/test-impact.json`:
60
+
61
+ ```json
62
+ {
63
+ "module_roots": ["src", "."],
64
+ "test_roots": ["tests"]
65
+ }
66
+ ```
67
+
68
+ Review and record [collection-policy fingerprints](../configuration.md#review-and-record-collection-policy)
69
+ before relying on selection in a repository with pytest configuration. Then use:
70
+
71
+ ```bash
72
+ uv run pytest-selective run --config .github/test-impact.json -- -q
73
+ uv run pytest-selective run --config .github/test-impact.json --full -- -q
74
+ ```
75
+
76
+ - [ ] Inventory every intended test root and any custom filename/collection policy.
77
+ - [ ] Check both local edits and committed base/head changes.
78
+ - [ ] Use the native plugin for custom collection/node-oriented workflows unless
79
+ you have verified that pre-collection file planning models them correctly.
80
+ - [ ] Keep explicit full execution for whole-suite coverage and conformance requirements.
81
+ - [ ] Do not enable selection globally while validating adoption.
82
+
83
+ **Done when:** an actual small source edit runs the intended tests, while a full
84
+ control still passes. **Rollback:** `--no-selective` for the plugin or `run --full`
85
+ for the runner. Remove a global selective setting if your project previously added one.
86
+
87
+ ## 3. Declare dependencies that Python imports cannot reveal
88
+
89
+ Follow the [reader-owned sidecar tutorial](sidecar-dependencies.md). For example,
90
+ place this beside the test that reads the fixture, as `test_prices.py.gv-dependency.yaml`:
91
+
92
+ ```yaml
93
+ dependencies:
94
+ - inputs: ["//tests/data/prices.json"]
95
+ kind: content
96
+ reason: The test reads expected prices from this JSON fixture.
97
+ ```
98
+
99
+ - [ ] Declare templates, fixtures, schema files, AST/text scans and subprocess helpers.
100
+ - [ ] Use `content` for bytes read as data, and `import` when the reader executes
101
+ Python and needs that module's transitive dependencies.
102
+ - [ ] Keep declarations beside readers; use central rules for actual shared policy.
103
+ - [ ] Review locks, environment configuration, external checkout pins and plugin startup.
104
+ - [ ] Retain default global inputs until a narrower environment contract is verified.
105
+ - [ ] Review nested/custom pytest configuration before refreshing fingerprints.
106
+ - [ ] Repair unmatched paths, invalid sidecars and duplicate keys; do not suppress them.
107
+
108
+ Inspect the plan before executing it:
109
+
110
+ ```bash
111
+ uv run pytest-selective affected --config .github/test-impact.json \
112
+ --base origin/main --working-tree --output selection.json \
113
+ --explanation-output explanation.json
114
+ uv run pytest-selective explain --config .github/test-impact.json \
115
+ --test tests/test_prices.py --source src/shop/prices.py
116
+ ```
117
+
118
+ Adapt the test/source paths. **Done when:** real edits to each declared input
119
+ select its readers, and an unrelated file does not broaden the plan. Deleted
120
+ inputs and both sides of a rename belong in your controls.
121
+
122
+ ## 4. Move CI selection ahead of expensive setup
123
+
124
+ Use the [complete CI recipe](../ci.md), or the
125
+ [whole-lane tutorial](skip-unrelated-ci.md) when a suite must run intact.
126
+
127
+ | Interface | Choose it when | Override / contract |
128
+ | --- | --- | --- |
129
+ | `gigaverse-app/pytest-selective@v1` | Plan ordinary pytest files before collection | Root convenience entry point; same implementation as `actions/test-impact` |
130
+ | `actions/test-impact@v1` | Use the direct action, including runners without root self-reference support | Shared `full: auto` checkpoints; consume `mode` and `test_roots_json` without reparsing reports |
131
+ | `actions/plan-lanes@v1` | Skip complete unaffected jobs while preserving coverage/conformance | Shared `full: auto` checkpoints; `lanes` is a boolean JSON map |
132
+ | `actions/pytest-expansion@v1` | Reject unexplained global conftest expansion before application setup | Configure roots/prefixes to your project; this does not itself run tests |
133
+
134
+ Specialized action paths above are relative to `gigaverse-app/pytest-selective/`.
135
+ See every input and output in the [action reference](../actions.md). Python
136
+ package versions, the moving Actions major and installed skills update separately.
137
+
138
+ - [ ] Collect a complete change set with enough Git history before selecting.
139
+ - [ ] Model lane execution inputs, including called actions/reusable workflows.
140
+ - [ ] Avoid broad `.github/**` globals when only execution workflows affect tests.
141
+ - [ ] Consume one successful plan consistently across matrix workers; do not run
142
+ a second hidden selector against an already selected exact-file manifest.
143
+ - [ ] Keep services, coverage and evidence validators intact inside admitted lanes.
144
+ - [ ] Do not invoke bare pytest on a `none` plan; it runs the whole suite.
145
+ - [ ] Missing history/operational uncertainty runs the full configured roots.
146
+ Invalid declarations remain visible failures to repair.
147
+ - [ ] Make the planner required and validate skips through an aggregate gate.
148
+ - [ ] Keep ordinary PRs and main pushes selective, with explicit full/manual and
149
+ scheduled checkpoints. Verify label-trigger behavior instead of assuming it.
150
+
151
+ **Done when:** the acceptance matrix below passes using actual commits and the
152
+ real workflow. A local plan alone does not establish which hosted jobs execute.
153
+
154
+ | Control | Expected result |
155
+ | --- | --- |
156
+ | Unreferenced README or PR-template change | Zero tests, including routing, tutorial and installed-package smoke lanes unless they declare that document as an input; metadata/docs checks may still run |
157
+ | Source or transitive helper | All affected reader tests or complete lanes run |
158
+ | Fixture, sidecar, subprocess entry point | Declared readers run |
159
+ | Shared conftest, lock, collection policy or execution workflow | Required broad/full work runs or the policy fails visibly for review |
160
+ | Deleted input / renamed module | Dependents from both Git trees remain covered |
161
+ | Missing history | Full execution, with the fallback explained |
162
+ | Failed planner / invalid configuration | Cannot yield a green skipped required gate |
163
+ | Explicit full override | Complete configured suite and existing validators run |
164
+
165
+ ## 5. Keep the graph narrow and check its blind spots
166
+
167
+ Run a representative full workload with the optional file-read recorder, then
168
+ audit the resulting records:
169
+
170
+ ```bash
171
+ uv run pytest --no-selective -p pytest_selective.impact_audit_plugin --impact-audit-dir impact-audit
172
+ uv run pytest-selective audit --config .github/test-impact.json --records impact-audit
173
+ ```
174
+
175
+ - [ ] Declare missing observed inputs at their reader and repeat the affected route.
176
+ - [ ] Keep full checkpoints for paths the recorder has not observed.
177
+ - [ ] Use `explain`, `dependencies` and `conftest-impact` to investigate unexpectedly broad selections.
178
+ - [ ] Move helpers/fixtures to the narrowest consuming scope before accepting a new startup floor.
179
+ - [ ] Review and set `floor_budget`; verify a deliberately excessive floor fails visibly.
180
+ - [ ] Add the [expansion and test-file isolation guards](../pytest-hygiene.md)
181
+ where needed; do not hide new global dependencies behind unexplained exceptions.
182
+ - [ ] Consider `warm` only after measuring graph construction; cache misses must
183
+ rebuild safely and must never become proof that tests passed.
184
+
185
+ Use the [selection-health guide](../selection-health.md) for complete commands.
186
+ Audits observe executed paths; they cannot prove that every possible runtime
187
+ dependency has been declared. Keep intentional external-state checks separate.
188
+
189
+ ## 6. Balance selected work without changing eligibility
190
+
191
+ Use [CI-esta timing retention](https://github.com/gigaverse-app/ci-esta/blob/main/docs/learned-test-timings.md)
192
+ when partial whole-file runs would otherwise erase useful costs. CI-esta owns
193
+ the lifecycle; pytest-selective owns inventory and partitioning.
194
+
195
+ - [ ] Use separate timing histories per environment and fixed marker lane.
196
+ - [ ] Validate complete files and all expected reports before learning.
197
+ - [ ] Restore once, then freeze the plan or duration input for every worker.
198
+ - [ ] Verify that every selected file is assigned exactly once and unknown files remain included.
199
+ - [ ] Compare existing xdist with proposed shard/pool layouts including startup and service costs.
200
+ - [ ] Keep timing retention, job evidence reuse and test eligibility as separate opt-ins.
201
+
202
+ Setting `--shard-seconds`, `--max-shards` or a duration manifest changes scheduling
203
+ estimates, not the dependency contract. A case-filtered local run may be valid for
204
+ the plugin while still being unsuitable as a complete-file timing observation.
205
+
206
+ ## 7. Know which commands and scripts to use
207
+
208
+ | Surface | Purpose / boundary |
209
+ | --- | --- |
210
+ | `pytest --selective`, `--no-selective`; `pytest-selective run` | Local execution, including uncommitted changes; choose collection behavior explicitly. |
211
+ | `affected`, `explain`, `dependencies`, `conftest-impact` | Plan and inspect dependency routes; preserve structured explanations for review. |
212
+ | `fingerprints`, `floor-budget`, `audit`, `warm` | Collection-policy review, breadth budgets, observed-read checks and cache preparation. A fingerprint refresh is a review step, not an automatic fix for every fallback. |
213
+ | `typecheck-scope` | Scope type checking with its own checker/global-input contract; retain a full fallback. Inspect `pytest-selective typecheck-scope --help` and the [scope implementation](../../src/pytest_selective/typecheck_scope.py). |
214
+ | `python -m pytest_selective.ci` | Whole-lane planning outside the GitHub action; the [CI recipe](../ci.md) documents its JSON and full override. There is no `pytest-selective lanes` command. |
215
+ | `pytest-selective-expansion` | Standalone expansion guard; use the [hygiene recipe](../pytest-hygiene.md). |
216
+ | [scripts/tutorial.py](../../scripts/tutorial.py) | Executable Git/selection/pytest example used in stage 1. |
217
+ | [scripts/benchmark_graph.py](../../scripts/benchmark_graph.py) | Compare cold, warm and incremental graph construction on fixed revisions. Its in-memory fingerprint refresh is for measurement, not adoption approval. |
218
+ | [scripts/benchmark_parsing.py](../../scripts/benchmark_parsing.py), [benchmark_rust.py](../../scripts/benchmark_rust.py) | Maintainer parser experiments; they do not enable a production alternate parser or establish hosted savings. |
219
+ | [scripts/compare_impacted.py](../../scripts/compare_impacted.py) | Optional comparison experiment; needs a separately installed pytest-impacted revision. Not a runtime dependency. |
220
+ | [scripts/measure_ci_runs.py](../../scripts/measure_ci_runs.py) | Compare recorded GitHub runs with explicit identities; inspect `--help` and the [measurement limits](../evidence.md). |
221
+ | [scripts/ci_file_config.py](../../scripts/ci_file_config.py), [run_ci_tests.py](../../scripts/run_ci_tests.py) | This toolkit's CI adapters; consumers normally use the public action/runner, not copied wrappers. |
222
+ | [scripts/public_actions.py](../../scripts/public_actions.py), [check_docs.py](../../scripts/check_docs.py), [check_metadata.py](../../scripts/check_metadata.py), [smoke_distribution.py](../../scripts/smoke_distribution.py), [render_brand.py](../../scripts/render_brand.py); `documentation/*.py` | Toolkit generation, documentation and release validation. These are maintainer tools, not extra adopter jobs. |
223
+
224
+ The Python graph/configuration models, inventory, sharding and hygiene helpers
225
+ are available for integrations, but reuse the public runner/actions for ordinary
226
+ adoption. Do not copy selection logic or create a second dependency declaration
227
+ format in an application helper.
228
+
229
+ ## 8. Measure and hand over an adoption that stays useful
230
+
231
+ - [ ] Compare successful full and selected runs on the same source head and tested
232
+ checkout, with unchanged environment and required checks.
233
+ - [ ] Record planning, collection, installation, services, artifacts and reporting
234
+ alongside pytest execution. Fewer files does not by itself mean lower latency.
235
+ - [ ] Retain run/attempt IDs, elapsed seconds and summed runner-seconds; repeat
236
+ before claiming an average. Keep cancelled experiments visible separately.
237
+ - [ ] Record configuration assumptions, full-run override, rollback and ownership.
238
+ - [ ] Route the adoption regression tests through their declared real inputs.
239
+ - [ ] Link these results from the PR and update your installed
240
+ [agent skill](../../skills/pytest-selective/SKILL.md) separately from package/action pins.
241
+
242
+ Use [CI-esta's measurement recipe](https://github.com/gigaverse-app/ci-esta/blob/main/docs/measured-impact.md)
243
+ to report the result. Installing a newer selector does not automatically configure
244
+ lane routing, runtime audits, hygiene policies, timing retention or evidence reuse.