Skip to main content

pytest-obligation

pytest-obligation logo

CI PyPI Python pytest plugin Typed License: Apache-2.0

Don’t lose orders, money, receipts, or work when the happy path breaks

Your app may commit an order, charge a customer, schedule a task, send a receipt, publish to Kafka, or call Shopify. A worker can die one line later; a broker reply can be lost; a retry can run the external effect twice. Ordinary tests usually exercise only the path where none of that happens.

pytest-obligation generates tests for work your application cannot afford to lose or repeat. Bind an ObligationContract to your real workflow and recovery path, yourself or with a coding agent. The harness generates standardized tests for crashes, retries, lost messages, and competing operations, exposing failure cases you might never think to write by hand. Its fault injection interrupts commits, workers, messages, and external calls, then checks whether recovery reaches the right outcome without lost work or duplicate effects. The agent can use the same fault-injection infrastructure to add focused tests for your application's particular risks. The core accepts plain Python callables: it assumes no framework, database, queue, or business domain.

It integrates as a pytest plugin in your existing test suite and CI. Read the pytest plugin guide for automatic discovery, test selection, reports, and configuration.

Coming from distributed systems and familiar with Jepsen? pytest-obligation brings a similar approach to your application's workflows. Read Jepsen-style fault testing for application work.

What it finds

  • Lost work: a Django transaction commits an order, but a Celery task or Kafka message is never published; a dead worker's task is never picked up.
  • Duplicate effects: a retry sends two receipts, charges twice, or repeats an external API call after the first result became uncertain.
  • Wrong final state: a stale worker overwrites a newer result, a checkout takes a payment without creating an order, or cleanup deletes work still owed.

These are not just hypothetical failures. Runs against Saleor, Wagtail, procrastinate, DBOS, RQ, and Celery show the real code, failure timing, observed result, and—in several cases—a fix that makes the same proof pass. For example, a Saleor Payments API checkout can capture a payment and lose the order when the worker dies between those steps; a DBOS notification step can send twice when it is replayed.

What a finding looks like

One crash history compares the normal outcome with the outcome after a specific interruption:

pytest_obligation.HistoriesDiverged: orders: handoff 'place order': normal operation reaches
('SENT', 1), but these histories reach something else: {'worker died after external call 1':
('SENT', 2)}. Work was lost or repeated. ...

The second send is visible instead of being hidden behind a green happy-path test. Known, independently reproduced gaps remain strict xfails; a change that fixes one turns it red so the finding must be re-evaluated. See how crash histories work and what a green result means.

Stronger tests without hand-writing more tests

Declare the workflow once in an ObligationContract and expose it through @due_work_contract_suite(CONTRACT). At pytest collection time, the harness generates cases for the applicable A–J guarantees: recovery, ownership, crash ambiguity, retention, convergence, derived obligations, gated execution, harmless replay, indivisible admission, and retry limits. As the harness gains checks for a profile you already bound, upgrading can generate more cases from the same contract. New profiles or bindings still need your assessment. The separate handoff scan spots newly introduced queue/task handoffs that need a contract; it does not silently claim they are proven. See the guarantees and limits.

What the run shows

pytest --due-work-summary lists generated cases, passes, and declared gaps:

A real harness run finding lost and repeated work

test_wagtail_tasks.py::TestDjangoTasksDb: 7 passed, 5 xfailed
  XFAIL   A-known_gap  (the worker selects only READY tasks, so a task left RUNNING by a worker that died is…)
  PASSED  D-assert_retention_preserves_non_terminal_work
  ...

For CI, --due-work-profile-report=profiles.json records what was declared, collected, selected, and actually executed. An xfail or unassessed profile is not a verified guarantee. Read the reporting guide.

Get started

Formerly due-work-harness. The package is being renamed to pytest-obligation; until its first PyPI release, install due-work-harness for the last published version. Do not install both distributions in the same environment: they provide the same Python modules. New code uses from pytest_obligation import ObligationContract. Existing due_work_harness imports and DueWorkContract are supported by an isolated compatibility shim. The --due-work-* options, due-work-harness command, and [tool.due-work-harness] configuration remain supported. See the compatibility and migration guide. Recovery loops also have poison isolation, cooperative-stop and database-legal row checks. The complete backend harness port includes Due work-table conformance and reusable source-policy checks. The cutover follow-through audit records the remaining improvements and their public test owners.

Install the Python test library in the application you want to test (Python 3.11+). Choose your package manager; add an optional extra only for an integration you use:

pip install pytest-obligation
uv add --dev pytest-obligation
poetry add --group dev pytest-obligation

Optional integrations:

Django integration PostgreSQL integration Celery integration MongoDB integration Redis and RQ integration Prefect integration Kafka aiokafka integration Hypothesis optional exploration

For example, use "pytest-obligation[django]" with any of the commands above for the Django/PostgreSQL integration. Other extras include [celery], [rq], [mongodb], [prefect], and [aiokafka]; see integration scope. The current source has A–J profiles. Check the adoption guide matching your installed version; older PyPI releases use an earlier profile catalog.

Then add the coding-agent plugin (it provides instructions, not the Python library). For Claude Code:

claude plugin marketplace add gigaverse-app/pytest-obligation
claude plugin install pytest-obligation@pytest-obligation

For Codex, add the repository marketplace, then open the Plugins Directory, select pytest-obligation, and install the plugin:

codex plugin marketplace add gigaverse-app/pytest-obligation --sparse .agents/plugins

It is not yet in the public plugin directory. The plugin works without a remote MCP server; tests run in your coding environment with your app and its required services.

Open your application in Claude Code or Codex and ask:

Use the prove-due-work skill to create an ObligationContract for our order-processing workflow. Bind the real database-to-queue handoff and recovery worker, generate and run the standard tests, then add focused crash/retry tests. Report what passed, what failed, and which guarantees remain unassessed.

Documentation

You want to… Read
Find the right workflow or public interface Harness guide and module map
Bind your application and run generated proofs Adoption walkthrough, runnable SQLite adopter
Inspect transaction and schedule policy Handoff coverage, source-policy APIs and examples
Understand recovery and outcome evidence Recovery edge cases, what a green result means
Replace the backend harness Completed port and ownership map
Contribute a proof or integration Architecture, contributor workflow

Start with the adoption guide if you want to bind a contract yourself. For framework boundaries and the handoff scan, see integrations and coverage. To probe another project, follow the upstream playbook.

Alpha; the public API may change before 1.0. Architecture · Contributing · Apache-2.0 license

Generated expected failures can be enforced with an application-owned debt baseline.

Recovery contracts can also prove selection completeness, including related lifecycle states and strict per-combination debt.

Metadata

Release files for pytest-obligation 0.11.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-obligation 0.11.0
File Size Uploaded
pytest_obligation-0.11.0.tar.gz 949.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-obligation 0.11.0
File Interpreter ABI Platform
pytest_obligation-0.11.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.4 MB

Release files / pytest_obligation-0.11.0.tar.gz

Download URL pytest_obligation-0.11.0.tar.gz
Size 949.3 kB
Tags Source
SHA-256 checksum
How to use checksums
c62d217b5b98266b581f6daad6960df1cb72704c53bc769d42f5683623130f9d
BLAKE2b-256 checksum
How to use checksums
6fb1632c01fcc559301a4005d81d802323ab93a685d9dfbe5741c6a599a773b5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / pytest_obligation-0.11.0-py3-none-any.whl

Download URL pytest_obligation-0.11.0-py3-none-any.whl
Size 417.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c40985d3ee4f35c76fd5363c3edcb26f7e089de2af742f99728b5924ffecbff2
BLAKE2b-256 checksum
How to use checksums
592359a31dcb50819918f6a35add54ad0570d7bcac86159b8621c51e947f498b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

0.13.1

2 release files

0.12.1

2 release files

0.12.0

2 release files

This release

0.11.0 This release

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page