due-work-harness
Proofs that your background work survives lost messages, dead workers and uncertain external calls — and that the test saying so isn't lying.
Most applications record something now and finish it later: send the confirmation email, generate the thumbnails, publish the post, retry the failed upload. That is due work. Its failures are quiet: the commit lands and the queue message doesn't; a worker dies between sending an email and recording that it did; a stale worker finishes after its replacement and overwrites the newer result. Everything looks fine in a test that runs the happy path once.
due-work-harness states the properties due work must have as executable
pytest proofs, and runs them against your production code — whatever queue,
job library or workflow engine it uses.
pip install due-work-harness # the core: pytest and pydantic only
pip install "due-work-harness[django]" # plus the Django/PostgreSQL integration
What it checks
Crash histories. Give it one transition that hands work off. It runs normal operation to define the outcome, then replays the same transition with every message lost, with the worker killed right after each commit it makes, and killed right after each external call — and requires every history to reach the same outcome. It finds commit boundaries itself; you don't name them.
from due_work_harness import CallableDelivery, ExternalCall, HandoffHistory, assert_crash_at_every_commit_converges
def test_placing_an_order_survives_any_death():
assert_crash_at_every_commit_converges(
CallableDelivery(name="orders", recover=run_workers_until_idle), # what production runs after a crash
HandoffHistory(
name="place order",
arrange=new_cart,
transition=place_order, # your real view or service method
observe=lambda cart: (order_status(cart), mailbox.count(cart)), # include what external systems saw
external_calls=(ExternalCall(mailer, "send"),),
),
)
A failure names the history and what it reached:
AssertionError: 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. ...
Lifecycle profiles. A declarative DueWorkContract binds your production
selection, tick and transitions, and requires a disposition — claim, decline
with a reason, not applicable, or known gap — for each of six profiles:
| Profile | The question it answers |
|---|---|
| A automatic recovery | After a lost message, is the work still found? Does anything actually run the sweep, index-served and bounded? |
| B bounded ownership | Who owns the work right now, and what happens when that owner's lease expires? |
| C crash ambiguity | When the provider's answer never arrives, can you tell "never ran" from "maybe ran"? |
| D durable retention | Can a cleanup pass delete work that is still owed? |
| E eventual convergence | When a result lands late, can it overwrite a newer one? |
| F fact-derived obligations | Can product state imply work nothing recorded — and is it still found? |
Plus two execution-safety contracts every adopter disposes: replay safety and bounded retry. See what a green result means and the ways a green suite can lie.
Tests that can't be faked green. A conformance suite fails differently from
ordinary tests: an adapter that copies production's query, or implements a tiny
state machine in the test file, makes every proof measure the copy. The
harness refuses test-authored selections and transitions, requires every
binding to reach code in your production_packages, forbids waiving its own
integrity checks, and pairs every "must not happen" proof with a positive
control that shows the same binding would act. When every crash history
agrees, recovery must still have changed something — agreement with an inert
recovery is not a pass.
Every handoff accounted for
A crash history proves one handoff; the coverage check makes sure none are
forgotten. It scans production code for every call that hands work off —
on_commit, a Celery .delay, a procrastinate .defer, a DBOS workflow start,
a Dramatiq .send, an RQ enqueue, a Django task's .enqueue —
attributes each to the exact function that makes it, and requires exactly one
disposition per function: a contract suite that insures it, or an exemption
that proves losing it costs nothing. Aliases, re-exports and handoffs passed
along uncalled are still found, and a declaration counts only if pytest would
actually run it; pytest --due-work-verify then checks that it did.
@due_work_contract_suite(ORDER_NOTIFICATIONS, covers=(DueWorkSource(OrderService.place),))
class TestOrderNotificationsDueWork:
pass
uv run due-work-harness check # static: imports neither your app nor your tests
In GitHub Actions: gigaverse-app/due-work-harness/check@v0 for the scan, and
gigaverse-app/due-work-harness/test@v0 for the generated suites (every
generated case carries the due_work mark). The path from uv add to CI is in
ADOPTING.md.
Framework-free by construction
The core depends only on pytest and pydantic — no Django, SQLAlchemy, Celery,
Procrastinate or DBOS. Proofs take plain callables. The few facts only a
framework knows — whether a transaction is open, how to interrupt a commit,
what plan a query runs — come from a Host you configure once:
# conftest.py
from due_work_harness import configure
from due_work_harness.integrations.django import django_host
configure(django_host(production_packages={"myapp"}))
| Extra | Supplies |
|---|---|
[django] |
django_host(): pytest-django marks, the transaction probe, a commit counter that kills the worker after any commit (including writes made through SELECT fn()), PostgreSQL plan inspection, lifecycle-state proofs |
[celery] |
beat-schedule evidence, a publication recorder that holds messages instead of sending them, a publication breaker that refuses one publish as a broker that is down would |
[procrastinate] |
its worker as recovery, "worker died holding this job" arrangement, the documented stalled-job recipe, periodic-task evidence |
[dbos] |
restarting an app through its own startup for process-level crash histories |
Where a test can't reach the worker at all — a workflow engine's own executor —
process_histories runs the real program in a child process, kills it at named
points with os._exit, restarts it the way production would, and applies the
same verdict. CI imports every core module with none of the frameworks
installed.
Demos: the harness against code we didn't write
demos/ runs the harness against demo applications, and real
open-source applications, exactly as their projects ship them, pinned to a
commit:
| Demo | Finding | Fix the positive control proves |
|---|---|---|
procrastinate demo_django |
A worker death between committing the book and deferring its job leaves the book never indexed | ATOMIC_REQUESTS = True |
index_book holds a transaction across its slow call |
— | |
| A job whose worker died is never picked up again | procrastinate's documented retry_stalled_jobs task |
|
| procrastinate itself | finish_job does not check the worker: a worker presumed dead finishes a job another worker has since fetched |
— (a fencing token on finish; reported by the demo's profile B claim) |
DBOS transactional-outbox |
A death after sending the notification, before DBOS records the step, notifies the customer twice | an idempotent notification |
| Saleor checkout | With the Payments API, a death between capturing the payment and creating the order charges the customer with no order; after 90 days the payment belongs to nothing | Saleor's Transactions API with automatic completion of paid checkouts |
| A death after the order commits leaves it unconfirmed, its history empty or half-written | — | |
A failing order_created callback, no death at all, makes Django skip the confirmation |
— | |
| The broker refusing any one of the order's webhooks, no death at all, loses the webhooks after it and the confirmation, and fails the checkout of a paid order | — | |
| Automatic completion dispatches a paid checkout again while its completion is still in flight, and reports no backlog | — |
The coverage scan finds the same handoffs statically: the create view's .defer and index_book's in the
procrastinate demo, and DBOS.start_workflow in DBOS's other outbox variant. A handoff made in SQL, as
transactional_enqueue.py does, is invisible to a static scan, which is where a crash history takes over.
Demos are teaching code; these are the things to change when copying them into production, and the harness shows each fix working.
Status
Alpha. The proofs were extracted from a production codebase, where they guard its background workflows in CI; the public API may still change before 1.0. Python 3.12+. Architecture: ARCHITECTURE.md.
Development
Managed with uv (uv sync --all-extras), linted with Ruff and
type-checked with Pyrefly. See CONTRIBUTING.md.
License
Apache-2.0. See LICENSE.
Metadata
Release files for due-work-harness 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| due_work_harness-0.1.0.tar.gz | 391.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| due_work_harness-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 587.1 kB
Release files / due_work_harness-0.1.0.tar.gz
| Download URL | due_work_harness-0.1.0.tar.gz |
|---|---|
| Size | 391.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
367cf6da7610bbce778c204991d8f5fac8c932263b78d96bfb4c616262dd811c
|
|
BLAKE2b-256 checksum How to use checksums |
f9e4b1ec4f47de4664e808cbc435b7ebcf1b000d3963f97e93cdc9708d691c23
|
| 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 Sep 28, 2026.
Transparency logRelease files / due_work_harness-0.1.0-py3-none-any.whl
| Download URL | due_work_harness-0.1.0-py3-none-any.whl |
|---|---|
| Size | 195.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
65d24248a5aca9bef789dcf67c241ae602f2bb6e3fa0da2c6cb0d83cd28e5977
|
|
BLAKE2b-256 checksum How to use checksums |
84ebb6b8f9e7daceddcabb13c10c861c3e0d741ec5befa86a1a58cf469931987
|
| 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 Sep 28, 2026.
Transparency log