Commit graph

1091 commits

Author SHA1 Message Date
Arthur Belleville
ab12bf0962
fix(07): WR-02 move rate limit check before validation in LoginPostHandler
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 18:55:27 +02:00
Arthur Belleville
b61f36f17e
fix(07): WR-01 NewRouter returns error instead of panicking on bad static FS
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 18:54:49 +02:00
Arthur Belleville
fbda7cbe5e
fix(07): CR-02 call cancel() explicitly after S3 Delete, not via defer
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 18:53:04 +02:00
Arthur Belleville
62edbca44e
docs(phase-07): evolve PROJECT.md after phase completion 2026-05-15 18:48:20 +02:00
Arthur Belleville
2a73bba4e1
docs(phase-07): complete phase execution 2026-05-15 18:47:58 +02:00
Arthur Belleville
5fc4705bd3
fix(07): replace minioadmin placeholder creds and add worker->web migration gate 2026-05-15 18:46:30 +02:00
Arthur Belleville
c8bce7669f
test(07): persist human verification items as UAT 2026-05-15 18:33:14 +02:00
Arthur Belleville
087a933fe7
test(07): add phase verification report 2026-05-15 18:33:02 +02:00
Arthur Belleville
9ff40ac821
docs(07): add code review report 2026-05-15 18:29:46 +02:00
Arthur Belleville
7bca961bb0
docs(07-03): complete production compose stack and runbook plan
- SUMMARY for 07-03: docker-compose.prod.yaml, deploy/Caddyfile, README runbook
2026-05-15 18:26:01 +02:00
Arthur Belleville
f261fb39b8
docs(07-03): extend README with Deploy, Rollback, and Incident Runbook sections
- Deploy section: prerequisites, first-time setup, deploying new versions (DEPLOY-05)
- First-time setup documents DATABASE_URL internal URL, SESSION_SECRET generation,
  full S3/R2 var list, chmod 600 .env.prod reminder (T-07-10), TLS staging note
- Rollback section: image tag redeployment + break-glass schema rollback via goose CLI
- Incident Runbook: /readyz 503, Caddy TLS rate limits, log viewing, distroless debug
  (ephemeral busybox container technique for shell-less runtime image, RESEARCH Pitfall 7)
2026-05-15 18:25:03 +02:00
Arthur Belleville
273f0632be
feat(07-03): add docker-compose.prod.yaml and deploy/Caddyfile
- Production compose stack with postgres, web, worker, caddy services (D-01..D-04, D-08)
- postgres service has no host ports binding (internal network only, T-07-09 mitigated)
- web and worker use same image with different command: values (/app/web, /app/worker)
- Both web and worker depend_on postgres with service_healthy condition (T-07-12 mitigated)
- Caddy handles TLS via Let's Encrypt with persistent caddy_data and caddy_config volumes (D-04)
- Caddyfile uses {$DOMAIN} env var interpolation for the site block (RESEARCH Pattern 6)
- Caddyfile includes Let's Encrypt staging note to avoid rate limits (RESEARCH Pitfall 4)
2026-05-15 18:23:13 +02:00
Arthur Belleville
45701bf8aa
chore: merge executor worktree (worktree-agent-ad2dff45f7520558c) 2026-05-15 18:20:51 +02:00
Arthur Belleville
2329e19e75
docs(07-02): complete plan summary — Dockerfile and .env.example S3/R2 vars 2026-05-15 18:20:38 +02:00
Arthur Belleville
0781403f5c
feat(07-02): add S3/R2, DOMAIN, and MAX_UPLOAD_SIZE_MB vars to .env.example
- Add S3_ENDPOINT, S3_BUCKET, S3_REGION, S3_ACCESS_KEY, S3_SECRET_KEY with MinIO dev defaults
- Add S3_USE_PATH_STYLE (true for MinIO, false for R2 virtual-hosted)
- Add MAX_UPLOAD_SIZE_MB=25 with default note
- Add commented DOMAIN=app.yourdomain.com for Caddy TLS in docker-compose.prod.yaml (D-04)
- Clarify TEST_DATABASE_URL is dev/test only and must not appear in .env.prod
- All original vars (DATABASE_URL, SESSION_SECRET, PORT, ENV) preserved
2026-05-15 18:19:58 +02:00
Arthur Belleville
f29bf0c765
feat(07-02): multi-stage Dockerfile for web + worker binaries
- Stage 1 (assets): downloads Tailwind v4.0.0 CLI, HTMX@2, Sortable.js 1.15.7; compiles minified CSS
- Stage 2 (builder): runs templ generate @v0.3.1020; CGO_ENABLED=0 go build for /app/web and /app/worker
- Stage 3 (runtime): gcr.io/distroless/static-debian12:nonroot; no CMD per D-08
- No .env files COPY'd into any layer (T-07-05 mitigated)
2026-05-15 18:19:32 +02:00
Arthur Belleville
5550befffc
chore: merge executor worktree (worktree-agent-a1df44c5ba4be47de) 2026-05-15 18:17:11 +02:00
Arthur Belleville
735106f797
docs(07-01): complete plan summary — embed anchor, RunMigrations, health endpoint split
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 18:16:05 +02:00
Arthur Belleville
bdd3cba314
feat(07-01): wire embed.FS into NewRouter and RunMigrations into cmd/web/main.go
- backend/internal/web/router.go: staticDir string -> staticFS fs.FS; /healthz uses HealthzHandler(); /readyz registered with ReadyzHandler(pinger); embedded FS served via fs.Sub()
- backend/cmd/web/main.go: import assets "backend"; db.RunMigrations(ctx, pool, assets.Migrations) before router; web.NewRouter now receives assets.Static
- All *_test.go NewRouter call sites updated from "./static" to os.DirFS("./static"); "os" import added where missing
2026-05-15 18:14:33 +02:00
Arthur Belleville
77e37cb21b
feat(07-01): embed.go + RunMigrations + HealthzHandler()/ReadyzHandler() split
- backend/embed.go: package assets with //go:embed all:static and //go:embed migrations
- backend/internal/db/migrate.go: RunMigrations using pgx/v5/stdlib bridge to goose.Up()
- backend/internal/web/handlers.go: HealthzHandler() no-arg liveness + ReadyzHandler(pinger) readiness
- backend/internal/web/handlers_test.go: TestHealthz_OK (no pinger), TestReadyz_OK, TestReadyz_Down added; TestHealthz_Down deleted
2026-05-15 18:14:26 +02:00
Arthur Belleville
aa3429717f
test(07-01): add failing tests for HealthzHandler (no-arg) and ReadyzHandler split
- TestHealthz_OK now calls HealthzHandler() with no args (liveness, no db field)
- TestHealthz_Down deleted (new HealthzHandler has no failure mode)
- TestReadyz_OK added: ReadyzHandler(stubPinger{err: nil}) -> 200 + db:ok
- TestReadyz_Down added: ReadyzHandler(stubPinger{err: ...}) -> 503 + degraded
2026-05-15 18:08:16 +02:00
Arthur Belleville
8ae83f6c50
docs(07): create phase plan 2026-05-15 18:06:08 +02:00
Arthur Belleville
dbe9d493be
docs(07): create phase 7 plan — deploy v1 (3 plans, 3 waves)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 17:57:46 +02:00
Arthur Belleville
8fbe87295a
docs(07): add validation strategy 2026-05-15 17:47:42 +02:00
Arthur Belleville
588c03dae2
docs(07): research phase domain 2026-05-15 17:46:37 +02:00
Arthur Belleville
e14fd36fdc
docs(07): capture phase context 2026-05-15 17:35:43 +02:00
Arthur Belleville
eec691442a
docs(06): resolve SC-3/SC-4 conflict — accept D-03/D-06 deferrals
ROADMAP SC-3 (list-failed-jobs CLI) and SC-4 (web binary enqueue) struck
through as deferred per CONTEXT.md decisions D-03 and D-06. VERIFICATION.md
status updated to complete (3/3 active success criteria).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 17:21:25 +02:00
Arthur Belleville
8b64b48490
docs(06): create gap-closure plan 04 — list-failed-jobs CLI and web enqueue wiring
Closes SC-3 (WORK-04) with a list-failed-jobs subcommand in cmd/worker and
SC-4 (WORK-01) by wiring a river insert-only client into cmd/web with a
/debug/enqueue-test handler that proves the web→worker enqueue path.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 17:12:25 +02:00
Arthur Belleville
7a54755618
docs(06): add phase verification report — gaps found
SC-3 (failed-job CLI surface) and SC-4 (web binary enqueue) not yet implemented.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 16:49:20 +02:00
Arthur Belleville
5e8dd4e4e4
docs(06): add code review report 2026-05-15 16:46:28 +02:00
Arthur Belleville
a81e112527
docs(06-03): human verification checkpoint passed
All WORK-01 through WORK-04 requirements verified live against Postgres + MinIO.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 16:43:48 +02:00
Arthur Belleville
9e07407484
docs(06-02): complete cmd/worker river wiring plan
- 06-02-SUMMARY.md: river wiring summary with commits, decisions, threat flag review
- STATE.md: decisions, metrics, notes, SUMMARY reference added for Plan 02
- ROADMAP.md: 06-02 marked complete (2/3 plans executed)
2026-05-15 16:39:54 +02:00
Arthur Belleville
e202ad3a9e
feat(06-02): add just worker target and document worker in README
- justfile: worker target depends on db-up, passes MinIO dev defaults
  (DATABASE_URL, S3_ENDPOINT/BUCKET/REGION/ACCESS_KEY/SECRET_KEY/USE_PATH_STYLE)
- README: replace skeleton section with full "Running the Worker" docs
  (just worker command, expected logs, single-worker constraint, graceful shutdown,
   failed job retry observation)
2026-05-15 16:38:01 +02:00
Arthur Belleville
6e70478417
feat(06-02): replace cmd/worker skeleton with full river wiring
- rivermigrate at startup (idempotent, before client construction)
- S3 store init from env vars (S3_ENDPOINT/S3_BUCKET/S3_ACCESS_KEY/S3_SECRET_KEY/S3_REGION/S3_USE_PATH_STYLE)
- signal.NotifyContext created AFTER all startup I/O (PATTERNS.md critical ordering)
- HeartbeatWorker + OrphanCleanupWorker registered via river.AddWorker
- river.Client with slog.Default() Logger, SlogErrorHandler, MaxWorkers:10
- HeartbeatArgs periodic every 1 min (RunOnStart:true), OrphanCleanupArgs every 1 hr
- StopAndCancel(10s timeout) on shutdown; pool.Close after StopAndCancel
2026-05-15 16:37:20 +02:00
Arthur Belleville
94ac095dee
docs(06-01): complete jobs foundation plan 2026-05-15 16:35:31 +02:00
Arthur Belleville
a1c2828dc4
feat(06-01): implement internal/jobs package with workers and error handler
- HeartbeatArgs + HeartbeatWorker (logs slog.Info on each tick)
- OrphanCleanupArgs + OrphanCleanupWorker (S3 delete then DB delete loop)
- NewOrphanCleanupWorker constructor with pool + FileStorer injection
- SlogErrorHandler implementing river.ErrorHandler (HandleError + HandlePanic)
- fileQuerier interface for test injection without real DB
- Unit tests: 7 tests pass (pure mock-based, no DB required)
- go build ./... exits 0
2026-05-15 16:34:08 +02:00
Arthur Belleville
62e5e3eb60
feat(06-01): add river dependency and ListOrphanFiles sqlc query
- go get github.com/riverqueue/river@v0.37.0 + riverpgxv5@v0.37.0
- append ListOrphanFiles :many query to files.sql (orphan tablo_files rows)
- regenerate sqlc: ListOrphanFilesRow{ID, TabloID, S3Key} exported
- go build ./... exits 0
2026-05-15 16:32:48 +02:00
Arthur Belleville
71d9c32e85
docs(06): create phase plan 2026-05-15 16:30:23 +02:00
Arthur Belleville
f242b7184f
docs(06): create phase 6 background worker plans
Three plans: Wave 1 adds river dependency + internal/jobs package with unit
tests; Wave 2 wires cmd/worker/main.go with full river runtime + justfile
target + README; Wave 3 is human-verify checkpoint.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 16:20:11 +02:00
Arthur Belleville
58cfac4955
docs(phase-6): add validation strategy 2026-05-15 16:12:51 +02:00
Arthur Belleville
27cecc6701
docs(06): research phase background-worker 2026-05-15 16:12:11 +02:00
Arthur Belleville
d8a130cd01
docs(state): record phase 6 context session 2026-05-15 15:13:11 +02:00
Arthur Belleville
9d1e24fc7e
docs(06): capture phase context 2026-05-15 15:13:06 +02:00
Arthur Belleville
8215f53356
docs(05-files): mark validation nyquist_compliant true, fill gap map
- Set nyquist_compliant: true and wave_0_complete: true in frontmatter
- Update Per-Task Verification Map: FILE-01..06 integration tests marked ⚠️
  (clean skip without TEST_DATABASE_URL); new unit tests marked  green
- Check all Wave 0 requirements as complete
- Fill in Validation Sign-Off checklist

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 13:29:13 +02:00
Arthur Belleville
cb7d5d1dd1
test(05-files): add pure unit tests for formatBytes, byteCountReader, and content-type sniff
Gap fill: three no-infrastructure unit tests that run without TEST_DATABASE_URL or S3_ENDPOINT:
- backend/templates/files_helpers_test.go — formatBytes boundary cases (B/KB/MB/GB)
- backend/internal/files/store_unit_test.go — byteCountReader accumulation, io.ErrUnexpectedEOF
  guard for small files, and MultiReader body reconstruction after 512-byte sniff

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 13:29:08 +02:00
Arthur Belleville
7fed6d1049
docs(phase-5): add security threat verification 2026-05-15 12:54:45 +02:00
Arthur Belleville
3a7726e6ed
docs(05): add code review fix report 2026-05-15 12:51:29 +02:00
Arthur Belleville
49e84c8176
fix(05-WR-01): raise ReadTimeout/WriteTimeout to 120s for large uploads
15s was too short for 25MB uploads on slow connections (~256KB/s takes
~100s). Both timeouts are raised to 120s to accommodate MAX_UPLOAD_SIZE_MB
at worst-case bandwidth with headroom.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 12:50:25 +02:00
Arthur Belleville
690ea2ddaf
fix(05): CR-01/WR-02/WR-03/WR-04 handlers_files.go fixes
- CR-01: add S3 cleanup before 500 when InsertTabloFile fails
- WR-02: validate empty filename, return 400 before S3 upload
- WR-03: remove dead errMsg variable (was silenced with _ = errMsg)
- WR-04: delete itoa/formatMBError helpers, inline strconv.Itoa

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 12:50:07 +02:00
Arthur Belleville
ab30db4a2f
docs(phase-5): complete phase execution 2026-05-15 12:46:25 +02:00