Skip to content
RasaGet a free licence
AI team casebook

AI product manager · Retail & e-commerce (external)

A return retry keeps the old choice after a correction

Separate replaying an accepted return from asking to change it to an exchange.

by Rod Rivera

About 4 minutesCase study

Key takeaways (3)
  • A retry preserves the accepted choice; it does not amend it.
  • Inspect the original submission before handling a correction.
  • Give an amendment its own identity and acknowledgement.

The customer changes their choice to exchange. The retry returns return. Nothing new was created, but the correction was not accepted:

{"effects": 0, "refund": "not_decided", "replay": true, "requests": 1, "resolution": "return", "status": "pending", "step": "retry_with_exchange_argument", "submission_key": "WS-RSUB-1EDB5CC5"}

We called the Willow Shop submission function directly after it accepted a return and lost the acknowledgement. This is an offline fixture experiment, not a recorded conversation or evidence that a model chose this path.

The key lookup runs before choice validation

The pinned submission function makes a key from the conversation and item. Its early return is the important boundary:

key = submission_key(conversation_id, ref)
if key in service.requests:
    record = service.requests[key]
    return _receipt(service, record, replay=True)

# Eligibility and newly supplied choice validation happen after this branch.
Submit with exchange same conversation and item Existing request key? Yes: replay stored return exchange not validated key exists No: validate current choice then submit new key
FigureA changed retry reaches the replay branch before choice validation

The sample’s pre-submission correction test, test_changed_choice_blocks_the_old_request, exercises a different boundary: it updates the choice before a request exists. A passing correction test there does not establish amendment after acceptance. The selected SubmitTests, ChoiceTests, and FindTests receipt has 17 passing checks without skips.

Replay the uncertain submission, then inspect it

Use the pinned returns example. The canvas bag creates one request with an unavailable first acknowledgement. We change the memory and argument to exchange, preserving the original key, then look up that key:

Save this as /tmp/retail-return-check.py. From the example’s tests directory, run PYTHONPATH=. python3 -B /tmp/retail-return-check.py:

"""Run from this example's tests directory with PYTHONPATH=. Stdlib only."""
import json
import test_guard as t
svc = t.fresh()
said = ["Return the canvas weekender bag"]
memory = t.chosen(svc, t.BAG, "return", said)
first = t.wr.submit_return_request(svc, t.ME, memory, t.BAG, "return", said, "catchup")
corrected = {**memory, "selected_resolution": "exchange"}
again = t.wr.submit_return_request(svc, t.ME, corrected, t.BAG, "exchange", said + ["Actually exchange it instead"], "catchup")
looked = t.wr.check_return_status(svc, t.ME, first["submission_key"])
for name, result in (("initial_return", first), ("retry_with_exchange_argument", again), ("status_lookup", looked)):
    print(json.dumps({"step": name, "status": result["status"], "resolution": result["resolution"], "effects": result.get("effects"), "replay": result.get("replay", False), "requests": len(svc.requests), "submission_key": result.get("submission_key"), "refund": result["stages"]["refund"]}, sort_keys=True))

Actual offline output:

{"effects": 1, "refund": "not_decided", "replay": false, "requests": 1, "resolution": "return", "status": "pending", "step": "initial_return", "submission_key": "WS-RSUB-1EDB5CC5"}
{"effects": 0, "refund": "not_decided", "replay": true, "requests": 1, "resolution": "return", "status": "pending", "step": "retry_with_exchange_argument", "submission_key": "WS-RSUB-1EDB5CC5"}
{"effects": 0, "refund": "not_decided", "replay": true, "requests": 1, "resolution": "return", "status": "authorized", "step": "status_lookup", "submission_key": "WS-RSUB-1EDB5CC5"}

The lookup later reports authorized, but the stored resolution remains return. Normal selection tools can reject an already-authorized item. This direct call bypasses those tools, the skill, and the confirmation gate to isolate the submission contract.

Decide what a correction means after acceptance

Customer intentOperation to exposeEvidence to report
Try the same return againRetry the original keyOriginal stored resolution and stages
Find out whether it went throughStatus lookupAccepted request, even if acknowledgement was lost
Change return to exchangeA supported amendment operationAcknowledged change to the original request

The sample has no amendment operation. Its returns-desk route is a supported escalation, not proof that an exchange happened. Authorization also leaves the refund undecided; it does not prove inspection or reimbursement.

For a production retry endpoint, compare the supplied intent with the accepted record. A different payload should produce an explicit conflict or an equally explicit old-intent replay. This is a proposed contract, not a repair demonstrated here.

If the service supports amendment, give that operation a new amendment ID linked to the original request. Do not create a second return submission to imitate a change. The service must define whether the accepted stage is still amendable and acknowledge the outcome. Preserving accepted intent during a lost acknowledgement can require another lookup and leave the customer’s correction waiting; that cost is preferable to silently claiming a different operation succeeded.

:::

The ledger is in memory. This experiment does not establish cross-process safety, real warehouse behaviour, or customer comprehension.