Files
recipe/.planning/phases/01-project-infrastructure-module-wiring/01-05-SUMMARY.md
2026-04-29 21:07:49 +02:00

8.9 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
01-project-infrastructure-module-wiring 05 infra
ktor
flyway
hocon
postgres
slf4j
kotlinx-serialization
phase provides
01-project-infrastructure-module-wiring recipe.jvm.server precompiled plugin (Plan 02) wires ktor-server-netty, ktor-server-content-negotiation, ktor-serialization-kotlinx-json, flyway-core, flyway-database-postgresql, postgresql JDBC, ktor-server-test-host, logback-classic. Plan 03 applied recipe.jvm.server + recipe.quality to server module and added implementation(projects.shared) so SERVER_PORT is reachable.
Running-but-empty server: GET /health returns {"status":"ok"} with Content-Type application/json
HOCON application.conf with localhost defaults + ${?ENV} overrides for PORT/DATABASE_URL/DATABASE_USER/DATABASE_PASSWORD
Database.migrate() Flyway boot sequence with fail-loud IllegalStateException contract on unreachable Postgres
server/src/main/resources/db/migration/ resource directory anchored by .gitkeep so classpath:db/migration resolves before Phase 3 adds V1__init.sql
configureRouting() extension extracted from Application.module() so tests compose routing without invoking Database.migrate (no Postgres in CI)
phase-02-auth
phase-03-households
phase-05-recipe-catalog
phase-11-deployment
added patterns
Flyway runtime API (flyway-core 12.x)
HOCON env-var override pattern
SLF4J server-side logging
HOCON ${?ENV} two-line override pattern (PITFALL #5 mitigation)
Fail-loud server boot: Database.migrate throws IllegalStateException on Flyway/JDBC failure
Routing extracted to Application.configureRouting() extension so testApplication composes routing without DB dependency
Server uses SLF4J/Logback (NOT Kermit — Kermit is client-only)
created modified
server/src/main/kotlin/dev/ulfrx/recipe/Database.kt
server/src/main/resources/application.conf
server/src/main/resources/db/migration/.gitkeep
server/src/main/kotlin/dev/ulfrx/recipe/Application.kt
server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt
Use HOCON ${?ENV} optional substitution (two-line default + override) rather than ${ENV:default} (invalid HOCON) or ${ENV} (required, crashes on unset)
Server logs via SLF4J/Logback, not Kermit — Kermit reserved for the multiplatform client
Database.migrate is fail-loud: IllegalStateException on any Flyway error; no silent degraded mode
cleanDisabled(true) is double-enforced (precompiled plugin CLI guard + programmatic Database.migrate guard)
Extract Application.configureRouting() so /health test runs without Postgres — preserves D-11 invariant that ./gradlew :server:test passes in fresh clones / CI
Default credentials in application.conf (recipe/recipe/recipe @ localhost:5432/recipe) match Plan 06 docker-compose for zero-config dev boot
HOCON ${?ENV} override: every secret/per-env value gets a default line followed by ${?ENV_VAR} optional substitution
Fail-loud infrastructure: critical boot operations (DB migration, future JWKS load) throw IllegalStateException rather than returning a status
Routing extraction for testability: features expose Application.configureXxx() extensions; module() is the production composition root
INFRA-02
~1 min (executor work — implementation commits authored ahead of executor invocation) 2026-04-24

Phase 01 Plan 05: Server /health + Flyway + HOCON Boot Summary

Running-but-empty Ktor server: HOCON-configured Flyway boot with fail-loud Postgres contract, GET /health returning {"status":"ok"}, and a routing extraction that lets tests verify the route without a running database.

Performance

  • Duration: Implementation commits span 2026-04-24 18:22:08 → 18:23:14 (~66s of authoring); executor verification + SUMMARY ~1 min
  • Started: 2026-04-24T18:22:08Z (commit 24018ef)
  • Completed: 2026-04-24T18:23:14Z (commit 59d0695)
  • Tasks: 3
  • Files modified: 5 (3 created, 2 modified)

Accomplishments

  • HOCON application.conf reads PORT + DATABASE_URL/USER/PASSWORD via the ${?ENV} two-line override pattern; defaults match the Plan 06 docker-compose stack so docker compose up -d postgres && ./gradlew :server:run works with zero env config.
  • Database.migrate(app: Application) runs Flyway.configure().dataSource(...).locations("classpath:db/migration").baselineOnMigrate(true).validateOnMigrate(true).cleanDisabled(true).load().migrate() and throws IllegalStateException on any failure — D-16 fail-loud contract satisfied.
  • db/migration/.gitkeep keeps the resource directory in the repo so Flyway's classpath resolution succeeds before Phase 3 introduces the first SQL migration.
  • Application.kt rewritten with explicit Ktor imports (D-11 allWarningsAsErrors clean), installs ContentNegotiation { json() }, calls Database.migrate(this), then delegates to Application.configureRouting() which exposes GET /health → Health(status="ok").
  • ApplicationTest.kt rewritten to compose configureRouting() directly (skipping Database.migrate) so ./gradlew :server:test --tests "*health*" passes without a running Postgres — required for fresh-clone / CI runs.

Task Commits

Each task was committed atomically prior to executor invocation (commits already in branch history):

  1. Task 1: HOCON config + db/migration/.gitkeep + Database.kt24018ef (feat)
  2. Task 2: Application.kt rewrite (ContentNegotiation, Flyway boot, /health)daefe6c (refactor)
  3. Task 3: ApplicationTest.kt rewrite (no-Postgres /health assertion)59d0695 (test)

Plan metadata: appended in this commit (docs).

Files Created/Modified

  • server/src/main/resources/application.conf (created) — HOCON config: ktor.deployment.port + database.{url,user,password} with ${?ENV} overrides
  • server/src/main/resources/db/migration/.gitkeep (created) — anchors the Flyway classpath resource directory in git
  • server/src/main/kotlin/dev/ulfrx/recipe/Database.kt (created) — object Database { fun migrate(app) } with fail-loud Flyway invocation, SLF4J logging
  • server/src/main/kotlin/dev/ulfrx/recipe/Application.kt (modified) — explicit imports; installs ContentNegotiation; runs Database.migrate; delegates to configureRouting(); exposes GET /health returning serializable Health(status)
  • server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt (modified) — replaces template testRoot() with health-endpoint test that composes routing without DB

Decisions Made

See key-decisions in frontmatter. Highlights:

  • HOCON ${?ENV} optional substitution chosen over ${ENV} (required) and ${ENV:default} (invalid HOCON) per PITFALL #5.
  • Server logging via SLF4J/Logback (not Kermit) because Logback is already wired in recipe.jvm.server and Kermit is reserved for the multiplatform client.
  • Application.configureRouting() extension extracted to satisfy the no-Postgres-required invariant for ./gradlew :server:test.

Deviations from Plan

None — plan executed exactly as written. All artifacts match the plan's must_haves (truths, artifacts, key_links) verified against the filesystem; explicit imports satisfy D-11; ${?ENV} lines all present; fail-loud contract intact; Database.migrate not referenced from the test.

Issues Encountered

None.

User Setup Required

None — no external service configuration required. Postgres for end-to-end boot is provided by the Plan 06 docker-compose stack; Plan 05's own success criteria (test passing without a running DB) require nothing from the operator.

Next Phase Readiness

  • Phase 2 (Auth) inherits a Ktor server with ContentNegotiation pre-installed, so JWT validation routes can return @Serializable DTOs immediately.
  • Phase 3 (Households) drops V1__init.sql into server/src/main/resources/db/migration/; the Flyway boot pathway is already validated.
  • Phase 11 (Deployment) inherits the HOCON ${?ENV} pattern; homelab deploy configures DATABASE_URL/USER/PASSWORD via env vars without touching application.conf.
  • Manual end-to-end verification (docker compose up -d postgres && ./gradlew :server:run && curl http://localhost:8080/health) deferred to Plan 07 / manual smoke per the plan's verification section.

Self-Check: PASSED

  • File server/src/main/resources/application.conf — FOUND
  • File server/src/main/resources/db/migration/.gitkeep — FOUND
  • File server/src/main/kotlin/dev/ulfrx/recipe/Database.kt — FOUND
  • File server/src/main/kotlin/dev/ulfrx/recipe/Application.kt — FOUND
  • File server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt — FOUND
  • Commit 24018ef (feat 01-05 Task 1) — FOUND in git log
  • Commit daefe6c (refactor 01-05 Task 2) — FOUND in git log
  • Commit 59d0695 (test 01-05 Task 3) — FOUND in git log

Phase: 01-project-infrastructure-module-wiring Completed: 2026-04-24