docs(01): create phase plan

Phase 01: Foundation
- 5 plan(s) in 4 wave(s)
- 2 parallel (wave 2: auth + tenant provisioning), 3 sequential
- Ready for execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
kevin-asprec
2026-03-04 18:13:27 +08:00
parent 2506a6745c
commit 7e6d286fca
6 changed files with 1100 additions and 7 deletions

View File

@@ -0,0 +1,218 @@
---
phase: 01-foundation
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- docker-compose.yml
- Dockerfile
- .dockerignore
- .env.example
- .env
- package.json
- tsconfig.json
- next.config.ts
- tailwind.config.ts
- postcss.config.mjs
- prisma/schema.prisma
- src/app/layout.tsx
- src/app/page.tsx
- src/app/globals.css
- src/lib/prisma.ts
- vitest.config.ts
- src/lib/__tests__/setup.test.ts
autonomous: true
must_haves:
truths:
- "Running docker compose up starts PostgreSQL 16, Redis 7, and the Next.js dev server with zero manual steps"
- "Prisma can connect to the Dockerized PostgreSQL and run migrations"
- "vitest runs and passes at least one smoke test"
- "The Next.js app renders at localhost:3000"
artifacts:
- path: "docker-compose.yml"
provides: "PostgreSQL, Redis, and app service definitions"
contains: "postgres"
- path: "prisma/schema.prisma"
provides: "Base Tenant and User models with RLS-ready tenant_id"
contains: "model Tenant"
- path: "src/lib/prisma.ts"
provides: "Singleton Prisma client"
exports: ["prisma"]
- path: "vitest.config.ts"
provides: "Test runner configuration"
contains: "vitest"
key_links:
- from: "docker-compose.yml"
to: "prisma/schema.prisma"
via: "DATABASE_URL env var"
pattern: "DATABASE_URL"
- from: "src/lib/prisma.ts"
to: "prisma/schema.prisma"
via: "PrismaClient import"
pattern: "PrismaClient"
---
<objective>
Scaffold the entire NetForge project from scratch: Next.js 15 with App Router, TypeScript, Tailwind CSS, Prisma ORM with PostgreSQL, Redis, Docker Compose for local dev, and Vitest for testing.
Purpose: Every subsequent plan depends on this foundation existing. Nothing can be built without the project scaffold, database connection, and dev environment.
Output: A running dockerized development environment with base schema and test infrastructure.
</objective>
<execution_context>
@C:\Users\KevinAsprec\.claude/get-shit-done/workflows/execute-plan.md
@C:\Users\KevinAsprec\.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-foundation/01-CONTEXT.md
</context>
<tasks>
<task type="auto">
<name>Task 1: Create Next.js project with Docker Compose dev environment</name>
<files>
docker-compose.yml
Dockerfile
.dockerignore
.env.example
.env
package.json
tsconfig.json
next.config.ts
tailwind.config.ts
postcss.config.mjs
src/app/layout.tsx
src/app/page.tsx
src/app/globals.css
</files>
<action>
Initialize a Next.js 15 project with App Router, TypeScript, and Tailwind CSS using `npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"` (answer yes to all prompts or use flags to avoid interactive mode).
Create `docker-compose.yml` with three services:
1. **db**: PostgreSQL 16 image, port 5432, volume for data persistence, POSTGRES_USER=netforge, POSTGRES_PASSWORD=netforge_dev, POSTGRES_DB=netforge_dev
2. **redis**: Redis 7 image, port 6379
3. **app**: Builds from Dockerfile, port 3000, depends_on db and redis, mounts source code as volume for hot reload, environment variables from .env
Create a `Dockerfile` for development (Node 20 alpine, installs deps, runs `npm run dev`).
Create `.env.example` and `.env` with:
- DATABASE_URL=postgresql://netforge:netforge_dev@db:5432/netforge_dev
- REDIS_URL=redis://redis:6379
- NEXTAUTH_SECRET=dev-secret-change-in-production
- NEXTAUTH_URL=http://localhost:3000
Create `.dockerignore` excluding node_modules, .next, .git.
Update `src/app/page.tsx` to render a simple "NetForge" heading so we can verify the app loads.
IMPORTANT: Also create a local DATABASE_URL variant in .env for running Prisma commands from the host (outside Docker):
- DATABASE_URL_LOCAL=postgresql://netforge:netforge_dev@localhost:5432/netforge_dev
Add a note in .env.example explaining when to use each.
</action>
<verify>
Run `docker compose up -d` and verify all three services start. Run `docker compose ps` to confirm all services are healthy/running. Visit http://localhost:3000 and confirm the page loads.
</verify>
<done>All three Docker services (db, redis, app) start successfully. Next.js app serves at localhost:3000.</done>
</task>
<task type="auto">
<name>Task 2: Prisma schema with base models and Vitest setup</name>
<files>
prisma/schema.prisma
src/lib/prisma.ts
vitest.config.ts
src/lib/__tests__/setup.test.ts
package.json
</files>
<action>
Install Prisma: `npm install prisma @prisma/client` and `npm install -D vitest @vitejs/plugin-react`.
Create `prisma/schema.prisma` with PostgreSQL provider and the following base models:
**Tenant** model:
- id: String @id @default(uuid())
- name: String (business name)
- slug: String @unique (URL-friendly identifier, auto-generated from name)
- ownerEmail: String
- status: TenantStatus enum (ACTIVE, SUSPENDED, PENDING_SUSPENSION)
- suspendedAt: DateTime? (when suspension was triggered)
- gracePeriodEndsAt: DateTime? (when service actually stops — 7 day grace period)
- createdAt: DateTime @default(now())
- updatedAt: DateTime @updatedAt
- users: User[]
**User** model:
- id: String @id @default(uuid())
- email: String
- passwordHash: String
- firstName: String
- lastName: String
- tenantId: String? (nullable for super-admins who are not tenant-scoped)
- tenant: Tenant? @relation
- roles: Role[] (array of enum values — multi-role support)
- isActive: Boolean @default(true)
- isSuperAdmin: Boolean @default(false)
- createdAt: DateTime @default(now())
- updatedAt: DateTime @updatedAt
- @@unique([email, tenantId]) — email unique within tenant
- @@index([tenantId])
**Role** enum: ADMIN, OFFICE_STAFF, COLLECTOR, TECHNICIAN, CLIENT
**TenantStatus** enum: ACTIVE, SUSPENDED, PENDING_SUSPENSION
IMPORTANT: Add a comment block at the top of schema.prisma noting that tenant_id is included on all tenant-scoped models for RLS. Future models (Subscriber, Invoice, etc.) MUST include tenantId.
Create `src/lib/prisma.ts` — singleton PrismaClient pattern (check for existing instance on globalThis in dev to avoid hot-reload connection exhaustion).
Create `vitest.config.ts` with:
- resolve alias for @/* pointing to src/*
- test.globals: true
- test.environment: 'node'
Create `src/lib/__tests__/setup.test.ts` — a smoke test that imports prisma client and asserts it's defined (does NOT need DB connection, just verifies the import chain works).
Run `npx prisma generate` to generate the Prisma client.
Run `npx prisma db push` to sync the schema to the Dockerized PostgreSQL (use DATABASE_URL_LOCAL if running from host, or exec into app container).
Add to package.json scripts:
- "test": "vitest run"
- "test:watch": "vitest"
- "db:push": "prisma db push"
- "db:generate": "prisma generate"
- "db:studio": "prisma studio"
</action>
<verify>
Run `npx vitest run` — the smoke test passes. Run `npx prisma db push` — schema syncs without errors. Run `npx prisma studio` briefly to confirm Tenant and User tables exist in the database.
</verify>
<done>Prisma schema with Tenant and User models is synced to PostgreSQL. Vitest runs and passes the smoke test. PrismaClient singleton is importable from @/lib/prisma.</done>
</task>
</tasks>
<verification>
1. `docker compose up -d` starts all services without errors
2. `docker compose ps` shows db, redis, and app running
3. http://localhost:3000 renders the Next.js app
4. `npx prisma db push` succeeds (schema matches DB)
5. `npx vitest run` passes all tests
6. `npx prisma studio` shows Tenant and User tables
</verification>
<success_criteria>
- Docker Compose starts PostgreSQL 16, Redis 7, and Next.js 15 dev server with one command
- Prisma schema has Tenant and User models with RLS-ready tenantId field
- Vitest is configured and runs at least one passing test
- All environment variables are documented in .env.example
</success_criteria>
<output>
After completion, create `.planning/phases/01-foundation/01-01-SUMMARY.md`
</output>