---
phase: 01-foundation
plan: 02
type: execute
wave: 2
depends_on: ["01-01"]
files_modified:
  - src/db/QuillContext.scala
  - src/db/Migrator.scala
  - resources/db/migration/V1__create_summer_users.sql
  - src/api/HealthRoutes.scala
autonomous: true

must_haves:
  truths:
    - "Quill compiles SQL queries at compile time (type errors for bad queries)"
    - "Flyway migrations run via ZIO effect when invoked"
    - "Migration creates summer_users table in PostgreSQL"
    - "GET /ready returns 200 when database is connected, 503 when not"
  artifacts:
    - path: "src/db/QuillContext.scala"
      provides: "Quill PostgreSQL context with ZIO integration"
      exports: ["quillLayer", "dataSourceLayer"]
    - path: "src/db/Migrator.scala"
      provides: "Flyway wrapper as ZIO service"
      exports: ["Migrator", "MigrationStatus"]
    - path: "resources/db/migration/V1__create_summer_users.sql"
      provides: "Initial database schema"
      contains: "CREATE TABLE summer_users"
  key_links:
    - from: "src/db/QuillContext.scala"
      to: "resources/application.conf"
      via: "Quill.DataSource.fromPrefix"
      pattern: "fromPrefix.*database"
    - from: "src/db/Migrator.scala"
      to: "src/db/QuillContext.scala"
      via: "ZLayer dependency on DataSource"
      pattern: "DataSource"
    - from: "src/api/HealthRoutes.scala"
      to: "src/db/QuillContext.scala"
      via: "/ready endpoint checks connection"
      pattern: "/ready"
---

<objective>
Integrate PostgreSQL via Quill with compile-time SQL validation and Flyway migrations wrapped in ZIO effects.

Purpose: Establish database connectivity with type-safe queries and version-controlled schema changes.
Output: Working database layer with migrations service and a /ready endpoint that verifies connectivity.
</objective>

<execution_context>
@/home/jin/.claude/get-shit-done/workflows/execute-plan.md
@/home/jin/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/01-foundation/01-CONTEXT.md
@.planning/phases/01-foundation/01-RESEARCH.md
@.planning/phases/01-foundation/01-01-SUMMARY.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create Quill PostgreSQL context</name>
  <files>src/db/QuillContext.scala</files>
  <action>
Create Quill context for PostgreSQL with ZIO integration.

src/db/QuillContext.scala:
- Import io.getquill.* and io.getquill.jdbczio.Quill
- Create dataSourceLayer: ZLayer[Any, Throwable, javax.sql.DataSource]
  - Use Quill.DataSource.fromPrefix("database") to read from HOCON config
- Create quillLayer: ZLayer[javax.sql.DataSource, Nothing, Quill.Postgres[SnakeCase]]
  - Use Quill.Postgres.fromNamingStrategy(SnakeCase)
- Export combined layer: dataSourceLayer >>> quillLayer

IMPORTANT: Never use "io" as a variable name in this file (Quill package conflict).

Configuration note: Quill reads HikariCP config from the "database" prefix in application.conf.
The existing database config structure should work, but may need adjustment to match HikariCP's expected format:
```hocon
database {
  dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
  dataSource {
    serverName = "localhost"
    portNumber = 5432
    databaseName = "summercms"
    user = "summercms"
    password = "summercms"
  }
}
```

Update resources/application.conf to use HikariCP-compatible format if needed.
  </action>
  <verify>`mill compile` succeeds with Quill context</verify>
  <done>QuillContext.scala exports dataSourceLayer and quillLayer, compiles without errors</done>
</task>

<task type="auto">
  <name>Task 2: Create Flyway migrator service</name>
  <files>
    src/db/Migrator.scala
    resources/db/migration/V1__create_summer_users.sql
  </files>
  <action>
Create Flyway wrapper as ZIO service and initial migration.

1. src/db/Migrator.scala:
- Case class MigrationStatus(current: Option[String], pending: Int)
- Trait Migrator with methods:
  - def migrate: Task[Int] (returns count of migrations run)
  - def status: Task[MigrationStatus]
- Object Migrator with:
  - val live: ZLayer[javax.sql.DataSource, Nothing, Migrator]
  - Implementation configures Flyway with:
    - dataSource from layer
    - locations: "classpath:db/migration"
    - table: "summer_migrations" (not default flyway_schema_history)
    - baselineOnMigrate: true (for existing databases)

2. resources/db/migration/V1__create_summer_users.sql:
```sql
-- Summer CMS initial schema
-- Creates the base users table for future auth

CREATE TABLE summer_users (
    id BIGSERIAL PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_summer_users_email ON summer_users(email);

-- Trigger for updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ language 'plpgsql';

CREATE TRIGGER update_summer_users_updated_at
    BEFORE UPDATE ON summer_users
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();
```

Naming follows conventions from CONTEXT.md:
- snake_case columns
- summer_ prefix for core tables
- id, created_at, updated_at by default
  </action>
  <verify>`mill compile` succeeds, migration file exists at correct path</verify>
  <done>Migrator service defined with migrate/status methods, V1 migration creates summer_users</done>
</task>

<task type="auto">
  <name>Task 3: Add /ready endpoint with database check</name>
  <files>src/api/HealthRoutes.scala</files>
  <action>
Update HealthRoutes to include database connectivity check.

Modify src/api/HealthRoutes.scala:
- Keep existing GET /health -> Response.text("ok") (always 200)
- Add GET /ready endpoint:
  - Requires DataSource from ZLayer
  - Attempts: ZIO.attempt(ds.getConnection.close())
  - Success: Response.text("ready") with 200
  - Failure: Response.status(Status.ServiceUnavailable) with 503

Pattern:
```scala
Method.GET / "ready" -> handler {
  ZIO.serviceWithZIO[javax.sql.DataSource] { ds =>
    ZIO.attempt {
      val conn = ds.getConnection
      conn.close()
    }.as(Response.text("ready"))
     .catchAll(_ => ZIO.succeed(Response.status(Status.ServiceUnavailable)))
  }
}
```

Update Main.scala to:
- Provide dataSourceLayer to the server
- DO NOT run migrations on startup (per CONTEXT.md: migrations run manually via CLI)
- The Migrator service is available but not auto-invoked

Note: The /ready endpoint will return 503 if no database is running, which is correct behavior.
Migrations will be triggered via CLI command in a later phase (Phase 5: CLI Scaffolding).
  </action>
  <verify>
With PostgreSQL running: `curl localhost:8080/ready` returns "ready"
Without PostgreSQL: `curl localhost:8080/ready` returns 503
  </verify>
  <done>/ready endpoint exists, returns 200 when DB connected, 503 when not</done>
</task>

</tasks>

<verification>
1. `mill compile` succeeds (Quill context compiles)
2. With PostgreSQL running:
   - `mill run` starts server (does NOT auto-run migrations)
   - `curl localhost:8080/ready` returns "ready"
3. Migrations can be run manually via Migrator service (CLI integration in Phase 5)
4. After manual migration: `psql -d summercms -c "\\dt"` shows summer_users table
5. Check migrations table: `psql -d summercms -c "SELECT * FROM summer_migrations"`
</verification>

<success_criteria>
- Quill context exists with PostgreSQL configuration
- Flyway migrator service exists as ZIO effect (available for CLI invocation)
- V1 migration creates summer_users table with proper schema
- /ready endpoint verifies database connectivity
- Compile-time SQL validation works (intentional bad query causes compile error)
- Server starts WITHOUT auto-running migrations
</success_criteria>

<output>
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`
</output>
