Two AI Agents, Two Days, One Coffee Ops App
Two AI Agents, Two Days, One Coffee Ops App
Coffee for Peace already had a system. It just wasn't a good one.
Every kilo of green coffee that comes in from the highlands, gets roasted, packed, and sold — tracked manually in Google Sheets, updated by hand, automated with a tangle of Apps Scripts that nobody fully understood anymore. It worked until it didn't. It broke when someone forgot to update a cell. The Shopify store had its own inventory numbers that disagreed with the sheet. And the provenance story — which lot, which farmer, which cooperative — existed in someone's head, not in any queryable form.
The coffeeforpeace.com rebuild told that story publicly. This project is the backend that makes the story true.
Source: github.com/byronPantoja/kapeyapaan-ops
What This Actually Is
Not a new inventory concept — Coffee for Peace knew what needed tracking. What was missing was automation and a real interface.
The goal: replace the Sheets-and-Scripts workflow with a proper system. Operators log green lot arrivals, roast batches, and packing sessions from their phones on the production floor. That data flows into Postgres, syncs two-ways with Shopify, and surfaces in a desktop admin dashboard. Google Sheets stays in the loop as a read-only reporting mirror — still useful for accountants, still familiar to the team — but it's no longer the source of truth.
Three parts to the build:
- Supabase + Next.js for the database, auth, and app
- Shopify sync via Edge Functions — push new inventory when bags are packed, pull orders when they sell
- Two interfaces — a mobile-first operator app for logging production, a desktop-first admin dashboard for oversight
The data model underneath is relational and append-only. Operators can't edit what they wrote. Corrections go through an explicit admin flow that records both values and requires a reason. This isn't rigidity for its own sake — it's what makes the provenance chain trustworthy. If a roast batch can be silently edited, the QR code on the bag means nothing.
How the Build Happened
This is the part worth documenting.
The entire thing — database schema, RLS policies, operator forms, Shopify sync functions, admin dashboard, bootstrap script — was built in two days. Not two weeks. Two days.
The approach: write a detailed planning spec first, break it into four isolated plans, then run two AI agents in parallel against those plans.
Claude Code handled Plans 1 and 2 in git worktrees. Gemini CLI handled Plans 3 and 4 simultaneously. Neither agent was blocked waiting on the other. Where they needed to share interfaces — Server Action signatures, Supabase table names, TypeScript types — the spec was the contract. Both agents read the same document. Superpowers was what kept the parallel sessions coherent: shared planning format, structured task tracking, a consistent way to hand context between sessions.
What each plan covered:
Plan 1 (Foundation): Six Supabase migrations, RLS policies, triggers for per-bag unit generation, Google OAuth, route protection.
Plan 2 (Inventory Core): Operator forms for green lot arrivals, roast batches, and pack batches with multi-source blend support. Mobile-first UI, live stock view, provenance lookup by bag code.
Plan 3 (Shopify Sync): Four Edge Functions. Webhook receiver with HMAC verification. Push trigger on new pack batches via pg_net. Retry and reconciliation crons — the reconcile function detects drift but never auto-corrects, because silently overwriting a manual Shopify adjustment would defeat the purpose.
Plan 4 (Admin Dashboard): Metrics dashboard, audit log, sync health monitoring, user whitelist management, SKU catalog with Shopify variant mapping, and a one-time bootstrap script to map existing Shopify variants to database SKUs.
The planning spec said six weeks. Two AI agents read it and produced four shipping plans in two days.
What the Schema Got Wrong (Until I Asked)
One thing neither agent could fix: the spec was wrong about blending.
I designed the inventory stages as: green beans → roast batch → blend batch → pack batch. Blend batch was its own table with a blend ratio column.
When I walked through this with Byron, he stopped me at the third step. "We don't bulk-blend. Each individual bag is its own ratio. We pack a 250g bag of 80/20 by weighing 200g of Arabica and 50g of Robusta into that specific bag."
The entire blend batch stage didn't exist. The schema collapsed to something cleaner:
pack_batches— the packing session ("we packed 40 bags of 80/20 today")pack_batch_sources— one row per source roast batch consumed in that session- The ratio is derived:
kg_used / total_kg_usedper source
Fewer tables. More accurate. The AI built exactly what the spec described. The spec was wrong. That's the part that still requires a human conversation.
What Broke (And What That Means)
Three bugs caught during review — none caught by the agents.
Duplicate route. Both (admin)/page.tsx and (operator)/page.tsx resolved to / because Next.js route groups don't add URL segments. Build error. Fix: moved all admin pages under admin/ so they resolve to /admin/*.
Convention change. Next.js 16 renamed middleware.ts to proxy.ts. The old filename still ran but threw a deprecation warning. Only caught when I started the dev server. The AGENTS.md now tells agents to read node_modules/next/dist/docs/ before touching Next.js conventions.
Nullable key. The users table was keyed on user.id — a nullable UUID that only populates when someone signs in for the first time. Whitelisted users who haven't signed in yet all collapse to key={null}. Fixed by keying on user.email, which is the actual primary key. Caught in the browser console on first render.
None complex. All caught immediately. None caught by the agent that wrote the code. That's the pattern: the agent implements correctly from the spec; it can't catch what only appears at runtime.
Where Things Stand
This is an MVP in the testing phase, not a finished product.
What's working: operators can log production. The Shopify sync pushes and pulls. The admin dashboard shows metrics, sync health, and an audit trail. Google OAuth gates access. The append-only database refuses silent edits.
What's not working yet: the Google Sheets reporting mirror isn't built (that's the next plan). The QR code provenance pages aren't public-facing yet. The chat-based entry interface — where an operator types "finished a roast, 5kg of GL-2026-0007, light Arabica, got 4.2kg out" and gets a structured preview — is planned for v1.1.
The point of shipping now is to find out what's actually wrong. The Shopify sync may drift in ways the reconciliation function doesn't catch. The operator forms may be clunky in practice. The admin dashboard may be missing the metric that matters. You don't find that from a spec. You find it from using the thing.
What's Next
The Google Sheets mirror is the last piece of v1 — eight tabs, nightly Edge Function, service account credentials, overwrite-not-append policy. Once that's in, the accountant workflow is unchanged and the operational data is correct.
After that, the plan is a structured review: what did the system catch that the manual process missed? Where did operators hit friction? What does the Shopify drift report actually show? That review shapes v1.1.
The chat interface comes after that. The Server Actions are already shaped like tool definitions. Wiring Claude up as a tool-using agent is a thin layer on top of what's already there.
The manual system worked. This one should work better — and tell us what "better" actually means once people are using it.
This post is part of a series on building a web developer portfolio with AI-assisted development: The Plan · Salon Site · Shopify Storefront · Dashboard + Portfolio · Coffee for Peace