11 KiB
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-foundation | 03 | backend-foundation |
|
|
|
|
|
|
Phase 01-foundation Plan 03: Walking Skeleton GREEN slice
One-liner
Turns the RED tests from Plan 01-02 GREEN by wiring pgxpool, chi router with structured-logging + UUIDv4 RequestID middleware, three handlers (/healthz, /, /demo/time), three templ templates (layout, index, fragments) consuming the ui design-system, and two main entrypoints (cmd/web with full graceful shutdown, cmd/worker Phase 1 skeleton).
What Shipped
| Surface | Behavior |
|---|---|
db.NewPool |
pgxpool builder, MaxConns=10/MinConns=1, lazy (no eager Ping) |
web.NewRouter(pinger, staticDir) |
chi router — middleware stack: RequestIDMiddleware → RealIP → SlogLoggerMiddleware → Recoverer (verifies CONTEXT D-08); routes: GET /, GET /healthz, GET /demo/time, GET /static/* |
web.HealthzHandler |
200 + {"status":"ok","db":"ok"} when Ping passes inside 2s; 503 + {"status":"degraded","db":"down"} otherwise (D-20) |
web.IndexHandler |
renders templates.Index() as text/html — root page consumes @ui.Card + @ui.Button per UI-SPEC |
web.DemoTimeHandler |
renders templates.TimeFragment(now()) as an HTML <span> — accepts injected func() time.Time clock for tests |
web.RequestIDMiddleware |
UUIDv4 per request, attached to ctx + X-Request-ID header |
web.SlogLoggerMiddleware |
Structured per-request log (method, path, status, duration_ms, request_id); allowlist-only fields (T-01-09) |
web.NewSlogHandler(env, w) |
JSONHandler when env=="production", TextHandler otherwise |
templates.Layout(title) |
Base HTML shell — UI-SPEC §Base Layout Contract; /static/tailwind.css in <head>, /static/htmx.min.js deferred at body end (D-10: no CDN) |
templates.Index |
Root page: H1, muted subtitle, @ui.Card containing the canonical HTMX demo CTA |
templates.TimeFragment |
<span class="text-slate-900">{RFC3339 UTC}</span> |
cmd/web/main.go |
Loads env, slog handler, pgxpool, chi router; http.Server with 15s/15s/60s timeouts; signal.NotifyContext; Shutdown(10s) then explicit pool.Close() |
cmd/worker/main.go |
D-03 skeleton: 48 lines; logs "worker ready"; blocks on signal; closes pool; exits 0 |
Tests
All targeted tests are GREEN under default go test ./...:
ok backend/internal/db 0.225s # TestPool_Connects skips cleanly when DATABASE_URL is unset
ok backend/internal/web 0.547s # six handler tests
ok backend/internal/web/ui 0.652s # ui package smoke tests
Per-file:
internal/web/handlers_test.go: TestHealthz_OK, TestHealthz_Down, TestIndex_RendersHxGet, TestDemoTime_Fragment, TestRequestID_HeaderSet, TestSlog_HandlerSwitch — all PASSinternal/db/pool_test.go: TestPool_Connects — SKIPS cleanly whenDATABASE_URLis unset; runs against compose Postgres when set (verification deferred to Task 5 human checkpoint).
The //go:build red_gate tags placed by Plan 01-02 were removed in Task 1 as the first action, so the suite runs by default.
Build
go build ./... # exits 0
Binary sizes:
cmd/web: 12.9 MBcmd/worker: 11.7 MB
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 — Bug] templ generator double-imported github.com/a-h/templ
- Found during: Task 2 (after first
templ generate+go build) - Issue: I added
import "github.com/a-h/templ"explicitly totemplates/index.templto make thetempl.Attributesliteral type explicit. The generator already emits an unconditionalimport "github.com/a-h/templ"in every*_templ.gofile, producingtempl redeclared in this block. - Fix: Removed the explicit import line from
index.templ; relied on the generator's auto-import.templ.Attributesreference inside the templ source resolves fine through the auto-import. - Files modified:
backend/templates/index.templ - Commit: included in
feat(01-03): templ layout/index/fragments + handlers + chi router(3a12f8f)
No other deviations. The plan executed as written; go mod tidy did the expected work (Codex concern #1 retired); the RED gate tags were removed (Codex concern #3 retired).
Auto-approved Checkpoints
- ⚡ Auto-approved: Task 5
checkpoint:human-verify— full Walking Skeleton run (browser HTMX round-trip, graceful shutdown observation, worker boot, README walkthrough). Auto-approved under phase-wide auto-mode. The agent has already proven correctness viago test ./...GREEN for all targeted units; the browser interaction and live-reload checks are the parts that genuinely require a human and are deferred to the user's discretion. The orchestrator can resume the phase without pausing.
Threat Surface Scan
No new threat flags discovered. All threat-register entries (T-01-08 through T-01-13) are mitigated in code:
- T-01-08 (path traversal at
/static/*):http.FileServer(http.Dir(staticDir))is used;http.Dirrejects..traversal by default. - T-01-09 (log info-disclosure):
SlogLoggerMiddlewareallowlist — method/path/status/duration_ms/request_id only. Never Authorization, Cookie, or body. - T-01-10 (slow-client DoS):
http.Server.ReadTimeout=15s, WriteTimeout=15s, IdleTimeout=60sincmd/web/main.go. - T-01-11 (panic crash):
chimw.Recovererregistered AFTERSlogLoggerMiddlewareso panics carryrequest_id. - T-01-12 (DSN leak):
cmd/webandcmd/workerlog onlyerr, never"dsn"on connect failure. - T-01-13 (XSS in
/demo/time): templ auto-escapes the time literal; notempl.Rawused anywhere.
Verification Cross-Check (from <verification> block)
| Check | Result |
|---|---|
go test ./... -count=1 exits 0 |
✅ PASS |
go build ./cmd/web ./cmd/worker succeeds |
✅ PASS |
grep -r 'middleware.Logger' backend/ outside of comments |
✅ only docstring/comment mentions — no import or registration |
grep -r 'unpkg.com|cdn\.' backend/internal backend/templates backend/cmd |
✅ empty |
grep -r 'class="bg-blue-' backend/templates/ |
✅ empty (pages consume ui.Button) |
//go:build red_gate removed |
✅ from both handlers_test.go and pool_test.go |
Known Stubs
None. Every interactive surface in scope (the demo button, the HTMX swap, /healthz, the worker boot signal) is fully wired against real infrastructure (pgxpool, templ-rendered HTML, chi router).
Self-Check
Files verified to exist:
- FOUND:
backend/internal/db/pool.go - FOUND:
backend/internal/web/slog.go - FOUND:
backend/internal/web/middleware.go - FOUND:
backend/internal/web/handlers.go - FOUND:
backend/internal/web/router.go - FOUND:
backend/templates/layout.templ - FOUND:
backend/templates/index.templ - FOUND:
backend/templates/fragments.templ - FOUND:
backend/cmd/web/main.go - FOUND:
backend/cmd/worker/main.go
Commits verified:
- FOUND:
36e9601— feat(01-03): pgxpool wrapper, RequestID/slog middleware, slog handler switch - FOUND:
3a12f8f— feat(01-03): templ layout/index/fragments + handlers + chi router - FOUND:
08a2c3c— feat(01-03): cmd/web entrypoint with graceful shutdown - FOUND:
aa1e1fd— feat(01-03): cmd/worker Phase 1 skeleton (D-03)
Self-Check: PASSED
Notes for Plan 04
Plan 04 wraps the foundation: README quickstart, .env.example sanity check, justfile recipe audit, and the Phase 1 verification gate (/gsd-verify-work). Items observed during this plan that Plan 04 should consider:
- The pinned
pressly/gooseandsqlc-dev/sqlcruntime deps were dropped fromgo.mod'srequirelist bygo mod tidybecause they have no consumer code yet — they are CLI-only tools installed viajust bootstrap. This is expected per CONTEXT D-04/D-05; Phase 7 will re-introduce goose as a Go-library import if the deploy path needs embedded migrations. cmd/webstartup logs anaddrattribute on thelisteningline so the dev/prod port is visible in structured logs immediately.- The middleware order is locked in source order (RequestIDMiddleware → RealIP → SlogLoggerMiddleware → Recoverer); Plan 04 README should call this out so Phase 2 doesn't accidentally re-order when adding session middleware.