The factory workshop · Rod Rivera
Define a feature that a test can reject
“Make retries safe” sounds like a feature request. It leaves several decisions unanswered. Are two requests duplicates because their email addresses match? Can two customer accounts use the same request key? What if the application restarts between requests?
Your job in this lesson is to turn that request into a contract that can reject a plausible but incorrect implementation.
Give the operation an identity
Our application accepts three fields: account, key and email. The account and key together identify one contact-update operation. The email is its payload.
{"account": "A", "key": "change-17", "email": "new@example.test"}
Repeating this request should return the original receipt and cause exactly one external write. Reusing change-17 for account A with a different email should return a conflict. Account B may use change-17 independently.
The payload comparison is exact after JSON parsing. Field order does not matter, but an altered email string does. This small contract has only one operation type. A service supporting several kinds of operation would need to decide whether operation type also belongs in the identity.
State what must remain true
Open tasks/c1.md. This is the change request passed to the coding agent. Compare its requirements with these cases before reading a worked solution.
| Case | Required result |
|---|---|
| Same account, key and payload twice | Original receipt; one external effect. |
| Same account and key, different payload | Conflict; no additional effect. |
| Two accounts use the same key | Separate operations and receipts. |
| Four identical requests arrive concurrently | Same confirmed receipt; one external effect in normal operation. |
| Application restarts after confirmation | Original receipt remains available; no repeat write. |
| Application loses the response after writing | Unknown; no invented confirmation or blind retry. |
Notice the last row. Duplicate prevention does not require pretending that uncertainty has disappeared. The application can retain a pending operation and refuse to repeat it until a later reconciliation step establishes what happened.
Predict the baseline’s failure
The baseline passed its own contract in lesson 1. It should fail the new contract:
python3 factory.py check reference/b0.py --contract c1 --out runs/baseline-against-c1-01
Expected result: "passed": false, exit 1. Read the failed checks in the report. Do not change those expectations to make the baseline green. The failed run demonstrates the gap that the requested change must close.
The checker sends HTTP requests and inspects the separate effect ledger. It does not accept a candidate’s printed claim that “all tests pass.” That distinction becomes more useful as a builder grows more persuasive.
Decide the scope before paying for generation
This workshop runs one application process with multiple request threads. It checks process restart, not simultaneous operation by several replicas. A module-level lock is allowed here, but would not coordinate two independent replicas. Record that limit in your handover.
The receiving system is also a simulator with a deliberately narrow contract. We are testing the production workflow on a small ordinary service, not proving a general ability to automate customer integrations.
Check your understanding
Question: Can the implementation use the email address alone as its duplicate key?
Answer: No. Two accounts can legitimately request the same email string, and one account can make separate operations with distinct keys. Deduplicating only by email changes the customer’s requested behavior.
Question: Why keep a test that fails on the original baseline?
Answer: It shows that the test can distinguish the requested behavior from the previous behavior. It is evidence that the change has work to do, rather than a test that both implementations satisfy by accident.
Your evidence: Keep the rejected baseline report. Add a short note naming the identity, the payload-conflict rule and the single-process limit. Those decisions belong to the task owner; the coding agent should not silently choose them.
Continue to building the candidate.