Files
recipe/.planning/phases/01-project-infrastructure-module-wiring/01-06-SUMMARY.md

156 lines
7.9 KiB
Markdown

---
phase: 01-project-infrastructure-module-wiring
plan: 06
subsystem: dev-ergonomics
tags: [docker-compose, postgres, readme, local-dev, infra]
dependency_graph:
requires: []
provides:
- "Local Postgres 16 dev instance matching application.conf HOCON defaults (recipe/recipe/recipe)"
- "Named volume recipe-pgdata for persistence across container restarts"
- "pg_isready healthcheck enabling docker compose up --wait usage"
- "README 'Local development' section documenting the two-command dev loop"
affects:
- "server/src/main/resources/application.conf (Plan 05 — credentials match contract)"
- "Phase 3 (Households + DB migrations) — depends on a working local Postgres"
- "Phase 11 (homelab deployment) — separate compose config will diverge from this dev-local one"
tech_stack:
added:
- "postgres:16 (Docker image, pinned major version)"
patterns:
- "Dev-local compose file committed to repo (non-secret literal creds)"
- "Healthcheck via pg_isready gating sequencing"
- "Named Docker volume for data persistence"
key_files:
created:
- "docker-compose.yml"
modified:
- "README.md"
decisions:
- "Kept it single-service: postgres only. Authentik stays on homelab (CONTEXT.md D-17); Ktor server runs via Gradle on the dev host for fast iteration."
- "Pinned postgres:16 (not :latest, not :15) matching D-17 scope statement."
- "No version: key in compose file — modern docker compose v2 treats it as legacy and emits warnings."
- "No .env file in this plan — inline POSTGRES_* is fine for single-dev + matching application.conf defaults (D-17 / PATTERNS.md recommendation)."
- "Port binding 5432:5432 is dev-local; README calls it out. Phase 11 homelab compose will use a different approach."
metrics:
duration_seconds: 92
duration_human: "1m32s"
tasks_completed: 2
files_created: 1
files_modified: 1
completed_at: "2026-04-24T16:22:48Z"
---
# Phase 01 Plan 06: Dev ergonomics — docker-compose + README Local development summary
Shipped `docker-compose.yml` (single postgres:16 service, named volume, healthcheck — credentials matching Plan 05's `application.conf` HOCON defaults exactly) and a "Local development" README section documenting the `docker compose up -d postgres && ./gradlew :server:run && curl /health` dev loop, while dropping the legacy `js` target docs per D-01.
## What was built
### docker-compose.yml (20 lines)
- `services.postgres`:
- `image: postgres:16` (pinned major version)
- `container_name: recipe-postgres`
- `environment`: `POSTGRES_DB / POSTGRES_USER / POSTGRES_PASSWORD` all literal `recipe`
- `ports: "5432:5432"` (dev-local loopback via host Docker)
- `volumes: recipe-pgdata:/var/lib/postgresql/data` (persistence)
- `healthcheck`: `pg_isready -U recipe -d recipe` every 5s, timeout 5s, 5 retries
- Top-level `volumes.recipe-pgdata:` (named volume declaration)
- No `version:` key (modern compose v2)
- No additional services (no Authentik — lives on user's homelab per D-17)
### README.md edits
**Edit A — dropped js target block** (lines 77-85 of previous README): the "- for the JS target (slower, supports older browsers)" paragraph and its two command blocks were deleted. The `wasmJs` paragraph is preserved intact.
**Edit B — inserted new "Local development" section** (after the iOS subsection, before the trailing `---` horizontal rule):
- Two-command boot: `docker compose up -d postgres` + `./gradlew :server:run`
- Smoke test: `curl http://localhost:8080/health` with expected `{"status":"ok"}` response
- Documented env-var overrides: `DATABASE_URL`, `DATABASE_USER`, `DATABASE_PASSWORD`, `PORT`
- Pre-commit formatter hint: `./gradlew spotlessApply` (D-10)
- Full-suite: `./gradlew check`
- DB reset: `docker compose down -v` (destroys `recipe-pgdata`)
All other existing headings (Android, Desktop/JVM, Server, iOS, web `wasmJs`) and the top introduction (lines 1-20) are unchanged. The trailing `---` + learn-more links paragraph is unchanged.
## Credential-match contract with Plan 05
The three compose env-vars are byte-identical to the literals in `server/src/main/resources/application.conf`:
| compose env | application.conf |
|-------------|------------------|
| `POSTGRES_DB: recipe` | JDBC URL path `/recipe` |
| `POSTGRES_USER: recipe` | `user = "recipe"` |
| `POSTGRES_PASSWORD: recipe` | `password = "recipe"` |
Verified via `grep -c '^\s*POSTGRES_\(DB\|USER\|PASSWORD\): recipe$' docker-compose.yml``3`.
## Requirements addressed
- **INFRA-02** — local development environment via `docker-compose.yml` and README dev loop documentation.
## Tasks executed
| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 | Create docker-compose.yml at repo root | `af4428f` | docker-compose.yml (new) |
| 2 | Add "Local development" section to README.md and drop js target docs | `f691400` | README.md (modified) |
## Deviations from Plan
None — plan executed exactly as written. No Rule 1-3 auto-fixes, no checkpoints, no auth gates. Both `<automated>` verify blocks and every acceptance criterion passed on first attempt.
## Threat surface scan
No new network endpoints, auth paths, file access patterns, or schema changes at trust boundaries were introduced beyond what the plan's `<threat_model>` already covers (T-01-06-01..04). The `5432:5432` host binding and literal `recipe/recipe/recipe` credentials are the exact surface the plan's STRIDE register dispositions (`mitigate`/`accept`) already cover. No new flags.
## Known stubs
None. Both deliverables are complete — no placeholders, no TODOs, no empty data paths.
## Verification
**Task 1 automated check:**
```
test -f docker-compose.yml && grep -q 'image: postgres:16' ... && grep -q 'pg_isready -U recipe -d recipe' ... && grep -q '^volumes:$' ...
→ VERIFY PASS
grep -c '^\s*POSTGRES_\(DB\|USER\|PASSWORD\): recipe$' docker-compose.yml → 3
```
**Task 2 automated check:**
```
grep -q 'Local development' && grep -q 'docker compose up -d postgres' && grep -q 'curl http://localhost:8080/health' && grep -q 'DATABASE_URL' && grep -q 'gradlew spotlessApply' && grep -q 'docker compose down -v' && ! grep -q 'jsBrowserDevelopmentRun' && grep -q 'wasmJsBrowserDevelopmentRun'
→ VERIFY PASS
```
**Acceptance criteria — Task 2 individually confirmed:**
- `Local development` appears exactly once (section heading)
- All 4 env-vars listed: `DATABASE_URL`, `DATABASE_USER`, `DATABASE_PASSWORD`, `PORT`
- `gradlew check` present
- Existing section headings (Android / Desktop (JVM) / Server / iOS) all preserved (grep `-c``1` each)
- `jsBrowserDevelopmentRun` absent; `wasmJsBrowserDevelopmentRun` present
- Top introduction (lines 1-20) unchanged
## Manual sanity checks (optional, not blocking)
Skipped per plan `<verification>`:
- `docker compose config` YAML parse — not blocking per plan; docker may not be running in this worktree sandbox.
- `docker compose up -d postgres && pg_isready` live test — not required; will be validated in Phase 3 when migrations land.
## Notes for downstream plans
- **Plan 05** (this wave) — credential contract lives in both files; any future change to the `recipe/recipe/recipe` triple MUST update both `application.conf` AND `docker-compose.yml` in the same commit.
- **Phase 3** (Households + DB migrations) — can add `depends_on: { postgres: { condition: service_healthy } }` to a future `server` service in compose if we ever run the Ktor server in Docker; the healthcheck is already wired for it.
- **Phase 11** (homelab deployment) — will ship a separate compose file (not editing this one) because homelab creds are secret and this file's creds are deliberately non-secret literals.
## Self-Check: PASSED
- `docker-compose.yml` exists at repo root: FOUND
- `README.md` contains "Local development" section: FOUND
- Commit `af4428f` (Task 1): FOUND in `git log`
- Commit `f691400` (Task 2): FOUND in `git log`
- All acceptance criteria from both tasks verified via grep
- No file deletions in either commit