I Made the Product Calls. Claude Code Wrote the Code.
The spec set the boundaries. I approved each phase before the next one started.
Two jobs for the agent
I kept the demo to one read and one write against a mock CRM.
- Track a shipment
- Verify the caller, then give its status and delivery time.
- Change a date
- Check the requested day. Save it only after the caller confirms.
A date change reaches the CRM only after a yes
Verify the caller
Check the tracking number and phone digits before looking up a shipment.
- No match
- Give the same reply as an unknown tracking number.
- Match
- Continue. The model still gets no personal details.
Check the requested date
Is it within the allowed window, not a Sunday, and different from the current date?
- No
- Explain why. Leave the CRM unchanged.
- Yes
- Read the date back to the caller.
Wait for the next turn
The date is not saved in the turn where it was suggested.
- No confirmation
- Keep the original delivery date.
- Caller says yes
- Save the new date in the mock CRM.
Verification, date limits, confirmation, and masking happen in Python—not in the prompt.
Six phases. Each one approved.
I did not let implementation run ahead of the decision. Nothing moved on until I approved it.
Constitution
Before choosing the stack, I set the boundaries: one caller, two jobs, fake shipment data, and no made-up facts.
Specify
I wrote down what the caller and agent can do, plus what must never happen. The model can ask for a tool; code decides whether it runs.
Clarify
One example date fell on a Sunday, when deliveries do not happen. I fixed the example and made Sunday an explicit edge case. We also settled on one identifier: the tracking number from the SMS.
- Sunday
- Not a delivery day, even if the caller asks confidently.
- Tracking number
- The caller already has it; there is no second “order number”.
- Verification
- One fixed method for this demo. Other methods can wait.
- Logs
- Phone numbers keep only their last two digits, not four.
Plan
For each choice, the plan records why I chose it and the main alternative I rejected.
- Same reply on failed verification
- An unknown shipment and a wrong phone number get one generic reply. It avoids revealing which shipments exist; equal response time was not measured.
- No personal details in the model
- The model never receives a name, phone number, or address. The lookup tool does not accept a tracking number.
- Confirm before writing
- The agent repeats the new date, then waits for a yes in a later turn. A prompt alone cannot save it.
- Customer setup in config
- Wording and rules live in validated YAML. A second customer should not need a code change.
- Spec gap
- I once let a model-down handoff ship before the spec described it. Since then: propose the behavior, get approval, update the spec, then build it.
Tasks
Claude Code broke the approved plan into tasks. I let it work on one task at a time: implement, run tests, show me the result, wait for approval, then commit with the task and requirement IDs.
Implement
I tested the text and voice flows; Claude Code ran the unit tests and model scenarios. Fourteen findings came back. Some changed the spec, some changed the code, and some stayed on the list.
One caller said “yirmi dokuz on dokuz.” Speech-to-text turned it into “29.19,” then “29 19.” The prompt had failed twice, so I moved digit joining into code and tested it again.
Three findings that changed the build
- Found
Callers paused between the tracking number and phone digits.
ChangedAdded a two-second pause timer and kept partial answers.
CheckedVoice test and scenarios.
- Found
Push-to-talk felt like a chatbot.
ChangedAdded hands-free mode; the mic reopens after each reply.
CheckedManual voice test.
- Found
A 10–15 second model reply left callers in silence.
ChangedAdded a working state and a short filler after three seconds.
CheckedText and voice tests.
What passed—and what did not
- Unit tests389 passed; one live test passed
- 31 of 31real-model scenario runs passed
- Personal-data scanclean across scenario logs
- Manual checkstext, push-to-talk, and hands-free voice passed
Still to run
The remaining reschedule and handoff edge cases, the alternate-customer demo, and a full regression run.
This is a single-user browser demo. Conversations stay in memory; the CRM is mocked; voice works in Chrome and Edge. A volunteered name that is not in the shipment record is still readable in the log.