Run the actual standard-library example
Download pattern_11.py and test_pattern_11.py into one directory. Use Python 3.11 or later. On macOS run python3 -B pattern_11.py and python3 -B -m unittest -v test_pattern_11.py. On Windows the equivalent launcher commands are py -3 -B pattern_11.py and py -3 -B -m unittest -v test_pattern_11.py. There are no external packages, model calls, or network requests.
The JSON output reports first and retry as reserved, compensation as compensated, and active as zero. This output describes a local database exercise. It is not evidence that a remote maintenance endpoint, payment system, or distributed workflow has been tested. The minus-B option prevents bytecode files from appearing beside downloadable source.
Architecture and request sequence
Read the architecture as text
- reserve call: Trusted scope and tenant intent
- Existing receipt check: Reject changed service intent
- SQLite transaction: Hold and receipt commit together
- compensate call: Delete hold; retain compensated receipt
- reserve call → Existing receipt check: Validated input
- Existing receipt check → SQLite transaction: Bounded processing
- SQLite transaction → compensate call: Recorded result
Read the sequence as text
- Caller → Boundary: Submit same intent
- Boundary → Store: Validate receipt
- Store → Boundary: Commit hold and receipt
- Caller → Boundary: Request compensation
- Boundary → Store: Retain compensated history
Read the tables before the control flow
ActionStore creates actions and holds tables. Both use the tenant and operation pair as their primary key. The actions row stores the service intent and current state; the holds row represents the local effect. A file path makes the state survive reopening the connection, while the default in-memory database keeps the demonstration self-contained. Tests use temporary directories for the persistence case and close connections before cleanup.
The schema is intentionally small. It lacks timestamps, retention enforcement, an authorization service, and a remote dispatch table. Adding those is a production design task. Keep the distinction between a receipt and an effect visible: deleting a hold does not delete its receipt, because a later retry still needs to know that the original intent has already been handled.
Follow reserve through its transaction
The reserve method checks the authorization flag and rejects blank identifiers before writing. The flag is a test seam supplied by a trusted caller in this exercise; it is not an authentication mechanism. The transaction obtains a write reservation, looks up the existing key, and compares the stored service. A matching retry returns the existing state. Conflicting intent raises ValueError without adding another hold.
For a new operation, the method inserts the hold and then the receipt inside one transaction. The fail parameter raises an injected exception between those writes. The connection context rolls the transaction back, so the effect cannot survive without its receipt in this local boundary. The test asserts zero active holds after failure and verifies that a subsequent attempt can succeed.
Inspect compensation and delayed retries
The compensate method looks up the receipt, rejects an unknown operation, deletes the matching hold, and updates the receipt state within a transaction. Repeating compensation leaves the same compensated state. Calling reserve afterward with the same intent returns that state; it does not recreate the hold. This behavior is important when a late client retry arrives after an operator has already reversed the effect.
The tests also demonstrate tenant-scoped identifiers: two tenants can use the same operation string without sharing a receipt. This is key partitioning, not a complete tenant-security implementation. The caller can still supply arbitrary tenant strings because authentication is outside this module. Pattern 12 adds a separate authorization boundary and explains why partitioned keys alone do not establish isolation.
Connect a model through a proposal interface
A future model adapter should return a proposal containing incident identifier, service, requested operation, and supporting evidence. It should not receive the database connection or choose whether authorization is required. A server assigns or validates the stable intent identifier after the user approves the action. The gateway then calls reserve with a verified principal and a resolved tenant context. The current example supplies deterministic inputs and has no simulated language model.
A proposed HTTP wrapper would map intent conflicts to a stable application error and return the stored operation state on a retry. Its request schema and status mapping need their own tests. Do not turn every exception into a generic retryable response: permission failure, conflicting intent, unknown state, and temporary storage failure require different caller behavior.
A reference deployment requires a larger boundary
For a local service deployment, mount the database on durable storage, run a bounded number of writers, and define backup and restore procedures. Do not start several disposable containers with separate in-memory stores and expect shared idempotency. A distributed deployment requires a database or service whose concurrency and durability contract matches the intended workload. These are reference deployment considerations; no cloud resources are provisioned here.
The tests establish eight bounded behaviors, including atomic interruption, restart retention, permission denial, and delayed retry after compensation. They do not establish remote exactly-once delivery or crash behavior under every filesystem failure. Use the provider's documented idempotency contract when replacing the local effect with a real integration, and retain evidence of the integration tests separately.