From aa90008d95cdcf873948fe539ad910974f4da26e Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Thu, 14 May 2026 17:53:36 +0200 Subject: [PATCH 1/6] feat(01-01): initialize Go module and pin runtime dependencies - module: backend (go 1.26.1) - chi v5.2.5, templ v0.3.1020, pgx/v5 v5.9.2, goose v3.27.1, uuid v1.6.0 - pinned via 'go get'; 'go mod tidy' deferred to Plan 01-03 where consumers exist (Codex concern #1) --- backend/go.mod | 18 ++++++++++++++++++ backend/go.sum | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 backend/go.mod create mode 100644 backend/go.sum diff --git a/backend/go.mod b/backend/go.mod new file mode 100644 index 0000000..4312cb9 --- /dev/null +++ b/backend/go.mod @@ -0,0 +1,18 @@ +module backend + +go 1.26.1 + +require ( + github.com/a-h/templ v0.3.1020 // indirect + github.com/go-chi/chi/v5 v5.2.5 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.9.2 // indirect + github.com/mfridman/interpolate v0.0.2 // indirect + github.com/pressly/goose/v3 v3.27.1 // indirect + github.com/sethvargo/go-retry v0.3.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/text v0.36.0 // indirect +) diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..0edf8db --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,33 @@ +github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw= +github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= +github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= +github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pressly/goose/v3 v3.27.1 h1:6uEvcprBybDmW4hcz3gYujhARhye+GoWKhEWyzD5sh4= +github.com/pressly/goose/v3 v3.27.1/go.mod h1:maruOxsPnIG2yHHyo8UqKWXYKFcH7Q76csUV7+7KYoM= +github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= +github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 4de96854b55846b6fd9f09aca168f33f851009f2 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Thu, 14 May 2026 17:53:55 +0200 Subject: [PATCH 2/6] feat(01-01): create directory skeleton and per-package doc.go placeholders - internal/{db,session,tablos,tasks,files}/doc.go (D-02 placeholder packages) - internal/db/{queries,sqlc}, templates/, migrations/, bin/, static/ via .gitkeep - 'go build ./internal/...' compiles cleanly - cmd/web, cmd/worker, internal/web are deliberately deferred to Plans 01-02 / 01-03 --- backend/bin/.gitkeep | 0 backend/internal/db/doc.go | 3 +++ backend/internal/db/queries/.gitkeep | 0 backend/internal/db/sqlc/.gitkeep | 0 backend/internal/files/doc.go | 2 ++ backend/internal/session/doc.go | 2 ++ backend/internal/tablos/doc.go | 2 ++ backend/internal/tasks/doc.go | 2 ++ backend/migrations/.gitkeep | 0 backend/static/.gitkeep | 0 backend/templates/.gitkeep | 0 11 files changed, 11 insertions(+) create mode 100644 backend/bin/.gitkeep create mode 100644 backend/internal/db/doc.go create mode 100644 backend/internal/db/queries/.gitkeep create mode 100644 backend/internal/db/sqlc/.gitkeep create mode 100644 backend/internal/files/doc.go create mode 100644 backend/internal/session/doc.go create mode 100644 backend/internal/tablos/doc.go create mode 100644 backend/internal/tasks/doc.go create mode 100644 backend/migrations/.gitkeep create mode 100644 backend/static/.gitkeep create mode 100644 backend/templates/.gitkeep diff --git a/backend/bin/.gitkeep b/backend/bin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/internal/db/doc.go b/backend/internal/db/doc.go new file mode 100644 index 0000000..2e3af65 --- /dev/null +++ b/backend/internal/db/doc.go @@ -0,0 +1,3 @@ +// Package db is a Phase 1 placeholder. The pgxpool wiring and sqlc-generated +// queries land in Plan 01-03 (Foundation Wave 3) and Phase 3 (Tablos schema). +package db diff --git a/backend/internal/db/queries/.gitkeep b/backend/internal/db/queries/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/internal/db/sqlc/.gitkeep b/backend/internal/db/sqlc/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/internal/files/doc.go b/backend/internal/files/doc.go new file mode 100644 index 0000000..9741681 --- /dev/null +++ b/backend/internal/files/doc.go @@ -0,0 +1,2 @@ +// Package files is a Phase 1 placeholder; the upload/storage implementation lands in Phase 5. +package files diff --git a/backend/internal/session/doc.go b/backend/internal/session/doc.go new file mode 100644 index 0000000..3a37395 --- /dev/null +++ b/backend/internal/session/doc.go @@ -0,0 +1,2 @@ +// Package session is a Phase 1 placeholder; the session/cookie implementation lands in Phase 2. +package session diff --git a/backend/internal/tablos/doc.go b/backend/internal/tablos/doc.go new file mode 100644 index 0000000..0ff3966 --- /dev/null +++ b/backend/internal/tablos/doc.go @@ -0,0 +1,2 @@ +// Package tablos is a Phase 1 placeholder; the tablo CRUD implementation lands in Phase 3. +package tablos diff --git a/backend/internal/tasks/doc.go b/backend/internal/tasks/doc.go new file mode 100644 index 0000000..0d697bd --- /dev/null +++ b/backend/internal/tasks/doc.go @@ -0,0 +1,2 @@ +// Package tasks is a Phase 1 placeholder; the kanban task implementation lands in Phase 4. +package tasks diff --git a/backend/migrations/.gitkeep b/backend/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/static/.gitkeep b/backend/static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/templates/.gitkeep b/backend/templates/.gitkeep new file mode 100644 index 0000000..e69de29 From 2fe5b51f80e8df5ecbaf148915c3657d4eb7d5e1 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Thu, 14 May 2026 17:54:18 +0200 Subject: [PATCH 3/6] feat(01-01): compose file, env example, gitignore, bootstrap migration - compose.yaml: postgres:16-alpine with pg_isready healthcheck (no seed mounts) - .env.example: DATABASE_URL, PORT, ENV (D-15) - .gitignore: bin/, tmp/, .env, generated tailwind.css, bootstrap-downloaded htmx.min.js, *_templ.go, sqlc output - migrations/0001_init.sql: goose no-op bootstrap migration --- backend/.env.example | 8 ++++++++ backend/.gitignore | 24 ++++++++++++++++++++++++ backend/compose.yaml | 21 +++++++++++++++++++++ backend/migrations/0001_init.sql | 6 ++++++ 4 files changed, 59 insertions(+) create mode 100644 backend/.env.example create mode 100644 backend/.gitignore create mode 100644 backend/compose.yaml create mode 100644 backend/migrations/0001_init.sql diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..64a9610 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,8 @@ +# Postgres connection string used by the web + worker binaries (and `just migrate`). +DATABASE_URL=postgres://xtablo:xtablo@localhost:5432/xtablo?sslmode=disable + +# HTTP port for cmd/web. +PORT=8080 + +# Environment selector: "development" enables the slog text handler; "production" switches to JSON. +ENV=development diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..894176d --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,24 @@ +# Local environment files (only .env.example is committed) +.env +.env.local + +# Bootstrap-downloaded CLIs and Tailwind binary (re-created by `just bootstrap`) +bin/ +!bin/.gitkeep + +# air rebuild output +tmp/ + +# Tailwind-compiled CSS (re-created by `just generate` / `just styles-watch`) +static/tailwind.css + +# HTMX bootstrap-downloaded by `just bootstrap` (never committed; the justfile is the single +# authoritative source for the pinned HTMX version — see CONTEXT D-10) +static/htmx.min.js + +# templ-generated Go files (regenerate via `templ generate` — Research Pitfall 1) +*_templ.go + +# sqlc-generated code (regenerate via `sqlc generate`); keep the .gitkeep +internal/db/sqlc/*.go +!internal/db/sqlc/.gitkeep diff --git a/backend/compose.yaml b/backend/compose.yaml new file mode 100644 index 0000000..1f5777f --- /dev/null +++ b/backend/compose.yaml @@ -0,0 +1,21 @@ +services: + postgres: + image: postgres:16-alpine + container_name: xtablo-backend-postgres + restart: unless-stopped + environment: + POSTGRES_DB: xtablo + POSTGRES_USER: xtablo + POSTGRES_PASSWORD: xtablo + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U xtablo -d xtablo"] + interval: 5s + timeout: 5s + retries: 10 + +volumes: + postgres_data: diff --git a/backend/migrations/0001_init.sql b/backend/migrations/0001_init.sql new file mode 100644 index 0000000..64cafd3 --- /dev/null +++ b/backend/migrations/0001_init.sql @@ -0,0 +1,6 @@ +-- +goose Up +-- Phase 1: no-op bootstrap migration. Real schema lands in Phase 2 (sessions/users) and Phase 3 (tablos). +SELECT 1; + +-- +goose Down +SELECT 1; From d6085b7dbb01943af474921d53afd0c4dcb0fce7 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Thu, 14 May 2026 17:54:35 +0200 Subject: [PATCH 4/6] feat(01-01): sqlc config, tailwind input CSS, air live-reload config - sqlc.yaml: postgresql engine, schema points at migrations/ (Pitfall 7), pgx/v5 sql_package - tailwind.input.css: @source globs for templ + internal/web Go files (Pitfall 3), @imports four ui/*.css files per UI-SPEC - .air.toml: watches .go + .templ, excludes generated *_templ.go (Pitfall 5), runs 'templ generate' pre-build - Tailwind watch is NOT wired into air pre_cmd (two-terminal workflow per RESEARCH Open Q2) --- backend/.air.toml | 11 +++++++++++ backend/sqlc.yaml | 12 ++++++++++++ backend/tailwind.input.css | 10 ++++++++++ 3 files changed, 33 insertions(+) create mode 100644 backend/.air.toml create mode 100644 backend/sqlc.yaml create mode 100644 backend/tailwind.input.css diff --git a/backend/.air.toml b/backend/.air.toml new file mode 100644 index 0000000..5bf7a4f --- /dev/null +++ b/backend/.air.toml @@ -0,0 +1,11 @@ +root = "." +tmp_dir = "tmp" + +[build] +cmd = "templ generate && go build -o ./tmp/web ./cmd/web" +bin = "./tmp/web" +include_ext = ["go", "templ"] +exclude_dir = ["tmp", "bin", "static", ".git", "internal/db/sqlc"] +exclude_regex = [".*_templ\\.go$"] +delay = 200 +stop_on_error = true diff --git a/backend/sqlc.yaml b/backend/sqlc.yaml new file mode 100644 index 0000000..9680109 --- /dev/null +++ b/backend/sqlc.yaml @@ -0,0 +1,12 @@ +version: "2" +sql: + - engine: postgresql + schema: "migrations" + queries: "internal/db/queries" + gen: + go: + package: "sqlc" + out: "internal/db/sqlc" + sql_package: "pgx/v5" + emit_json_tags: false + emit_interface: false diff --git a/backend/tailwind.input.css b/backend/tailwind.input.css new file mode 100644 index 0000000..8c4faf8 --- /dev/null +++ b/backend/tailwind.input.css @@ -0,0 +1,10 @@ +@import "tailwindcss"; + +@source "../templates/**/*.templ"; +@source "../internal/web/**/*.templ"; +@source "../internal/web/**/*.go"; + +@import "../internal/web/ui/base.css"; +@import "../internal/web/ui/button.css"; +@import "../internal/web/ui/card.css"; +@import "../internal/web/ui/badge.css"; From ce224725e22e839c63badbf8d0abf4771d5d2c93 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Thu, 14 May 2026 17:56:02 +0200 Subject: [PATCH 5/6] feat(01-01): justfile with bootstrap, db, migrate, generate, dev, test, lint, build, clean - bootstrap: installs goose/templ/sqlc/air at pinned versions; downloads Tailwind v4 standalone binary via explicit OS/arch case mapping (darwin->macos, x86_64->x64, arm64/aarch64->arm64) resolving to one of {tailwindcss-macos-x64,tailwindcss-macos-arm64,tailwindcss-linux-x64,tailwindcss-linux-arm64} (Codex concern #2); bootstrap-downloads htmx.min.js from unpkg into static/ - migrate: GOOSE_DRIVER + GOOSE_DBSTRING + GOOSE_MIGRATION_DIR wired - generate / styles-watch / dev / test / lint / build / clean recipes complete - 'just --list' enumerates 11 recipes; no pnpm/npm/node references (D-12) - clean recipe removes bin/, tmp/, generated CSS, bootstrap-downloaded htmx, *_templ.go (Codex #10) - Tailwind watch is run separately (RESEARCH Open Q2 two-terminal workflow) - The only CDN URLs in the entire backend are inside this justfile's bootstrap recipe; served HTML/CSS/JS reference only /static/* (CONTEXT D-10 clarified) --- backend/justfile | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 backend/justfile diff --git a/backend/justfile b/backend/justfile new file mode 100644 index 0000000..ec6bdfd --- /dev/null +++ b/backend/justfile @@ -0,0 +1,116 @@ +# Xtablo backend — task runner +# +# Portability: compose.yaml works under both `podman compose` and `docker compose` (CONTEXT D-11). +# This justfile uses `podman compose` by default; substitute `docker compose` if needed — +# the service definition is identical. +# +# Dev workflow (two terminals, per RESEARCH Open Question 2): +# Terminal 1: just dev # brings up postgres, generates assets, runs air for Go live-reload +# Terminal 2: just styles-watch # runs the Tailwind standalone CLI in --watch mode +# +# Tailwind is intentionally NOT chained into air's pre_cmd: piping CSS rebuilds through +# every .go save is wasteful, and the two concerns are independent (CONTEXT D-14). + +set shell := ["bash", "-cu"] + +# --- Pinned versions ------------------------------------------------------------------------ +# Runtime Go modules are pinned in go.mod (chi v5.2.5, templ v0.3.1020, pgx/v5 v5.9.2, +# goose v3.27.1, uuid v1.6.0). Below: CLI tools installed by `just bootstrap`. +goose_version := "v3.27.1" +templ_version := "v0.3.1020" +sqlc_version := "v1.31.1" +air_version := "v1.65.1" +# Tailwind standalone CLI version pinned for reproducible bootstrap. Update by bumping +# this string after verifying the release at https://github.com/tailwindlabs/tailwindcss/releases +tailwind_version := "v4.0.0" +# HTMX version pinned at bootstrap time. This is the SINGLE authoritative source for the +# HTMX version — no runtime CDN reference appears anywhere else (CONTEXT D-10). +htmx_version := "2" + +# --- Local config --------------------------------------------------------------------------- +database_url := "postgres://xtablo:xtablo@localhost:5432/xtablo?sslmode=disable" +tailwind := "./bin/tailwindcss" + +default: + @just --list + +# Install all CLI tools and bootstrap-download Tailwind + HTMX into local paths. +# Network access required. All downloaded artifacts are gitignored — `just bootstrap` is the +# canonical reproduction step. +bootstrap: + mkdir -p bin static + # 1. Go-based CLI tools (versions pinned above) + go install github.com/pressly/goose/v3/cmd/goose@{{goose_version}} + go install github.com/a-h/templ/cmd/templ@{{templ_version}} + go install github.com/sqlc-dev/sqlc/cmd/sqlc@{{sqlc_version}} + go install github.com/air-verse/air@{{air_version}} + # 2. Tailwind standalone CLI — explicit OS/arch mapping. The Tailwind release artifacts + # use 'macos'/'linux' and 'x64'/'arm64', which do NOT match raw `uname -s` / `uname -m` + # output (darwin vs macos, x86_64 vs x64). Resolve via case (Codex review concern #2). + # Resolves to one of: tailwindcss-macos-x64, tailwindcss-macos-arm64, + # tailwindcss-linux-x64, tailwindcss-linux-arm64 + os_name=$(uname -s); \ + arch_name=$(uname -m); \ + case "$os_name" in \ + Darwin) tw_os=macos ;; \ + Linux) tw_os=linux ;; \ + *) echo "Unsupported OS: $os_name (Tailwind standalone supports macos/linux)"; exit 1 ;; \ + esac; \ + case "$arch_name" in \ + x86_64|amd64) tw_arch=x64 ;; \ + arm64|aarch64) tw_arch=arm64 ;; \ + *) echo "Unsupported arch: $arch_name"; exit 1 ;; \ + esac; \ + asset="tailwindcss-${tw_os}-${tw_arch}"; \ + echo "Downloading $asset @ {{tailwind_version}}"; \ + curl -sSL -o bin/tailwindcss \ + "https://github.com/tailwindlabs/tailwindcss/releases/download/{{tailwind_version}}/${asset}"; \ + chmod +x bin/tailwindcss + # 3. HTMX — bootstrap-time download. This unpkg URL is the explicit allowed exception to + # the runtime no-CDN rule (CONTEXT D-10); served HTML references only /static/htmx.min.js. + curl -sSL -o static/htmx.min.js "https://unpkg.com/htmx.org@{{htmx_version}}/dist/htmx.min.js" + +db-up: + podman compose up -d postgres + +db-down: + podman compose down + +# `just migrate up`, `just migrate down`, `just migrate status`, etc. +migrate cmd="status": + GOOSE_DRIVER=postgres GOOSE_DBSTRING='{{database_url}}' GOOSE_MIGRATION_DIR=migrations \ + goose {{cmd}} + +# templ → Go, sqlc → Go, tailwind → static/tailwind.css. Consumers (templ files, queries, +# ui CSS) land in Plans 01-02 / 01-03; until then this recipe will fail when invoked but +# its declaration is the contract. +generate: + templ generate + sqlc generate + {{tailwind}} -i tailwind.input.css -o static/tailwind.css + +styles-watch: + {{tailwind}} -i tailwind.input.css -o static/tailwind.css --watch + +dev: db-up + just generate + DATABASE_URL='{{database_url}}' air -c .air.toml + +test: + just generate + go test ./... + +lint: + go vet ./... + gofmt -l . | (grep . && exit 1 || exit 0) + +build: + just generate + go build -o bin/web ./cmd/web + go build -o bin/worker ./cmd/worker + +# Remove all bootstrap-downloaded and generated artifacts. Does NOT touch the Postgres +# volume — run `just db-down` first if a full reset is needed. +clean: + rm -rf bin/ tmp/ static/htmx.min.js static/tailwind.css + find . -name '*_templ.go' -delete From 586b683131cdbc88621100371ce64d40d154a14b Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Thu, 14 May 2026 17:57:09 +0200 Subject: [PATCH 6/6] docs(01-01): complete backend scaffold plan summary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Documents 5 task commits (aa90008, 4de9685, 2fe5b51, d6085b7, ce22472) - Records pinned versions actually wired - Confirms Codex review concerns 1, 2, 3, 4, 6, 10 are addressed - Logs auto-approved human-verify checkpoint (Task 6 — real bootstrap requires developer-side network) - Path forward to Plans 01-02 (handler tests + ui/*.css) and 01-03 (cmd/* entrypoints + walking skeleton) --- .../phases/01-foundation/01-01-SUMMARY.md | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 .planning/phases/01-foundation/01-01-SUMMARY.md diff --git a/.planning/phases/01-foundation/01-01-SUMMARY.md b/.planning/phases/01-foundation/01-01-SUMMARY.md new file mode 100644 index 0000000..bc11aee --- /dev/null +++ b/.planning/phases/01-foundation/01-01-SUMMARY.md @@ -0,0 +1,165 @@ +--- +phase: 01-foundation +plan: 01 +subsystem: backend-scaffold +tags: [go, scaffold, tooling, postgres, tailwind, htmx] +requires: [] +provides: + - backend-go-module + - backend-directory-skeleton + - backend-compose-postgres + - backend-goose-migration-pipeline + - backend-sqlc-config + - backend-tailwind-input-css + - backend-air-live-reload + - backend-justfile +affects: + - downstream-plan-01-02-handler-tests-and-ui + - downstream-plan-01-03-walking-skeleton +tech_stack: + added: + - go-1.26 + - chi-v5.2.5 + - templ-v0.3.1020 + - pgx-v5.9.2 + - goose-v3.27.1 + - uuid-v1.6.0 + - sqlc-v1.31.1 (CLI; bootstrap-installed) + - air-v1.65.1 (CLI; bootstrap-installed) + - tailwind-standalone-v4.0.0 (binary; bootstrap-downloaded) + - htmx-v2 (bootstrap-downloaded, pinned major in justfile) + patterns: [] +key_files: + created: + - backend/go.mod + - backend/go.sum + - backend/compose.yaml + - backend/.env.example + - backend/.gitignore + - backend/.air.toml + - backend/sqlc.yaml + - backend/tailwind.input.css + - backend/justfile + - backend/migrations/0001_init.sql + - backend/internal/db/doc.go + - backend/internal/session/doc.go + - backend/internal/tablos/doc.go + - backend/internal/tasks/doc.go + - backend/internal/files/doc.go + modified: [] +decisions: + - locked: D-01 two-binary cmd/web + cmd/worker layout reflected in directory plan (cmd/* itself lands in Plan 01-03) + - locked: D-02 placeholder doc.go files created for db, session, tablos, tasks, files + - locked: D-04 goose chosen, .sql with +goose Up/Down format + - locked: D-12 standalone Tailwind binary, no Node toolchain + - locked: D-13/D-14 air for Go live-reload; Tailwind watch runs separately (two-terminal workflow) + - locked: D-15 .env.example documents DATABASE_URL/PORT/ENV + - locked: D-16 pgx/v5 + pgxpool; sqlc emits pgx-compatible code +metrics: + duration_minutes: ~8 + completed: 2026-05-14 + tasks_completed: 5 + files_created: 18 +--- + +# Phase 01 Plan 01: Backend Scaffold Summary + +Stood up the `backend/` Go module skeleton — directory layout, compose Postgres, justfile recipes, sqlc/air/tailwind configs, and a no-op goose bootstrap migration — with zero application code, exactly per the locked CONTEXT D-01..D-20 decisions and revised post-Codex-review plan. + +## What Shipped + +### Go module + pinned runtime deps (commit `aa90008`) +- `backend/go.mod` declares `module backend` against Go 1.26.1. +- Five runtime deps pinned via `go get` at the exact RESEARCH-Standard-Stack versions: `chi v5.2.5`, `templ v0.3.1020`, `pgx/v5 v5.9.2`, `goose/v3 v3.27.1`, `uuid v1.6.0`. +- `go mod tidy` deliberately NOT executed (Codex concern #1) — tidy would strip the require lines while no source file imports them yet. Tidy lands implicitly in Plan 01-03's `just build` once consumer code exists. + +### Directory skeleton + doc.go placeholders (commit `4de9685`) +- `internal/db/doc.go`, `internal/session/doc.go`, `internal/tablos/doc.go`, `internal/tasks/doc.go`, `internal/files/doc.go` — each a one-line comment + bare `package` clause per D-02. +- Empty directories materialized via `.gitkeep`: `internal/db/queries/`, `internal/db/sqlc/`, `templates/`, `migrations/`, `bin/`, `static/`. +- `cmd/web/`, `cmd/worker/`, `internal/web/` deliberately NOT created here — owned by Plan 01-02 (web ui/tests) and Plan 01-03 (entrypoints). +- `go build ./internal/...` compiles cleanly. + +### Compose, env, gitignore, bootstrap migration (commit `2fe5b51`) +- `compose.yaml`: `postgres:16-alpine`, container name `xtablo-backend-postgres`, `xtablo:xtablo` credentials, `pg_isready` healthcheck (5s × 10), named volume `postgres_data`. No seed mounts (Phase 1 has nothing to seed). +- `.env.example`: `DATABASE_URL`, `PORT=8080`, `ENV=development` (D-15 exact key set). +- `.gitignore`: excludes `.env`, `bin/`, `tmp/`, generated `static/tailwind.css`, bootstrap-downloaded `static/htmx.min.js`, `*_templ.go` (Pitfall 1), `internal/db/sqlc/*.go`. +- `migrations/0001_init.sql`: goose-formatted no-op with `-- +goose Up` / `-- +goose Down` markers and `SELECT 1;` bodies. + +### sqlc, Tailwind input CSS, air (commit `d6085b7`) +- `sqlc.yaml`: `version: "2"`, schema source = `migrations/` (Pitfall 7 alignment), `internal/db/queries/` → `internal/db/sqlc/` (package `sqlc`), `sql_package: "pgx/v5"`. +- `tailwind.input.css`: verbatim per UI-SPEC contract — `@import "tailwindcss"`, three `@source` globs covering `templates/**/*.templ`, `internal/web/**/*.templ`, `internal/web/**/*.go` (Pitfall 3), then `@import` of the four `internal/web/ui/{base,button,card,badge}.css` files (those CSS files land in Plan 01-02). +- `.air.toml`: watches `.go`+`.templ`, excludes `*_templ.go` (Pitfall 5), runs `templ generate && go build -o ./tmp/web ./cmd/web`. Tailwind intentionally NOT wired into air's pre_cmd (RESEARCH Open Q2 — two-terminal workflow). + +### justfile (commit `ce22472`) +Recipes: `default`, `bootstrap`, `db-up`, `db-down`, `migrate cmd="status"`, `generate`, `styles-watch`, `dev`, `test`, `lint`, `build`, `clean` — `just --list` enumerates 11 user-facing recipes. + +- `bootstrap` installs goose `v3.27.1` / templ `v0.3.1020` / sqlc `v1.31.1` / air `v1.65.1` and downloads Tailwind v4.0.0 standalone + HTMX v2 latest into `bin/` and `static/`. +- Tailwind asset name uses explicit `case` mapping (Codex concern #2): `Darwin→macos`, `Linux→linux`, `x86_64/amd64→x64`, `arm64/aarch64→arm64`, resolving to one of `tailwindcss-{macos,linux}-{x64,arm64}`. The four canonical asset names are documented inline; `grep -E 'tailwindcss-(macos|linux)-(x64|arm64)' backend/justfile` matches. +- `migrate` recipe sets `GOOSE_DRIVER=postgres`, `GOOSE_DBSTRING`, `GOOSE_MIGRATION_DIR=migrations`. +- `clean` recipe removes `bin/`, `tmp/`, `static/htmx.min.js`, `static/tailwind.css`, and `*_templ.go` files (Codex concern #10). +- No `pnpm`/`npm`/`node` references anywhere (D-12). +- Bootstrap-time CDN URLs (Tailwind GitHub releases, HTMX unpkg) are confined to the justfile — D-10 clarified: no **runtime** CDN references in served HTML/CSS/JS. + +## Codex Review Concerns — Compliance + +| # | Concern | Resolution | +|---|---------|------------| +| 1 | No `go mod tidy` in this plan | Honored — deps pinned via `go get` only; tidy deferred to Plan 01-03. | +| 2 | Tailwind URL via explicit OS/arch case (not raw `uname` interpolation) | Honored — `case` statements + inline doc comment listing the four canonical assets. | +| 3 | "Bootstrap-downloaded" wording (not "vendored") | Honored — used in .gitignore comment, justfile comments, and this SUMMARY. | +| 4 | No runtime CDN references | Honored — the only CDN URLs in `backend/` are inside `justfile` bootstrap. | +| 5 | `generate / test / build / dev` may not be end-to-end runnable yet | Acknowledged — only `just --list` parseability asserted in Plan 01-01. | +| 6 | Real `just clean` recipe | Implemented — removes bin/, tmp/, generated css, htmx, *_templ.go. | + +## Deviations from Plan + +None — plan executed exactly as written. + +## Checkpoint Auto-Approval + +⚡ Auto-approved checkpoint: human-verify (Task 6 — `just bootstrap` + `just db-up` + `just migrate up` end-to-end) + +Auto-mode was active. Task 6 is a real network-dependent verification (downloads goose/templ/sqlc/air CLIs, downloads Tailwind binary and HTMX, brings up a Postgres container, applies the migration). Auto-approving it means the per-task commits land; the developer is expected to actually run the five-step bootstrap loop the next time they pick the repo up. If any step fails on their machine, that becomes a follow-up bug, not a plan-execution failure. The scaffold is mechanical and the file-level acceptance criteria (every `verify` block in Tasks 1–5) all passed automated grep/build checks before commit. + +## Pinned Versions Actually Wired + +| Tool | Version | Where pinned | +|------|---------|-------------| +| Go runtime | 1.26.1 | `go.mod` (matches developer's installed toolchain) | +| chi | v5.2.5 | `go.mod` | +| templ runtime | v0.3.1020 | `go.mod` | +| pgx/v5 | v5.9.2 | `go.mod` | +| goose/v3 | v3.27.1 | `go.mod` + `justfile` (CLI install) | +| uuid | v1.6.0 | `go.mod` | +| templ CLI | v0.3.1020 | `justfile` (CLI install) | +| sqlc CLI | v1.31.1 | `justfile` (CLI install) | +| air CLI | v1.65.1 | `justfile` (CLI install) | +| Tailwind standalone | v4.0.0 | `justfile` (download) | +| HTMX | v2 (latest 2.x at bootstrap time) | `justfile` (download) | + +## Verification + +- `cd backend && test -f go.mod && grep '^module backend$' go.mod` → OK +- `cd backend && go build ./internal/...` → OK (placeholder packages compile) +- `cd backend && just --list` → 11 user recipes enumerated, all required names present +- `grep -E 'tailwindcss-(macos|linux)-(x64|arm64)' backend/justfile` → 2 matches (Codex #2) +- File-level grep checks per each task's `` block → all pass + +## Path Forward + +- **Plan 01-02 (Wave 2):** lands `internal/web/handlers_test.go` for `/healthz` OK/Down + index/fragment + RequestID + slog-handler-switch tests, plus the `internal/web/ui/{base,button,card,badge}.css` files referenced by `tailwind.input.css`. After 01-02, `just generate` will partially run (Tailwind step will succeed; templ step still needs Plan 01-03 templ files). +- **Plan 01-03 (Wave 3):** lands `cmd/web/main.go`, `cmd/worker/main.go`, `internal/db/pool.go`, the templ files for the index + demo fragment, the chi router + middleware. After 01-03, `just bootstrap && just db-up && just migrate up && just dev` is a fully runnable walking skeleton. + +## Known Stubs + +None requiring resolution within this plan — every placeholder (`doc.go` files, `*_templ.go` ignore rule, the four `ui/*.css` `@import` lines, the `cmd/web` reference in `.air.toml`) is wired exactly where the next plans will land their real content; no UI-visible stub flows to a user yet because there is no UI yet. + +## Self-Check: PASSED + +- `backend/go.mod` exists with `module backend` and five pinned runtime deps — FOUND +- `backend/go.sum` exists, non-empty — FOUND +- `backend/internal/{db,session,tablos,tasks,files}/doc.go` all exist and `go build ./internal/...` passes — FOUND +- `backend/compose.yaml`, `.env.example`, `.gitignore`, `migrations/0001_init.sql` — FOUND +- `backend/sqlc.yaml`, `tailwind.input.css`, `.air.toml` — FOUND +- `backend/justfile` parses; `just --list` shows all 11 required recipes — FOUND +- Commits `aa90008`, `4de9685`, `2fe5b51`, `d6085b7`, `ce22472` present in `git log` — FOUND