Agent failure labs / Guide 67
Fresh Inventory Is Not a Reservation
Reproduce an AI inventory freshness validation failure when two requests see the last unit, then combine snapshot age with a conditional inventory claim.
Illustrative engineering guide. Examples are not client deployments, production-ready integrations, or evidence of measured results. Validate the design with your domain and security owners.

“Reserve the last blue filter at the north warehouse.” Two hypothetical assistants receive that request from different operators. Both read an inventory snapshot that is only two seconds old. Both promise success. Freshness checks pass, but there is still only one unit. The defect is treating a recent observation as an exclusive claim.
This lab separates snapshot age from inventory concurrency. It does not redesign replenishment, forecasting, or warehouse picking. The reproducible exercise uses one SKU, one warehouse, and two contenders carrying the same revision. The naive path accepts both from a shared snapshot. The fixed path allows only the first conditional reservation and requires the second contender to refresh.
Define the Stock Being Claimed
The stock key must include tenant, warehouse, SKU, and the relevant unit of measure. “Blue filter” is only a candidate description. Resolve it to a reviewed product identifier before authorization or reservation. A pack of ten and a single item cannot share an unqualified quantity field. Availability may also exclude damaged, quarantined, or already allocated stock according to the source system’s definitions.
Freshness needs an observation timestamp from the inventory source and a server-controlled current time. Python’s datetime documentation distinguishes aware and naive timestamps. Our fixture uses explicit UTC timestamps and rejects future-dated observations. The thirty-second age limit is an illustrative local policy value, not an industry standard or a guarantee of physical stock accuracy.
The mock store uses a revision number as a conditional claim. The second request carries a now-stale revision even though its timestamp is still acceptable. That is the central lesson: time freshness and state freshness answer different questions. Neither can establish that a shelf count is physically correct if the upstream system is wrong.
from datetime import datetime, timezone, timedelta
from copy import deepcopy
now = datetime(2026, 9, 7, 10, 0, tzinfo=timezone.utc)
stock = {"tenant": "A", "warehouse": "north", "sku": "filter-blue-each",
"available": 1, "revision": 12, "observed": now - timedelta(seconds=2)}
intent = {"warehouse": "north", "sku": "filter-blue-each", "quantity": 1,
"revision": 12}
def naive(snapshot, request):
return snapshot["available"] >= request["quantity"]
def guarded(current, request, actor_tenant, clock):
if actor_tenant != current["tenant"]:
return "denied"
if (request["warehouse"], request["sku"]) != (current["warehouse"], current["sku"]):
return "wrong_stock_key"
if type(request["quantity"]) is not int or request["quantity"] <= 0:
return "invalid_quantity"
age = (clock - current["observed"]).total_seconds()
if not 0 <= age <= 30:
return "refresh_required"
if request["revision"] != current["revision"]:
return "conflict"
if current["available"] < request["quantity"]:
return "unavailable"
current["available"] -= request["quantity"]
current["revision"] += 1
return "reserved_mock"
snapshot = deepcopy(stock)
assert naive(snapshot, intent) and naive(snapshot, intent)
assert guarded(stock, intent, "A", now) == "reserved_mock"
assert guarded(stock, intent, "A", now) == "conflict"
assert stock["available"] == 0 and stock["revision"] == 13
assert guarded(deepcopy(snapshot), intent, "B", now) == "denied"
assert guarded(deepcopy(snapshot), intent, "A", now + timedelta(seconds=40)) == "refresh_required"
assert guarded(deepcopy(snapshot), dict(intent, quantity=True), "A", now) == "invalid_quantity"
assert snapshot["available"] == 1
This is an explicitly synthetic deterministic simulation of a possible failure, not a real model experiment or an inventory-service benchmark. The functions operate on local dictionaries only. They do not call a warehouse API, create real reservations, modify files, or send orders. Sequential Python calls illustrate a race outcome; they do not implement a thread-safe or distributed transaction.
Why a Freshness Prompt Cannot Serialize Requests
“Check the latest stock before reserving” still leaves a gap between the read and the write. Another operator can claim the unit during that gap. The application needs the source system to evaluate preconditions and create the reservation as one controlled operation. A language model cannot enforce that atomicity by describing the correct sequence more carefully.
PostgreSQL’s transaction isolation documentation explains statement snapshots and the rechecking of conditions when competing updates affect the same row. A production inventory service can use a conditional update, appropriate locking, or another source-supported atomic reservation primitive. If an invariant spans several rows, its protection must span those rows too; a single-row example is not sufficient evidence.
Keep candidate search and reservation separate. The search response may advertise observed availability with a timestamp. The reservation response must identify a particular claim, quantity, stock key, and expiration where applicable. Human approval is required according to the operation’s business impact, and it must bind to those details. Do not let an agent substitute another warehouse after approval merely because it has stock.
Recover Without Creating Phantom Stock
The smallest implementation uses an inventory adapter and an operation ledger. Specialist agents might interpret product descriptions or resolve catalog synonyms, but they must not choose units or locations without deterministic checks. OWASP’s authorization guidance supports checking each request against the actor’s allowed resources and actions. Permission to view inventory does not imply permission to allocate it.
Production dependencies omitted include the authentication source, database transactions, provider semantics, approval records, and state persistence. The server obtains tenant identity and warehouse grants independently of the candidate. Catalog notes and supplier descriptions are untrusted content. An instruction embedded in a SKU description cannot expand a reservation’s scope or authorize access to another tenant’s stock.
A reservation timeout means unknown. Reconcile the original operation with the authoritative inventory service before retrying. If the source supports idempotency, preserve the operation key and exact payload according to its documented retention and replay semantics. If it does not, a local ledger alone cannot prevent duplicate remote effects after a crash; design a human reconciliation path rather than claiming exactly-once behavior.
Likewise, release a reservation only when ownership and current status are verified. Automatically adding one unit back after a timeout can create phantom availability if no reservation existed, or release another process’s claim. A receipt must have authoritative origin and match tenant, warehouse, SKU, quantity, and operation. A connector’s status: success is not enough to promise that an item is held.
Expose stale, unavailable, conflict, pending verification, and reserved as distinct states. This distinction lets an operator choose an alternative deliberately. Keep logs limited to operation identifiers, state changes, and bounded reason codes; avoid customer details or commercially sensitive inventory dumps. Physical discrepancies still require warehouse reconciliation, independent of software revision checks.
Acceptance Tests
- Two requests using the same one-unit snapshot must both pass the naive check, establishing the oversubscription defect.
- The fixed path must reserve one unit once and reject the second request’s stale revision without making quantity negative.
- A snapshot older than the illustrative age policy must require refresh even when its revision still matches.
- A future observation timestamp must be treated as a clock or source-data problem rather than exceptionally fresh inventory.
- Wrong warehouse, wrong SKU, unauthorized tenant, and Boolean quantity inputs must fail before any mock decrement.
- A timeout and subsequent replay must reconcile the same reservation, while a release attempt must verify claim ownership before restoring availability.
Measure Promises Against Claims
Use synthetic contention fixtures as the baseline, then replay realistic stock-key distributions in a nonmutating harness. Count false reservation promises divided by all reservation promises. Separately measure valid requests blocked by freshness or conflict checks over valid requests. Distinguish software contention from physical count discrepancies, because their remediation owners differ.
Begin with timestamped availability displays and human-operated reservations. Proposed promotion gates include no negative stock in the contention suite and traceable reconciliation for every unknown outcome. These are targets, not reported production achievements. Keep unresolved reservations in latency and completion denominators rather than counting them as failures that disappeared.
The retail replenishment guide covers planning around stock, while the hotel booking lab shows a product-selection prerequisite. Backend API engineering should start with one conditional reservation endpoint. A recent answer becomes a dependable promise only after a verified claim exists.