RIG
A solo contractor cannot answer the phone from inside a crawlspace, and writing a quote after a ten-hour day means it goes out late or wrong. RIG is the AI layer that answers those calls, drafts the quote from a single photo, and keeps every job in one feed — so the business side runs while the contractor stays on the tools.
RIG · EARLY-ACCESS LANDING PAGE
Why this exists
Solo trade operators — the target user is one person doing field work and back-office work alone — lose a large share of inbound calls simply because they cannot hold a phone and a wrench at once. What gets through still has to turn into a quote, and typing one up after a full day of jobs is slow enough that bids go out late or get skipped. RIG exists to take that second job off their hands.
1 # What was broken
2
3 - missed calls while on the tools = lost leads
4 - quotes take hours of typing after a full day
5 - job notes, photos, client replies — all scattered
6
7 > goal: an AI layer that runs the business side
The decisions I made
Firestore is the single source of truth, read through onSnapshot listeners instead of REST — a job's status change reaches every screen live, with no polling. Every stored estimate pins the pricebook and labor-model version it was priced against, so a price update tomorrow never rewrites a quote a client already accepted. We built it as a two-person team, working with a 9-skill agent team of my own design — a task orchestrator that routes work to specialist agents (estimation, field ops, UI, platform) behind a quality gate before anything ships.
1 [web/PWA] ──▶ [Firestore] ◀── onSnapshot
2 │
3 ┌─────────┴────────┐
4 [Gemini vision] [Vapi voice]
5 photo → quote call → job (n8n)
6
7 // dev: 9 agent skills · 1 task orchestrator
8 // trade-off: realtime listeners over simple REST
The part I'm proud of
The estimator never lets the AI silently pick a price. Every material line is resolved through a matching cascade — exact code, exact name, known synonym, fuzzy token match — each step stamped with its own confidence, and a line only auto-prices once that confidence clears a threshold. Below it, anything unmatched falls back to a historical or trade-basket median rather than guessing. Nothing gets a number without a reason attached to it.
1 // never price a line without a reason
2 if (exactCode) return { method: "exact-code", confidence: 1 };
3 if (exactName) return { method: "exact-name", confidence: .98 };
4 if (exactSynonym) return { method: "exact-synonym", confidence: .92 };
5 if (nearest.score >= 0.72)
6 return { method: "token-match", confidence: nearest.score };
7 return { method: "unmatched", confidence: nearest.score || 0 };
8
9 // below 0.85 → hybrid or historical median, never a guess
What changed afterwards
RIG went through a real mentor review inside a startup incubator, and its early-access landing page is live while the app gets ready to ship — the review is also where I found out the hard way that a materials catalog with 6 entries and a reservation flag hardcoded to true do not survive contact with an actual reviewer. Both are fixed now, alongside a wider catalog and labor-rate set. What I'd do differently: write the failure paths — the call that isn't a booking, the material that doesn't match anything — before the happy path, not after a deadline forces the audit.
1 ## Before → After mentor review
2
3 reservationIntent hardcoded true → detected
4 material catalog 6 items → 46, 9 trades
5 labor templates 4 → 16, w/ PT synonyms
6
7 ### What I'd do differently
8 test the failure paths before the happy path