xtablo-source/.planning/ROADMAP.md
Arthur Belleville f115082bd5
docs(05): create phase 5 file upload plans (4 plans, 4 waves)
Adds 4 PLAN.md files for Phase 5 — Files. Wave 1 lays the S3
foundation (aws-sdk-go-v2, migration, FileStorer, MinIO compose).
Wave 2 delivers the upload + list vertical slice with 3-tab tablo
layout. Wave 3 closes download + delete. Wave 4 is the browser
verify checkpoint.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 11:58:52 +02:00

12 KiB

Roadmap: Xtablo Go+HTMX Rewrite

Created: 2026-05-14 Project mode: Vertical MVP (each phase delivers an end-to-end user-visible slice where possible) Milestone: v1 — Tablos workflow on Go+HTMX

7 phases, sequential. Earlier phases are foundational; later phases build atop them. Phase boundaries are deliberate review points — especially for DB schema decisions (Phases 2, 3, 4, 5).


Phase Summary

# Phase Goal Requirements
1 Foundation Fresh backend/ Go package boots, renders HTMX, talks to Postgres FOUND-01..05
2 Authentication Complete (7/7) AUTH-01..07
3 Tablos CRUD Complete (3/3) TABLO-01..06
4 3/4 In Progress
5 Files A user can attach, list, download, delete files on a tablo FILE-01..06
6 Background Worker A second binary runs jobs against the same Postgres WORK-01..04
7 Deploy v1 The product runs in production on a single host DEPLOY-01..05

Phase Details

Phase 1: Foundation

Goal: A fresh backend/ Go package boots a web server, renders an HTMX-driven base layout, and connects to a local Postgres with migrations. Mode: mvp Requirements: FOUND-01, FOUND-02, FOUND-03, FOUND-04, FOUND-05 Success Criteria:

  1. just dev starts the web server on a local port and live-reloads on .go and template changes
  2. GET /healthz returns 200 with a JSON {status:"ok", db:"ok"} only when the DB is reachable
  3. The root route renders a Tailwind-styled HTMX page that loads without console errors and includes a working hx-get example
  4. just migrate up applies migrations from backend/migrations/ against the local Postgres
  5. A new dev can clone the repo, run compose up -d + just dev, and see the page within ~5 minutes following backend/README.md

User-in-loop: Approve directory layout (backend/cmd/web, backend/cmd/worker, backend/internal/...) and pick the migration tool (goose vs golang-migrate).

Plans: 4 plans Plans:

  • 01-01-PLAN.md — Project scaffold: Go module, compose, justfile, env, sqlc/air/tailwind config, bootstrap migration
  • 01-02-PLAN.md — RED gate: failing handler tests + internal/web/ui design-system package (Button/Card/Badge)
  • 01-03-PLAN.md — GREEN slice: pgxpool, chi router, middleware, templates, cmd/web + cmd/worker, end-to-end HTMX demo
  • 01-04-PLAN.md — README quickstart + clean-clone onboarding walkthrough (closes FOUND-05)

Phase 2: Authentication

Goal: A new user can sign up, log in with email + password, and stay logged in across requests using server-managed sessions. Mode: mvp Requirements: AUTH-01, AUTH-02, AUTH-03, AUTH-04, AUTH-05, AUTH-06, AUTH-07 Success Criteria:

  1. Signing up creates a user row with hashed password (argon2id or bcrypt) and starts a session
  2. Logging in with valid credentials issues a signed HTTP-only cookie; invalid credentials show a clear error
  3. Hitting any protected route while unauthenticated redirects to /login; logged-in users on /login go to /
  4. Logout invalidates the session server-side (cookie cleared + DB session row deleted)
  5. All POST routes require a valid CSRF token; missing/invalid tokens return 403
  6. 5 failed logins per email/IP per minute triggers rate-limiting

User-in-loop: Approve the users and sessions table schemas (columns, indexes, deletion semantics) before sqlc generation. Approve hash algorithm choice.

Plans: 7/7 plans executed Plans:

  • 02-01-PLAN.md — Schema + sqlc + auth-package skeleton (citext + users + sessions, test DB harness)
  • 02-02-PLAN.md — argon2id password hashing (TDD: Hash/Verify with PHC encoding)
  • 02-03-PLAN.md — Session store + cookie + ResolveSession/RequireAuth/RedirectIfAuthed middleware
  • 02-04-PLAN.md — Signup vertical slice (form → validate → hash → InsertUser → session → cookie → redirect)
  • 02-05-PLAN.md — Login vertical slice + in-memory rate limiter (AUTH-07)
  • 02-06-PLAN.md — Logout + protect GET / + layout header logout button (AUTH-04, AUTH-05)
  • 02-07-PLAN.md — Mount gorilla/csrf + @ui.CSRFField templ helper across every form (AUTH-06)

Phase 3: Tablos CRUD

Goal: A logged-in user can list, create, view, edit, and delete their tablos end-to-end through HTMX-driven flows. Mode: mvp Requirements: TABLO-01, TABLO-02, TABLO-03, TABLO-04, TABLO-05, TABLO-06 Success Criteria:

  1. Dashboard lists the current user's tablos newest-first; empty state shows a "Create your first tablo" CTA
  2. Creating a tablo via the create form inserts a row, dismisses the modal/inline form, and prepends the new tablo via HTMX swap
  3. Tablo detail page renders title and description; non-owners (or unauthenticated) get a 404
  4. Editing title/description updates the row and re-renders the affected fragments without a full page reload
  5. Deleting a tablo removes it from the list (with a confirmation step) and is irreversible via the UI
  6. All actions work without JS errors and degrade gracefully if HTMX is unavailable (forms still submit)

User-in-loop: Approve the tablos table schema (ownership model, soft-delete vs hard-delete, slug strategy).

Plans: 3/3 plans executed Plans:

  • 03-01-PLAN.md — Wave 0: migration 0003_tablos + sqlc queries + handlers_tablos_test.go RED scaffold + button.css danger/neutral variants
  • 03-02-PLAN.md — Vertical slice 1: dashboard list + inline-form create (HTMX OOB swap; TABLO-01, TABLO-02, TABLO-06)
  • 03-03-PLAN.md — Vertical slice 2: detail + inline edit (title/description) + inline-confirmation delete (TABLO-03, TABLO-04, TABLO-05, TABLO-06)

Phase 4: Tasks (Kanban)

Goal: Inside a tablo, a user can run a kanban board — create, edit, move, reorder, and delete tasks across columns. Mode: mvp Requirements: TASK-01, TASK-02, TASK-03, TASK-04, TASK-05, TASK-06, TASK-07 Success Criteria:

  1. A tablo detail page shows a kanban board with at least three columns
  2. Creating a task inserts it into the target column and renders without full reload
  3. Editing a task updates title/description in place
  4. Moving a task between columns persists the new column and refreshes the source + target columns
  5. Reordering within a column persists and survives reload
  6. Deleting a task removes it from the board with a confirmation
  7. Two concurrent edits don't corrupt order (last-write-wins is acceptable for v1, documented)

User-in-loop: Approve the task_columns (or fixed-column) schema and the ordering strategy (fractional indices, gaps-of-100, linked list — to be decided with research). Approve whether reorder is drag-and-drop or button-driven.

Plans: 3/4 plans executed Plans:

  • 04-01-PLAN.md — Wave 0: migration 0004_tasks + sqlc queries + handlers_tasks_test.go RED scaffold + soft-danger button CSS + Sortable.js bootstrap
  • 04-02-PLAN.md — Vertical slice 1: kanban board render + task create + task delete (TASK-01, TASK-02, TASK-06)
  • 04-03-PLAN.md — Vertical slice 2: task inline edit + Sortable.js drag reorder/move (TASK-03, TASK-04, TASK-05, TASK-07)
  • 04-04-PLAN.md — Human-verify checkpoint: full kanban board browser verification

Phase 5: Files

Goal: A user can attach files to a tablo, list them, download them via signed URLs, and delete them — backed by S3-compatible storage. Mode: mvp Requirements: FILE-01, FILE-02, FILE-03, FILE-04, FILE-05, FILE-06 Success Criteria:

  1. Uploading a file from a tablo detail page creates a tablo_files row and stores bytes in the configured S3 bucket
  2. The files list shows original filename, size, and uploaded-at; sorted newest first
  3. Downloads use signed URLs with a short TTL (e.g. 5 minutes) generated server-side
  4. Deleting a file removes both the DB row and the S3 object; failures are surfaced and logged
  5. Only the tablo owner can upload/list/download/delete files for a given tablo (verified by tests)
  6. Configurable max upload size enforced server-side, with a friendly error message above the form

User-in-loop: Approve the tablo_files schema (key strategy, content-type handling, dedup). Approve upload method (direct PUT vs server-proxied).

Plans: 4 plans Plans:

  • 05-01-PLAN.md — Wave 1: go get aws-sdk-go-v2 + migration 0005_files + sqlc queries + files.Store + FileStorer interface + RED test scaffold + MinIO in compose.yaml
  • 05-02-PLAN.md — Wave 2: handlers_files.go (FilesDeps + FileUploadHandler + TabloFilesTabHandler + TabloTasksTabHandler) + tablos.templ 3-tab layout + files.templ (upload form + list) + router + main.go wiring (FILE-01, FILE-02, FILE-03, FILE-06)
  • 05-03-PLAN.md — Wave 3: FileDownloadHandler (302 → presigned URL) + FileDeleteConfirmHandler + FileDeleteHandler (S3-first delete) + FileDeleteConfirmFragment + full TestFile* green (FILE-04, FILE-05, FILE-06)
  • 05-04-PLAN.md — Wave 4: Human-verify checkpoint: tab navigation + upload/list/download/delete end-to-end browser walkthrough

Phase 6: Background Worker

Goal: A second binary (cmd/worker) runs against the same Postgres, processes jobs from a queue, and proves end-to-end with at least one real job. Mode: mvp Requirements: WORK-01, WORK-02, WORK-03, WORK-04 Success Criteria:

  1. cmd/worker starts, connects to Postgres, and registers handlers; logs are structured and graceful shutdown works
  2. At least one real job (chosen during plan-phase — e.g. periodic orphan-file cleanup) runs on a schedule and is observable in logs
  3. A failing job is retried with backoff and visible via a simple CLI surface (backend list-failed-jobs or admin route)
  4. Web binary can enqueue a job; worker picks it up within a few seconds
  5. README documents how to run the worker locally alongside the web binary

User-in-loop: Approve the queue library/approach (river vs asynq vs hand-rolled pg_notify) and pick the proof-of-life job.

Phase 7: Deploy v1

Goal: The product runs in production on a single host, behind a documented deploy + rollback workflow. Mode: mvp Requirements: DEPLOY-01, DEPLOY-02, DEPLOY-03, DEPLOY-04, DEPLOY-05 Success Criteria:

  1. A multi-stage Dockerfile builds both web and worker and the image starts either via a subcommand
  2. The container runs on the chosen single-host target (e.g. Hetzner VM / Fly.io / Cloud Run) with env-injected config
  3. Deploy step runs migrations against the production database before traffic is shifted
  4. /healthz and /readyz return appropriate status codes during startup, steady state, and shutdown
  5. backend/README.md documents: first-time deploy, routine deploy, rollback, and incident triage basics

User-in-loop: Approve the deploy target choice (Hetzner / Fly / Cloud Run) and the secret-management strategy (env vars vs .env file vs SOPS).


Coverage

  • v1 requirements: 40
  • Mapped to phases: 40
  • Unmapped: 0 ✓

Notes

  • Sequential execution: each phase depends on the previous. Auth must work before Tablos; Tablos must exist before Tasks/Files can attach to them.
  • The user is in the loop on DB schema decisions at the start of Phases 2, 3, 4, and 5. Treat schema approval as a hard gate inside plan-phase — sqlc generation does not run until the schema is approved.
  • Visual design is intentionally undefined per phase; UI plans choose Tailwind patterns inline. A gsd-ui-phase step can be invoked for any phase if a more deliberate UI contract is desired (Phases 3, 4, 5 are the strongest candidates).
  • The legacy apps/* JS app is the behavioral reference — .planning/codebase/ is the source of truth for "what does the JS version do today?".

Roadmap created: 2026-05-14 Phase 3 plans added: 2026-05-15 Phase 4 plans added: 2026-05-15 Phase 5 plans added: 2026-05-15