You will write a one-page spec for a feature, get an implementation plan from an AI, catch two deliberately bad steps in a provided "teammate plan", and only then let the agent implement. The skill being trained is the steer-and-intervene loop: engineers who review plans before code catch mistakes at the cheapest possible moment.
This is how experienced teams run Copilot agent mode and Claude Code plan mode in production, and it is the difference between agentic speed and agentic mess.
~75 minutes hands-on · Core.
Fully inline scaffold. Create a folder orders-svc/ with these three files, then pip install fastapi uvicorn pytest httpx.
app.py:
from fastapi import FastAPI, HTTPException
app = FastAPI()
ORDERS = {}
NEXT_ID = {"v": 1}
@app.post("/orders")
def create_order(payload: dict):
if "sku" not in payload or "qty" not in payload:
raise HTTPException(422, "sku and qty required")
oid = NEXT_ID["v"]; NEXT_ID["v"] += 1
ORDERS[oid] = {"id": oid, "sku": payload["sku"],
"qty": int(payload["qty"]), "status": "placed"}
return ORDERS[oid]
@app.get("/orders/{oid}")
def get_order(oid: int):
if oid not in ORDERS:
raise HTTPException(404, "not found")
return ORDERS[oid]
test_app.py:
from fastapi.testclient import TestClient
from app import app, ORDERS, NEXT_ID
def setup_function(f):
ORDERS.clear(); NEXT_ID["v"] = 1
client = TestClient(app)
def test_create_and_get():
r = client.post("/orders", json={"sku": "A1", "qty": 2})
assert r.status_code == 200 and r.json()["id"] == 1
assert client.get("/orders/1").json()["status"] == "placed"
def test_missing_fields():
assert client.post("/orders", json={"sku": "A1"}).status_code == 422
FEATURE.md — the ticket you must implement:
Add idempotent order creation. Clients send an Idempotency-Key header on
POST /orders. Same key + same payload => return the original order, do not
create a duplicate. Same key + different payload => 409. Keys expire after
24h (design for it; a per-key stored timestamp is enough — no background
job). No new dependencies. Existing tests must keep passing.
Verification checklist: pytest -q shows 2 passed; the service runs under uvicorn app:app.
SPEC.md: behaviour table (key present/absent × payload same/different), storage shape, error contract, what is explicitly out of scope, and the test list you expect to exist afterwards. A spec without a test list is a wish.PLAN (agent-generated, review before approving):
1. Add an IDEMPOTENCY dict mapping key -> (payload_hash, order_id, created_at).
2. In create_order, if the key exists, return the stored order immediately.
3. Hash the payload with Python's built-in hash() on the sorted items for
comparison.
4. On payload mismatch for an existing key, return 409.
5. To keep responses fast, check and store the idempotency record before
validating sku/qty.
6. Add tests for replay, conflict, and no-header behaviour.
SPEC.md and the code, and ask for a numbered file-level plan. Decision point: diff it against the teammate plan — does it repeat either trap? If yes, correct it in your reply and demand a revised plan; do not implement from a plan you haven't corrected.pytest -q.The plan is the cheapest artifact you will ever review. Every minute spent correcting a plan saves ten spent debugging its implementation.
pytest -qoriginal 2 tests plus ≥4 new tests pass (replay returns same id; conflict → 409; no header → normal behaviour; both trap-regression tests).curl -X POST calls with the same Idempotency-Key return the same id, and GET /orders/2 is 404.Keep SPEC.md, your written trap critique, the corrected plan, and the final diff (git diff output). Self-grading rubric:
Pass = 7/10.
setup_function reset must also clear your new idempotency store — add it there, not by reordering tests.In restricted environments, run the identical lab on the sanctioned tool (Copilot Business agent mode, or an in-tenancy model via Continue.dev with a Bedrock/Azure key). If no agent mode is sanctioned, the lab still works: chat produces the plan, you implement by hand — the spec-and-plan-review discipline is the deliverable, not the keystrokes. Never paste real client specs into unsanctioned tools; this scaffold is clean-room, which is exactly why practice belongs here.
hash() is salted per process for strings (PYTHONHASHSEED), so payload hashes are not stable across restarts or workers — identical payloads would 409 after a redeploy. Correct: a deterministic digest, e.g., hashlib.sha256 over json.dumps(payload, sort_keys=True).