FDE Capstone: The Warehouse Got the Same Order Three Times
The warehouse got the same order three times
Goal
Implement a Python CLI that validates the customer's synthetic order CSV and makes reservations in a fictional warehouse. Even on a rerun of the same file or a lost response, it does not create duplicate reservations, and it hands off the unresolved items.
Why it matters
A successful API call and the completion of the customer's business are different. If you unconditionally resend just because you did not get a response, you can create an order that was already saved again. Conversely, if you block all retries, you lose a normal order that failed before saving. This lab deals with the input contract, the idempotency key, a persistent ledger, and a retry limit together. It is a fictional robot parts warehouse with no real shipments, payments, or customer information. You integrate only through the HTTP contract, without reading or modifying the other side's ledger file directly.
The expected time is 80 minutes. Extend with +time before the default 60-minute session ends (up to 180 minutes). When the session ends, the files in /root/handoff disappear. Keep the code you need separately in your personal workspace before it ends.
Materials and execution contract
- Customer brief: /opt/data/handoff-brief.md
- CLI, report, and handoff document contract: /opt/data/handoff-program-contract.md
- Synthetic orders: /opt/data/handoff-orders.csv
- Starter code: /opt/data/handoff-sync-starter.py
- Execution evaluator: /opt/lab/handoff/evaluate.py
First read the brief and the execution contract. The input is at most 256KiB and 100 rows, and the quantity is the characters 1–5. Prepare only the orders that were not held as the API body order_id, sku, and quantity. Exclude the email from the transmission, the log, and the report. Pass the evaluator the program path and a check name among plan, send, chaos, repeat, drift, and outage. Example: python3 /opt/lab/handoff/evaluate.py /root/handoff/sync.py plan The evaluator creates isolated temporary data and a server and shuts them down, so you do not need to start a separate server first. The evaluation data changes some of the order numbers, SKUs, and quantities. Do not hardcode the results of the provided CSV.
Steps
- Create /root/handoff and copy the starter code to sync.py. In scope.json, record the workflow warehouse-reservation, the business key order_id, send_pii=false, max_attempts=4, retryable_statuses=[503], conflict_policy=hold_order, and approval=review_only. The key names in the JSON are workflow, business_key, send_pii, max_attempts, retryable_statuses, conflict_policy, and approval.
- Implement the whole-CSV validation and the plan report in sync.py. The default mode makes 0 HTTP requests and outcomes is an empty array. An invalid row or conflict in the same order holds the whole order, and for identical content only the first row is a candidate and the rest are duplicates. Confirm with the plan check.
- Implement the --send branch of sync.py. On a normal server, POST only the candidates, and with GET /orders/, confirm exactly one reservation and its contents and ID. The seven fields of the report, the four fields of outcomes, and exit codes 0 and 2 follow the execution contract. Reconcile against the real ledger with the send check.
- In sync.py, implement a 1-second limit per request, the same order key, at most four attempts for 503 and connection errors, and a 0.1-second interval. Pass the chaos check so that it handles both a 503 before saving and a response lost after saving. Do not retry a 4xx.
- Make sure sync.py uses the same business key in the next run too. The repeat check restarts the server with the same ledger and reruns the same input. If new reservations increase or IDs change, fix the key that changes with every run and the retry branch.
- Implement sync.py so that it does not route around a changed quantity of an already reserved order with a new key. The drift check changes some quantities in the second run. A 409 must stop after one POST, be left as rejected and null, and keep the existing reservation.
- Make sure sync.py stops after four POSTs per order on a persistent 503 and records unconfirmed and null. Run the outage check. Do not mark even a result you could not confirm by lookup as a success.
- Run all six checks with the current implementation and make handoff.json. Using the evaluator's receipt mode, leave the implementation and sample hashes, the case list, the review_only decision, the unresolved invalid quantity and conflicting order, and the limits of the experiment. The final grading reads scope.json, sync.py, and handoff.json together and re-verifies the execution.
Notes
- A self-run example of plan mode: python3 /root/handoff/sync.py --input /opt/data/handoff-orders.csv --base-url http://127.0.0.1:8031 --report /root/handoff/plan.json
- The plan mode above does not connect to a server. To test --send directly, first use the fictional API start command in the brief.
- Generate the handoff: python3 /opt/lab/handoff/evaluate.py /root/handoff/sync.py receipt > /root/handoff/handoff.json
- If the implementation changes, verify and regenerate the handoff document again. Do not change only the hash.
- Passing verification is not a production approval. It is a single synthetic warehouse, and it is also not a separate security boundary that blocks a malicious program with the same UID.
Fix the scope of work from the customer's words
Distinguish the business key from the delivery event. For a conflict, instead of having an engineer pick the quantity, hold the whole order.
Classify the seven rows before sending
Read the whole file with csv.DictReader, group by order_id, and then classify. Match the CSV parser's default field limit to the input limit as well. If you send row 7 and then discover the conflict in row 8, it is too late.
Confirm a normal reservation by lookup
Reconcile the ID, SKU, and quantity between the POST response and the GET ledger. A send result with remaining holds is expressed with exit code 2.
Recover from a failure before saving and a lost response
Keep the same order_id as the idempotency key. The error kind, the number of attempts, and whether the business was confirmed are separate things.
The next owner reruns the same file
Check that the key has no mix of the current time or a run UUID. Not only the number of existing reservations but also the IDs must be the same.
Do not hide a changed quantity as a new order
If you use the body hash as the key, a changed quantity becomes a new reservation. Leave a 409 as a rejected result, not as a retry or a key swap.
Stop when the warehouse keeps being unwell
Even when you reach the maximum attempts, do not change it to a success state. A reservation ID that was not confirmed is null.
Hand off the execution evidence and the unresolved items
Re-verify the current code in receipt mode. If you fixed the code, do not fix only the hash; run the verification again.