Boat — Local Development Runbook This runbook explains how to run the Boat MVP locally, seed demo data, and troubleshoot common issues. Project layout highlights - [app.page()](src/app/page.tsx:1) — Graph landing page that fetches /api/graph and renders the Cytoscape graph - [components/Graph.tsx](src/components/Graph.tsx:1) — Cytoscape wrapper with zoom/pan, fit, and selection details - [components/PersonForm.tsx](src/components/PersonForm.tsx:1) — Create person form - [components/PeopleSelect.tsx](src/components/PeopleSelect.tsx:1) — Typeahead selector against /api/people - [components/ConnectionForm.tsx](src/components/ConnectionForm.tsx:1) — Create connection form with ordered introducer chain - [app.people.new.page()](src/app/people/new/page.tsx:1) — Page for adding a person - [app.connections.new.page()](src/app/connections/new/page.tsx:1) — Page for adding a connection - [api.people.route()](src/app/api/people/route.ts:1), [api.people.id.route()](src/app/api/people/[id]/route.ts:1) — People API - [api.connections.route()](src/app/api/connections/route.ts:1), [api.connections.id.route()](src/app/api/connections/[id]/route.ts:1) — Connections API - [api.graph.route()](src/app/api/graph/route.ts:1) — Graph snapshot API - [lib.db()](src/lib/db.ts:1) — Prisma client singleton - [lib.validators()](src/lib/validators.ts:1) — Zod schemas - [prisma.schema()](prisma/schema.prisma:1) — Data model - [prisma.migration.sql](prisma/migrations/20251114191515_connection_pair_constraints/migration.sql:1) — Undirected uniqueness + self-edge constraints - [prisma.seed()](prisma/seed.ts:1) — Seed script for 20 demo people and ~30 connections Prerequisites - Docker (to run local Postgres quickly) - Node 20+ and npm 1) Start Postgres (Docker) If you haven’t yet started the Docker Postgres container, run: - docker volume create boat-pgdata - docker run -d --name boat-postgres -p 5432:5432 -e POSTGRES_DB=boat -e POSTGRES_USER=boat -e POSTGRES_PASSWORD=boat -v boat-pgdata:/var/lib/postgresql/data postgres:16 Verify it’s running: - docker ps | grep boat-postgres 2) Environment variables This repo already includes [.env](.env:1) pointing to the Docker Postgres: DATABASE_URL="postgresql://boat:boat@localhost:5432/boat?schema=public" 3) Install deps and generate Prisma Client From the app directory: - npm install - npx prisma generate 4) Migrate the database - npx prisma migrate dev --name init - npx prisma migrate dev (if new migrations were added) Note: Constraints for undirected uniqueness and self-edges are included in [prisma.migration.sql](prisma/migrations/20251114191515_connection_pair_constraints/migration.sql:1). 5) Seed demo data (20 people) - npm run db:seed This executes [prisma.seed()](prisma/seed.ts:1) and inserts people and connections. 6) Run the Next.js dev server Important: run from the app directory, not workspace root. - npm run dev Access: - http://localhost:3000 (or the alternate port if 3000 is busy) 7) Using the app - Landing page shows the global graph - Toolbar buttons: - Add Person → [app.people.new.page()](src/app/people/new/page.tsx:1) - Add Connection → [app.connections.new.page()](src/app/connections/new/page.tsx:1) - Reload — refetches data for the graph - Click nodes or edges to show details in the side panel 8) API quick tests - List people: - curl "http://localhost:3000/api/people?limit=10" - Create person: - curl -X POST "http://localhost:3000/api/people" -H "Content-Type: application/json" -d '{"name":"Jane Demo","sectors":["agriculture"]}' - Create/update connection (undirected): - curl -X POST "http://localhost:3000/api/connections" -H "Content-Type: application/json" -d '{"personAId":"","personBId":"","introducedByChain":[],"eventLabels":["event:demo"]}' Troubleshooting A) “npm enoent Could not read package.json at /home/maxime/boat/package.json” - You ran npm in the workspace root. Use the app directory: - cd boat-web - npm run dev B) “Unable to acquire lock … .next/dev/lock” - Another Next dev instance is running or a stale lock exists. - Kill dev: pkill -f "next dev" (Unix) - Remove lock: rm -f .next/dev/lock - Then: npm run dev C) “Failed to load external module @prisma/client … cannot find module '.prisma/client/default'” - Prisma client must be generated after schema changes or misconfigured generator. - Ensure generator in [prisma.schema()](prisma/schema.prisma:7) is: generator client { provider = "prisma-client-js" } - Regenerate: npx prisma generate - If still failing, remove stale output and regenerate: - rm -rf node_modules/.prisma - npx prisma generate D) Port 3000 already in use - Run on a different port: - npm run dev -- -p 3001 Tech notes - The undirected edge uniqueness is enforced via functional unique index on LEAST/GREATEST and a no-self-edge CHECK in [prisma.migration.sql](prisma/migrations/20251114191515_connection_pair_constraints/migration.sql:1). - Deleting a person cascades to connections (MVP behavior). - Sectors, interests, and eventLabels are free-text arrays (TEXT[]). - Introduced-by chain is an ordered list of person IDs (existing people only). - UI intentionally minimal and open as per MVP brief. Acceptance checklist mapping - Create person: [api.people.route()](src/app/api/people/route.ts:1) + [PersonForm.tsx](src/components/PersonForm.tsx:1) ✔ - Create connection: [api.connections.route()](src/app/api/connections/route.ts:1) + [ConnectionForm.tsx](src/components/ConnectionForm.tsx:1) ✔ - Global graph view: [api.graph.route()](src/app/api/graph/route.ts:1) + [Graph.tsx](src/components/Graph.tsx:1) ✔ - Persistence: Postgres via Prisma ✔