xtablo-source/.planning/ROADMAP.md
Arthur Belleville f53b54637b
docs(03): plan phase 3 — Tablos CRUD (3 plans, 3 waves)
Plans cover TABLO-01..06 via MVP vertical slices: foundation (migration
+ sqlc + test scaffold + button CSS), list+create (dashboard, inline
form, OOB swap), and detail+edit+delete (ownership 404, inline edit
fragments, inline confirm delete). Includes Nyquist VALIDATION.md and
PATTERNS.md with real analog excerpts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 00:08:08 +02:00

163 lines
10 KiB
Markdown

# 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 | An authenticated user can manage their tablos end-to-end | TABLO-01..06 |
| 4 | Tasks (Kanban) | A user can run a kanban board inside a tablo | TASK-01..07 |
| 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:
- [x] 01-01-PLAN.md — Project scaffold: Go module, compose, justfile, env, sqlc/air/tailwind config, bootstrap migration
- [x] 01-02-PLAN.md — RED gate: failing handler tests + `internal/web/ui` design-system package (Button/Card/Badge)
- [x] 01-03-PLAN.md — GREEN slice: pgxpool, chi router, middleware, templates, cmd/web + cmd/worker, end-to-end HTMX demo
- [x] 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:
- [x] 02-01-PLAN.md — Schema + sqlc + auth-package skeleton (citext + users + sessions, test DB harness)
- [x] 02-02-PLAN.md — argon2id password hashing (TDD: Hash/Verify with PHC encoding)
- [x] 02-03-PLAN.md — Session store + cookie + ResolveSession/RequireAuth/RedirectIfAuthed middleware
- [x] 02-04-PLAN.md — Signup vertical slice (form → validate → hash → InsertUser → session → cookie → redirect)
- [x] 02-05-PLAN.md — Login vertical slice + in-memory rate limiter (AUTH-07)
- [x] 02-06-PLAN.md — Logout + protect GET / + layout header logout button (AUTH-04, AUTH-05)
- [x] 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 plans
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.
### 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).
### 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*