Imported from YOYOMAII/foundry (
AGENTS.md). Install upstream withnpx skills add YOYOMAII/foundry. Copyright stays with the author.
This is NOT the Next.js you know
This repo uses Next.js 16. APIs, conventions, and file structure may differ from training data. Before writing Next.js code, read the relevant guide in node_modules/next/dist/docs/.
Foundry Agent Instructions
Read this file and docs/FOUNDRY_PRODUCT_SPEC.md before implementing features.
Package Manager
Use pnpm, not npm or yarn. This repo is pnpm-only.
pnpm install
pnpm run dev
pnpm run lint
pnpm run build
Do not run npm install, npm run, npx, or yarn. Use pnpm exec or pnpm dlx instead of npx.
Product Contract
Foundry turns one user idea into a public startup system:
- business understanding
- market research
- BrandDNA
- three brand directions
- selected direction
- Website JSON
- marketing assets
- launch plan
- Vercel deployment
BrandDNA is the source of truth. Any generated artifact that conflicts with BrandDNA must be corrected or regenerated.
Current Stack
- Next.js 16 App Router
- React 19
- TypeScript
- Tailwind CSS 4
- Clerk for auth
- Supabase Postgres for data
- Gemini on Vertex AI for generation
- Exa for research
- Inngest for workflows
- Vercel for public publishing
- E2B optional for sandbox validation
Implementation Rules
- Use Server Components by default.
- Use Client Components only for browser interactivity.
- Put Route Handlers under
app/api/**/route.ts. - With Next.js 16 and Clerk, use
proxy.tsfor auth middleware patterns. - Do not call Route Handlers from Server Components for internal data access. Query the server-side data layer directly.
- Keep generation prompts, provider keys, Supabase secret keys, Clerk secret keys, Vercel tokens, and Exa keys server-only.
- Only variables intended for browser use may start with
NEXT_PUBLIC_. - Validate generated JSON with schemas before saving it as accepted output.
- Store citations for research-backed claims.
- Do not fake deployment. Mark a deployment
liveonly after the public URL is verified. - Build the generated-site editor as an in-app browser-style preview with direct editing controls. Users should be able to change colors and simple CSS theme tokens without touching code.
- Do not add Stripe, paid checkout, booking payments, or ecommerce payment flows yet.
Database Migrations
Schema changes are managed with Supabase migrations in supabase/migrations/, not Prisma Migrate. Prisma is the ORM only (prisma/schema.prisma must stay aligned with applied SQL).
supabase migration new <name> # create SQL file
pnpm run db:migrate # push to linked remote project
pnpm run db:migrate:status # compare local vs remote
pnpm run db:generate # regenerate Prisma client
Do not run prisma migrate deploy or prisma db push against production — they bypass Supabase migration history and cause drift.
Supabase Rules
Clerk is the auth provider. Supabase is the database.
- Prefer server-only Supabase access for the MVP.
- Use Supabase publishable and secret keys for new work.
- Never expose a Supabase secret key in client code.
- Enable RLS on exposed tables.
- Do not grant Data API access to internal tables.
- If a table must be reachable through the Data API, add explicit grants and RLS policies in migrations.
- Do not use user-editable metadata for authorization.
- Do not use
SECURITY DEFINERto bypass permission problems unless there is a reviewed, documented reason.
Supabase changed key handling and table exposure behavior in 2026. Check current Supabase docs before changing database access, keys, RLS, or grants.
Workflow Rules
Use Inngest for the long-running generation pipeline.
Expected events:
project.createdbusiness.extractedresearch.completedbrand_dna.generatedbrand_direction.selectedwebsite.generatedmarketing.generatedlaunch_plan.generateddeployment.requesteddeployment.completedproject.failed
Pause after generating three brand directions. Resume only after the user selects a direction.
Data Integrity
Every generated artifact must store:
project_idbrand_dna_version_idsource_direction_idwhen applicablegenerated_bygenerated_atvalidation_status
Do not mutate existing BrandDNA versions. Create a new version and regenerate dependent artifacts.
Public Website Rules
Public URLs follow:
https://<slug>.tryfoundry.app
Slug rules:
- lowercase ASCII
- letters, numbers, and hyphens only
- max 63 characters
- no reserved names
- collision-safe suffixes
The first implementation should prefer one Vercel-hosted public renderer with a wildcard domain. Later per-site deployments can be added behind the same deployment record interface.
Generated websites must be editable before publishing. The MVP editor should support:
- live preview in a browser-like frame
- BrandDNA color token edits
- typography token edits
- section background and accent color edits
- copy and CTA edits
- save draft, preview published, publish
- undo or version history for generated website changes
Simple visual edits should update Website JSON theme tokens and CSS variables, not generate arbitrary untracked CSS.
File Ownership
Recommended structure:
app/
(app)/
(public)/
api/
components/
inngest/
lib/
ai/
auth/
brand/
research/
supabase/
validation/
vercel/
types/
supabase/
migrations/
Keep product contracts in types/ and validation schemas close to the code that uses them.
Tailwind CSS 4 Syntax
This repo uses Tailwind CSS 4. CSS variable references use the shorthand syntax, not the var() wrapper.
| Tailwind v3 (wrong) | Tailwind v4 (correct) |
|---|---|
text-[var(--brand-accent)] |
text-(--brand-accent) |
bg-[var(--brand-background)] |
bg-(--brand-background) |
border-[var(--brand-border)]/30 |
border-(--brand-border)/30 |
Using the v3 syntax generates lint warnings. Run pnpm run lint and fix any [var(--X)] before merging.
Next.js 16 Middleware
In Next.js 16, the middleware file is named proxy.ts (not middleware.ts). This is a framework-level rename; the code is identical to a middleware.ts. Do not create a middleware.ts — it will conflict with or shadow proxy.ts.
proxy.ts handles:
- Clerk auth protection for
(app)routes - Tenant subdomain rewriting (
slug.tryfoundry.app → /site/slug) FOUNDRY_DEV_AUTH_BYPASSfor local development
Route Groups
The app has three route groups. Do not add routes to the wrong group.
| Group | Path | Purpose |
|---|---|---|
(app) |
/dashboard, /projects/** |
Authenticated Foundry product — uses full app chrome, dark theme |
(public) |
/site/[slug] |
Customer-facing generated websites — NO app chrome, brand-themed |
(marketing) |
/, /login, /sign-in, /sign-up |
Foundry marketing/auth pages |
Each group has its own layout.tsx. When adding new pages, confirm which group they belong to.
Public Site CSS Isolation
Critical: app/globals.css sets html, body { height: 100%; overflow: hidden } and body { background: #101014 } for the Foundry dashboard. Without isolation, customer sites are completely unscrollable and show the dark app background.
app/(public)/layout.tsx resets these with a <style> block:
html, body {
height: auto !important;
min-height: 100vh !important;
overflow: auto !important;
background: transparent !important;
}
Never remove this layout or its style reset. Customer sites become broken without it.
Additionally, SiteShell in lib/website/section-view.tsx uses h-dvh overflow-y-auto scroll-smooth to be self-scrolling. This works both on the public site AND inside the Puck editor iframe (which keeps body overflow: hidden). Do not change SiteShell to min-h-screen without keeping overflow-y-auto.
Brand CSS Variables
All customer site styling uses --brand-* CSS custom properties set as inline styles on SiteShell. Do not use hard-coded colors or Tailwind palette classes in site renderer components.
| Variable | Default | Purpose |
|---|---|---|
--brand-primary |
#111111 |
Primary brand color |
--brand-secondary |
#333333 |
Secondary/muted section backgrounds |
--brand-accent |
#0066cc |
CTAs, highlights, badges |
--brand-background |
#ffffff |
Page and card backgrounds |
--brand-foreground |
#111111 |
Body text |
--brand-muted |
#666666 |
Secondary text, nav links |
--brand-border |
#e5e5e5 |
Borders, dividers |
--brand-font-heading |
Inter/system-ui | Heading typeface |
--brand-font-body |
Inter/system-ui | Body typeface |
Shorthand Tailwind usage: text-(--brand-accent), bg-(--brand-background)/90.
Website Section Types
lib/website/section-view.tsx renders these section type values. If you add a new type, update both the AI generation prompt in lib/ai/prompts/index.ts AND the renderer switch.
type |
Component | Required fields |
|---|---|---|
hero |
HeroSection |
headline, subheadline |
problem |
ItemGridSection (muted bg) |
items[] |
solution |
ItemGridSection (accent border) |
items[] |
features |
ItemGridSection |
items[] |
pricing |
PricingSection |
plans[] |
faq |
FaqSection |
items[] with q/a or question/answer |
final_cta |
FinalCtaSection |
headline, subheadline |
* (default) |
ItemGridSection |
falls back gracefully |
Sections with hidden: true are skipped. Sections without a required array (plans, items) silently return null.
Publish Flow
The publish endpoint does NOT directly set isPublished. It dispatches an Inngest event. The actual persistence happens in lib/pipeline/run-step.ts.
Full flow:
- User clicks Publish →
POST /api/projects/[id]/publish - API dispatches
dispatchWebsiteValidate(projectId, draftId, "publish")via Inngest - Inngest runs
validateAndRepairWebsiteVersion()then calls the publish step lib/pipeline/run-step.tssetsisPublished: true,publishedAt: new Date()revalidatePublishedSite(slug)is called to bust Next.js ISR cacheGET slug.tryfoundry.appnow serves the new content (cache TTL 1 hour otherwise)
If you add a new way to change published content, you MUST call revalidatePublishedSite(slug) or the live site will serve stale content for up to 1 hour.
Local Subdomain Development
To test customer sites locally at slug.localhost:3000:
# .env.local
FOUNDRY_PUBLIC_DOMAIN=localhost:3000
Then visit http://yourslug.localhost:3000. Chrome and Firefox support *.localhost natively — no /etc/hosts edits needed.
next.config.ts already has allowedDevOrigins: ["*.localhost:3000", "*.lvh.me:3000"]. Do not remove these entries.
The proxy middleware detects *.localhost:3000 when FOUNDRY_PUBLIC_DOMAIN=localhost:3000 and rewrites the path to /site/[slug].
Lovable-style React Code Generation
New projects generate websites as compiled React code, not WebsiteJSON.
- Type guard:
isReactSiteVersion()andReactSiteVersionintypes/foundry.ts - Union type:
AnyWebsiteVersion = WebsiteJSON | ReactSiteVersion - Pipeline step ID: stays
website_json— do not rename (Inngest history + deduplication) - Generation:
buildReactPagePrompt()→generateTextWithChain()→compileComponentInE2B()(E2B + Vite + Tailwind v4) - Storage:
website_versions.data = { type: "code", source, html, slug, colorOverrides?, sectionVisibility? } - Public route:
app/(public)/site/[slug]/page.tsx— code sites render via fullscreensrcDociframe; legacy JSON usesSiteRenderer - Legacy sites: WebsiteJSON still renders via
SiteRendererin the route handler fallback path - Editor:
CodeSiteStudiofor code sites;FoundryPuckStudiofor legacy JSON sites - E2B build time: 15–45s with
E2B_TEMPLATE_ID=foundry-vite; 2–5 min without template (coldnpm install). Inngest routemaxDurationis 300s. - Setup: see
docs/E2B_SETUP.md—E2B_API_KEY,FOUNDRY_USE_MOCK_PIPELINE=false,INNGEST_DEV=1 - Inngest sub-steps at
website_json:website-plan,website-generate-tsx,website-e2b-compile(compile+persist, minimal step output). Spine steps usepipeline-{stepId}. - User errors: never surface E2B/Vite logs in chat — Executions terminal + dev-only error panel
Website Validation Rules
The gradient check in lib/validation/website-local.ts uses a narrow regex intentionally:
/(?:linear|radial|conic|repeating-linear|repeating-radial)-gradient\s*\(/i
This matches only actual CSS gradient function calls — NOT text like "gradient-free design" or "gradient aesthetic" which appear in AI-generated copy. Do not broaden this regex to /gradient/i or similar — it will cause false positives and break the pipeline.
Workspace AI Memory
Per-workspace memory uses Supabase pgvector (768-dim Gemini embeddings). Read docs/WORKSPACE_MEMORY.md before changing memory code.
- Service:
lib/memory/workspace-memory.ts - UI:
components/memory/workspace-memory-panel.tsxon/dashboard - Mem0 agent skills:
.agents/skills/mem0,.agents/skills/mem0-integrate workspaceIdmaps to Mem0user_idfor scoping- Never expose
SUPABASE_SERVICE_ROLE_KEYor embedding keys to the client
Verification
Before finishing code changes, run the relevant checks:
pnpm run lint
pnpm run build
For UI changes, run the app and inspect the changed flow in a browser. For deployment changes, verify the resulting public URL returns a successful response.
Worktree Safety
This repo may contain user changes. Do not revert unrelated edits. Before modifying a file with existing changes, inspect the diff and preserve user work.