pytest-selective 1.3.0__tar.gz → 1.5.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.
- pytest_selective-1.3.0/.github/test-lanes.json → pytest_selective-1.5.0/.github/plan-lanes.json +1 -21
- pytest_selective-1.5.0/.github/test-lanes.json +22 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/ci.yml +19 -18
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/public-actions.yml +6 -4
- pytest_selective-1.5.0/.release-please-manifest.json +3 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/AGENTS.md +4 -3
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/CHANGELOG.md +14 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/CONTRIBUTING.md +41 -17
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/PKG-INFO +1 -1
- pytest_selective-1.5.0/contracts/selection/conftest.py.gv-dependency.yaml +5 -0
- pytest_selective-1.5.0/contracts/selection/test_selection_contract.py.gv-dependency.yaml +6 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/ci-toolkit.md +8 -1
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/ci.md +24 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/configuration.md +6 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/selection-health.md +5 -2
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/tutorials/adoption-checklist.md +3 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/tutorials/sidecar-dependencies.md +42 -2
- pytest_selective-1.5.0/documentation/case-study.md +293 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/mkdocs.yml +14 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/pyproject.toml +1 -1
- pytest_selective-1.5.0/scripts/check_lane_routing.py +145 -0
- pytest_selective-1.5.0/scripts/ci_file_config.py +18 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/smoke_distribution.py +2 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/skills/pytest-selective/SKILL.md +10 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/ci.py +15 -11
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_declarations.py +16 -5
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_graph.py +11 -3
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_models.py +2 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_ci_admission.py +2 -1
- pytest_selective-1.5.0/tests/test_ci_admission.py.gv-dependency.yaml +5 -0
- pytest_selective-1.5.0/tests/test_release_workflow.py.gv-dependency.yaml +5 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_repository_ci.py +76 -71
- pytest_selective-1.5.0/tests/test_repository_ci.py.gv-dependency.yaml +7 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_ci.py +76 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_impact_audit_declarations.py +16 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_impact_graph.py +137 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/uv.lock +1 -1
- pytest_selective-1.3.0/.release-please-manifest.json +0 -3
- pytest_selective-1.3.0/scripts/ci_file_config.py +0 -13
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.dry-lint/baseline.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.dry-lint/progress.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.dry-lint.toml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.gitattributes +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/dependabot.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/validation-inputs.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/docs.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/dry-lint-audit.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/dry-lint-judge.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/dry-lint.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/observe-ci.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/pr-title.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/release-please.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/release.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/weekly-checkpoint.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.github/workflows/windows-performance.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.gitignore +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.pre-commit-config.yaml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/.readthedocs.yaml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/CLAUDE.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/LICENSE +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/PROVENANCE.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/README.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/RELEASING.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/actions/README.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/actions/plan-lanes/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/actions/pytest-expansion/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/actions/test-impact/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/actions/validation-gate/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/agentsync.toml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/contracts/selection/README.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/contracts/selection/conftest.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/contracts/selection/quint-connect.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/contracts/selection/quint.ini +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/contracts/selection/runtime_deps.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/contracts/selection/selection.qnt +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/contracts/selection/selection_driver.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/contracts/selection/test_selection_contract.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/README.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/actions.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/agents.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/assets/banner.png +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/assets/banner.svg +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/assets/brand.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/assets/icon.png +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/assets/logo.svg +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/assets/raster-sources.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/assets/social-preview.png +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/assets/social-preview.svg +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-22.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/benchmarks/windows-runtime.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/comparison-pytest-impacted.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/evidence.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/local-development.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/pytest-hygiene.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/shared-plan-execution.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/tutorial.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/tutorials/skip-unrelated-ci.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/graph-based-inputs-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/ratchet-duplicates-zero-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/docs/work-in-progress/tickets/rerun-selection-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/assets/routing-comparison.svg +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/check_site.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/ecosystem.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/hooks.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/index.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/llms.txt +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/overrides/main.html +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/publishing.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/requirements.in +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/documentation/requirements.txt +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/release-please-config.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/benchmark_graph.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/benchmark_parsing.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/benchmark_rust.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/check_docs.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/check_metadata.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/compare_impacted.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/measure_ci_runs.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/public_actions.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/render_brand.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/run_ci_tests.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/scripts/tutorial.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/skills/pytest-selective/agents/openai.yaml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/__init__.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/__main__.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/ast_imports.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/checkpoint.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/execution.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/gate.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/git_operations.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/hygiene.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_audit.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_audit_declarations.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_cli.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_explanation.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_first_party.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_name_scope.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/impact_selection.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/input_policy.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/local.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/parse_batch.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/plugin.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/py.typed +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/pytest_expansion.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/pytest_plugins.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/pytest_sharding.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/test_inventory.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/typecheck_scope.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/src/pytest_selective/working_tree.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/__init__.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_checkpoint_policy.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_ci_measurements.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_ci_test_execution.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_distribution_metadata.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_hygiene.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_public_actions.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_pytest_expansion.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_release_workflow.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_repository_hygiene.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/test_tutorial.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/__init__.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/git_repo.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_ast_imports.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_gate.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_git_repo.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_global_inputs.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_impact_audit.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_impact_explanation.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_impact_first_party.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_impact_name_scope.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_parse_batch.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_portability.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_pull_request_merge_base.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_pytest_plugin.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_pytest_plugins.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_pytest_sharding.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_typecheck_scope.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.5.0}/tests/testing/test_working_tree.py +0 -0
pytest_selective-1.3.0/.github/test-lanes.json → pytest_selective-1.5.0/.github/plan-lanes.json
RENAMED
|
@@ -25,27 +25,6 @@
|
|
|
25
25
|
"src/**",
|
|
26
26
|
"tests/**"
|
|
27
27
|
]
|
|
28
|
-
},
|
|
29
|
-
"contracts": {
|
|
30
|
-
"roots": [
|
|
31
|
-
"contracts"
|
|
32
|
-
],
|
|
33
|
-
"inputs": [
|
|
34
|
-
"src/**",
|
|
35
|
-
"contracts/**",
|
|
36
|
-
"tests/testing/git_repo.py"
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
"package": {
|
|
40
|
-
"inputs": [
|
|
41
|
-
"src/**",
|
|
42
|
-
"pyproject.toml",
|
|
43
|
-
"uv.lock",
|
|
44
|
-
"README.md",
|
|
45
|
-
"LICENSE",
|
|
46
|
-
"scripts/check_metadata.py",
|
|
47
|
-
"scripts/smoke_distribution.py"
|
|
48
|
-
]
|
|
49
28
|
}
|
|
50
29
|
},
|
|
51
30
|
"global_inputs": [
|
|
@@ -56,6 +35,7 @@
|
|
|
56
35
|
"pyproject.toml",
|
|
57
36
|
"**/uv.lock",
|
|
58
37
|
"uv.lock",
|
|
38
|
+
".github/plan-lanes.json",
|
|
59
39
|
".github/test-lanes.json",
|
|
60
40
|
".github/workflows/ci.yml",
|
|
61
41
|
".github/workflows/release.yml",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "workflow",
|
|
3
|
+
"lanes": {
|
|
4
|
+
"tests": {
|
|
5
|
+
"roots": ["tests"]
|
|
6
|
+
}
|
|
7
|
+
},
|
|
8
|
+
"rules": [
|
|
9
|
+
{
|
|
10
|
+
"name": "python-environment",
|
|
11
|
+
"when": ["pyproject.toml", "**/pyproject.toml", "uv.lock", "**/uv.lock", ".python-version"],
|
|
12
|
+
"select": ["all"],
|
|
13
|
+
"reason": "the locked test environment; documentation/requirements*.txt belongs to Read the Docs"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "distribution-smoke",
|
|
17
|
+
"when": ["scripts/smoke_distribution.py"],
|
|
18
|
+
"select": ["lane:tests"],
|
|
19
|
+
"reason": "the distributions job runs the smoke only when the tests lane is planned"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -13,6 +13,9 @@ concurrency:
|
|
|
13
13
|
group: ci-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.number || github.run_id }}
|
|
14
14
|
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == 1 }}
|
|
15
15
|
jobs:
|
|
16
|
+
# Plans whole lanes before any runner starts: each lane is the name a job's condition reads, and a change selects it
|
|
17
|
+
# through the tests, scripts and actions that job runs. `ci-esta lanes` shows the lanes and why; .github/test-lanes.json
|
|
18
|
+
# holds only what the workflow cannot show.
|
|
16
19
|
select-tests:
|
|
17
20
|
runs-on: ubuntu-latest
|
|
18
21
|
permissions:
|
|
@@ -31,7 +34,7 @@ jobs:
|
|
|
31
34
|
CONTRACT_LANE_JOBS: '{"selection":"Selection contract (Quint)"}'
|
|
32
35
|
outputs:
|
|
33
36
|
# Planned lanes after reuse: a reusing lane is false only when every one of its jobs is proven by a receipt.
|
|
34
|
-
lanes: ${{ steps.
|
|
37
|
+
lanes: ${{ steps.contract-evidence.outputs.admitted-lanes || steps.plan.outputs.lanes }}
|
|
35
38
|
# The plan before reuse, for steps that follow changed runtime inputs rather than test execution.
|
|
36
39
|
planned: ${{ steps.plan.outputs.lanes }}
|
|
37
40
|
mode: ${{ steps.files.outputs.mode }}
|
|
@@ -49,9 +52,10 @@ jobs:
|
|
|
49
52
|
if: github.event_name == 'pull_request'
|
|
50
53
|
run: 'true'
|
|
51
54
|
- id: plan
|
|
52
|
-
uses:
|
|
55
|
+
uses: gigaverse-app/ci-esta/actions/plan-work@v1
|
|
53
56
|
with:
|
|
54
57
|
config: .github/test-lanes.json
|
|
58
|
+
# File selection has no workflow graph, so it reads the complete lane file public-actions.yml plans with.
|
|
55
59
|
- name: Share the repository graph and execution inputs with file selection
|
|
56
60
|
env:
|
|
57
61
|
PYTHONPATH: src
|
|
@@ -82,6 +86,8 @@ jobs:
|
|
|
82
86
|
input-paths: ${{ steps.inputs.outputs.tests }}
|
|
83
87
|
reuse-after-merge: 'true'
|
|
84
88
|
reuse-version-only-release: 'true'
|
|
89
|
+
planned-lanes: ${{ steps.plan.outputs.lanes }}
|
|
90
|
+
lane-keys: '{"tests":["py311-ubuntu","py311-windows","py311-macos","py314-ubuntu","py314-windows","py314-macos","minimum"]}'
|
|
85
91
|
- id: contract-evidence
|
|
86
92
|
if: >-
|
|
87
93
|
${{ (github.event_name == 'pull_request' || github.event_name == 'push') &&
|
|
@@ -93,21 +99,9 @@ jobs:
|
|
|
93
99
|
input-paths: ${{ steps.inputs.outputs.contracts }}
|
|
94
100
|
reuse-after-merge: 'true'
|
|
95
101
|
reuse-version-only-release: 'true'
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
PLANNED_LANES: ${{ steps.plan.outputs.lanes }}
|
|
100
|
-
REUSED: ${{ steps.evidence.outputs.reused || '{}' }}
|
|
101
|
-
CONTRACT_REUSED: ${{ steps.contract-evidence.outputs.reused || '{}' }}
|
|
102
|
-
run: |
|
|
103
|
-
admitted=$(jq -c \
|
|
104
|
-
--argjson tests "$TEST_LANE_JOBS" --argjson reused "$REUSED" \
|
|
105
|
-
--argjson contracts "$CONTRACT_LANE_JOBS" --argjson contract_reused "$CONTRACT_REUSED" '
|
|
106
|
-
def proven($jobs; $proof): [$jobs | keys[] | $proof[.] == true] | all;
|
|
107
|
-
(if proven($tests; $reused) then .tests = false else . end)
|
|
108
|
-
| (if proven($contracts; $contract_reused) then .contracts = false else . end)' <<<"$PLANNED_LANES")
|
|
109
|
-
echo "reused=$REUSED contracts=$CONTRACT_REUSED"
|
|
110
|
-
echo "lanes=$admitted" | tee -a "$GITHUB_OUTPUT"
|
|
102
|
+
# Chained: the tests step already admitted its lane; this step admits the contracts lane on top.
|
|
103
|
+
planned-lanes: ${{ steps.evidence.outputs.admitted-lanes }}
|
|
104
|
+
lane-keys: '{"contracts":["selection"]}'
|
|
111
105
|
quality:
|
|
112
106
|
runs-on: ubuntu-latest
|
|
113
107
|
steps:
|
|
@@ -119,11 +113,18 @@ jobs:
|
|
|
119
113
|
- run: uv run --locked pre-commit run --all-files
|
|
120
114
|
env:
|
|
121
115
|
SKIP: dry-lint
|
|
116
|
+
# Each lane-gated job's condition, the gate's lane-jobs and the lanes the workflow defines must agree.
|
|
122
117
|
- name: Check aggregate gate against the execution lanes
|
|
123
118
|
run: >-
|
|
124
119
|
uv run --no-project --python 3.14
|
|
125
|
-
--with "ci-esta>=1.
|
|
120
|
+
--with "ci-esta[selection]>=1.13.0,<2.0.0"
|
|
126
121
|
ci-esta check-workflow --workflow .github/workflows/ci.yml --lanes .github/test-lanes.json
|
|
122
|
+
# Plans one-file changes with CI-esta against real commits and fails on any lane a change should not run.
|
|
123
|
+
- name: Check the lane routing
|
|
124
|
+
run: >-
|
|
125
|
+
uv run --no-project --python 3.14
|
|
126
|
+
--with "ci-esta[selection]>=1.13.0,<2.0.0"
|
|
127
|
+
python scripts/check_lane_routing.py
|
|
127
128
|
- name: Validate GitHub Actions
|
|
128
129
|
run: docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.7 -color
|
|
129
130
|
tests:
|
|
@@ -15,10 +15,12 @@ jobs:
|
|
|
15
15
|
- uses: actions/checkout@v7
|
|
16
16
|
with:
|
|
17
17
|
fetch-depth: 0
|
|
18
|
+
# This workflow smokes the repository's own actions, so its planner is plan-lanes itself, run selectively on every
|
|
19
|
+
# change against .github/plan-lanes.json; CI's lanes come from CI-esta's plan-work.
|
|
18
20
|
- id: select
|
|
19
21
|
uses: ./actions/plan-lanes
|
|
20
22
|
with:
|
|
21
|
-
config: .github/
|
|
23
|
+
config: .github/plan-lanes.json
|
|
22
24
|
artifact-name: public-action-selection
|
|
23
25
|
public-actions:
|
|
24
26
|
needs: select-tests
|
|
@@ -35,7 +37,7 @@ jobs:
|
|
|
35
37
|
python - <<'PY'
|
|
36
38
|
import json
|
|
37
39
|
from pathlib import Path
|
|
38
|
-
graph = json.loads(Path('.github/
|
|
40
|
+
graph = json.loads(Path('.github/plan-lanes.json').read_text())['graph']
|
|
39
41
|
Path('smoke-impact.json').write_text(json.dumps(graph))
|
|
40
42
|
PY
|
|
41
43
|
- uses: ./
|
|
@@ -57,7 +59,7 @@ jobs:
|
|
|
57
59
|
- uses: ./actions/plan-lanes
|
|
58
60
|
id: lanes
|
|
59
61
|
with:
|
|
60
|
-
config: .github/
|
|
62
|
+
config: .github/plan-lanes.json
|
|
61
63
|
base: ${{ github.sha }}
|
|
62
64
|
full: 'true'
|
|
63
65
|
artifact-name: ''
|
|
@@ -68,7 +70,7 @@ jobs:
|
|
|
68
70
|
python - <<'PY'
|
|
69
71
|
import json, os
|
|
70
72
|
# A full checkpoint admits every configured lane.
|
|
71
|
-
configured = json.load(open('.github/
|
|
73
|
+
configured = json.load(open('.github/plan-lanes.json', encoding='utf-8'))['lanes']
|
|
72
74
|
assert json.loads(os.environ['LANES']) == dict.fromkeys(configured, True)
|
|
73
75
|
PY
|
|
74
76
|
- name: Require successful planning after fallback execution
|
|
@@ -19,9 +19,10 @@ on pytest-selective itself.
|
|
|
19
19
|
- Planning supports committed revisions and explicit working-tree snapshots. Preserve full execution on
|
|
20
20
|
operational uncertainty and diagnostics for invalid declarations. An empty
|
|
21
21
|
selection is not an empty pytest argument list.
|
|
22
|
-
- Select consumer routing regressions by their declared execution/configuration inputs.
|
|
23
|
-
`.github/test-lanes.json`
|
|
24
|
-
|
|
22
|
+
- Select consumer routing regressions by their declared execution/configuration inputs. Declare a
|
|
23
|
+
file a test or CI script reads by path next to its reader; `.github/test-lanes.json` holds only
|
|
24
|
+
what CI-esta cannot see in the workflow, and `scripts/check_lane_routing.py` pins the lanes. Preserve
|
|
25
|
+
explicit full/manual checkpoints and the `test:full` override.
|
|
25
26
|
- The README's performance numbers are scenario-specific observations. Keep
|
|
26
27
|
claims linked to [raw evidence](docs/benchmarks/2026-10-22-routing.md); distinguish
|
|
27
28
|
runner-seconds, elapsed time and billing. Skill/example edits need usable links
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.4.0...v1.5.0) (2026-11-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **declarations:** share reader-relative dependency rules ([#139](https://github.com/gigaverse-app/pytest-selective/issues/139)) ([7eea4ba](https://github.com/gigaverse-app/pytest-selective/commit/7eea4ba3ec3fd4607acf1e52b171e6eabd0c96fc))
|
|
9
|
+
|
|
10
|
+
## [1.4.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.3.0...v1.4.0) (2026-11-09)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **lanes:** partition test roots by filename patterns ([27c10ec](https://github.com/gigaverse-app/pytest-selective/commit/27c10ec608f052a36f58d9b10730048ab9d38598))
|
|
16
|
+
|
|
3
17
|
## [1.3.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.2.0...v1.3.0) (2026-11-09)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -65,6 +65,7 @@ uv run --group quint pytest-quint-connect report '**/.artifacts/quint-connect/re
|
|
|
65
65
|
| Private working-tree snapshots, staged inputs and index timestamp preservation | `working_tree.py` |
|
|
66
66
|
| Python module names, Unicode matching and dotted import relationships | `ast_imports.py` |
|
|
67
67
|
| Test-file discovery and cache exclusions | `test_inventory.py` |
|
|
68
|
+
| Reader-owned declaration grammar, path expansion and dependency kinds | `impact_declarations.py`, with the canonical models in `impact_models.py` |
|
|
68
69
|
| Repository-relative path containment, including the dot root | `impact_graph.within` |
|
|
69
70
|
|
|
70
71
|
The expansion guard imports the first three owners directly when run as a script;
|
|
@@ -98,24 +99,47 @@ limitations. The pull request template provides a short checklist. See
|
|
|
98
99
|
|
|
99
100
|
## CI lane selection
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
CI plans whole lanes with [CI-esta](https://github.com/gigaverse-app/ci-esta)'s
|
|
103
|
+
`plan-work` before any runner starts: `tests` (the Python matrix and
|
|
104
|
+
`pytest-minimum`), `contracts` (the Quint selection contract) and `package` (the
|
|
105
|
+
distribution build), the names the jobs' conditions read. A change selects a lane
|
|
106
|
+
through what its jobs run: the tests that import it, the files tests declare they
|
|
107
|
+
read, and the scripts and actions each job executes. Documentation-only PRs keep
|
|
102
108
|
quality checks (including Markdown links) and the documentation build, while
|
|
103
|
-
skipping every
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
skipping every lane. A README or LICENSE change runs the `package` lane, because
|
|
110
|
+
both distributions embed them; a README change also runs `contracts`, whose job
|
|
111
|
+
checks the README's contract report, and the one test whose metadata check reads it. `pyproject.toml` and `uv.lock` run every
|
|
112
|
+
lane. `ci-esta lanes --base origin/main` shows what a change would run and why.
|
|
113
|
+
[.github/test-lanes.json](.github/test-lanes.json) extends the workflow and holds
|
|
114
|
+
only what it cannot show: the `tests` lane's roots (its jobs run `pytest-selective
|
|
115
|
+
execute-plan`, which CI-esta does not read as a pytest command), a
|
|
116
|
+
`python-environment` rule that leaves the documentation site's requirements out,
|
|
117
|
+
and a rule that plans the tests lane when the installed-distribution smoke script
|
|
118
|
+
changes, because `distributions` smokes only when that lane is planned. When a test
|
|
119
|
+
or a script starts reading a file by path, declare it next to the reader (a
|
|
120
|
+
`# gv:test-dependency: PATH` comment for simple inputs, or a directory
|
|
121
|
+
`.gv-dependencies.yaml` with `relative_to: reader` for repeated local layouts).
|
|
122
|
+
Keep per-reader sidecars for exceptions needing separate declarations. Review
|
|
123
|
+
`pytest-selective audit --write-declarations` output for shared conventions before
|
|
124
|
+
retaining its individual sidecars; preserve `content` versus `import` semantics.
|
|
125
|
+
`quality` runs [scripts/check_lane_routing.py](scripts/check_lane_routing.py),
|
|
126
|
+
which plans one-file changes with CI-esta against real Git commits, including a
|
|
127
|
+
full checkpoint, and `ci-esta check-workflow`, which keeps the job conditions, the
|
|
128
|
+
gate and the lane file in agreement. Manual/called/release runs and the
|
|
129
|
+
`test:full` label run everything; a failed planner also runs all tests.
|
|
130
|
+
Installed-distribution smoke checks follow the tests lane's plan; README-only
|
|
131
|
+
changes skip them while still verifying the README embedded in both release
|
|
132
|
+
archives.
|
|
133
|
+
|
|
134
|
+
Within an admitted lane, the repository's public file-selection action selects
|
|
135
|
+
affected test files once; the platform matrix and minimum-pytest job execute that
|
|
136
|
+
same plan. CI-esta receives both lane and file explanations, learns JUnit timings,
|
|
137
|
+
and validates the aggregate gate. File selection has no workflow graph, so it reads
|
|
138
|
+
[.github/plan-lanes.json](.github/plan-lanes.json), a complete lane file: its graph
|
|
139
|
+
plus the execution inputs that run every test file (scripts, workflows, actions,
|
|
140
|
+
dependency and pytest policy). `public-actions.yml` plans with the same file through
|
|
141
|
+
this repository's own `plan-lanes` action, selectively on every change, and smokes
|
|
142
|
+
its full checkpoint. Declare subprocess
|
|
119
143
|
and non-import dependencies at their readers. Full plans collect whole-suite
|
|
120
144
|
coverage; scoped plans produce JUnit timing evidence without presenting partial
|
|
121
145
|
coverage as a whole-suite result. The public [shared-plan executor](docs/shared-plan-execution.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pytest-selective
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.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/
|
|
@@ -36,7 +36,14 @@ PR, on a version-only release PR, and on a PR push that changes nothing the
|
|
|
36
36
|
tests read. A `PR evidence #N head SHA` step ties each PR run to its pull
|
|
37
37
|
request, because GitHub often leaves that link out. The distribution smoke test
|
|
38
38
|
still follows the plan before reuse (`planned`), and partial proof runs the
|
|
39
|
-
whole lane.
|
|
39
|
+
whole lane. The contract lane works the same way with its own receipt and inputs
|
|
40
|
+
(including `README.md`, which its README check reads).
|
|
41
|
+
|
|
42
|
+
CI-esta decides admission: each evidence step gets the planned lanes and
|
|
43
|
+
`lane-keys`, the keys of every job a lane gates, and reports `admitted-lanes`.
|
|
44
|
+
The contract step chains on the tests step's result. `ci-esta check-workflow`
|
|
45
|
+
then proves `lane-keys` name every job each lane gates, matrix cells included,
|
|
46
|
+
so a newly added cell can never be skipped as proven.
|
|
40
47
|
|
|
41
48
|
The unconditional CI job runs `ci-esta check-workflow` against this workflow and
|
|
42
49
|
lane configuration, so renamed lanes or omitted metadata prerequisites cannot
|
|
@@ -285,3 +285,27 @@ The action also uploads the report as the `test-lane-selection` artifact; set
|
|
|
285
285
|
pull-request sticky's lane section: which lanes ran and why, and the runner time
|
|
286
286
|
the skipped jobs took on the default branch's last full run. See
|
|
287
287
|
[how the two fit together in a Python project](https://github.com/gigaverse-app/ci-esta/blob/main/docs/integrations.md#getting-the-most-from-both-in-a-python-project).
|
|
288
|
+
|
|
289
|
+
## Split whole lanes by test filename
|
|
290
|
+
|
|
291
|
+
Lanes sharing a directory can partition its tests with `test_patterns` and
|
|
292
|
+
`exclude_patterns`. Both match repository-relative POSIX paths using the same
|
|
293
|
+
globs as lane inputs; `*` also matches path separators. Roots still limit the
|
|
294
|
+
search. The defaults include every test under the roots and exclude none.
|
|
295
|
+
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"unit": {"roots": ["tests"], "exclude_patterns": ["*_integration.py"]},
|
|
299
|
+
"integration": {"roots": ["tests"], "test_patterns": ["*_integration.py"]}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Put these entries under `lanes` in the lane configuration. A source dependency
|
|
304
|
+
still selects every lane containing an affected test. Deleted tests use their
|
|
305
|
+
base-revision membership; an affected test claimed by no lane causes a full
|
|
306
|
+
fallback. Explicit inputs and full checkpoints are unaffected by these filters.
|
|
307
|
+
|
|
308
|
+
Same-named modules in independent module roots are unrelated unless an import
|
|
309
|
+
connects them. An actual ambiguous bare import conservatively retains every
|
|
310
|
+
possible target. Use qualified imports to make independent demo modules
|
|
311
|
+
unambiguous; selection does not guess a runtime import order.
|
|
@@ -89,6 +89,12 @@ root. `content` follows the file's bytes; `import` also follows its dependencies
|
|
|
89
89
|
The default is `import`. Inputs must match tracked files. Invalid patterns,
|
|
90
90
|
missing matches, duplicate YAML keys, and aliases are errors to repair.
|
|
91
91
|
|
|
92
|
+
For repeated conventions, a directory `.gv-dependencies.yaml` can match `readers`
|
|
93
|
+
and set `relative_to: reader`: each unanchored input then resolves beside each
|
|
94
|
+
matched reader. The default remains `relative_to: manifest`, and `//` always
|
|
95
|
+
anchors at the repository root. This keeps sibling readers' inputs separate;
|
|
96
|
+
see [shared conventions](tutorials/sidecar-dependencies.md#share-a-convention-without-sharing-every-input).
|
|
97
|
+
|
|
92
98
|
Declare lock files, environment manifests, and other suite-wide inputs explicitly:
|
|
93
99
|
|
|
94
100
|
```json
|
|
@@ -29,8 +29,11 @@ uv run pytest-selective audit --config .github/test-impact.json --records impact
|
|
|
29
29
|
|
|
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
|
-
edge at the reader using a dependency comment
|
|
33
|
-
route.
|
|
32
|
+
edge at the reader using a dependency comment, shared directory manifest or YAML
|
|
33
|
+
sidecar, then recheck the route. Prefer a shared `relative_to: reader` convention
|
|
34
|
+
for repeated layouts, preserving each reader's own input scope and dependency kind.
|
|
35
|
+
`audit --write-declarations` writes individual sidecars as a starting point;
|
|
36
|
+
review repeated patterns before keeping them. Existing shared rules satisfy the audit. See
|
|
34
37
|
[write the missing declarations](configuration.md#write-the-missing-declarations). [Work through the sidecar tutorial](tutorials/sidecar-dependencies.md), or consult
|
|
35
38
|
[declaration syntax](configuration.md#declare-inputs-that-imports-cannot-describe).
|
|
36
39
|
The audit covers observed paths; keep representative full runs for paths it has
|
|
@@ -100,6 +100,9 @@ dependencies:
|
|
|
100
100
|
- [ ] Use `content` for bytes read as data, and `import` when the reader executes
|
|
101
101
|
Python and needs that module's transitive dependencies.
|
|
102
102
|
- [ ] Keep declarations beside readers; use central rules for actual shared policy.
|
|
103
|
+
- [ ] Replace repeated per-reader layouts with directory manifest rules using
|
|
104
|
+
`relative_to: reader`; compare affected readers and lanes before and after,
|
|
105
|
+
including sibling inputs, new matching files and deletions.
|
|
103
106
|
- [ ] Review locks, environment configuration, external checkout pins and plugin startup.
|
|
104
107
|
- [ ] Retain default global inputs until a narrower environment contract is verified.
|
|
105
108
|
- [ ] Review nested/custom pytest configuration before refreshing fingerprints.
|
|
@@ -20,7 +20,7 @@ cd selective-sidecar-demo
|
|
|
20
20
|
git init
|
|
21
21
|
python -m venv .venv
|
|
22
22
|
source .venv/bin/activate
|
|
23
|
-
python -m pip install "pytest-selective>=
|
|
23
|
+
python -m pip install "pytest-selective>=1,<2"
|
|
24
24
|
mkdir -p tests/data
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -142,7 +142,7 @@ change should select its reader. There is no universal documentation exemption.
|
|
|
142
142
|
| --- | --- | --- |
|
|
143
143
|
| Dependency comment | In the reading Python file | One or two inputs fit naturally beside the code |
|
|
144
144
|
| File sidecar | `reader.py.gv-dependency.yaml` | You need an explicit dependency kind or want declarations outside Python |
|
|
145
|
-
| Directory manifest | `.gv-dependencies.yaml` | Several related readers share inputs |
|
|
145
|
+
| Directory manifest | `.gv-dependencies.yaml` | Several related readers share inputs or a per-reader layout convention |
|
|
146
146
|
|
|
147
147
|
Instead of the sidecar above, a comment in `tests/test_prices.py` can declare:
|
|
148
148
|
|
|
@@ -172,6 +172,46 @@ are relative to its directory. `//` anchors either pattern at the repository
|
|
|
172
172
|
root, for example `//schemas/receipt.json`. Do not use `../` to reach another
|
|
173
173
|
directory.
|
|
174
174
|
|
|
175
|
+
### Share a convention without sharing every input
|
|
176
|
+
|
|
177
|
+
When each reader consumes files beside itself, use `relative_to: reader` in a
|
|
178
|
+
directory manifest. For example, `contracts/.gv-dependencies.yaml` can replace
|
|
179
|
+
one sidecar per contract:
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
dependencies:
|
|
183
|
+
- readers: ["**/test_*_contract.py"]
|
|
184
|
+
relative_to: reader
|
|
185
|
+
inputs: ["model.qnt", "quint-connect.json", "history/**", "//contracts/quint.ini"]
|
|
186
|
+
kind: content
|
|
187
|
+
reason: Each contract reads its own model, manifest and history under the shared pytest configuration.
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The `readers` pattern is still relative to the manifest. Each unanchored input
|
|
191
|
+
is resolved separately beside each matched reader; `//` still means the repository
|
|
192
|
+
root. Changing one contract's model selects its readers, without connecting sibling
|
|
193
|
+
contracts to that model. New matching readers inherit the same convention. Every
|
|
194
|
+
input pattern must match for **each** reader; a missing history directory in one
|
|
195
|
+
contract is an error even if a sibling has one. Omit inputs your convention does
|
|
196
|
+
not require, or put distinct conventions in separate rules.
|
|
197
|
+
|
|
198
|
+
The default, `relative_to: manifest`, preserves existing behavior. Both modes
|
|
199
|
+
support `content` and `import`; a file sidecar has the same directory as its reader,
|
|
200
|
+
so either mode means the same thing there. Expansion is reused for readers in the
|
|
201
|
+
same directory within a rule and rebuilt against each Git tree, including added
|
|
202
|
+
files and deleted inputs. Editing the shared manifest selects all its readers;
|
|
203
|
+
keep unrelated conventions in separate manifests.
|
|
204
|
+
|
|
205
|
+
For a source scanner, the same mechanism can declare `readers: ["*/test_inventory.py"]`
|
|
206
|
+
with `inputs: ["**/*.py"]`, `relative_to: reader` and `kind: content`. Each inventory
|
|
207
|
+
then follows only the Python source under its own directory. Prefer this to a broad
|
|
208
|
+
rule joining every scanner to every project's source.
|
|
209
|
+
|
|
210
|
+
Keep the convention where its readers live. A project-specific layout belongs in
|
|
211
|
+
its manifest; it does not need a framework-specific plugin in the selector. Review
|
|
212
|
+
`audit --write-declarations` output for repeated layouts before retaining individual
|
|
213
|
+
sidecars. An existing shared rule already satisfies the audit for covered reads.
|
|
214
|
+
|
|
175
215
|
### Reading content versus executing Python
|
|
176
216
|
|
|
177
217
|
| Input use | Kind | What changes can select the reader |
|