⌂ Home
Gemini Code Assist: AI Pair Programming in Your IDE
Domain Capstone · API
⏰ 3-5 hours 🏆 Capstone Project
📦
Capstone — TrackFlow: B2B Order Tracking API Build the API a wholesale distributor's customers hit 10,000 times a day: purchase-order lifecycle with an enforced state machine, pluggable carrier tracking, and SLA breach detection. Prerequisite: modules G00–G09.

Capstone: TrackFlow

"Where's my order?" is the most expensive question in B2B commerce — every call to a human rep costs ~$8–15. The answer is an API. You're building it.

Domain Briefing: The Purchase-Order Lifecycle

The Domain, Plainly

A restaurant chain sends your distributor a purchase order (PO)A buyer's formal document requesting goods: PO number, line items with SKUs and quantities, requested delivery date. In B2B, the PO number is the key both sides use for everything. for 40 cases of cooking oil. From that moment, the order moves through a strict lifecycle: submitted, confirmed against inventory, picked in the warehouse, handed to a carrier, delivered. Two things make this harder than a shopping-cart tutorial. First, not all transitions are legal — a DELIVERED order cannot become CANCELLED, an order on credit hold can't ship — and every illegal transition that slips through becomes a financial dispute. Second, B2B contracts carry an SLAService Level Agreement — a contractual promise, e.g. "orders confirmed by 2pm ship within 2 business days." Breaches often trigger penalty credits.: ship within N days of confirmation or owe the customer credits. Distributors live and die by catching breaches before the customer's procurement system does.

Why This Is a Real API Design Test

This project is three durable API patterns in one: a state machine over HTTP (the transition endpoint), the adapter pattern (multiple carrier APIs behind one interface — the same shape as payment gateways and SMS providers), and computed business views (the SLA breach report). Master these three and most CRUD-plus services you'll ever build are variations.

What You Build

The Spec

trackflow — a Flask + SQLite API with:

  • PO creation with line items; an enforced lifecycle state machine with audited transitions
  • Two mock carrier adapters (FastShip, EconoFreight) behind one interface — tracking events per shipped order
  • An SLA engine: promised-ship-date computed at confirmation; GET /sla/breaches reports at-risk and breached orders
  • Full pytest suite (state machine exhaustively tested); GEMINI.md + .aiexclude from day zero; carrier adapter #2 shipped via agent mode; PR-reviewed before merge
SUBMITTED CONFIRMED PICKING SHIPPED DELIVERED ON_HOLD (from SUBMITTED/CONFIRMED/PICKING → back to where it was) CANCELLED (terminal; only before SHIPPED)
The Rules That Make It Real (encode ALL of these)
  • Forward-only happy path; no skipping states (SUBMITTED → SHIPPED is illegal).
  • ON_HOLD remembers and restores the previous state on release.
  • CANCELLED is allowed from SUBMITTED/CONFIRMED/PICKING/ON_HOLD — never from SHIPPED or DELIVERED.
  • Illegal transition → 409 Conflict with {"error": "...", "current_state": "...", "allowed": [...]}.
  • Every transition appends to an immutable order_events audit table (who, when, from, to).
  • SHIPPED requires a carrier + tracking number in the request body.

Executing This Capstone in VS Code / PyCharm

Everything in this project is built through Gemini Code Assist inside your IDE — never by copying finished code from elsewhere. Each phase uses some of these five moves; every paste-block below names its move in the header.

Before you start (5-point checklist)
  1. Create the project folder and open it: VS Code File → Open Folder (PyCharm: File → Open). New files via the Explorer panel / Project tool window.
  2. Confirm Gemini Code Assist is signed in: the ✦ icon in VS Code's Activity Bar opens the panel with no error badge (PyCharm: the Gemini tool window on the right edge greets you by account). Setup is module G01.
  3. Open the integrated terminal for git, Flask, curl, and pytest: Ctrl+` in VS Code (PyCharm: Alt+F12). Run the Flask server in one terminal tab and curl from a second (the + button splits/creates tabs).
  4. After creating GEMINI.md in Phase 1, start a new chat so the rules load into context (the G05 lesson).
  5. Chat and smart commands act on the active file or selection — keep the file you're working on focused, and select code before asking about it (the G03 gotcha).
MoveVS CodePyCharm / IntelliJ
1 · Spec-commentType the comment in the editor and pause — ghost text appears. Tab accepts, Ctrl+→ accepts word-by-word, Esc rejects (G02)Same: type, pause, Tab; hover the suggestion to cycle alternatives
2 · Chat / smart cmdClick the ✦ icon in the Activity Bar → chat panel opens. Select code first to scope it; /explain /fix /doc /simplify work here (G03)Gemini tool window (right edge) → Chat tab; same slash commands
3 · Generate testsSelect the function/class → right-click → Gemini Code Assist → Generate unit tests; save the output into tests/ (G04)Select → right-click → Gemini → Generate unit tests
4 · Agent ticketIn the chat input, switch the Agent toggle ON → paste the ticket → review the plan in the panel → Accept/Reject each diff → click Allow when it asks to run a command (G06)Gemini tool window → Agent tab → paste the ticket; leave Auto-approve changes OFF; review diffs the same way
5 · Headless CLIIntegrated terminal: gemini -p "..." — same login, same GEMINI.md rules as the IDE (G08)

Example: a block labeled "Move 4 · paste in Agent mode" means: open the Gemini panel, flip the Agent toggle (VS Code) or open the Agent tab (PyCharm), paste, and work the plan→diff→approve loop.

API Contracts

endpoint contracts — this is your GEMINI.md "API rules" section
POST /orders
  body: {"po_number": "PO-88412", "customer": "Riverside Restaurants",
         "lines": [{"sku": "OIL-CAN-5L", "qty": 40, "unit_price": 18.50}]}
  201 + order | 400 invalid | 409 duplicate po_number

GET /orders/<po_number>            200 (order + events + sla block) | 404
GET /orders?state=SHIPPED&customer=  200 filtered list

POST /orders/<po_number>/transition
  body: {"to": "SHIPPED", "actor": "warehouse-3",
         "carrier": "fastship", "tracking_number": "FS123456"}  (carrier fields req. for SHIPPED)
  200 + updated order | 409 illegal transition | 400 missing fields | 404

GET /orders/<po_number>/tracking   200 carrier events via adapter | 409 if not yet SHIPPED

GET /sla/breaches?as_of=2026-06-15
  200 {"breached": [...], "at_risk": [...]}   # at_risk = due within 1 business day
SLA rule: promised_ship_date = confirmation date + 2 business days (skip Sat/Sun). Breached = not SHIPPED by end of promised date. WHY business days matter: naive date math flags every Friday order as breached on Sunday — the classic bug this capstone wants you to catch with a test.

Build Phases

PhaseWhat happensSkills from
1. FoundationGEMINI.md with contracts + rulesG05
2. CoreOrders + state machine + auditG02–G03
3. TestsExhaustive transition matrixG04
4. CarriersAdapter #1 by hand, #2 by agentG02, G06
5. SLA + ShipBreach engine, PR review, deployG06, G09–G10
About the "expected output" blocks

Each phase shows a sample of success. Your generated code will phrase things differently — match the shape: the status codes, the JSON fields named in the contracts, and the invariants (64 matrix cases, 409 bodies that include allowed). Run the server with python app.py in one terminal tab and curl from a second.

Foundation: the contract is the GEMINI.md

New repo trackflow/. Paste the endpoint contracts and the six state-machine rules into GEMINI.md verbatim, plus your standing conventions (storage layer separation, JSON errors, parameterized SQL, type hints, pytest temp-DB fixture). Add .aiexclude (*.db, .env). Commit before writing any code — in this capstone, the spec quality directly determines how little correcting you'll do later.

Core: the state machine deserves its own module

Build in this order, each via spec-comments with immediate verification: storage.py (orders, order_lines, order_events tables), then lifecycle.py — a pure module with no Flask and no SQL:

Move 1 · type in the editor (lifecycle.py), pause for ghost text, Tab to accept
# Pure state machine for PO lifecycle. No I/O.
# TRANSITIONS maps each state to the set of legal next states per the
# six rules in GEMINI.md (ON_HOLD restores previous state; CANCELLED
# only before SHIPPED; terminal states have no exits).
# validate_transition(current, to, previous_state=None) returns None if legal,
# else a string reason. Raises ValueError on unknown state names.
TRANSITIONS: dict[str, set[str]] = {

Then app.py routes that only orchestrate: validate input → call lifecycle → call storage → map results to status codes. Use chat on your own transition table (Move 2: select the TRANSITIONS dict in the editor, open the ✦ panel, type /explain plus the question): "list every (from, to) pair this table allows — do any violate the six rules?" — an AI audit of AI-written code, with you as the judge.

Tests: the matrix, exhaustively

Move 3, with a twist: select the whole of lifecycle.py → right-click → Gemini Code Assist → Generate unit tests — then refine the result in chat (Move 2) with an explicit demand: "a parameterized pytest that checks EVERY (from, to) state pair — all 8×8 — against the expected legality, plus the ON_HOLD restore behavior and the SHIPPED-requires-carrier rule at the API level." 64 combinations is exactly what humans skip and generators don't.

expected output — tests, the happy path, and a correct 409
PS> pytest tests/ -q
.........................................................................  [100%]
73 passed in 0.84s        # 64 matrix cases + API/lifecycle tests

PS> curl -X POST http://localhost:5001/orders -H "Content-Type: application/json" -d "{\"po_number\":\"PO-88412\",\"customer\":\"Riverside Restaurants\",\"lines\":[{\"sku\":\"OIL-CAN-5L\",\"qty\":40,\"unit_price\":18.50}]}"
{"po_number":"PO-88412","state":"SUBMITTED","customer":"Riverside Restaurants",
 "lines":[{"sku":"OIL-CAN-5L","qty":40,"unit_price":18.5}],
 "created_at":"2026-06-11T09:12:44"}                          <-- 201

PS> curl -X POST http://localhost:5001/orders/PO-88412/transition -H "Content-Type: application/json" -d "{\"to\":\"SHIPPED\",\"actor\":\"warehouse-3\"}"
{"error":"illegal transition SUBMITTED -> SHIPPED",
 "current_state":"SUBMITTED",
 "allowed":["CONFIRMED","ON_HOLD","CANCELLED"]}               <-- 409

PS> curl http://localhost:5001/orders/PO-88412
{"po_number":"PO-88412","state":"SUBMITTED", ...,
 "events":[{"from":null,"to":"SUBMITTED","actor":"api","at":"2026-06-11T09:12:44"}],
 "sla":{"promised_ship_date":null,"status":"NOT_CONFIRMED"}}  <-- 200
Checkpoint The parameterized matrix test exists and passes; a curl sequence SUBMITTED→CONFIRMED→PICKING→SHIPPED→DELIVERED works; SUBMITTED→SHIPPED returns 409 with the allowed list; the events table shows the full audit trail.

Carriers: hand-build one, delegate the second

Write carriers/base.py (an abstract CarrierAdapter with get_tracking_events(tracking_number) -> list[dict]) and carriers/fastship.py yourself with comment-driven generation (Move 1) — a mock returning deterministic fake events (created → in_transit → out_for_delivery → delivered, derived from a hash of the tracking number so tests are stable). Then hand the pattern to the agent (G06):

Move 4 · paste in Agent mode (Agent toggle ON / Agent tab)
Add a second carrier adapter "econofreight" following the existing
CarrierAdapter pattern in carriers/. Differences: EconoFreight event
payloads use {"status_code": "IT", "ts": ...} style codes (map them to our
normalized events), and its mock should simulate a 2-day-slower timeline.
Register it in the carrier factory, make POST .../transition accept it,
and add tests mirroring the fastship ones. Plan first.
expected output — normalized events, regardless of carrier
# after transitioning PO-88412: CONFIRMED -> PICKING -> SHIPPED (carrier fastship, FS123456)
PS> curl http://localhost:5001/orders/PO-88412/tracking
{"po_number":"PO-88412","carrier":"fastship","tracking_number":"FS123456",
 "events":[
   {"status":"created",          "at":"2026-06-09T08:14:00"},
   {"status":"in_transit",       "at":"2026-06-10T02:31:00"},
   {"status":"out_for_delivery", "at":"2026-06-11T07:05:00"}]}     <-- 200

# an econofreight order returns the SAME event vocabulary —
# its IT/OFD status codes were mapped inside the adapter:
{"po_number":"PO-88436","carrier":"econofreight","tracking_number":"EF777102",
 "events":[
   {"status":"created",   "at":"2026-06-08T11:02:00"},
   {"status":"in_transit","at":"2026-06-10T19:44:00"}]}            <-- 200

# tracking before SHIPPED:
{"error":"PO-88451 is in state PICKING; tracking available after SHIPPED"}  <-- 409
Checkpoint GET /orders/<po>/tracking returns normalized events for orders shipped on either carrier — proof the adapter seam works. The agent's diff stayed inside carriers/, the factory, and tests; if it "improved" unrelated code, you rejected those hunks.

SLA engine, review gate, ship

Build sla.py (business-day math — write the Friday-confirmation test FIRST, then generate the implementation until it passes) and the /sla/breaches endpoint. Then the full G09–G10 finish: push to GitHub with .gemini/config.yaml + styleguide.md (encode the 409 contract and audit-trail rule as "must flag"), open the SLA feature as a PR, process the bot review, merge clean — and optionally deploy to Cloud Run (the G10 prep checklist applies unchanged: requirements.txt, Procfile, PORT, ephemeral-SQLite caveat).

expected output — the SLA report, including the Friday case
PS> pytest tests/test_sla.py -q
........                                                       [100%]
8 passed in 0.21s
# the test that matters: confirmed Friday 2026-06-12 ->
# promised_ship_date == Tuesday 2026-06-16 (weekend skipped, NOT Sunday 06-14)

PS> curl "http://localhost:5001/sla/breaches?as_of=2026-06-15"
{"breached":[
   {"po_number":"PO-88371","customer":"Marina Foods","state":"CONFIRMED",
    "promised_ship_date":"2026-06-10","days_late":3}],
 "at_risk":[
   {"po_number":"PO-88393","customer":"Hilltop Cafes","state":"PICKING",
    "promised_ship_date":"2026-06-15"}]}                       <-- 200

Grading Rubric

Stretch Goals

  • Webhooks: POST /webhooks/carrier receiving simulated carrier status pushes that auto-transition SHIPPED→DELIVERED — with signature verification.
  • Idempotency keys: make POST /orders safe to retry via an Idempotency-Key header (real procurement systems retry aggressively).
  • Pagination + ETags: page the order list and add conditional GET — ask chat to explain the trade-offs first.
  • Front it: the Frontend capstone builds an ops dashboard directly on this API — do them as a pair for a full-stack portfolio piece.
Where this goes on a resume

"Designed a B2B order-tracking API with an audited lifecycle state machine, pluggable carrier adapters, and SLA breach detection; exhaustive parameterized test coverage; AI-assisted development with human-reviewed agent workflows and automated PR review."