diff --git a/.planning/phases/01-foundation/01-HUMAN-UAT.md b/.planning/phases/01-foundation/01-HUMAN-UAT.md new file mode 100644 index 0000000..1150bc4 --- /dev/null +++ b/.planning/phases/01-foundation/01-HUMAN-UAT.md @@ -0,0 +1,36 @@ +--- +status: partial +phase: 01-foundation +source: [01-VERIFICATION.md] +started: 2026-05-14T17:34:09Z +updated: 2026-05-14T17:34:09Z +--- + +## Current Test + +[awaiting human testing] + +## Tests + +### 1. Live-reload under `just dev` +expected: Start `just dev`, edit `backend/internal/web/handlers.go` (e.g., add a log line), confirm air rebuilds and restarts. Edit `backend/templates/index.templ`, run `just generate`, refresh browser, confirm the change is visible. (Success Criterion #1) +result: [pending] + +### 2. Browser console + HTMX round-trip +expected: Load http://localhost:8080 with devtools open. Console shows zero errors. Page is Tailwind-styled. Click "Fetch server time" — `GET /demo/time` fires and the HTML fragment swaps into `#demo-out`. (Success Criterion #3) +result: [pending] + +### 3. 5-minute clean-clone walkthrough +expected: On a fresh clone (or fresh machine), follow `backend/README.md` Quickstart from `cp .env.example .env` through `just bootstrap`, `just db-up`, `just migrate up`, `just dev`. The HTMX page renders within ~5 minutes. (Success Criterion #5) +result: [pending] + +## Summary + +total: 3 +passed: 0 +issues: 0 +pending: 3 +skipped: 0 +blocked: 0 + +## Gaps diff --git a/.planning/phases/01-foundation/01-VERIFICATION.md b/.planning/phases/01-foundation/01-VERIFICATION.md new file mode 100644 index 0000000..3102e20 --- /dev/null +++ b/.planning/phases/01-foundation/01-VERIFICATION.md @@ -0,0 +1,130 @@ +--- +phase: 01-foundation +verified: 2026-05-14T17:34:09Z +status: human_needed +score: 5/5 automated must-haves verified; 3 success criteria require human confirmation +overrides_applied: 0 +human_verification: + - test: "Run `just dev` from a fresh state and edit a `.go` file and a `.templ` file" + expected: "air rebuilds and restarts the web server on `.go` saves; `just generate` (or styles-watch) regenerates templates on `.templ` saves; browser at http://localhost:8080 reflects the change after refresh" + why_human: "Live-reload behavior cannot be observed without running the dev loop interactively (Success Criterion #1)" + - test: "Open http://localhost:8080 in a browser with devtools open" + expected: "Page renders with Tailwind styling, no console errors, clicking 'Fetch server time' triggers `hx-get /demo/time` and swaps the timestamp into `#demo-out`" + why_human: "Browser console-error-free check and HTMX round-trip require a live browser (Success Criterion #3)" + - test: "Clean clone walkthrough" + expected: "A new developer with Go ≥ 1.22, just, podman/docker, and curl can clone the repo, follow `backend/README.md` Quickstart (cp .env.example .env; just bootstrap; just db-up; just migrate up; just dev), and see the HTMX page within ~5 minutes" + why_human: "End-to-end onboarding timing cannot be measured without a real fresh clone on a real machine (Success Criterion #5)" +--- + +# Phase 1: Foundation Verification Report + +**Phase Goal:** A fresh `backend/` Go package boots a web server, renders an HTMX-driven base layout, and connects to a local Postgres with migrations. +**Verified:** 2026-05-14T17:34:09Z +**Status:** human_needed +**Re-verification:** No — initial verification + +## Goal Achievement + +### Observable Truths (ROADMAP Success Criteria) + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| SC1 | `just dev` starts web server on local port and live-reloads on `.go` and template changes | ? UNCERTAIN | `justfile` `dev` recipe brings up db, runs `just generate`, then `air -c .air.toml`. `.air.toml` has `include_ext = ["go", "templ"]` and `exclude_regex = [".*_templ\\.go$"]` — config supports live-reload on both file types. Requires interactive run to confirm. | +| SC2 | `GET /healthz` returns 200 JSON `{status:"ok", db:"ok"}` only when DB reachable | ✓ VERIFIED | `internal/web/handlers.go:16-35` HealthzHandler pings with 2s timeout; returns `{"status":"ok","db":"ok"}` on success and `{"status":"degraded","db":"down"}` with 503 on failure. Covered by `handlers_test.go` (tests pass). | +| SC3 | Root route renders Tailwind-styled HTMX page with working `hx-get` example | ✓ VERIFIED (code) / ? UNCERTAIN (browser) | `templates/layout.templ` loads `/static/tailwind.css` and `/static/htmx.min.js` (no CDN). `templates/index.templ` includes `hx-get="/demo/time"`, `hx-target="#demo-out"`, `hx-swap="innerHTML"`. Handler chain wired in `router.go`. Browser console-error check requires human. | +| SC4 | `just migrate up` applies migrations from `backend/migrations/` | ✓ VERIFIED | `justfile` `migrate` recipe runs `goose` with `GOOSE_MIGRATION_DIR=migrations`. `migrations/0001_init.sql` present with `-- +goose Up/Down` markers (no-op bootstrap). goose v3.27.1 pinned. | +| SC5 | Fresh clone → `compose up -d` + `just dev` → page within ~5 min | ? UNCERTAIN | `README.md` documents complete Quickstart with prerequisites, `cp .env.example .env`, `just bootstrap`, `just db-up`, `just migrate up`, `just dev`. Requires actual clone-and-time walkthrough. | + +**Score:** 3/5 verified from codebase, 2 require human verification, 1 partial (SC3 code verified, browser pending). + +### Required Artifacts + +| Artifact | Expected | Status | Details | +|----------|----------|--------|---------| +| `backend/cmd/web/main.go` | HTTP server entry, slog, pgxpool, graceful shutdown | ✓ VERIFIED | 89 lines; signal.NotifyContext, ReadTimeout/WriteTimeout/IdleTimeout, srv.Shutdown with 10s timeout, pool.Close after shutdown | +| `backend/cmd/worker/main.go` | Worker skeleton boot/log/idle | ✓ VERIFIED | 48 lines; logs "worker ready", waits on ctx.Done(), closes pool | +| `backend/internal/web/router.go` | chi router with RequestID, RealIP, Slog, Recoverer; routes /, /healthz, /demo/time, /static/* | ✓ VERIFIED | Middleware stack correct order; Pinger interface (pgxpool satisfies); routes wired | +| `backend/internal/web/handlers.go` | Healthz, Index, DemoTime handlers | ✓ VERIFIED | All three handlers present with correct contracts | +| `backend/internal/web/middleware.go` | RequestID + Slog middleware | ✓ VERIFIED | File exists in directory | +| `backend/internal/web/slog.go` | NewSlogHandler (env-aware JSON/text) | ✓ VERIFIED | Used by both cmd/web and cmd/worker | +| `backend/internal/web/ui/` | Custom templ component library (Button, Card, Badge) | ✓ VERIFIED | Package compiles, tests pass (`backend/internal/web/ui` green) | +| `backend/internal/db/pool.go` | pgxpool factory, lazy connection | ✓ VERIFIED | NewPool with MaxConns=10/MinConns=1, no eager Ping | +| `backend/templates/layout.templ` | Tailwind-styled base layout, /static/htmx.min.js | ✓ VERIFIED | No CDN, max-w-5xl container, correct asset refs | +| `backend/templates/index.templ` | Root page with HTMX hx-get demo | ✓ VERIFIED | Uses `@ui.Card` and `@ui.Button` with hx-get/hx-target/hx-swap | +| `backend/templates/fragments.templ` | TimeFragment for /demo/time | ✓ VERIFIED | Imported and rendered by DemoTimeHandler | +| `backend/migrations/0001_init.sql` | Goose migration | ✓ VERIFIED | Up/Down markers present, no-op SELECT 1 | +| `backend/justfile` | bootstrap, dev, db-up, db-down, migrate, generate, styles-watch, test, lint, build, clean | ✓ VERIFIED | All recipes present; pinned versions for goose/templ/sqlc/air/tailwind/htmx | +| `backend/compose.yaml` | Local Postgres with healthcheck | ✓ VERIFIED | postgres:16-alpine, pg_isready healthcheck, port 5432 | +| `backend/.air.toml` | Live-reload config | ✓ VERIFIED | include_ext=[go,templ], exclude_regex for *_templ.go | +| `backend/.env.example` | DATABASE_URL, PORT, ENV documented | ✓ VERIFIED | All three keys present | +| `backend/README.md` | Quickstart, fallback, troubleshooting | ✓ VERIFIED | Full Quickstart, docker fallback, troubleshooting section, layout, env vars table, commands table | +| `backend/go.mod` | Pinned chi, templ, pgx, goose, uuid | ✓ VERIFIED | Build succeeds, no missing deps | + +### Key Link Verification + +| From | To | Via | Status | Details | +|------|-----|-----|--------|---------| +| cmd/web/main.go | db.NewPool | direct call | ✓ WIRED | line 50 | +| cmd/web/main.go | web.NewRouter | direct call with pool | ✓ WIRED | line 57; pool passes as Pinger | +| router.go | HealthzHandler | r.Get("/healthz", ...) | ✓ WIRED | line 39 | +| router.go | IndexHandler | r.Get("/", ...) | ✓ WIRED | line 38 | +| router.go | DemoTimeHandler | r.Get("/demo/time", ...) | ✓ WIRED | line 40 | +| router.go | static FS | r.Get("/static/*", ...) | ✓ WIRED | line 43, http.StripPrefix + http.Dir | +| index.templ | /demo/time | hx-get attribute | ✓ WIRED | matches route in router.go | +| layout.templ | /static/htmx.min.js |