Conversation designer / UX specialist · Banking & fintech (external)
A later yes must not erase a hardship disclosure
Keep hardship in the conversation state, stop automatic plan selection after a disclosure, and preserve a request when the hardship team is unavailable.
Key takeaways (3)
- A later request for a plan cannot erase an earlier hardship disclosure.
- A staged offer still needs a current confirmation before recording.
- Unavailable support should preserve the request without recording a plan.
The customer’s test inputs say, in Spanish, that they cannot cover rent, then ask for the six-payment plan. The later request does not erase the earlier hardship statement.
Customer, authored test input: No me alcanza para la renta.
Customer, next authored test input: Bueno, anóteme en el de seis pagos.
The first line means “I don’t have enough for rent.” The next asks to be put on the six-payment plan. These are supplied text inputs, not transcribed speech. The fictional Northgate Bank tool returns:
{"memory": null, "staging": "hardship_declared"}
{"detail": "hardship_declared", "effects": 0, "reason": "choice_not_confirmed"}
Decide which earlier turns remain relevant
A conversation designer cannot treat every new request as a clean slate. In this teaching policy, difficulty meeting basic needs stops automatic repayment-plan selection. The later plan request supplies intent, but it does not establish that the difficulty was resolved.
The first refusal happens while staging the offer. The proof then inserts staged state directly and attempts the final write. Recording is refused too, with no effect. That second part catches a guard placed only at the start of the conversation.
Put the hardship stop inside confirmation
The confirmation helper reads the customer history and open referrals before it checks the staged offer and read-back. The hardship stop is the first branch in this excerpt.
if hardship_declared(conversation.user_texts) or state.open_referral():
return False, "hardship_declared"
staged = state.staged
if offer is None or not staged or staged["offer_id"] != offer["offer_id"] or memory_tag != staged["tag"]:
return False, "not_staged"
question = fold(conversation.confirmation_question or "")
if fold(_offer_view(offer)["terms_spoken"]) not in question or fold(account["label"]) not in question:
return False, "not_read_back"
if conversation.confirmation_answer is None:
return False, "not_answered"
This detector reads a bounded set of authored Spanish phrases in the companion source. The checks cover those inputs. They do not establish recall across indirect disclosures, dialects or speech-recognition mistakes. Have the collections owner supply the disclosures that matter for the intended release.
Give an unavailable team a different reply
The loan hardship fixture cannot accept a referral. The callback test preserves the request and returns a callback reference. That is a different state from an accepted referral, and neither is a recorded repayment plan.
| Service state | Permissible next step | Claim to avoid |
|---|---|---|
| Hardship referral accepted | Give the referral reference | Saying a plan was agreed |
| Hardship team unavailable | Preserve request and callback reference | Saying the team accepted a referral |
| Customer withdraws agreement | Clear staged choice or withdraw recorded plan | Treating an earlier yes as current |
The support team needs to receive the preserved request. The fixture creates a record; it does not place a phone call. Agree who owns unfulfilled callbacks and what the customer is told when a team remains unavailable.
Test the final write, not just the first reply
Run these checks from the pinned companion project. They use Python’s standard library and make no model calls. The outputs below were recorded on 7 October 2026; elapsed times can differ.
git clone https://github.com/RasaHQ/rasa-community-resources.git
cd rasa-community-resources
git checkout 4aa0c4419dc193fef7a969c12d59edcf720f2606
cd examples/mantle-voice-banking-collections-gemini/tests
- Any system
python3 -m unittest test_guard.SelectAndRecordTests test_guard.WithdrawReferralCallbackTests -q
----------------------------------------------------------------------
Ran 19 tests in 0.001s
OK
The companion tests contain the assertions for these cases. To print the first-screen trace yourself, run this script from the same directory:
Show the script that printed the fixture states
Paste it into a file such as trace.py, then run python3 trace.py. It prints selected fields from the actual tool results; it does not invent replies.
"""Run from the pinned companion project tests directory. Stdlib only."""
import json
import test_guard as t
case = t.SelectAndRecordTests()
case.setUp()
users = ("No me alcanza para la renta.", "Bueno, anóteme en el de seis pagos.")
staged, memory = case.stage("OFR-4471-6M", users=users)
print(json.dumps({"staging": staged["status"], "memory": memory}, sort_keys=True))
case.state.staged = {"account_ending": "4471", "offer_id": "OFR-4471-6M", "tag": "OFR-4471-6M/1"}
result = case.record("OFR-4471-6M", {"plan_tag": "OFR-4471-6M/1"}, users=users)
print(json.dumps({"reason": result["reason"], "detail": result["detail"], "effects": result["effects"]}, sort_keys=True))For conversation review, replay the two authored turns before checking a normal yes. Then attempt the final write with staged state and inspect the result. Design the handoff summary so it carries the disclosure and unresolved request without inventing a resolved outcome.
The complete fixture implementation defines the service state and remaining branches cited here.