From 8a9dd991376e6e33c64a9adf52e01dff692a968f Mon Sep 17 00:00:00 2001 From: Greg Jackson Date: Tue, 5 May 2026 10:19:06 +0100 Subject: [PATCH] fix: pre-deploy local build check + client/server boundary rules (#226) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three-layer prevention for client bundle contamination in Next.js projects: 1. deploy-to-staging.js: run local build (turbo run build or next build) before pushing to Vercel. Catches webpack/TS errors in seconds instead of burning 3 remote retries × 30s each. 2. 01-building.md: guardrail rule — never add re-exports from server-only modules to files reachable from /client entry points. Webpack bundles the entire transitive graph of "use client" modules. 3. 00-foundation-building.md: when scaffolding shared workspace packages for Next.js App Router, enforce sideEffects:false, separate /client export, and import 'server-only' on DB modules from day one. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/launcher/deploy-to-staging.js | 15 +++++++++++++++ src/prompts/loop/00-foundation-building.md | 5 +++++ src/prompts/loop/01-building.md | 1 + 3 files changed, 21 insertions(+) diff --git a/src/launcher/deploy-to-staging.js b/src/launcher/deploy-to-staging.js index 3da136a..74b71af 100644 --- a/src/launcher/deploy-to-staging.js +++ b/src/launcher/deploy-to-staging.js @@ -100,6 +100,21 @@ const DEPLOY_HANDLERS = { }; function deployVercel(projectDir) { + // Pre-deploy: run a local build to catch webpack/TS errors before pushing + // to Vercel. This avoids burning 3 remote retries (each taking 30-60s) on + // errors that are instantly detectable locally (client bundle contamination, + // TypeScript errors, missing modules). See #226. + const hasTurbo = fs.existsSync(path.join(projectDir, 'turbo.json')); + const localBuildCmd = hasTurbo ? 'npx turbo run build' : 'npx next build'; + try { + run(localBuildCmd, { cwd: projectDir, timeout: 300000 }); + log('Local build passed — proceeding to remote deploy'); + } catch (err) { + const detail = (err && (err.stderr || err.stdout || err.message)) || String(err); + log(`Local build failed — aborting deploy to avoid wasted remote retries:\n${String(detail).slice(0, 1000)}`); + throw new Error(`Local build failed: ${String(detail).slice(0, 300)}`); + } + // Vercel: deploy via CLI (project must be linked via .vercel/project.json). // Use --prod because Vercel Hobby plan preview URLs return 401 (auth required). // Production deploys are publicly accessible for health checks. diff --git a/src/prompts/loop/00-foundation-building.md b/src/prompts/loop/00-foundation-building.md index 891d703..315c8b1 100644 --- a/src/prompts/loop/00-foundation-building.md +++ b/src/prompts/loop/00-foundation-building.md @@ -51,6 +51,11 @@ Read `foundation_spec` from `cycle_context.json`. Your scope is EXACTLY what's l - All relationships, foreign keys, indexes designed for the full product — not just one feature - Migrations that run cleanly on fresh and existing databases - Seed data for every entity — realistic data that matches the domain (GPS waypoints for fleet management, recipe ingredients for a cooking app, not "test123" and "foo bar") +- **Next.js App Router projects:** shared workspace packages MUST scaffold separate entry points to prevent client bundle contamination: + - Add `"sideEffects": false` to the shared package's `package.json` + - Add a `"./client"` export in the `"exports"` field pointing to a client-safe barrel (no DB, no Node.js APIs) + - Add `import 'server-only'` at the top of any file that imports database drivers (`pg`, `@neondatabase/serverless`, etc.) + - The `/client` entry point MUST NOT transitively import any module that uses `fs`, `net`, `dns`, or `tls` ### Auth Flows - Registration, login, logout, session persistence diff --git a/src/prompts/loop/01-building.md b/src/prompts/loop/01-building.md index 8fae8ac..3ee0377 100644 --- a/src/prompts/loop/01-building.md +++ b/src/prompts/loop/01-building.md @@ -70,6 +70,7 @@ Write to `cycle_context.json` (APPEND to existing arrays, never overwrite): - When you modify a file, check one hop of imports/importers for breakage. Fix what you broke. - "I'll add tests later" is not an option. Red-green-refactor is the build order, not a suggestion. - If a test passes before you write implementation, investigate — either the test is wrong or the feature already exists. +- Next.js client/server boundary: NEVER add re-exports from server-only modules (database drivers, Node.js APIs like fs/net/dns) to files reachable from a `/client` entry point or imported by `"use client"` components. Webpack bundles the entire transitive graph of client modules — it cannot tree-shake re-exports. Use `import 'server-only'` at the top of any file that imports database drivers. ## Git