Build Log · June 21, 2026
Forge: The Handling Engine
Forge grew a brainstem — a handling engine that tells you exactly what to do next, Today view replacing Dashboard, mobile-first cards, attention-aware pipeline, and call workflow wiring.
- Forge
- UX
- Mobile
- Handling
- Twilio
- handling engine (lib/forge/handling.ts)
- 1
- Today view replacing Dashboard/Inbox
- 1
- call workflow wired (90s delay, cancelOn)
- 1
- mobile layout spins fixed
- 4
- unit test files added
- 2
handling engine (lib/forge/handling.ts)
Today view replacing Dashboard/Inbox
call workflow wired (90s delay, cancelOn)
mobile layout spins fixed
unit test files added
Timeline
Start
Mobile layout spins
Dashboard overflow, stat cards, horizontal scroll — fixed in sequence
30m
Call workflow
Twilio <Dial> forward, 90s Inngest delay, answeredCallDisposition, forge/lead.handled cancelOn
1hr
Auth + Twilio URL fix
getTenantId fallback removed, Twilio webhook URL built from NEXT_PUBLIC_APP_URL
1.5hr
Unit tests
auth.test.ts + twilioSignature.test.ts, fixed @/ alias resolution in vitest.config.ts
2hr
moveDeal triggerEvent decoupling
Stage names no longer drive Inngest events — triggerEvent column on pipeline_stages
2.5hr
Handling engine + Today view
lib/forge/handling.ts, TodayCard, attention-aware DealCard, Customers page, nav rename
Done
Forge v2 committed
All changes in forge/ subrepo
What shipped
lib/forge/handling.ts — getHandlingState(), getPrimaryActions(), extractMemoryText(), TONE_COLORS — pure functions, no DB writes
TodayCard component — vertical mobile layout, handling state badge always visible, contextual primary actions, Done/Snooze secondary
Today page — replaces Dashboard/Inbox/Home everywhere (nav, page title, dynamic date header)
Pipeline page — fetches most important active event per deal, passes handling + primaryActions into DealCard
DealCard — attention panel with handling label + primary action buttons when active event exists
Customers page (was Contacts) — active deals ordered by createdAt desc, excludes Paid/Complete, memory snippets via extractMemoryText()
ContactsList — client component with live search + memory snippet (violet, italic)
Call workflow — Twilio <Dial> forward to owner_phone, outcome callback to /api/webhooks/twilio/call-status
missedCallSms Inngest function — 90s delay, cancelOn forge/lead.handled so Gino calling back prevents awkward missed-call text
answeredCallDisposition — texts Gino after answered call asking what happened (new estimate / scheduled / not a lead)
leadStatus column on deals — new_lead → missed_call_queued → missed_call_sent → contacted / not_a_lead / awaiting_reply
forge/lead.handled fires on: stage move, markContacted, markNotALead, customer SMS reply
Needs Contact section on dashboard — unhandled missed calls with Got it / Not a lead one-tap buttons
getTenantId(): removed hardcoded fallback — missing org now redirects to sign-in
Twilio webhook: URL built from NEXT_PUBLIC_APP_URL (req.url is relative on Vercel)
vitest.config.ts: resolve.tsconfigPaths fixes @/ alias resolution
tests/unit/auth.test.ts — covers no-org-fallback case
tests/unit/twilioSignature.test.ts — covers URL construction logic
moveDeal: triggerEvent column on pipeline_stages — stage display names no longer drive Inngest events
migration 0005_thin_leader.sql — ALTER TABLE pipeline_stages ADD COLUMN trigger_event
Dashboard: compact Needs Contact list — one row per lead, no dead space
Multiple mobile layout fixes: overflow, stat card wrap, min-w-0 on flex children, height constraints removed
Deleted orphaned EventActions.tsx and LeadActions.tsx
PrimaryActionButton shared component in components/forge/ used by TodayCard and DealCard
“Forge grew a brainstem. It now knows what to do — not just what happened.”