pytest-obligation 0.8.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_obligation-0.8.0/.agents/plugins/marketplace.json +20 -0
- pytest_obligation-0.8.0/.claude/skills/find-upstream-weaknesses/SKILL.md +91 -0
- pytest_obligation-0.8.0/.claude-plugin/marketplace.json +12 -0
- pytest_obligation-0.8.0/.gitattributes +1 -0
- pytest_obligation-0.8.0/.github/workflows/ci.yml +470 -0
- pytest_obligation-0.8.0/.github/workflows/pr-title.yml +34 -0
- pytest_obligation-0.8.0/.github/workflows/release.yml +193 -0
- pytest_obligation-0.8.0/.gitignore +12 -0
- pytest_obligation-0.8.0/.python-version +1 -0
- pytest_obligation-0.8.0/.release-please-manifest.json +1 -0
- pytest_obligation-0.8.0/ADOPTING.md +285 -0
- pytest_obligation-0.8.0/ARCHITECTURE.md +159 -0
- pytest_obligation-0.8.0/CHANGELOG.md +148 -0
- pytest_obligation-0.8.0/CONTRIBUTING.md +63 -0
- pytest_obligation-0.8.0/LICENSE +202 -0
- pytest_obligation-0.8.0/PKG-INFO +227 -0
- pytest_obligation-0.8.0/README.md +172 -0
- pytest_obligation-0.8.0/RELEASING.md +75 -0
- pytest_obligation-0.8.0/check/action.yml +88 -0
- pytest_obligation-0.8.0/demos/README.md +164 -0
- pytest_obligation-0.8.0/demos/__init__.py +0 -0
- pytest_obligation-0.8.0/demos/cached_database.py +73 -0
- pytest_obligation-0.8.0/demos/dbos_transactional_outbox/__init__.py +0 -0
- pytest_obligation-0.8.0/demos/dbos_transactional_outbox/conftest.py +4 -0
- pytest_obligation-0.8.0/demos/dbos_transactional_outbox/pyproject.toml +9 -0
- pytest_obligation-0.8.0/demos/dbos_transactional_outbox/run_demo_process.py +56 -0
- pytest_obligation-0.8.0/demos/dbos_transactional_outbox/test_handoff_inventory.py +24 -0
- pytest_obligation-0.8.0/demos/dbos_transactional_outbox/test_transactional_outbox.py +408 -0
- pytest_obligation-0.8.0/demos/fetch_upstream.py +46 -0
- pytest_obligation-0.8.0/demos/procrastinate_demo_django/__init__.py +0 -0
- pytest_obligation-0.8.0/demos/procrastinate_demo_django/conftest.py +6 -0
- pytest_obligation-0.8.0/demos/procrastinate_demo_django/pyproject.toml +5 -0
- pytest_obligation-0.8.0/demos/procrastinate_demo_django/settings.py +16 -0
- pytest_obligation-0.8.0/demos/procrastinate_demo_django/test_demo_django.py +417 -0
- pytest_obligation-0.8.0/demos/procrastinate_demo_django/test_handoff_inventory.py +20 -0
- pytest_obligation-0.8.0/demos/saleor_checkout/conftest.py +26 -0
- pytest_obligation-0.8.0/demos/saleor_checkout/prepare_db.py +57 -0
- pytest_obligation-0.8.0/demos/saleor_checkout/pyproject.toml +153 -0
- pytest_obligation-0.8.0/demos/saleor_checkout/run.sh +50 -0
- pytest_obligation-0.8.0/demos/saleor_checkout/test_handoff_inventory.py +23 -0
- pytest_obligation-0.8.0/demos/saleor_checkout/test_order_confirmation.py +775 -0
- pytest_obligation-0.8.0/demos/saleor_checkout/test_payment_evidence.py +285 -0
- pytest_obligation-0.8.0/demos/test_readme_gif.py +92 -0
- pytest_obligation-0.8.0/demos/throwaway_postgres.sh +16 -0
- pytest_obligation-0.8.0/demos/wagtail_tasks/__init__.py +0 -0
- pytest_obligation-0.8.0/demos/wagtail_tasks/cdn.py +15 -0
- pytest_obligation-0.8.0/demos/wagtail_tasks/conftest.py +19 -0
- pytest_obligation-0.8.0/demos/wagtail_tasks/pyproject.toml +20 -0
- pytest_obligation-0.8.0/demos/wagtail_tasks/settings.py +35 -0
- pytest_obligation-0.8.0/demos/wagtail_tasks/test_feature_detection.py +90 -0
- pytest_obligation-0.8.0/demos/wagtail_tasks/test_wagtail_tasks.py +557 -0
- pytest_obligation-0.8.0/docs/adopter-capabilities.md +64 -0
- pytest_obligation-0.8.0/docs/aiokafka.md +48 -0
- pytest_obligation-0.8.0/docs/async-integrations-plan.md +15 -0
- pytest_obligation-0.8.0/docs/coverage.md +38 -0
- pytest_obligation-0.8.0/docs/demo.tape +77 -0
- pytest_obligation-0.8.0/docs/demo_env.sh +10 -0
- pytest_obligation-0.8.0/docs/false-greens.md +107 -0
- pytest_obligation-0.8.0/docs/how-it-works.md +94 -0
- pytest_obligation-0.8.0/docs/integrations.md +32 -0
- pytest_obligation-0.8.0/docs/interleavings.md +129 -0
- pytest_obligation-0.8.0/docs/jepsen-analogy.md +80 -0
- pytest_obligation-0.8.0/docs/mongodb.md +39 -0
- pytest_obligation-0.8.0/docs/package-migration.md +46 -0
- pytest_obligation-0.8.0/docs/plans/backend-conformance-port.md +34 -0
- pytest_obligation-0.8.0/docs/plans/deep-adopter-coverage.md +26 -0
- pytest_obligation-0.8.0/docs/plans/generated-capability-adoption.md +18 -0
- pytest_obligation-0.8.0/docs/prefect.md +44 -0
- pytest_obligation-0.8.0/docs/pytest-plugin.md +103 -0
- pytest_obligation-0.8.0/docs/upstream-playbook.md +209 -0
- pytest_obligation-0.8.0/docs/upstream-templates/README.md +17 -0
- pytest_obligation-0.8.0/docs/upstream-templates/eli5-issue.md +23 -0
- pytest_obligation-0.8.0/docs/upstream-templates/eli5-pr.md +26 -0
- pytest_obligation-0.8.0/docs/upstream-templates/issue.md +40 -0
- pytest_obligation-0.8.0/docs/upstream-templates/map-the-handoffs.md +35 -0
- pytest_obligation-0.8.0/docs/upstream-templates/pr.md +59 -0
- pytest_obligation-0.8.0/docs/what-a-green-result-means.md +286 -0
- pytest_obligation-0.8.0/docs/work-in-progress/tickets/canonical-obligation-api-test-plan.md +16 -0
- pytest_obligation-0.8.0/examples/adopter/README.md +65 -0
- pytest_obligation-0.8.0/examples/adopter/__init__.py +1 -0
- pytest_obligation-0.8.0/examples/adopter/adopter_app/__init__.py +0 -0
- pytest_obligation-0.8.0/examples/adopter/adopter_app/catalog.py +119 -0
- pytest_obligation-0.8.0/examples/adopter/adopter_app/summaries.py +26 -0
- pytest_obligation-0.8.0/examples/adopter/pyproject.toml +25 -0
- pytest_obligation-0.8.0/examples/adopter/test_bindings.py +380 -0
- pytest_obligation-0.8.0/examples/adopter/tests/__init__.py +0 -0
- pytest_obligation-0.8.0/examples/adopter/tests/conftest.py +4 -0
- pytest_obligation-0.8.0/examples/adopter/tests/test_catalog.py +12 -0
- pytest_obligation-0.8.0/examples/adopter/tests/test_catalog_failures.py +70 -0
- pytest_obligation-0.8.0/examples/adopter/tests/test_summaries_exemption.py +22 -0
- pytest_obligation-0.8.0/examples/adopter/uv.lock +350 -0
- pytest_obligation-0.8.0/plugins/pytest-obligation/.claude-plugin/plugin.json +10 -0
- pytest_obligation-0.8.0/plugins/pytest-obligation/.codex-plugin/plugin.json +30 -0
- pytest_obligation-0.8.0/plugins/pytest-obligation/README.md +14 -0
- pytest_obligation-0.8.0/plugins/pytest-obligation/assets/icon.svg +6 -0
- pytest_obligation-0.8.0/plugins/pytest-obligation/skills/probe-upstream-due-work/SKILL.md +18 -0
- pytest_obligation-0.8.0/plugins/pytest-obligation/skills/prove-due-work/SKILL.md +28 -0
- pytest_obligation-0.8.0/plugins/pytest-obligation/skills/prove-due-work/references/integrations.md +16 -0
- pytest_obligation-0.8.0/pyproject.toml +120 -0
- pytest_obligation-0.8.0/release-please-config.json +26 -0
- pytest_obligation-0.8.0/scripts/check_adoption_report.py +63 -0
- pytest_obligation-0.8.0/scripts/check_core_is_framework_free.py +35 -0
- pytest_obligation-0.8.0/shims/due_work_harness/__init__.py +70 -0
- pytest_obligation-0.8.0/shims/due_work_harness/__init__.pyi +2 -0
- pytest_obligation-0.8.0/shims/due_work_harness/binding.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coherence.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/contract.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/cli.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/config.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/declarations.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/handoffs.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/project.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/report.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/scan.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/coverage/sites.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/crash_histories.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/evidence/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/evidence/observation.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/evidence/observation_report.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/exemptions.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/faults.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/gap_probes.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/helpers.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/host.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/aiokafka.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/celery.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/celery_worker.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/clocks.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/dbos.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/admission.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/callbacks.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/commands.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/commits.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/lifecycle_references.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/lifecycle_states.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/lock_order.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/receivers.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/references.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/selection.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django/writes.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/django_tasks.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/mongodb.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/postgres_plans.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/prefect.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/procrastinate.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/redis.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/rq.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/integrations/task_queues.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/adapters/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/adapters/integration.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/bindings.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/engine/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/engine/catalog.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/engine/causality.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/engine/provider.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/engine/runner.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/exploration/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/exploration/hypothesis.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/model.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/ports.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/testing/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/interleavings/testing/reference.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/models.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/process_histories.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/automatic_recovery.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/bounded_ownership.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/catalog.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/coverage.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/crash_ambiguity.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/durable_retention.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/eventual_convergence.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/fact_derived_obligations.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/gated_execution.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/harmless_replay.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/indivisible_admission.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/job_retry_limits.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/profiles/reporting.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/py.typed +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/pytest_plugin.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/recording.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/references/__init__.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/references/eligibility.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/references/in_memory.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/references/in_memory_handoffs.pyi +1 -0
- pytest_obligation-0.8.0/shims/due_work_harness/worker_death.pyi +1 -0
- pytest_obligation-0.8.0/src/pytest_obligation/__init__.py +245 -0
- pytest_obligation-0.8.0/src/pytest_obligation/binding.py +884 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coherence.py +118 -0
- pytest_obligation-0.8.0/src/pytest_obligation/contract.py +1850 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/__init__.py +55 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/cli.py +145 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/config.py +103 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/declarations.py +262 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/handoffs.py +467 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/project.py +261 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/report.py +45 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/scan.py +136 -0
- pytest_obligation-0.8.0/src/pytest_obligation/coverage/sites.py +152 -0
- pytest_obligation-0.8.0/src/pytest_obligation/crash_histories.py +948 -0
- pytest_obligation-0.8.0/src/pytest_obligation/evidence/__init__.py +13 -0
- pytest_obligation-0.8.0/src/pytest_obligation/evidence/observation.py +130 -0
- pytest_obligation-0.8.0/src/pytest_obligation/evidence/observation_report.py +76 -0
- pytest_obligation-0.8.0/src/pytest_obligation/exemptions.py +87 -0
- pytest_obligation-0.8.0/src/pytest_obligation/faults.py +41 -0
- pytest_obligation-0.8.0/src/pytest_obligation/gap_probes.py +314 -0
- pytest_obligation-0.8.0/src/pytest_obligation/helpers.py +204 -0
- pytest_obligation-0.8.0/src/pytest_obligation/host.py +338 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/__init__.py +0 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/aiokafka.py +104 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/celery.py +218 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/celery_worker.py +336 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/clocks.py +21 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/dbos.py +94 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/__init__.py +142 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/admission.py +35 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/callbacks.py +56 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/commands.py +74 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/commits.py +102 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/lifecycle_references.py +565 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/lifecycle_states.py +630 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/lock_order.py +428 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/receivers.py +47 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/references.py +35 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/selection.py +242 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django/writes.py +186 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/django_tasks.py +257 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/mongodb.py +179 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/postgres_plans.py +343 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/prefect.py +86 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/procrastinate.py +308 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/redis.py +217 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/rq.py +455 -0
- pytest_obligation-0.8.0/src/pytest_obligation/integrations/task_queues.py +124 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/__init__.py +42 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/adapters/__init__.py +1 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/adapters/integration.py +99 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/bindings.py +393 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/engine/__init__.py +1 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/engine/catalog.py +292 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/engine/causality.py +33 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/engine/provider.py +99 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/engine/runner.py +326 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/exploration/__init__.py +1 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/exploration/hypothesis.py +99 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/model.py +152 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/ports.py +34 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/testing/__init__.py +1 -0
- pytest_obligation-0.8.0/src/pytest_obligation/interleavings/testing/reference.py +102 -0
- pytest_obligation-0.8.0/src/pytest_obligation/models.py +90 -0
- pytest_obligation-0.8.0/src/pytest_obligation/process_histories.py +207 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/__init__.py +6 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/automatic_recovery.py +1414 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/bounded_ownership.py +680 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/catalog.py +51 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/coverage.py +77 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/crash_ambiguity.py +291 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/durable_retention.py +119 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/eventual_convergence.py +308 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/fact_derived_obligations.py +389 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/gated_execution.py +389 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/harmless_replay.py +154 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/indivisible_admission.py +122 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/job_retry_limits.py +219 -0
- pytest_obligation-0.8.0/src/pytest_obligation/profiles/reporting.py +162 -0
- pytest_obligation-0.8.0/src/pytest_obligation/py.typed +0 -0
- pytest_obligation-0.8.0/src/pytest_obligation/pytest_plugin.py +245 -0
- pytest_obligation-0.8.0/src/pytest_obligation/recording.py +130 -0
- pytest_obligation-0.8.0/src/pytest_obligation/references/__init__.py +19 -0
- pytest_obligation-0.8.0/src/pytest_obligation/references/eligibility.py +222 -0
- pytest_obligation-0.8.0/src/pytest_obligation/references/in_memory.py +632 -0
- pytest_obligation-0.8.0/src/pytest_obligation/references/in_memory_handoffs.py +527 -0
- pytest_obligation-0.8.0/src/pytest_obligation/worker_death.py +65 -0
- pytest_obligation-0.8.0/test/action.yml +63 -0
- pytest_obligation-0.8.0/tests/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/aiokafka/test_offsets.py +202 -0
- pytest_obligation-0.8.0/tests/celery_worker/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/celery_worker/app.py +33 -0
- pytest_obligation-0.8.0/tests/celery_worker/test_catalog.py +48 -0
- pytest_obligation-0.8.0/tests/celery_worker/test_celery_task_bindings.py +61 -0
- pytest_obligation-0.8.0/tests/celery_worker/test_celery_worker_contract.py +118 -0
- pytest_obligation-0.8.0/tests/celery_worker/test_celery_worker_platform.py +16 -0
- pytest_obligation-0.8.0/tests/core/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/core/automatic_recovery/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/core/automatic_recovery/test_plan_verdict.py +498 -0
- pytest_obligation-0.8.0/tests/core/automatic_recovery/test_sweep.py +1193 -0
- pytest_obligation-0.8.0/tests/core/conftest.py +23 -0
- pytest_obligation-0.8.0/tests/core/contract/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/core/contract/child_suites.py +64 -0
- pytest_obligation-0.8.0/tests/core/contract/conftest.py +25 -0
- pytest_obligation-0.8.0/tests/core/contract/declarations.py +122 -0
- pytest_obligation-0.8.0/tests/core/contract/reference_contract_cases.py +57 -0
- pytest_obligation-0.8.0/tests/core/contract/test_binding.py +53 -0
- pytest_obligation-0.8.0/tests/core/contract/test_contract.py +1525 -0
- pytest_obligation-0.8.0/tests/core/contract/test_declared_findings_session.py +39 -0
- pytest_obligation-0.8.0/tests/core/contract/test_gap_probes.py +95 -0
- pytest_obligation-0.8.0/tests/core/contract/test_generated_cases_are_marked.py +22 -0
- pytest_obligation-0.8.0/tests/core/contract/test_inversion_tripwire.py +433 -0
- pytest_obligation-0.8.0/tests/core/contract/test_record_findings_session.py +35 -0
- pytest_obligation-0.8.0/tests/core/contract/test_safety_contract.py +188 -0
- pytest_obligation-0.8.0/tests/core/coverage/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/core/coverage/builders.py +32 -0
- pytest_obligation-0.8.0/tests/core/coverage/conftest.py +5 -0
- pytest_obligation-0.8.0/tests/core/coverage/test_cli.py +105 -0
- pytest_obligation-0.8.0/tests/core/coverage/test_due_work_summary.py +77 -0
- pytest_obligation-0.8.0/tests/core/coverage/test_due_work_verify.py +147 -0
- pytest_obligation-0.8.0/tests/core/coverage/test_exempt_due_work_suite.py +91 -0
- pytest_obligation-0.8.0/tests/core/coverage/test_scan_finds_sites.py +471 -0
- pytest_obligation-0.8.0/tests/core/coverage/test_scan_requires_one_disposition.py +354 -0
- pytest_obligation-0.8.0/tests/core/coverage/test_scan_sites_in_transaction.py +151 -0
- pytest_obligation-0.8.0/tests/core/evidence/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/core/evidence/test_observation.py +62 -0
- pytest_obligation-0.8.0/tests/core/profiles/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/core/profiles/test_bounded_ownership.py +565 -0
- pytest_obligation-0.8.0/tests/core/profiles/test_crash_ambiguity.py +268 -0
- pytest_obligation-0.8.0/tests/core/profiles/test_durable_retention.py +61 -0
- pytest_obligation-0.8.0/tests/core/profiles/test_eventual_convergence.py +164 -0
- pytest_obligation-0.8.0/tests/core/profiles/test_execution_eligibility.py +272 -0
- pytest_obligation-0.8.0/tests/core/profiles/test_fact_derived_obligations.py +390 -0
- pytest_obligation-0.8.0/tests/core/safety/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/core/safety/test_bounded_retry.py +170 -0
- pytest_obligation-0.8.0/tests/core/safety/test_replay_safe_execution.py +109 -0
- pytest_obligation-0.8.0/tests/core/test_adoption_report_guard.py +43 -0
- pytest_obligation-0.8.0/tests/core/test_compatibility.py +99 -0
- pytest_obligation-0.8.0/tests/core/test_crash_histories.py +267 -0
- pytest_obligation-0.8.0/tests/core/test_external_call_seams.py +333 -0
- pytest_obligation-0.8.0/tests/core/test_indivisible_admission.py +119 -0
- pytest_obligation-0.8.0/tests/core/test_interleaving_suites.py +90 -0
- pytest_obligation-0.8.0/tests/core/test_interleavings.py +537 -0
- pytest_obligation-0.8.0/tests/core/test_models.py +28 -0
- pytest_obligation-0.8.0/tests/core/test_package_identity.py +25 -0
- pytest_obligation-0.8.0/tests/core/test_process_histories.py +139 -0
- pytest_obligation-0.8.0/tests/core/test_profile_catalog.py +125 -0
- pytest_obligation-0.8.0/tests/core/test_profile_coverage.py +120 -0
- pytest_obligation-0.8.0/tests/core/test_recording.py +65 -0
- pytest_obligation-0.8.0/tests/django/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/django/automatic_recovery/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/django/automatic_recovery/test_selection_inspector.py +483 -0
- pytest_obligation-0.8.0/tests/django/automatic_recovery/test_terminal_states.py +536 -0
- pytest_obligation-0.8.0/tests/django/conftest.py +30 -0
- pytest_obligation-0.8.0/tests/django/contract/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/django/contract/test_contract_suites.py +147 -0
- pytest_obligation-0.8.0/tests/django/settings.py +18 -0
- pytest_obligation-0.8.0/tests/django/test_admission.py +63 -0
- pytest_obligation-0.8.0/tests/django/test_claim_race.py +156 -0
- pytest_obligation-0.8.0/tests/django/test_commit_observer.py +241 -0
- pytest_obligation-0.8.0/tests/django/test_crash_histories.py +255 -0
- pytest_obligation-0.8.0/tests/django/test_lock_order.py +355 -0
- pytest_obligation-0.8.0/tests/django/test_management_command.py +76 -0
- pytest_obligation-0.8.0/tests/django/test_observed_selection.py +71 -0
- pytest_obligation-0.8.0/tests/django/test_postgresql_required.py +15 -0
- pytest_obligation-0.8.0/tests/django/test_publication_breaker.py +66 -0
- pytest_obligation-0.8.0/tests/mongodb/test_mongodb.py +181 -0
- pytest_obligation-0.8.0/tests/prefect/test_catalog.py +42 -0
- pytest_obligation-0.8.0/tests/prefect/test_prefect.py +153 -0
- pytest_obligation-0.8.0/tests/redis_databases.py +47 -0
- pytest_obligation-0.8.0/tests/rq/__init__.py +0 -0
- pytest_obligation-0.8.0/tests/rq/conftest.py +44 -0
- pytest_obligation-0.8.0/tests/rq/connection.py +6 -0
- pytest_obligation-0.8.0/tests/rq/jobs.py +42 -0
- pytest_obligation-0.8.0/tests/rq/test_catalog.py +28 -0
- pytest_obligation-0.8.0/tests/rq/test_redis_commits.py +104 -0
- pytest_obligation-0.8.0/tests/rq/test_rq_worker_contract.py +172 -0
- pytest_obligation-0.8.0/tests_support/catalog_executor.py +26 -0
- pytest_obligation-0.8.0/tests_support/mongodb_app.py +18 -0
- pytest_obligation-0.8.0/tests_support/prefect_app.py +17 -0
- pytest_obligation-0.8.0/tests_support/sample_production/__init__.py +11 -0
- pytest_obligation-0.8.0/tests_support/sample_production/admission.py +64 -0
- pytest_obligation-0.8.0/tests_support/sample_production/cache.py +11 -0
- pytest_obligation-0.8.0/tests_support/sample_production/feed.py +25 -0
- pytest_obligation-0.8.0/tests_support/sample_production/tasks.py +9 -0
- pytest_obligation-0.8.0/uv.lock +4381 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pytest-obligation",
|
|
3
|
+
"interface": {
|
|
4
|
+
"displayName": "pytest-obligation"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "pytest-obligation",
|
|
9
|
+
"source": {
|
|
10
|
+
"source": "local",
|
|
11
|
+
"path": "../../plugins/pytest-obligation"
|
|
12
|
+
},
|
|
13
|
+
"policy": {
|
|
14
|
+
"installation": "AVAILABLE",
|
|
15
|
+
"authentication": "ON_INSTALL"
|
|
16
|
+
},
|
|
17
|
+
"category": "Developer Tools"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: find-upstream-weaknesses
|
|
3
|
+
description: Use to probe a real open-source project (an application first, or a framework) with due-work-harness, find background work that can be lost, repeated or misrecorded, and disclose it upstream as issues plus a PR that carries the harness contract. Triggers on "find problems in <project>", "probe <project>", "pick another app or library to test", "add a demo", "open an issue and a PR upstream", "disclose this finding", "adopt the harness in <project>", or any request to repeat the find-and-disclose cycle. Not for adopting the harness in the user's own repository (see ADOPTING.md).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Find weaknesses in a real project, then disclose them
|
|
7
|
+
|
|
8
|
+
The full method is [docs/upstream-playbook.md](../../../docs/upstream-playbook.md); read the step you are
|
|
9
|
+
on when you reach it. The copy-and-fill templates are in
|
|
10
|
+
[docs/upstream-templates/](../../../docs/upstream-templates/). This file is the runbook: what to do, in
|
|
11
|
+
order, and the rules that are not negotiable.
|
|
12
|
+
|
|
13
|
+
The installable plugin's [probe-upstream-due-work](../../../plugins/pytest-obligation/skills/probe-upstream-due-work/SKILL.md)
|
|
14
|
+
skill is the portable route for people working outside this repository. This repository-local skill
|
|
15
|
+
retains the maintainer-specific release and disclosure workflow below; neither skill replaces
|
|
16
|
+
gigaverse-backend's own due-work runtime and conformance instructions.
|
|
17
|
+
|
|
18
|
+
## Rules that do not bend
|
|
19
|
+
|
|
20
|
+
- **Expose, don't fix.** Deliver a test that fails for a stated reason and a contract that finds it. Never
|
|
21
|
+
propose or write the fix in an issue or PR.
|
|
22
|
+
- **Every adoption PR MUST carry `ObligationContract` and a collected
|
|
23
|
+
`@due_work_contract_suite(CONTRACT)` class.** Follow the
|
|
24
|
+
[required adoption shape](../../../ADOPTING.md#required-adoption-shape): declare
|
|
25
|
+
all A–J profiles for the current source (or the installed release's matching catalog), put histories in the contract, execute the generated
|
|
26
|
+
cases, and link the declaration, suite and `--due-work-summary` report. Ordinary
|
|
27
|
+
tests and direct crash-helper calls are supporting evidence, never a substitute.
|
|
28
|
+
The decorator generates pytest cases, not Python files; use `--junitxml` when a
|
|
29
|
+
generated result file is needed and retain it as a CI artifact.
|
|
30
|
+
- **Claim only what you ran.** A sentence in a report is either measured or marked as code reading. Agents'
|
|
31
|
+
findings (including your own from an earlier turn) are hypotheses until step 5.
|
|
32
|
+
- **Posting is outward-facing.** Issues, PRs, comments and releases go out under the user's account.
|
|
33
|
+
Do it only with the user's authorization for this project in this session; if unsure, ask once, with
|
|
34
|
+
the draft. Follow the target's own contribution rules over these: some ask for a human-written PR
|
|
35
|
+
description or forbid AI-authored contributions, and then the user decides.
|
|
36
|
+
- **Every comment you post opens with the self-identification line**
|
|
37
|
+
`> I'm {model}, AI can make mistakes. {a fresh 3-5 word joke}` (no joke on incident threads).
|
|
38
|
+
- **Every probe strengthens the harness.** A gap the probe hit becomes a general capability in the harness,
|
|
39
|
+
released before the upstream PRs pin it.
|
|
40
|
+
|
|
41
|
+
## The cycle
|
|
42
|
+
|
|
43
|
+
Work in a scratch directory, not in a checkout that matters. Track the steps as tasks.
|
|
44
|
+
|
|
45
|
+
1. **Choose.** Prefer an application whose failure a customer or money feels; take a framework when apps
|
|
46
|
+
depend on it and nobody has probed it. Count handoffs with `uv run due-work-harness check` (or the
|
|
47
|
+
`scan` API), require that the project already recovers some of them, that its own tests run on this
|
|
48
|
+
machine (Python floor, database, system packages), and that outside contributions are accepted. Read its
|
|
49
|
+
CONTRIBUTING, templates and AI policy now. Search its tracker for prior reports. If two targets fit,
|
|
50
|
+
pick one and say why; ask only if the choice needs the user's judgement (policy, risk to a relationship).
|
|
51
|
+
2. **Set up.** Fork and clone; put Postgres/Redis in containers the way their CI does; **run their
|
|
52
|
+
unmodified suite first** so a red baseline is known before it is blamed on you.
|
|
53
|
+
3. **Map.** `due-work-harness sites` and `due-work-harness in-transaction` list the candidates first
|
|
54
|
+
(a throwaway `[tool.due-work-harness]` table in the fork is enough). Then run the prompt in
|
|
55
|
+
[map-the-handoffs.md](../../../docs/upstream-templates/map-the-handoffs.md) through an agent (fill the
|
|
56
|
+
placeholders from the scan). Keep the result as ranked hypotheses.
|
|
57
|
+
4. **Adopt in the fork.** A branch such as `test/<what-breaks>`, a `tests/due_work/` directory named the way
|
|
58
|
+
their runner collects it, a host (`django_host`, `redis_host`, or a new one), bindings that call the
|
|
59
|
+
real code (a string command that reaches no project code is refused: pass the command class), a
|
|
60
|
+
disposition for every profile with a reason, and one history per user-facing action whose observation
|
|
61
|
+
includes what users and external systems saw (not something the project's own cleanup deletes). A
|
|
62
|
+
management-command recovery is `management_command(name, after=...)`. Use `--due-work-summary` to read
|
|
63
|
+
the run.
|
|
64
|
+
5. **Confirm each finding.** Reproduce the smallest version with a script that uses the project's own API
|
|
65
|
+
and no harness. Test the neighbouring claim before you make it, check server or library versions, and
|
|
66
|
+
search the tracker again. Drop what you cannot reproduce or what the project defends.
|
|
67
|
+
6. **Declare and mark.** `Findings(delivered, {label: outcome})` on each history (generate it with
|
|
68
|
+
`--due-work-record-findings`, read it, then paste), `handoff_gaps` with the
|
|
69
|
+
issue link, an `expectedFailure` plain test that fails for the stated reason (remove the marker once and
|
|
70
|
+
read the failure), and the *this is where the magic happens* comment above each decorated class.
|
|
71
|
+
7. **Improve the harness.** If the probe needed something the harness lacks, or you wrote a helper twice,
|
|
72
|
+
build it in the harness on its own branch (test it, in CI, on the stack it failed on), then run a DRY
|
|
73
|
+
round from demo to application to integration to core. Release it (see RELEASING.md) and raise the pins.
|
|
74
|
+
8. **Disclose.** One issue per finding (template `issue.md`), then one PR (`pr.md`) that links the magic
|
|
75
|
+
point at the pushed commit and shows the `--due-work-summary` capture, then the plain-language comments
|
|
76
|
+
with ASCII art (`eli5-issue.md`, `eli5-pr.md`) on each. Fill their template and policy first.
|
|
77
|
+
9. **Keep it green.** Watch CI. The usual causes are in the playbook's table: lockfile, an environment that
|
|
78
|
+
does not install the harness, a version-dependent table, a bot commit (merge, never rebase), workflows
|
|
79
|
+
waiting for approval, a `main` that is already red (prove it, say so, leave their test alone).
|
|
80
|
+
10. **Record.** Update the demo and README tables if the project is a shipped demo, and keep the scratch
|
|
81
|
+
material until the threads close.
|
|
82
|
+
|
|
83
|
+
## Before you say it is done
|
|
84
|
+
|
|
85
|
+
- [ ] their own suite ran green here first; the baseline is known
|
|
86
|
+
- [ ] each finding has an independent reproduction, and only measured claims are in the text
|
|
87
|
+
- [ ] `ObligationContract`, its decorated collected class, `Findings`, gaps, plain test, and the magic comment are in the pushed commit
|
|
88
|
+
- [ ] generated node IDs were collected and executed; the PR links the declaration, class, and summary/artifact, with gaps counted separately
|
|
89
|
+
- [ ] the harness gap (if any) is fixed, tested in CI on that stack, released, and the pins raised
|
|
90
|
+
- [ ] issues, PR and plain-language comments are posted with the self-identification line, no fix offered
|
|
91
|
+
- [ ] CI is green, or its red is explained with evidence
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pytest-obligation",
|
|
3
|
+
"description": "Generate ObligationContract tests and investigate lost or duplicate work in Python projects.",
|
|
4
|
+
"owner": {"name": "Gigaverse"},
|
|
5
|
+
"plugins": [
|
|
6
|
+
{
|
|
7
|
+
"name": "pytest-obligation",
|
|
8
|
+
"source": "./plugins/pytest-obligation",
|
|
9
|
+
"description": "Generate ObligationContract tests in your app or verify due-work bugs in open-source projects."
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* text=auto eol=lf
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
# Every job installs from uv.lock with --locked: a lock that no longer matches
|
|
16
|
+
# pyproject.toml fails CI instead of silently resolving something new.
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
lint:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
- uses: astral-sh/setup-uv@v7
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
- run: uv sync --locked --only-group lint
|
|
27
|
+
- run: uv run --no-sync ruff check
|
|
28
|
+
- run: uv run --no-sync ruff format --check
|
|
29
|
+
- name: actionlint
|
|
30
|
+
run: docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.7 -color
|
|
31
|
+
|
|
32
|
+
typecheck:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v7
|
|
36
|
+
- uses: astral-sh/setup-uv@v7
|
|
37
|
+
with:
|
|
38
|
+
enable-cache: true
|
|
39
|
+
# The demos type-check against the upstream code they run (Saleor's in its own job).
|
|
40
|
+
- run: uv run --no-project python demos/fetch_upstream.py procrastinate dbos-demo-apps
|
|
41
|
+
- run: uv sync --locked --all-extras --group demos
|
|
42
|
+
- run: uv pip install -e demos/.upstream/procrastinate
|
|
43
|
+
- run: uv run --no-sync pyrefly check
|
|
44
|
+
|
|
45
|
+
core:
|
|
46
|
+
# The core must work with no framework installed: the package, pytest and pytest-xdist only.
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
strategy:
|
|
49
|
+
fail-fast: false
|
|
50
|
+
matrix:
|
|
51
|
+
python: ["3.11", "3.12", "3.13", "3.14"]
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v7
|
|
54
|
+
- uses: astral-sh/setup-uv@v7
|
|
55
|
+
with:
|
|
56
|
+
enable-cache: true
|
|
57
|
+
python-version: ${{ matrix.python }}
|
|
58
|
+
# xdist is no framework: it lets the verify option's self-tests split a session across workers.
|
|
59
|
+
- run: uv sync --python ${{ matrix.python }} --locked --no-default-groups --group xdist
|
|
60
|
+
- run: uv run --python ${{ matrix.python }} --no-sync python scripts/check_core_is_framework_free.py
|
|
61
|
+
- run: uv run --python ${{ matrix.python }} --no-sync python -m compileall -q src/pytest_obligation
|
|
62
|
+
- run: uv run --python ${{ matrix.python }} --no-sync pytest tests/core
|
|
63
|
+
|
|
64
|
+
minimum-models:
|
|
65
|
+
# Exercise nested generic declarations on the supported minimum, without Hypothesis or frameworks.
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v7
|
|
69
|
+
- uses: astral-sh/setup-uv@v7
|
|
70
|
+
with:
|
|
71
|
+
enable-cache: true
|
|
72
|
+
python-version: "3.12"
|
|
73
|
+
- run: uv sync --locked --no-default-groups --group xdist
|
|
74
|
+
# This lane deliberately checks the declared lower bound, not uv.lock's current release.
|
|
75
|
+
- run: uv pip install 'pydantic==2.11.0'
|
|
76
|
+
- run: uv run --no-sync python scripts/check_core_is_framework_free.py
|
|
77
|
+
- run: uv run --no-sync pytest tests/core
|
|
78
|
+
- run: uv run --no-sync pytest examples/adopter/tests --due-work-require-assessed
|
|
79
|
+
|
|
80
|
+
exploration:
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
steps:
|
|
83
|
+
- uses: actions/checkout@v7
|
|
84
|
+
- uses: astral-sh/setup-uv@v7
|
|
85
|
+
with:
|
|
86
|
+
enable-cache: true
|
|
87
|
+
- run: uv sync --locked --no-default-groups --extra exploration --group xdist
|
|
88
|
+
- run: uv run --no-sync pytest tests/core
|
|
89
|
+
|
|
90
|
+
django:
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
services:
|
|
93
|
+
postgres:
|
|
94
|
+
image: postgres:16
|
|
95
|
+
env:
|
|
96
|
+
POSTGRES_PASSWORD: postgres
|
|
97
|
+
ports: ["5432:5432"]
|
|
98
|
+
options: >-
|
|
99
|
+
--health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 10
|
|
100
|
+
env:
|
|
101
|
+
PGHOST: localhost
|
|
102
|
+
PGUSER: postgres
|
|
103
|
+
PGPASSWORD: postgres
|
|
104
|
+
PGPORT: "5432"
|
|
105
|
+
PGDATABASE: pytest_obligation
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/checkout@v7
|
|
108
|
+
- uses: astral-sh/setup-uv@v7
|
|
109
|
+
with:
|
|
110
|
+
enable-cache: true
|
|
111
|
+
# pytest-django gives each xdist worker its own test database.
|
|
112
|
+
- run: uv sync --locked --no-default-groups --extra django --extra celery --group xdist
|
|
113
|
+
- run: uv run --no-sync pytest tests/django --ds=tests.django.settings -n 4
|
|
114
|
+
|
|
115
|
+
# The same suite on psycopg2, which Django uses when psycopg 3 is not installed: pretix and many older
|
|
116
|
+
# projects run on it, and the host's autocommit-write probe once assumed psycopg 3.
|
|
117
|
+
django-psycopg2:
|
|
118
|
+
runs-on: ubuntu-latest
|
|
119
|
+
services:
|
|
120
|
+
postgres:
|
|
121
|
+
image: postgres:16
|
|
122
|
+
env:
|
|
123
|
+
POSTGRES_PASSWORD: postgres
|
|
124
|
+
ports: ["5432:5432"]
|
|
125
|
+
options: >-
|
|
126
|
+
--health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 10
|
|
127
|
+
env:
|
|
128
|
+
PGHOST: localhost
|
|
129
|
+
PGUSER: postgres
|
|
130
|
+
PGPASSWORD: postgres
|
|
131
|
+
PGPORT: "5432"
|
|
132
|
+
PGDATABASE: due_work_harness_psycopg2
|
|
133
|
+
steps:
|
|
134
|
+
- uses: actions/checkout@v7
|
|
135
|
+
- uses: astral-sh/setup-uv@v7
|
|
136
|
+
with:
|
|
137
|
+
enable-cache: true
|
|
138
|
+
- run: uv sync --locked --no-default-groups --extra django --extra celery --group xdist
|
|
139
|
+
- run: uv pip uninstall psycopg psycopg-binary && uv pip install psycopg2-binary
|
|
140
|
+
- run: uv run --no-sync pytest tests/django --ds=tests.django.settings -n 4
|
|
141
|
+
|
|
142
|
+
# Redis-backed queues, and Celery's real worker as a child process on a Redis broker.
|
|
143
|
+
redis:
|
|
144
|
+
runs-on: ubuntu-latest
|
|
145
|
+
services:
|
|
146
|
+
redis:
|
|
147
|
+
image: redis:7
|
|
148
|
+
ports: ["6379:6379"]
|
|
149
|
+
options: >-
|
|
150
|
+
--health-cmd "redis-cli ping" --health-interval 5s --health-timeout 5s --health-retries 10
|
|
151
|
+
env:
|
|
152
|
+
REDIS_URL: redis://localhost:6379/14
|
|
153
|
+
steps:
|
|
154
|
+
- uses: actions/checkout@v7
|
|
155
|
+
- uses: astral-sh/setup-uv@v7
|
|
156
|
+
with:
|
|
157
|
+
enable-cache: true
|
|
158
|
+
- run: uv sync --locked --no-default-groups --extra rq --extra celery --extra exploration --group xdist
|
|
159
|
+
# Each xdist worker takes its own four Redis databases (tests/redis_databases.py), so cases that
|
|
160
|
+
# empty theirs cannot wipe another worker's; four workers use all 16 of the service's databases.
|
|
161
|
+
- run: uv run --no-sync pytest tests/rq tests/celery_worker -p no:django -rxX -n 4 --durations=10 --due-work-explore=smoke --due-work-require-assessed --due-work-profile-report=profiles-redis.json
|
|
162
|
+
|
|
163
|
+
- name: Require the promised adopters and executed capabilities
|
|
164
|
+
run: |
|
|
165
|
+
uv run --no-sync python scripts/check_adoption_report.py profiles-redis.json --contract "catalog on RQ" --contract "catalog through Celery" --verified E G H I
|
|
166
|
+
uv run --no-sync python scripts/check_adoption_report.py profiles-redis.json --contract "rq reference jobs" --verified G J --exercised H B
|
|
167
|
+
- name: Keep executed profile evidence
|
|
168
|
+
if: always()
|
|
169
|
+
uses: actions/upload-artifact@v4
|
|
170
|
+
with:
|
|
171
|
+
name: profiles-redis
|
|
172
|
+
path: |
|
|
173
|
+
profiles-*.json
|
|
174
|
+
demos/**/profiles-*.json
|
|
175
|
+
if-no-files-found: error
|
|
176
|
+
|
|
177
|
+
mongodb:
|
|
178
|
+
runs-on: ubuntu-latest
|
|
179
|
+
env:
|
|
180
|
+
MONGODB_URI: mongodb://localhost:27017/?directConnection=true
|
|
181
|
+
steps:
|
|
182
|
+
- uses: actions/checkout@v7
|
|
183
|
+
- uses: astral-sh/setup-uv@v7
|
|
184
|
+
with:
|
|
185
|
+
enable-cache: true
|
|
186
|
+
- run: uv sync --locked --no-default-groups --extra mongodb
|
|
187
|
+
# Transactions need a replica set on this disposable runner.
|
|
188
|
+
- run: |
|
|
189
|
+
docker run -d --name due-work-mongodb -p 27017:27017 mongo:7 --replSet dwh --bind_ip_all
|
|
190
|
+
for attempt in $(seq 1 30); do
|
|
191
|
+
if docker exec due-work-mongodb mongosh --quiet --eval 'rs.initiate({_id:"dwh",members:[{_id:0,host:"localhost:27017"}]})'; then break; fi
|
|
192
|
+
if [ "$attempt" -eq 30 ]; then exit 1; fi
|
|
193
|
+
sleep 1
|
|
194
|
+
done
|
|
195
|
+
- run: uv run --no-sync pytest tests/mongodb -q
|
|
196
|
+
|
|
197
|
+
aiokafka:
|
|
198
|
+
runs-on: ubuntu-latest
|
|
199
|
+
services:
|
|
200
|
+
kafka:
|
|
201
|
+
image: apache/kafka:3.9.1
|
|
202
|
+
ports: ["9092:9092"]
|
|
203
|
+
env:
|
|
204
|
+
KAFKA_NODE_ID: "1"
|
|
205
|
+
KAFKA_PROCESS_ROLES: broker,controller
|
|
206
|
+
KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
|
|
207
|
+
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
|
|
208
|
+
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
|
|
209
|
+
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9093
|
|
210
|
+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
|
|
211
|
+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1"
|
|
212
|
+
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: "0"
|
|
213
|
+
options: >-
|
|
214
|
+
--health-cmd "/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list"
|
|
215
|
+
--health-interval 5s --health-timeout 10s --health-retries 12
|
|
216
|
+
env:
|
|
217
|
+
KAFKA_BOOTSTRAP_SERVERS: localhost:9092
|
|
218
|
+
steps:
|
|
219
|
+
- uses: actions/checkout@v7
|
|
220
|
+
- uses: astral-sh/setup-uv@v7
|
|
221
|
+
with:
|
|
222
|
+
enable-cache: true
|
|
223
|
+
- run: uv sync --locked --no-default-groups --extra aiokafka
|
|
224
|
+
- run: uv run --no-sync pytest tests/aiokafka -q
|
|
225
|
+
|
|
226
|
+
prefect:
|
|
227
|
+
runs-on: ubuntu-latest
|
|
228
|
+
steps:
|
|
229
|
+
- uses: actions/checkout@v7
|
|
230
|
+
- uses: astral-sh/setup-uv@v7
|
|
231
|
+
with:
|
|
232
|
+
enable-cache: true
|
|
233
|
+
- run: uv sync --locked --no-default-groups --extra prefect --extra exploration
|
|
234
|
+
- run: uv run --no-sync pytest tests/prefect -q --due-work-explore=smoke --due-work-require-assessed --due-work-profile-report=profiles-prefect.json
|
|
235
|
+
|
|
236
|
+
- name: Require the promised adopters and executed capabilities
|
|
237
|
+
run: |
|
|
238
|
+
uv run --no-sync python scripts/check_adoption_report.py profiles-prefect.json --contract "catalog through Prefect engine" --verified E G H I
|
|
239
|
+
- name: Keep executed profile evidence
|
|
240
|
+
if: always()
|
|
241
|
+
uses: actions/upload-artifact@v4
|
|
242
|
+
with:
|
|
243
|
+
name: profiles-prefect
|
|
244
|
+
path: |
|
|
245
|
+
profiles-*.json
|
|
246
|
+
demos/**/profiles-*.json
|
|
247
|
+
if-no-files-found: error
|
|
248
|
+
|
|
249
|
+
demos:
|
|
250
|
+
runs-on: ubuntu-latest
|
|
251
|
+
services:
|
|
252
|
+
postgres:
|
|
253
|
+
image: postgres:16
|
|
254
|
+
env:
|
|
255
|
+
POSTGRES_PASSWORD: postgres
|
|
256
|
+
ports: ["5432:5432"]
|
|
257
|
+
options: >-
|
|
258
|
+
--health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 10
|
|
259
|
+
env:
|
|
260
|
+
PGHOST: localhost
|
|
261
|
+
PGUSER: postgres
|
|
262
|
+
PGPASSWORD: postgres
|
|
263
|
+
PGPORT: "5432"
|
|
264
|
+
steps:
|
|
265
|
+
- uses: actions/checkout@v7
|
|
266
|
+
with:
|
|
267
|
+
# The check action builds the harness from this checkout; its version comes from git tags.
|
|
268
|
+
fetch-depth: 0
|
|
269
|
+
- uses: astral-sh/setup-uv@v7
|
|
270
|
+
with:
|
|
271
|
+
enable-cache: true
|
|
272
|
+
# Saleor too: the README's demo GIF shows the coverage scan of its checkout. Wagtail's source is
|
|
273
|
+
# what the Wagtail demo's coverage check reads; the demo itself runs the wheel from PyPI.
|
|
274
|
+
- run: uv run --no-project python demos/fetch_upstream.py procrastinate dbos-demo-apps saleor wagtail
|
|
275
|
+
- run: uv sync --locked --no-default-groups --extra django --group demos
|
|
276
|
+
# procrastinate's demos ship only in its source tree, pinned by fetch_upstream.py.
|
|
277
|
+
- run: uv pip install -e demos/.upstream/procrastinate
|
|
278
|
+
- name: Postgres without durability
|
|
279
|
+
run: demos/throwaway_postgres.sh
|
|
280
|
+
# The durations are the evidence for the next change to this job's speed.
|
|
281
|
+
- name: procrastinate demo_django
|
|
282
|
+
env:
|
|
283
|
+
PGDATABASE: procrastinate_demo
|
|
284
|
+
run: >-
|
|
285
|
+
uv run --no-sync pytest demos/procrastinate_demo_django --ds=demos.procrastinate_demo_django.settings
|
|
286
|
+
-rxX --durations=15 --due-work-require-assessed --due-work-profile-report=profiles-procrastinate.json
|
|
287
|
+
- name: DBOS transactional-outbox
|
|
288
|
+
run: uv run --no-sync pytest demos/dbos_transactional_outbox -p no:django -rxX --durations=15 --due-work-require-assessed --due-work-profile-report=profiles-dbos.json
|
|
289
|
+
# Migrating Wagtail's test database is most of its run, and depends only on the pinned packages
|
|
290
|
+
# (uv.lock) and the demo's settings: restored from the cached dump when the key matches.
|
|
291
|
+
- uses: actions/cache@v4
|
|
292
|
+
with:
|
|
293
|
+
path: demos/.ci-cache/wagtail_tasks.dump
|
|
294
|
+
key: wagtail-test-db-pg16-${{ hashFiles('uv.lock', 'demos/wagtail_tasks/settings.py', 'demos/cached_database.py') }}
|
|
295
|
+
- name: Wagtail's test database, migrated once
|
|
296
|
+
# As pytest-django creates a test database: every app's migrations, syncdb for the rest, no checks.
|
|
297
|
+
run: >-
|
|
298
|
+
uv run --no-sync python demos/cached_database.py test_wagtail_tasks --dump demos/.ci-cache/wagtail_tasks.dump
|
|
299
|
+
-- uv run --no-sync python -m django migrate --settings=demos.wagtail_tasks.settings
|
|
300
|
+
--run-syncdb --skip-checks --no-input --verbosity 0
|
|
301
|
+
# Wagtail 8.0 on django-tasks-db, both from PyPI; the demo's own directory is pytest's rootdir.
|
|
302
|
+
- name: Wagtail on django-tasks-db
|
|
303
|
+
working-directory: demos/wagtail_tasks
|
|
304
|
+
env:
|
|
305
|
+
PGDATABASE: wagtail_tasks
|
|
306
|
+
run: uv run --no-sync --project ../.. pytest --reuse-db --due-work-verify --durations=15 --due-work-require-assessed --due-work-profile-report=profiles-wagtail.json
|
|
307
|
+
- name: the README's demo GIF prints what the harness prints
|
|
308
|
+
run: uv run --no-sync pytest demos/test_readme_gif.py -p no:django --durations=15
|
|
309
|
+
# Each demo is an adopter: its handoffs are checked the way an adopter's CI checks them.
|
|
310
|
+
- name: procrastinate demo_django, every handoff accounted for
|
|
311
|
+
uses: ./check
|
|
312
|
+
with:
|
|
313
|
+
working-directory: demos/procrastinate_demo_django
|
|
314
|
+
package: ${{ github.workspace }}
|
|
315
|
+
- name: DBOS transactional-outbox, every handoff accounted for
|
|
316
|
+
uses: ./check
|
|
317
|
+
with:
|
|
318
|
+
working-directory: demos/dbos_transactional_outbox
|
|
319
|
+
package: ${{ github.workspace }}
|
|
320
|
+
- name: Wagtail on django-tasks-db, every handoff accounted for
|
|
321
|
+
uses: ./check
|
|
322
|
+
with:
|
|
323
|
+
working-directory: demos/wagtail_tasks
|
|
324
|
+
package: ${{ github.workspace }}
|
|
325
|
+
|
|
326
|
+
- name: Require the promised adopters and executed capabilities
|
|
327
|
+
run: |
|
|
328
|
+
uv run --no-sync python scripts/check_adoption_report.py profiles-procrastinate.json --contract "procrastinate demo_django: create book" --contract "procrastinate demo_django: create book, with its fixes" --exercised I
|
|
329
|
+
uv run --no-sync python scripts/check_adoption_report.py profiles-dbos.json --contract "DBOS transactional-outbox: place order" --verified I --exercised H J
|
|
330
|
+
uv run --no-sync python scripts/check_adoption_report.py demos/wagtail_tasks/profiles-wagtail.json --contract "wagtail: image and document lifecycle" --contract "wagtail: publishing a page behind a CDN" --exercised I --verified H J
|
|
331
|
+
uv run --no-sync python scripts/check_adoption_report.py demos/wagtail_tasks/profiles-wagtail.json --contract "wagtail: image and document lifecycle" --exercised E
|
|
332
|
+
- name: Keep executed profile evidence
|
|
333
|
+
if: always()
|
|
334
|
+
uses: actions/upload-artifact@v4
|
|
335
|
+
with:
|
|
336
|
+
name: profiles-demos
|
|
337
|
+
path: |
|
|
338
|
+
profiles-*.json
|
|
339
|
+
demos/**/profiles-*.json
|
|
340
|
+
if-no-files-found: error
|
|
341
|
+
|
|
342
|
+
saleor:
|
|
343
|
+
# Saleor's checkout, unmodified, in Saleor's own environment built from its uv.lock.
|
|
344
|
+
runs-on: ubuntu-latest
|
|
345
|
+
services:
|
|
346
|
+
postgres:
|
|
347
|
+
image: postgres:16
|
|
348
|
+
env:
|
|
349
|
+
POSTGRES_PASSWORD: postgres
|
|
350
|
+
ports: ["5432:5432"]
|
|
351
|
+
options: >-
|
|
352
|
+
--health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 10
|
|
353
|
+
env:
|
|
354
|
+
DATABASE_URL: postgres://postgres:postgres@localhost:5432/saleor
|
|
355
|
+
# Absolute: run.sh works in the demo directory.
|
|
356
|
+
SALEOR_DATABASE_DUMP: ${{ github.workspace }}/demos/.ci-cache/saleor.dump
|
|
357
|
+
steps:
|
|
358
|
+
- uses: actions/checkout@v7
|
|
359
|
+
with:
|
|
360
|
+
# The check action builds the harness from this checkout; its version comes from git tags.
|
|
361
|
+
fetch-depth: 0
|
|
362
|
+
- uses: astral-sh/setup-uv@v7
|
|
363
|
+
with:
|
|
364
|
+
enable-cache: true
|
|
365
|
+
# Saleor's pin lives in fetch_upstream.py; its lock is fetched after this step.
|
|
366
|
+
cache-dependency-glob: |
|
|
367
|
+
uv.lock
|
|
368
|
+
demos/fetch_upstream.py
|
|
369
|
+
- run: uv run --no-project python demos/fetch_upstream.py saleor
|
|
370
|
+
- name: Type-check the demo against Saleor
|
|
371
|
+
run: demos/saleor_checkout/run.sh --typecheck
|
|
372
|
+
- name: Saleor, every handoff accounted for
|
|
373
|
+
uses: ./check
|
|
374
|
+
with:
|
|
375
|
+
working-directory: demos/saleor_checkout
|
|
376
|
+
package: ${{ github.workspace }}
|
|
377
|
+
- name: Postgres without durability
|
|
378
|
+
run: demos/throwaway_postgres.sh
|
|
379
|
+
# Saleor's migrations depend only on its pin (in fetch_upstream.py): their result is cached as a
|
|
380
|
+
# dump, restored in seconds instead of migrating for two minutes.
|
|
381
|
+
- uses: actions/cache@v4
|
|
382
|
+
with:
|
|
383
|
+
path: ${{ env.SALEOR_DATABASE_DUMP }}
|
|
384
|
+
key: saleor-test-db-pg16-${{ hashFiles('demos/fetch_upstream.py', 'demos/saleor_checkout/prepare_db.py', 'demos/cached_database.py') }}
|
|
385
|
+
# One xdist worker per runner CPU. Four workers each migrating their own database took
|
|
386
|
+
# 193 s apiece; migrating once and cloning a copy per worker costs one migration.
|
|
387
|
+
- name: Saleor's test databases, migrated once
|
|
388
|
+
run: demos/saleor_checkout/run.sh --prepare-db 4
|
|
389
|
+
# The durations are the evidence for the next change to this job's speed. --dist worksteal was
|
|
390
|
+
# tried: pytest took 75 s and 80 s, inside the 66 to 91 s this step takes with the default on the
|
|
391
|
+
# same code, as runners vary. No gain was measurable, so the default stays.
|
|
392
|
+
- name: Saleor checkout
|
|
393
|
+
run: demos/saleor_checkout/run.sh -rxX --due-work-explore=smoke --due-work-verify -n 4 --reuse-db --durations=15 --due-work-require-assessed --due-work-profile-report=profiles-saleor.json
|
|
394
|
+
|
|
395
|
+
- name: Require the promised adopters and executed capabilities
|
|
396
|
+
run: |
|
|
397
|
+
demos/.upstream/saleor/.venv/bin/python scripts/check_adoption_report.py demos/saleor_checkout/profiles-saleor.json --contract "saleor checkout" --contract "saleor checkout, Transactions API with automatic completion" --exercised H I
|
|
398
|
+
demos/.upstream/saleor/.venv/bin/python scripts/check_adoption_report.py demos/saleor_checkout/profiles-saleor.json --contract "saleor payment event reports" --verified E --exercised I
|
|
399
|
+
- name: Keep executed profile evidence
|
|
400
|
+
if: always()
|
|
401
|
+
uses: actions/upload-artifact@v4
|
|
402
|
+
with:
|
|
403
|
+
name: profiles-saleor
|
|
404
|
+
path: |
|
|
405
|
+
profiles-*.json
|
|
406
|
+
demos/**/profiles-*.json
|
|
407
|
+
if-no-files-found: error
|
|
408
|
+
|
|
409
|
+
build:
|
|
410
|
+
# The published wheel alone: it installs without any framework, imports, and ships py.typed.
|
|
411
|
+
runs-on: ubuntu-latest
|
|
412
|
+
steps:
|
|
413
|
+
- uses: actions/checkout@v7
|
|
414
|
+
with:
|
|
415
|
+
# The version is derived from tags, so build with the history that has them.
|
|
416
|
+
fetch-depth: 0
|
|
417
|
+
- uses: astral-sh/setup-uv@v7
|
|
418
|
+
with:
|
|
419
|
+
enable-cache: true
|
|
420
|
+
- run: uv build
|
|
421
|
+
- run: unzip -l dist/*.whl | grep -q "pytest_obligation/py.typed"
|
|
422
|
+
- run: unzip -l dist/*.whl | grep -q "due_work_harness/py.typed"
|
|
423
|
+
- run: uv venv /tmp/wheel && uv pip install --python /tmp/wheel dist/*.whl
|
|
424
|
+
- run: /tmp/wheel/bin/python scripts/check_core_is_framework_free.py
|
|
425
|
+
- name: Verify old and new adopters against the installed wheel
|
|
426
|
+
run: /tmp/wheel/bin/python -m pytest tests/core/test_compatibility.py tests/core/test_package_identity.py -q
|
|
427
|
+
|
|
428
|
+
actions:
|
|
429
|
+
# The published GitHub Actions, run against the example adopter the way a project would use them.
|
|
430
|
+
runs-on: ubuntu-latest
|
|
431
|
+
steps:
|
|
432
|
+
- uses: actions/checkout@v7
|
|
433
|
+
with:
|
|
434
|
+
fetch-depth: 0
|
|
435
|
+
- name: check passes on a project whose every handoff is accounted for
|
|
436
|
+
uses: ./check
|
|
437
|
+
with:
|
|
438
|
+
working-directory: examples/adopter
|
|
439
|
+
package: ${{ github.workspace }}
|
|
440
|
+
- name: test runs the project's generated suites without Hypothesis
|
|
441
|
+
uses: ./test
|
|
442
|
+
with:
|
|
443
|
+
working-directory: examples/adopter
|
|
444
|
+
pytest-args: --due-work-require-assessed
|
|
445
|
+
- name: Generated histories also support optional search
|
|
446
|
+
uses: ./test
|
|
447
|
+
with:
|
|
448
|
+
working-directory: examples/adopter
|
|
449
|
+
sync-args: --extra exploration
|
|
450
|
+
pytest-args: --due-work-explore=smoke --due-work-require-assessed --due-work-profile-report=profiles-example.json
|
|
451
|
+
- name: Replay and sensitivity controls
|
|
452
|
+
working-directory: examples/adopter
|
|
453
|
+
run: uv run --no-sync pytest tests/test_catalog_failures.py
|
|
454
|
+
- name: Require example capabilities
|
|
455
|
+
working-directory: examples/adopter
|
|
456
|
+
run: uv run --no-sync python ../../scripts/check_adoption_report.py profiles-example.json --contract "example catalog" --verified E G H I
|
|
457
|
+
- name: Keep example profile evidence
|
|
458
|
+
if: always()
|
|
459
|
+
uses: actions/upload-artifact@v4
|
|
460
|
+
with:
|
|
461
|
+
name: profiles-example
|
|
462
|
+
path: examples/adopter/profiles-example.json
|
|
463
|
+
if-no-files-found: error
|
|
464
|
+
- name: check fails once a handoff loses its disposition
|
|
465
|
+
run: |
|
|
466
|
+
rm examples/adopter/tests/test_summaries_exemption.py
|
|
467
|
+
if uvx --from "$GITHUB_WORKSPACE" due-work-harness check --root examples/adopter; then
|
|
468
|
+
echo "::error::the check passed a project with an unaccounted handoff" >&2
|
|
469
|
+
exit 1
|
|
470
|
+
fi
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: PR title
|
|
2
|
+
|
|
3
|
+
# Pull requests are squash-merged with their title as the commit message, and
|
|
4
|
+
# release-please reads those commits to choose the next version. The title must
|
|
5
|
+
# therefore be a Conventional Commit: see RELEASING.md.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
pull_request_target:
|
|
9
|
+
types: [opened, edited, synchronize, reopened]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
pull-requests: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
conventional-commit:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
# Reads only the title through the API; it never checks out the pull request's code.
|
|
19
|
+
- uses: amannn/action-semantic-pull-request@v5
|
|
20
|
+
env:
|
|
21
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
22
|
+
with:
|
|
23
|
+
types: |
|
|
24
|
+
feat
|
|
25
|
+
fix
|
|
26
|
+
perf
|
|
27
|
+
revert
|
|
28
|
+
docs
|
|
29
|
+
refactor
|
|
30
|
+
test
|
|
31
|
+
build
|
|
32
|
+
ci
|
|
33
|
+
chore
|
|
34
|
+
requireScope: false
|