--- gsd_state_version: 1.0 milestone: v4.0 milestone_name: Figma Design Parity status: ready_to_plan last_updated: 2026-05-18T14:04:17.721Z last_activity: 2026-05-18 -- Phase 20 execution started progress: total_phases: 5 completed_phases: 2 total_plans: 9 completed_plans: 9 percent: 40 stopped_at: Phase 20 complete (3/3) — ready to discuss Phase 21 --- # STATE **Project:** Xtablo Go+HTMX Product **Milestone:** v3.0 — Design System & Visual Polish **Created:** 2026-05-14 ## Project Reference See: `.planning/PROJECT.md` (updated 2026-05-17) **Core value:** A user can sign in and run the Tablos workflow — organize work, attach files, discuss, and plan scheduled events — without a JS framework or managed chat provider. **Current focus:** Phase 21 — task grid & roadmap views ## Current Position Phase: 21 Plan: Not started Status: Ready to plan Last activity: 2026-05-18 ## Previous Milestone Status | # | Phase | Status | |---|-------|--------| | 8 | Social Sign-in | ✓ Complete | | 9 | Etapes | ✓ Complete | | 10 | Events | ✓ Complete | | 11 | Individual Planning | ✓ Complete | | 12 | Native Tablo Chat | ✓ Complete | ## Verification Record - 2026-05-15: Phase 8 execution complete. Local verification passed with `go test ./... -count=1`; database-backed integration coverage skips unless `TEST_DATABASE_URL` is configured. - 2026-05-15: Phase 9 UAT complete. Five browser checkpoints passed after fixing the selected-etape create gap; backend verification passed with `TEST_DATABASE_URL='postgres://xtablo:xtablo@localhost:5432/xtablo?sslmode=disable' go test ./... -count=1`. - 2026-05-16: Phase 10 execution complete. Events UAT approved; backend verification passed with `TEST_DATABASE_URL='postgres://xtablo:xtablo@localhost:5432/xtablo?sslmode=disable' go test ./... -count=1`. - 2026-05-16: Phase 12 execution complete. Discussion realtime UAT approved after duplicate-row and composer-reset fixes; backend verification passed with `just generate` and `TEST_DATABASE_URL='postgres://xtablo:xtablo@localhost:5432/xtablo?sslmode=disable' go test ./... -count=1`. - 2026-05-16: Milestone v2.0 archived after acknowledging 7 legacy/open artifact items as deferred. ## Deferred Items Items acknowledged and deferred at v3.0 milestone close on 2026-05-17 (all pre-existing from v1.0/v2.0): | Category | Item | Status | |----------|------|--------| | uat_gap | Phase 05: 05-HUMAN-UAT.md | partial; 6 pending scenarios | | uat_gap | Phase 07: 07-HUMAN-UAT.md | partial; 3 pending scenarios | | verification_gap | Phase 03: 03-VERIFICATION.md | human_needed | | verification_gap | Phase 04: 04-VERIFICATION.md | human_needed | | verification_gap | Phase 05: 05-VERIFICATION.md | human_needed | | verification_gap | Phase 07: 07-VERIFICATION.md | human_needed | ## Decisions - **Consolidated internal/auth package** (not split with internal/session) — RESEARCH Open Question 3 - **compose Postgres + schema isolation for tests** (not testcontainers-go) — RESEARCH Open Question 1 - **goose.SetTableName per test-schema** prevents public goose_db_version table collision - **argon2id** over bcrypt for password hashing — D-08, confirmed by user - **Hand-rolled PHC encode/decode** (Pattern 1 verbatim) — no alexedwards/argon2id wrapper dep; keeps code lean and self-tested - **Store.now injectable field** (not method/interface) — simpler for single-test-clock use case in MaybeExtend threshold tests - **sha256.Sum256 inlined at Create + Lookup** (not in helper) — satisfies >= 2 grep acceptance criterion; makes D-05 hashing visible at usage sites - **SignupForm/SignupErrors in templates package** (not handlers or separate forms pkg) — avoids import cycle between templates and internal/web - **setupTestDB duplicated into web package test file** — Go prohibits importing _test.go files across packages; duplication chosen over non-test shared infra - **nil-Store guard in auth.ResolveSession** — enables Phase 1 unit tests to pass AuthDeps{} zero value without panic - **errInvalidCreds const** (not inline string) — satisfies D-20 single-source-of-truth grep gate; both credential failure paths use the constant - **NewLimiterStoreWithClock exported** — cross-package integration tests inject a frozen clock without a test-helper shim - **Status 401 for non-HTMX credential failures** — consistent with HTTP semantics; HTMX path returns 200+fragment - **Layout takes *auth.User explicitly** (not thread-through-ctx) — easier to test templates in isolation; no magic context values - **TestIndex_RendersHxGet rewritten to TestIndex_UnauthRedirects** — GET / is now protected; Phase 1 test was a false positive after this plan - **csrf.PlaintextHTTPRequest(r) in dev mode** — skips TLS Referer check in plain-HTTP local dev/httptest; production unaffected - **trustedOrigins variadic arg in auth.Mount + NewRouter** — test routers pass "localhost"; production passes none - **sqlc-generated files not committed** — backend/.gitignore excludes internal/db/sqlc/*.go; just generate reproduces them (03-01) - **NewRouter extended with TablosDeps parameter** — second explicit deps param before csrfKey; all call sites updated (03-01) - **index_templ.go deleted manually after emptying index.templ** — templ generates broken import when .templ has no components (03-02) - **Non-HTMX validation error re-fetches tablos and renders full dashboard** — no form state threading; simpler than threading form through full page (03-02) - **_zone hidden field (title|desc) in edit fragments** — unified POST /tablos/{id} handler reads _zone to select which display fragment to return; misuse only affects response body, not DB state (03-03) - **loadOwnedTablo helper for all parametric handlers** — uuid.Parse + GetTabloByID + ownership check factored into single function; 404 on any failure, 403 never used (D-04) (03-03) - **TabloDeleteButtonFragment canonical delete-zone** — TabloCard delegates to it; single source of truth for zone HTML across card and detail-page contexts (03-03) - **task_status ENUM order matches visual column order** (todo, in_progress, in_review, done) — ENUM ordinal sorting aligns with kanban column display order per Pitfall 6 (04-01) - **Down migration drops TABLE before TYPE** — tasks table dropped before task_status type per Pitfall 3; type cannot be dropped while still referenced (04-01) - **TasksDeps stub in test file** — declared in handlers_tasks_test.go; moved to handlers_tasks.go in Plan 02 to avoid file dependency before handlers exist (04-01) - **TaskColumns/TaskColumnLabels in templates package** — moved from web package to templates to avoid web↔templates import cycle; handlers reference via templates.TaskColumns (04-02) - **TabloDetailPage accepts tasks []sqlc.Task** — kanban board embedded below tablo header; TabloDetailHandler fetches tasks via ListTasksByTablo before rendering (04-02) - **Dual reorder payload** — TaskReorderHandler supports array form (task_id[]/task_col[]) and single-value form (task_id/status/position) for test scaffold + Sortable.js compatibility (04-03) - **GetTaskByID before UpdateTask in reorder** — preserves title+description (T-04-08), validates task-to-tablo ownership at fetch time (T-04-10) (04-03) - **fileQuerier interface in OrphanCleanupWorker** — enables mock injection for pure unit tests without real DB; pool field retained for production (06-01) - **river deps as // indirect until Plan 02** — cmd/worker wiring in Plan 02 will promote river to direct dependency; expected Go module behavior (06-01) - **signal.NotifyContext after rivermigrate and S3 init** — startup I/O uses context.Background(); signal context created after all I/O succeeds; prevents river.Client.Start receiving a pre-cancelled context (RESEARCH Pitfall 2) (06-02) - **pool.Close() explicit after StopAndCancel, not via defer** — StopAndCancel fully returns before pool is closed; matches PATTERNS.md pool close ordering (06-02) - **Social-only users use NULL password_hash** — email/password login rejects them through the generic invalid-credentials path while provider sign-in owns the session path (08-01) - **Verified provider identity links by provider subject first, then verified email** — prevents duplicate local users while keeping provider subject as the strongest identity key (08-02) - **Apple callback uses GET/query response mode for the first working version** — keeps the callback inside existing CSRF middleware boundaries; Apple dashboard configuration must match (08-03) - **Provider buttons degrade to disabled controls when config is missing** — auth pages remain deployable without provider credentials (08-04) - **Account providers page is read-only in Phase 8** — linked identity visibility shipped before unlink/add-password account management (08-05) - **Apple sign-in disabled after UAT scope change** — Apple controls are hidden, Apple auth routes are not mounted, and provider docs only cover Google for now (08-UAT) - **Google button width: max-content (not 100%)** — matches Google Material Design branding guidelines; pill button naturally sized, not stretched to card width (14-02) - **Roboto font loaded in auth_layout.templ only** — auth pages are the only gsi-material-button context; global stylesheet pollution avoided (14-02) - **AuthProviderButtonsBlock/AuthProviderButtonControl removed** — superseded by GoogleButton from auth_components.templ; single-provider design, components consolidated (14-02) - **Air dev .css watching added** — .css in include_ext + Tailwind in rebuild cmd; CSS edits now trigger reload without manual restart (14-02) ## Performance Metrics | Phase | Plan | Duration | Tasks | Files | |-------|------|----------|-------|-------| | 02-authentication | 01 | ~10min | 3 | 9 | | 02-authentication | 02 | ~8min | 2 | 4 | | 02-authentication | 03 | ~15min | 2 | 5 | | 02-authentication | 04 | ~25min | 2 | 11 | | 02-authentication | 05 | ~7min | 2 | 9 | | 02-authentication | 06 | ~12min | 1 | 9 | | 02-authentication | 07 | ~25min | 1 | 18 | | 03-tablos-crud | 01 | ~15min | 3 | 10 | | 03-tablos-crud | 02 | ~4min | 3 | 8 | | 03-tablos-crud | 03 | ~30min | 3 | 5 | | 04-tasks-kanban | 01 | ~4min | 3 | 7 | | 04-tasks-kanban | 02 | ~20min | 3 | 12 | | Phase 04-tasks-kanban P03 | ~15min | 3 tasks | 3 files | | 06-background-worker | 01 | ~15min | 2 | 9 | | Phase 06-background-worker P01 | ~15min | 2 tasks | 9 files | | 06-background-worker | 02 | ~10min | 2 | 3 | | Phase 08 P01 | 25min | 2 tasks | 8 files | | Phase 08 P02 | 35min | 2 tasks | 9 files | | Phase 08 P03 | 30min | 2 tasks | 7 files | | Phase 08 P04 | 20min | 2 tasks | 8 files | | Phase 08 P05 | 20min | 2 tasks | 7 files | | Phase 11 P01 | ~15min | 2 tasks | 15 files | | Phase 11 P02 | ~20min | 2 tasks | 4 files | | Phase 12 P01 | ~25min | 3 tasks | 22 files | | Phase 12 P02 | ~25min | 2 tasks | 5 files | | Phase 12 P03 | ~45min | 4 tasks | 9 files | | Phase 14 P01 | 4min | 2 tasks | 5 files | | Phase 14 P02 | ~30min | 2 tasks | 4 files | | Phase 17-chat-planning P02 | 15min | 3 tasks | 6 files | ## Notes - Existing `go-backend/` is set aside; new code lives in a fresh `backend/` Go package. - DB schema is changing from the JS/Supabase version — user is in the loop on every schema decision (Phases 2–5). - Phase 2 Plan 01 SUMMARY: `.planning/phases/02-authentication/02-01-SUMMARY.md` - Phase 2 Plan 02 SUMMARY: `.planning/phases/02-authentication/02-02-SUMMARY.md` - Phase 2 Plan 03 SUMMARY: `.planning/phases/02-authentication/02-03-SUMMARY.md` - Commits (02-01): 513044d (migration), 799c260 (sqlc), 2c84f42 (auth package + test harness) - Commits (02-02): 3bb3828 (RED tests), ee36a5c (GREEN implementation) - Commits (02-03): fd2301d (session store + cookie helpers), 1d07830 (middleware) - Commits (02-04): 73935ed (signup templates + smoke tests), efdc16b (handler + router + integration tests) - Phase 2 Plan 04 SUMMARY: `.planning/phases/02-authentication/02-04-SUMMARY.md` - Commits (02-05): b5c20c7 (LimiterStore + tests), 7d8c498 (login handler + integration tests) - Phase 2 Plan 05 SUMMARY: `.planning/phases/02-authentication/02-05-SUMMARY.md` - Commits (02-06): b5c3fc4 (RED tests), 8b54ff4 (logout + protected routes + layout) - Phase 2 Plan 06 SUMMARY: `.planning/phases/02-authentication/02-06-SUMMARY.md` - Commits (02-07): ae2d356 (RED tests), 389e1bc (GREEN csrf implementation) - Phase 2 Plan 07 SUMMARY: `.planning/phases/02-authentication/02-07-SUMMARY.md` - Phase 3 Plan 01 SUMMARY: `.planning/phases/03-tablos-crud/03-01-SUMMARY.md` - Commits (03-01): f1b8d6e (migration + queries), c8f44b1 (TablosDeps stub + test scaffold), 2c1b186 (button CSS variants) - Phase 3 Plan 02 SUMMARY: `.planning/phases/03-tablos-crud/03-02-SUMMARY.md` - Commits (03-02): 43ddf25 (tablos templates + layout footer), 5db9215 (tablo handlers + router wiring), c08da7f (delete retired index.templ) - 03-02 complete — all 3 tasks done including human-verify checkpoint approval - Phase 3 Plan 03 SUMMARY: `.planning/phases/03-tablos-crud/03-03-SUMMARY.md` - Commits (03-03): 6f167e2 (detail/edit/delete templates), ab6937c (handlers + router + all 10 TABLO tests green), b5fa318 (checkpoint approved) - **Phase 3 complete** — All 3 plans done; TABLO-01..06 closed; 10/10 TABLO tests green; full browser verify passed 2026-05-15 - Phase 4 Plan 01 SUMMARY: `.planning/phases/04-tasks-kanban/04-01-SUMMARY.md` - Commits (04-01): c9c8262 (migration + sqlc queries), 8b9543d (RED test scaffold + form structs), 55fb32f (Sortable.js + soft-danger CSS) - Phase 4 Plan 02 SUMMARY: `.planning/phases/04-tasks-kanban/04-02-SUMMARY.md` - Commits (04-02): 181ae79 (handlers + router + main.go), 889164b (templates + tablos.templ + layout.templ), 92ebb5f (activate task tests) - Phase 4 Plan 03 SUMMARY: `.planning/phases/04-tasks-kanban/04-03-SUMMARY.md` - Commits (04-03): 2b299e2 (TaskEditHandler + TaskUpdateHandler + TaskEditFragment + Sortable.js init), 5f87d7e (TaskReorderHandler + reorder test skips removed), f6deb87 (TestTaskOrderPersists active — full suite green) - Phase 6 Plan 01 SUMMARY: `.planning/phases/06-background-worker/06-01-SUMMARY.md` - Commits (06-01): 62e5e3e (river dep + ListOrphanFiles sqlc query), a1c2828 (internal/jobs package + unit tests) - Phase 6 Plan 02 SUMMARY: `.planning/phases/06-background-worker/06-02-SUMMARY.md` - Commits (06-02): 6e70478 (cmd/worker full river wiring), e202ad3 (just worker target + README docs) - Phase 8 Plan 01 SUMMARY: `.planning/phases/08-social-sign-in/08-01-SUMMARY.md` - Phase 8 Plan 02 SUMMARY: `.planning/phases/08-social-sign-in/08-02-SUMMARY.md` - Phase 8 Plan 03 SUMMARY: `.planning/phases/08-social-sign-in/08-03-SUMMARY.md` - Phase 8 Plan 04 SUMMARY: `.planning/phases/08-social-sign-in/08-04-SUMMARY.md` - Phase 8 Plan 05 SUMMARY: `.planning/phases/08-social-sign-in/08-05-SUMMARY.md` - Commits (08): 2d004cd (social identity schema foundation), 6779663 (Google sign-in), a8b6a03 (Apple implementation later disabled), 59fd6b1 (auth page provider controls), 6e65836 (account providers view + docs) - Phase 9 Plan 01 SUMMARY: `.planning/phases/09-etapes/09-01-SUMMARY.md` - Phase 9 Plan 02 SUMMARY: `.planning/phases/09-etapes/09-02-SUMMARY.md` - Phase 9 Plan 03 SUMMARY: `.planning/phases/09-etapes/09-03-SUMMARY.md` - Phase 9 Plan 04 SUMMARY: `.planning/phases/09-etapes/09-04-SUMMARY.md` - Phase 9 UAT: `.planning/phases/09-etapes/09-UAT.md` — 5/5 checkpoints passed after selected-etape create fix. - Commits (09): a8a3e5f/565bb88 (first etape slice), 9b89282/4af623a (management), 9f6c7eb/b22d79d (assignment selector), 55263e4/3a3ecf5/cf07c29 (reorder hardening), 0c95049/ee62ff9/f9fc7a1 (UAT fixes) - Phase 14 Plan 01 SUMMARY: `.planning/phases/14-auth-pages/14-01-SUMMARY.md` - Phase 14 Plan 02 SUMMARY: `.planning/phases/14-auth-pages/14-02-SUMMARY.md` - Commits (14-01): cf116ff (logo assets + auth.css), e4d5f96 (auth_components.templ + auth_layout.templ) - Commits (14-02): 808eaec (auth_login.templ), 65e3dbf (auth_signup.templ), 4624fb3 (Roboto + icon fix), 70fe384 (button width fix), 6e64cfb (air CSS watch) --- *Last updated: 2026-05-16 after Phase 14 Plan 02 execution complete* ## Operator Next Steps - Start the next milestone with /gsd-new-milestone