Files
familysync/.planning/phases/01-foundation-broker-spike/01-01-SUMMARY.md
T
Lucas Berger 6b500170ab docs(01-01): complete walking skeleton plan — SUMMARY
Tasks 1-2 done: monorepo scaffold, Drizzle schema, /health slice.
Stopped at Task 3 checkpoint (drizzle-kit push requires live Docker stack).
2026-06-04 09:54:10 -04:00

9.5 KiB


phase: 01-foundation-broker-spike plan: "01" subsystem: infra tags: [hono, drizzle, mariadb, mysql2, vitest, docker, pnpm, react, vite, typescript]

Dependency graph

requires: [] provides:

  • pnpm monorepo workspace (apps/api + apps/pwa)
  • Hono API scaffold with /health route (unauthenticated, real DB round-trip)
  • Drizzle ORM schema: users, memberCredentials, calendars, calendarEvents
  • drizzle(mysql2 pool) db singleton
  • Docker Compose stack: api + mariadb:11 (healthcheck) + redis
  • Vitest harness with Wave 0 test stubs
  • React PWA shell fetching /health
  • drizzle.config.ts for drizzle-kit push/migrate affects:
  • 01-02 (OIDC auth — imports db, users schema)
  • 01-03 (broker — imports db, all schemas, crypto pattern)
  • 01-04 (spike — imports broker module)

Tech tracking

tech-stack: added: - hono@4.12.23 - "@hono/node-server@2.0.4" - "@hono/oidc-auth@1.8.3" - "@hono/zod-validator@0.8.0" - drizzle-orm@0.45.2 - drizzle-kit@0.31.10 - mysql2@3.22.4 - tsdav@2.2.2 - ical.js@2.2.1 - zod@^3.25.0 - node-cron@^4.2.1 - vitest@^4.1.8 - react@^19.0.0 - "@tanstack/react-query@5.101.0" - zustand@5.0.14 - vite@8.0.16 patterns: - Hono app exported from src/index.ts for testability (no server start on import) - db singleton pattern (drizzle mysql2 pool, connectionLimit 10) - vi.mock at module top level for test isolation (Vitest hoisting) - Wave 0 test stubs using it.todo to document future tests before implementation

key-files: created: - package.json (root workspace, pnpm@11.5.1) - pnpm-workspace.yaml (apps/*, allowBuilds.esbuild: true) - .gitignore (.env excluded — secrets never committed) - .env.example (all env var names documented) - docker-compose.yml (api + mariadb:11 + redis) - docker-compose.dev.yml (dev overrides) - apps/api/package.json (pinned deps) - apps/api/tsconfig.json (strict, NodeNext, ES2023) - apps/api/Dockerfile (node:22-alpine, multi-stage) - apps/api/vitest.config.ts (environment: node, globals: true) - apps/api/drizzle.config.ts (dialect: mysql) - apps/api/src/db/schema.ts (users/memberCredentials/calendars/calendarEvents) - apps/api/src/db/client.ts (db export) - apps/api/src/routes/health.ts (GET / with SELECT 1 round-trip) - apps/api/src/index.ts (Hono app, /health mounted before auth) - apps/api/tests/health.test.ts (2 tests pass) - apps/api/tests/helpers/db.ts (mock helpers + sample VEVENTs) - apps/api/tests/auth/user.test.ts (5 todos — Plan 02) - apps/api/tests/broker/crypto.test.ts (5 todos — Plan 03) - apps/api/tests/broker/sync.test.ts (6 todos — Plan 03) - apps/api/tests/broker/poller.test.ts (5 todos — Plan 03) - apps/pwa/package.json - apps/pwa/tsconfig.json - apps/pwa/vite.config.ts (proxy /health + /api to :3000) - apps/pwa/index.html - apps/pwa/src/main.tsx (QueryClientProvider) - apps/pwa/src/App.tsx (fetches /health, renders stack: up/down) modified: []

key-decisions:

  • "Export app from src/index.ts without auto-starting server: enables direct import in Vitest tests without binding a port"
  • "Use vi.mock at module top level (not inside test): Vitest hoists vi.mock — placing inside describe/it causes warnings"
  • "pnpm-workspace.yaml allowBuilds.esbuild: true: pnpm 11 uses allowBuilds syntax, not onlyBuiltDependencies"
  • "zod pinned at ^3.25.0 (not ^4): conservative per RESEARCH — @hono/zod-validator@0.8.0 accepts both but v3 avoids unknown v4 API differences"

patterns-established:

  • "Pattern: Hono testability — export app from index.ts, use import.meta.url guard to start server only when run directly"
  • "Pattern: db mock — vi.mock('../src/db/client.js') at module level; override per-test with vi.mocked().mockRejectedValueOnce"
  • "Pattern: Wave 0 stubs — it.todo with plan reference so future agents know which plan fills each test"

requirements-completed: [CAL-01]

Metrics

duration: 6min completed: "2026-06-04"

Phase 01 Plan 01: Walking Skeleton — Summary

pnpm monorepo with Hono API, Drizzle/MariaDB schema (4 tables), Docker Compose stack, and /health route with real DB round-trip — Tasks 1-2 complete; stopped at checkpoint Task 3 (drizzle-kit push requires running Docker stack)

Performance

  • Duration: ~6 min
  • Started: 2026-06-04T13:46:55Z
  • Completed: 2026-06-04T13:53:00Z (Tasks 1-2; Task 3 is a human-action checkpoint)
  • Tasks: 2 of 3 complete (Task 3 is a blocking checkpoint)
  • Files modified: 28

Accomplishments

  • Full pnpm monorepo scaffold: apps/api (Hono + Drizzle + all pinned deps) and apps/pwa (Vite/React 19 + TanStack Query)
  • Drizzle schema with all 4 tables (users, memberCredentials, calendars, calendarEvents) following D-10 (oidc_iss+oidc_sub composite key) and D-13 (separate dtstart_utc/dtstart_date for all-day events)
  • /health route with real DB round-trip (SELECT 1) — GREEN: 2 tests pass, 503 on DB error
  • Docker Compose stack with mariadb:11 healthcheck, api depends_on service_healthy, redis stub
  • Wave 0 test harness: 5 test files, 21 todos (auth/user, broker/crypto, broker/sync, broker/poller) + 2 passing health tests
  • React PWA shell fetching /health and rendering stack: up/down

Task Commits

Each task committed atomically:

  1. Task 1: Scaffold monorepo, Docker Compose stack, and Vitest harness3f59156 (chore)
  2. Task 2: RED gate (failing health test)f31711a (test)
  3. Task 2: Drizzle schema + DB client + /health slice (GREEN)96cda58 (feat)

Files Created/Modified

Key files (full list in frontmatter key-files):

  • apps/api/src/db/schema.ts — 4 Drizzle mysqlTable definitions with all constraints
  • apps/api/src/db/client.tsdb singleton export (drizzle mysql2 pool)
  • apps/api/src/routes/health.ts — GET /health with SELECT 1 round-trip
  • apps/api/src/index.ts — Hono app, /health before auth, serveStatic
  • apps/api/drizzle.config.ts — drizzle-kit push/migrate config
  • apps/pwa/src/App.tsx — React shell fetching /health
  • docker-compose.yml — mariadb:11 + healthcheck + api depends_on service_healthy

Decisions Made

  • Exported app from src/index.ts without auto-starting server (import.meta.url guard) so Vitest tests can import it directly without a real HTTP port
  • pnpm 11 uses allowBuilds.esbuild: true in pnpm-workspace.yaml (not onlyBuiltDependencies) — pnpm rewrote this during install
  • zod pinned ^3.25.0 per RESEARCH recommendation (not v4)

Deviations from Plan

None — plan executed exactly as specified. One minor pnpm API difference (allowBuilds syntax) was auto-handled.

Auto-fixed Issues

1. [Rule 3 - Blocking] pnpm 11 allowBuilds syntax

  • Found during: Task 1 (pnpm install)
  • Issue: pnpm install failed with ERR_PNPM_IGNORED_BUILDS: esbuild@*. pnpm 11 uses allowBuilds map (not onlyBuiltDependencies list used in older versions)
  • Fix: Set allowBuilds.esbuild: true in pnpm-workspace.yaml
  • Files modified: pnpm-workspace.yaml
  • Verification: pnpm install succeeded; all deps installed
  • Committed in: 3f59156 (Task 1 commit)

2. [Rule 1 - Bug] vi.mock hoisting in health test

  • Found during: Task 2 (writing TDD RED test)
  • Issue: Placing vi.mock() inside describe() blocks caused Vitest hoisting warnings; tests used resetModules approach which conflicted with hoisting behavior
  • Fix: Moved vi.mock to module top level; used vi.mocked().mockRejectedValueOnce() for per-test override
  • Files modified: apps/api/tests/health.test.ts
  • Verification: Both health tests pass; no hoisting warnings
  • Committed in: 96cda58 (Task 2 feat commit)

Total deviations: 2 auto-fixed (1 blocking, 1 bug) Impact on plan: Both fixes necessary for install/tests to work. No scope creep.

Issues Encountered

  • pnpm 11 changed the onlyBuiltDependencies API to allowBuilds map syntax — resolved automatically

User Setup Required

Task 3 requires manual steps. See checkpoint details returned to orchestrator:

  1. Copy .env.example to .env and set DB_PASSWORD + DB_ROOT_PASSWORD
  2. docker compose up -d mariadb and wait for healthy
  3. DB_HOST=127.0.0.1 ... pnpm exec drizzle-kit push from apps/api
  4. Confirm SHOW TABLES lists 4 tables
  5. docker compose up -d and curl http://localhost:3000/health{"ok":true,"db":"up"}

Known Stubs

None. The serveStatic warning for ./public in tests is expected (no built PWA in test env) and is not a stub — the route exists and will serve correctly when the PWA is built.

Threat Surface Scan

No new threat surface beyond what was planned in the threat model:

  • T-01-01: .env excluded from git via .gitignore
  • T-01-02: familysync user (not root) in docker-compose.yml ✓
  • T-01-03: /health unauthenticated, returns only {ok, db}

Next Phase Readiness

  • Task 3 checkpoint must be cleared first (drizzle-kit push + Docker stack verification)
  • After Task 3: Plan 02 (OIDC auth) can proceed — imports db, users schema
  • Plan 03 (broker) depends on Plan 02 identity layer + this schema

Self-Check: PASSED

  • apps/api/src/db/schema.ts exists: FOUND
  • apps/api/src/db/client.ts exists: FOUND
  • apps/api/src/routes/health.ts exists: FOUND
  • apps/api/src/index.ts exists: FOUND
  • docker-compose.yml exists with healthcheck: FOUND
  • apps/pwa/src/App.tsx fetches /health: FOUND
  • Commits 3f59156, f31711a, 96cda58: FOUND

Phase: 01-foundation-broker-spike Completed: 2026-06-04 (Tasks 1-2; Task 3 at checkpoint)