chore: merge executor worktree (worktree-agent-a98c01ef03e47adfc) [plan 01-01]

This commit is contained in:
Arthur Belleville 2026-05-14 17:57:38 +02:00
commit a639fdf502
No known key found for this signature in database
22 changed files with 435 additions and 0 deletions

View file

@ -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 15) 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 `<verify>` 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

11
backend/.air.toml Normal file
View file

@ -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

8
backend/.env.example Normal file
View file

@ -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

24
backend/.gitignore vendored Normal file
View file

@ -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

0
backend/bin/.gitkeep Normal file
View file

21
backend/compose.yaml Normal file
View file

@ -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:

18
backend/go.mod Normal file
View file

@ -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
)

33
backend/go.sum Normal file
View file

@ -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=

View file

@ -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

View file

View file

View file

@ -0,0 +1,2 @@
// Package files is a Phase 1 placeholder; the upload/storage implementation lands in Phase 5.
package files

View file

@ -0,0 +1,2 @@
// Package session is a Phase 1 placeholder; the session/cookie implementation lands in Phase 2.
package session

View file

@ -0,0 +1,2 @@
// Package tablos is a Phase 1 placeholder; the tablo CRUD implementation lands in Phase 3.
package tablos

View file

@ -0,0 +1,2 @@
// Package tasks is a Phase 1 placeholder; the kanban task implementation lands in Phase 4.
package tasks

116
backend/justfile Normal file
View file

@ -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

View file

View file

@ -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;

12
backend/sqlc.yaml Normal file
View file

@ -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

0
backend/static/.gitkeep Normal file
View file

View file

@ -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";

View file