← All work

I Made the Product Calls. Claude Code Wrote the Code.

The spec set the boundaries. I approved each phase before the next one started.

Ajan Kargo Spec-driven development Turkish voice agent Fictional cargo company

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

  1. 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.
  2. 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.
  3. 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.

  1. Constitution

    Before choosing the stack, I set the boundaries: one caller, two jobs, fake shipment data, and no made-up facts.

  2. 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.

  3. 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.
  4. 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.
  5. 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.

  6. 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.

Test finds a gapApprove a spec changeUpdate code and re-test

Three findings that changed the build

  • Found

    Callers paused between the tracking number and phone digits.

    Changed

    Added a two-second pause timer and kept partial answers.

    Checked

    Voice test and scenarios.

  • Found

    Push-to-talk felt like a chatbot.

    Changed

    Added hands-free mode; the mic reopens after each reply.

    Checked

    Manual voice test.

  • Found

    A 10–15 second model reply left callers in silence.

    Changed

    Added a working state and a short filler after three seconds.

    Checked

    Text 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.