Foundry Foundry

E0: Foundation — Epic Design Doc

Status: 🔄 In Refinement (Step 0) Authors: Dan Hannah & Clay Created: 2026-04-18 Parent: QuoteAI Project Design Doc


Overview

Goals & Non-Goals

Goals:

  • Stand up a working local dev environment in Docker: Postgres 16 + pgvector + Next.js 14 shell
  • Create the full database schema from the project design doc's data model
  • Scaffold the repo layout so E1, E2, and E3 have clear homes for their code
  • Establish env-var conventions, secrets handling, and local setup docs
  • Provide a make dev (or equivalent) one-command bring-up

Non-Goals:

  • No ingestion logic (E1)
  • No MCP servers (E2)
  • No UI features beyond a shell page that proves the stack is wired (E3)
  • No production deploy, hosting config, or CI
  • No auth — explicitly deferred past Demo MVP

Problem Statement

We have a design doc and a revised MVP plan, but zero code. Every other epic is blocked until the local stack runs. Without a foundation, E1 can't target a schema, E2 can't talk to a database, and E3 can't render anything.

The goal is to hit "I can run docker compose up, open localhost, and see a working page backed by an empty but schema-complete Postgres+pgvector" in as few hours as possible — then stop. Nothing else.

What Is This Epic?

Foundation is the scaffolding epic. It creates the repo skeleton, the Docker environment, the database schema, and a minimal Next.js app that proves the connection layer works end-to-end. When E0 is done, every other epic has a concrete target to build into.


Context

Dependents

Every other MVP epic depends on E0:

  • E1 (Ingestion + Vector DB) — needs the schema to exist before it can load data
  • E2 (MCP Servers) — needs the DB connection layer and env conventions
  • E3 (UI) — needs the Next.js shell to build pages into

Dependencies

  • Node.js 20+ installed locally
  • Docker Desktop running
  • A directory to put the repo (likely ~/Documents/Code/quoteai)

No external APIs, no cloud accounts. Fully local.

Current State

No code. The design doc lives in Foundry at projects/quoteai/design.md. The Brehob source data lives in a shared Google Drive folder (QuoteAI - Brehob Quote History) — relevant to E1, not E0.

Affected Systems

System / LayerHow It's Affected
Local dev environmentCreated from scratch — Docker Compose, env files, Makefile
Repository structureInitialized — monorepo pattern with app/, ingestion/, mcp-servers/, db/
Database schemaFully created from the project design doc's SQL
Next.js appShell only — routing structure, layout, dark-mode theme primitive, a /health page that queries the DB

Design

Repo Structure

quoteai/
├── app/                          # Next.js 14 (App Router)
│   ├── app/
│   │   ├── layout.tsx            # root layout, dark-mode-first
│   │   ├── page.tsx              # placeholder home
│   │   └── health/
│   │       └── page.tsx          # "✓ DB connected" smoke test
│   ├── lib/
│   │   └── db.ts                 # Postgres client (pg or @neondatabase/serverless)
│   ├── package.json
│   └── tsconfig.json
├── db/
│   ├── schema.sql                # full schema from design doc
│   ├── migrations/               # numbered SQL files, applied on container boot
│   │   └── 001_init.sql
│   └── seed/                     # empty for E0; populated by E1
├── ingestion/                    # E1 lives here (empty stub for E0)
│   └── .gitkeep
├── mcp-servers/                  # E2 lives here (empty stub for E0)
│   ├── equipment/.gitkeep
│   └── quotes/.gitkeep
├── docker-compose.yml            # postgres + pgvector + next dev container (optional)
├── .env.example                  # documented env contract
├── .env                          # local only, gitignored
├── Makefile                      # make dev, make db-reset, make test
├── README.md
└── CLAUDE.md                     # project-specific instructions for CC

Docker Compose Target

# docker-compose.yml — shape only, not final
services:
  postgres:
    image: pgvector/pgvector:pg16
    environment:
      POSTGRES_DB: quoteai
      POSTGRES_USER: quoteai
      POSTGRES_PASSWORD: quoteai
    ports: ["5432:5432"]
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/migrations:/docker-entrypoint-initdb.d

Next.js runs on the host (not in Docker) for fast HMR during dev. Docker only hosts Postgres for MVP.

Data Model Changes

Apply the full schema from the project design doc's Data Model section, plus three E1-driven additions folded in now (pre-E1-ingest, so no migration later).

Tables created in 001_init.sql:

  • products (catalog + vector)
  • past_quotes (raw text + vector)
  • quote_line_items (the description library — atomic retrieval units)
  • vendor_quotes (reference)
  • quote_log (empty for Demo MVP but schema exists)
  • quote_log_items (empty for Demo MVP but schema exists)
  • feedback
  • All ivfflat vector indexes

E1-driven additions (per agreed decision 2026-04-18 in E1 Decisions Log). Added to products, past_quotes, and quote_line_items:

extractor_version TEXT,   -- e.g., "haiku-4-5-v1" — enables A/B comparison when extraction prompts evolve
source_hash TEXT,         -- hash of raw input text (pre-parse) — skip file entirely if unchanged
content_hash TEXT,        -- hash of extracted content block (post-parse) — re-embed only changed blocks within a file

Why these three now: The E1 ingestion pipeline will write extractor_version per row for prompt-versioning; the re-embed detection logic needs both hash columns. Landing them in 001_init.sql is free right now (no data yet); migrating after E1 ingests would be painful.

Quote log tables are created even though they're not populated until Full MVP — saves a migration later.

Approach

  1. git init the repo at ~/Documents/Code/quoteai
  2. Scaffold Next.js 14 with TypeScript, Tailwind, App Router
  3. Add shadcn/ui primitives (button, form, input, card) — dark-mode-first since Dan prefers dark mode
  4. Write docker-compose.yml with pgvector/pgvector:pg16
  5. Write db/migrations/001_init.sql with full schema
  6. Write app/lib/db.ts with a typed Postgres client
  7. Add /health page that runs SELECT 1 and shows status
  8. Write README.md and CLAUDE.md (CC operating instructions for this repo)
  9. Write Makefile with dev, db-reset, test targets

API / Interface Changes

No external APIs. Internal interface to establish:

  • app/lib/db.ts exports a singleton Postgres pool with typed query helper
  • Environment variables: DATABASE_URL, ANTHROPIC_API_KEY (placeholder, unused until Full MVP), OPENAI_API_KEY (for embeddings — used by E1)

Edge Cases & Gotchas

ScenarioExpected BehaviorWhy It's Tricky
pgvector extension not loadingContainer fails to start with clear errorCREATE EXTENSION vector; must run before any table with VECTOR(...) columns. Ensure migration order.
ivfflat index creation on empty tablesSucceeds but is uselessivfflat needs data to build its lists. For E0 (empty DB), indexes are created but will rebuild automatically once E1 loads data. Fine.
Running on Apple Silicon vs IntelBoth should workpgvector/pgvector:pg16 publishes multi-arch images. Verify on first boot.
DATABASE_URL mismatch between app and docker-composeApp can't connect.env.example documents the exact URL; README calls it out.

Testing Strategy

QuoteAI doesn't have a full Validation Pipeline yet (that's Routr's level of rigor). For E0, testing is minimal and manual.

Test Layers

LayerApplies?Notes
Unit testsNoNo real logic to test in E0
Integration testsYesOne smoke test: /health page returns "DB connected" with all expected tables present
Visual testsNoDeferred to E3
E2E / manualYesmake dev → open browser → see shell page → see health page green

Verification Checklist

  1. make dev brings up Postgres and Next.js with zero manual steps beyond copying .env.example to .env
  2. /health page shows DB connection OK + a list of created tables matching the schema
  3. psql into the container confirms pgvector extension is installed and all tables exist
  4. make db-reset wipes and rebuilds the DB from migrations cleanly

Stories

StorySummaryStatusPR
S0Repo init + directory structure + .gitignore + CLAUDE.md
S1Next.js 14 scaffold with Tailwind + shadcn/ui + dark mode
S2Docker Compose with pgvector/pg16 + init migration
S3Full schema migration from design doc (db/migrations/001_init.sql)
S4app/lib/db.ts + /health smoke test page
S5Makefile (dev, db-reset, test) + README + .env.example

Known Issues / Tech Debt

IssueSeverityNotes
No CI/CD🟢 LowIntentional for Demo MVP. Add when production hardening begins.
No auth layer🟢 LowIntentional — Brehob-single-tenant for Demo MVP.
quote_log / feedback tables exist but unused🟢 LowAccepted — saves a migration later.

Open Questions

Open questions that need a human call before implementation.

#QuestionRecommendationStatus
1Package manager — pnpm vs npm vs yarn?pnpm — monorepo-friendly via workspaces, fast, correct dependency resolution. Used extensively in the broader @claymore-dev ecosystem.Needs confirmation
2Monorepo tooling — pnpm workspaces only, or add Turborepo / Nx?pnpm workspaces only for MVP. Turborepo is nice for caching but premature at 4 packages. Revisit when build time hurts.Needs confirmation
3Database client — raw pg, Drizzle, or Prisma?Drizzle — type-safe, plays well with shadcn/ui crowd, lightweight, no codegen server. Raw pg fine if you want zero abstraction. Prisma feels heavy for this scope.Needs decision
4CLAUDE.md content — what goes in the project-specific CC instructions file?Draft sections: (a) project overview + link to design doc, (b) repo layout reference, (c) common commands (pnpm ingest, make dev), (d) MCP server setup for CC in this repo, (e) coding conventions (e.g., Zod for all API boundaries), (f) pointers to next.md per-session handoffs.Needs draft approval
5Postgres credentials pattern — hardcoded quoteai/quoteai/quoteai in .env.example, or generate random on first make dev?Recommend: hardcoded in .env.example for simplicity; .env is gitignored. For demo on localhost, not worth the ceremony. Full MVP deploy will use proper secrets.Needs confirmation
6Node version pinning.nvmrc or Volta?Recommend .nvmrc with Node 20 LTS. Simplest, works everywhere.Needs confirmation
7/health page scope — just DB ping, or also show MCP server reachability?For E0: just DB ping. MCP server reachability is E2's concern.Needs confirmation
8Project-specific next.md — today next.md is workspace-scoped and can get crowded across projects. Should QuoteAI have its own projects/quoteai/next.md?Recommend: yes, per-project next.md inside the project repo. Workspace next.md stays for cross-project context. This is a methodology improvement — worth a process.md PR.Needs decision (methodology)

Risks

RiskLikelihoodImpactMitigation
pgvector container behaves differently than expected on Apple SiliconLowMediumUse official pgvector/pgvector:pg16 image which is multi-arch; test on Dan's laptop before locking the tag
shadcn/ui versioning causes install frictionLowLowPin a known-good version in the initial setup
Schema drift between design doc and migrationMediumMediumGenerate migration SQL directly from the design doc's Data Model section; diff against design doc on PR

Decisions Log

DateDecisionRationaleAlternatives Considered
2026-04-18Next.js runs on host, not in DockerFast HMR during dev; one less networking complicationFull dockerized stack (rejected: slower dev loop)
2026-04-18Single schema migration for E0No users yet — we can rewrite at will until E1 loads dataIncremental migrations (rejected: premature ceremony)
2026-04-18Create quote_log / feedback tables now despite not using themSaves a migration in Full MVP; schema is in the design doc anywayOnly create what Demo uses (rejected: creates migration debt)
2026-04-18shadcn/ui + TailwindDark-mode-first (user preference), easy to make polished quickly, composableChakra (rejected: heavier), plain Tailwind only (rejected: more scaffolding per component)
2026-04-18Fold E1 schema additions (extractor_version, source_hash, content_hash) into 001_init.sqlPer E1 agreement. Added to products, past_quotes, quote_line_items in the initial migration. Cheaper to land now (no data yet) than to migrate after E1 ingests. Also resolves E1 Open Q #6 (hash strategy) by committing to both source and content hashes.Post-E1 migration (rejected: more pain, more risk); only extractor_version (rejected: hash columns are cheap to add in the same touch)

Once E0 is shipped, stories for E1 can begin. No other epic can start until make dev is green.

Review

🔒

Enter your access token to view annotations