Skip to content
Step 6Phase 2 — System init

Architecture

The boilerplate ships flat. Net-new feature logic gets a domain folder under src/features/. Server Components stay the default; client islands are the exception. Integrations live in libs/.

At a glance

Purpose: decide where each PRD feature lives before any code, on a server-first model.

Run: the plan phase of the PRD, informed by the memory bank's gap analysis.

Output: scaffolded src/features/<domain>/ folders + updated ai_rules/context_map.md.

Feeds: the build loop (Step 8) implements into this structure.

Why it exists

An agent left to its own devices invents folder structures and duplicates components that already exist. This step fixes the layout: reuse the flat core for everything the boilerplate already does, and isolate net-new feature logic into self-contained domain folders so features stay decoupled and the agent always knows where code goes.

The layout

Project structure
app/                      # routes (Server Components by default)
components/                # shared UI (check the README before creating)
libs/                     # integrations: supabase/, resend, analytics, seo.tsx, api.ts
lib/                      # small helpers
src/features/<domain>/    # NET-NEW feature logic, co-located:
  ├── components/         #   the feature's UI
  ├── hooks/              #   client hooks
  ├── actions/           #   server actions
  └── schema.ts          #   zod schema
components/ui/            # shared primitives (shadcn) — net-new features reuse these
Inventory before you create
Before adding a component, read components/README.md. Before adding a utility, read libs/README.md. The inventory is the truth; "what a Next.js project usually has" is not. This is the anti-hallucination checklist in action.

The RSC boundary

1

Server Components by default

Static and SEO content renders on the server. Data reads (Supabase) and secrets stay server-side.

2

Client islands only where interactive

A form, a toggle, a checkout button gets "use client" and is pushed to a leaf. Server-only logic (env reads, the Supabase server client) never leaks into a client component.

3

No ssr:false in a Server Component

It crashes the build. Lazy-load a client island without ssr:false, or move the dynamic() into a client file.

Tsx
// crashes the build inside a Server Component
const Footer = dynamic(() => import("@/components/Footer"), { ssr: false });

// fix: no ssr:false, or move dynamic() into a client file
const Footer = dynamic(() => import("@/components/Footer"));
4

The integration layer

External calls (Supabase, Resend, Stripe, any LLM/agent call) go through libs/, env-gated and fail-soft: when env vars are absent, factories return null and callers degrade instead of crashing. The zero-env build stays green.

Best practices baked in

  • Reuse the flat core. Only net-new feature logic earns a src/features/<domain>/ folder.
  • Server-first; the smallest possible client island per surface.
  • Env-gated, fail-soft integrations. The build is green with zero env vars.
  • Update context_map.md and the relevant README whenever files are added or removed.
  • @/ import alias for all internal modules, never ../../.
Feeds the next step
With the structure decided, MCP + skills (Step 7) gives the agent live context, and the build loop (Step 8) implements each feature into these folders one task at a time.