Forge v2 — Session Summary

ForgeKit · 2026-06-17 · app.forgekits.build

Automations, test suite, brand identity system, splash page, and settings. Phase 0 + Phase 1 complete.

4 Live Automations

  • Missed call SMS auto-reply
  • Estimate follow-up (day 3, 7, 14)
  • Appointment confirmation
  • Review request (24h post-complete)

Automation Hub

  • Toggle sequences on/off
  • Inline step template editing
  • Trigger descriptions + step counts
  • Active state persisted to DB

Test Suite

  • Unit: config, formatting, rendering
  • API: real Neon DB (deals, notes, sequences)
  • E2E: Playwright stubs (auth, contacts, pipeline)
  • Sequential execution to avoid PK conflicts

Splash Page

  • Full-bleed hero (desktop + mobile images)
  • Auto-advancing feature carousel
  • Hero banner with brand copy
  • Auth-aware CTAs

Brand Identity System v1

  • Assets in /public/brand/forgekit/
  • Favicons (6 sizes) wired to layout
  • OG image for social previews
  • F-mark in nav, sidebar, footer

Settings Page

  • Editable org name
  • Your Mobile Number (owner alerts)
  • Forge SMS Number (Twilio outbound)
  • Server action + optimistic feedback

Pipeline Stats Bar

  • Pipeline value (open deals)
  • Estimates out count
  • Jobs scheduled count
  • Completed this month

Sidebar + Navigation

  • Logo links back to splash /
  • F-mark from identity system v1
  • Splash accessible to logged-in users
  • Dashboard → sign-in CTA adapts to auth state
Phase What it covers Status
Phase 0 — Core CRM Pipeline, contacts, auth, DB, demo data ✓ Done
Phase 1 — Messaging Twilio, Inngest sequences, Automation Hub ✓ Done (1 gap)
Phase 1 gap Inbound reply → note on deal + stop sequence ⚠ Next session
Prod env vars TWILIO + INNGEST keys needed in Vercel for live SMS ⚠ Next session
Phase 2 — Files + AI Photo upload (Vercel Blob), AI captions, dashboard stats → Up next
Phase 3 — Knowledge Document Q&A, pgvector RAG, second vertical Not started
Phase 4 — Platform Multi-tenant, Stripe, self-service onboarding Not started

Inngest v4 — Breaking API change in createFunction

The triggers array moved from a separate second argument into the first options object. TypeScript caught it at build time.

// v3 — broken in v4
createFunction({ id, name }, [{ event: 'forge/x' }], handler)

// v4 — correct
createFunction({ id, name, triggers: [{ event: 'forge/x' }] }, handler)

Rewrote all 3 sequence functions with the correct signature.

vi.mock hoisting — Doesn't work inside helper functions

Vitest hoists vi.mock() calls at compile time, but only when they appear at the top level of a test file. Wrapping them in a helper function (mockUnauthenticated()) meant they ran at call time, after the real mock had already been set, overriding it.

Moved all vi.mock calls to the top level of each test file directly.

Parallel test seed conflicts — Duplicate PK errors

Multiple test files seeding the same org concurrently caused duplicate primary key violations on pipeline_stages.

Added fileParallelism: false to vitest.config.ts. Sequential file execution eliminates the conflict.

dotenvx not overriding system DATABASE_URL

dotenvx run -f .env.test won't set a variable that already exists in the environment. The system had DATABASE_URL pointing at prod.

Added --overload flag to all test scripts. Forces .env.test values regardless of existing env.

Orphaned stage IDs across test runs

When the test org was deleted by slug (not by ID), pipeline_stages rows with fixed UUIDs remained. Re-inserting those UUIDs on the next run caused PK conflicts.

Seed helper now explicitly deletes by inArray(pipelineStages.id, FIXED_STAGE_IDS) before deleting the org.

vitest.config.ts type-checked by Next.js build

Next.js 16 picks up all .ts files in the project via its tsconfig glob. Vitest types don't match the Next.js compiler config — caused build failures.

Added vitest.config.ts, playwright.config.ts, and tests/** to tsconfig.json exclude array.

Splash image text bleed-through

The FORGEKIT splash PNG has a large wordmark in the lower half that competed with the page headline. The initial overlay (opacity-60 + from-black/40) wasn't strong enough.

Dropped image to opacity-30 and added a solid bg-[#0D1117]/70 overlay layer on top.

Mobile splash image cropped on both sides

The 1920×1080 desktop splash crops badly on portrait/mobile screens — text cut off on both sides.

Used the forgekit-splash-mobile-1170x2532.png from the identity pack. Responsive swap via md:hidden / hidden md:block.

Inngest for background jobs

The event-driven model is exactly right. Stage change fires an event, Inngest function handles sequencing and retries. step.sleep() for multi-day delays is clean and durable. Zero cron configuration needed.

⚙️

Server actions for mutations

Settings save, automation toggle, step template edit — all use server actions. No API route boilerplate. Form submit → action → revalidatePath → done. Clean and fast to write.

🧪

Real DB in API tests

Using the actual Neon database (not mocks) caught real constraint violations and orphan FK issues that mocks would have hidden. Sequential execution is acceptable given the correctness gains.

🎨

Identity system v1 as a real design system

Having SVGs, PNGs, feature cards, favicons, and brand tokens all organized with claude-implementation-notes.md made the splash page fast to build and consistent throughout.

🔄

Crossfade carousel without a library

All slides rendered with position:absolute, active at opacity:1, others at opacity:0. CSS transition-opacity handles the fade. SSR-compatible, works with Next.js Image, no external dependency.

📱

owner_phone vs twilioNumber distinction

Correctly separating the business owner's personal cell (where Forge sends alerts) from the Twilio outbound number (what customers receive texts from) is the right data model — two different directions of communication.

1

Inngest v4 function signature

triggers is inside the first object argument, not a separate array. Always check Inngest docs before writing createFunction.

2

vi.mock must be top-level

Never wrap vi.mock in a helper function — it won't hoist. Put it at the top of each test file directly.

3

dotenvx --overload for test env

Without --overload, dotenvx won't override an already-set DATABASE_URL. Always use --overload in test scripts.

4

Vitest sequential for shared DB

Any test suite hitting a shared database needs fileParallelism: false. Parallel seed operations cause PK conflicts.

5

Exclude test configs from Next.js tsconfig

Add vitest.config.ts, playwright.config.ts, and tests/** to tsconfig.json exclude. Next.js 16 will otherwise type-check them and fail the build.

6

Inngest step.sleep() for multi-day delays — never raw Vercel Cron

step.sleep('label', '72h') creates a durable checkpoint. No process held, retries managed by Inngest. Raw cron would require scanning the full message log on every tick.

discovery.md

  • Forge v2 full retro added
  • v1 summary preserved

app-inventory.md

  • Forge section added (full)
  • Cross-app AI table updated

reusable-patterns.md

  • 7 new patterns added
  • Inngest, server actions, carousel, image swap, owner_phone split, real-DB tests, vertical config

extraction-backlog.md

  • Inngest sequence engine → forgekit-core
  • Vitest seed helper → forgekit-testing

technical-roadmap.md

  • Phase 0 + Phase 1 marked done
  • Health table updated
  • Remaining gap noted

build-playbook.md

  • Testing Rules section added
  • Background Jobs section added
  • v2 retro entry added

Open Items

Required

Set Twilio env vars in Vercel — TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER. Without these, no SMS fires in production. Use Vercel dashboard only (never CLI — BOM corruption risk).

Required

Set Inngest env vars in Vercel — INNGEST_EVENT_KEY, INNGEST_SIGNING_KEY. Without these, Inngest runs in dev mode only. Sequences won't persist between deploys.

Phase 1 gap

Inbound reply → note on deal + stop sequence — The Twilio webhook receives inbound messages but doesn't yet match the caller's phone number to a contact, write a Note on their active deal, and stop the running sequence. This is the last piece of Phase 1.

Phase 2

Photo upload on deals (Vercel Blob) — Before/after job photos attached to a deal. Claude generates an AI caption on upload. Side-by-side display on deal page.

Phase 2

Dashboard stats screen — One screen showing Gino's business health: open estimates, jobs this week, unpaid count, revenue this month.