Commit graph

1037 commits

Author SHA1 Message Date
Arthur Belleville
facd2846f2
docs(05-04): complete Phase 5 verification checkpoint
- Pre-flight automation passed: go test ./... exits 0, go build exits 0
- Human-verify checkpoint auto-approved (AUTO_MODE=true)
- All FILE-01..06 requirements confirmed implemented in Plans 01-03
2026-05-15 12:37:48 +02:00
Arthur Belleville
fe4f166ce3
docs(phase-05): update tracking after wave 3 2026-05-15 12:36:06 +02:00
Arthur Belleville
e30d3c3d7f
docs(05-03): complete file download + delete plan SUMMARY
- All six FILE requirements (FILE-01..06) implemented across Plans 01-03
- TDD gates documented (RED 98a5a02, GREEN 9d4dd4f)
- Self-check passed
2026-05-15 12:35:13 +02:00
Arthur Belleville
9d4dd4f3e2
feat(05-03): implement FileDownloadHandler, FileDeleteConfirmHandler, FileDeleteHandler
- FileDownloadHandler: nil guard → loadOwnedTabloForFile → PresignDownload → 302 redirect (FILE-04)
- FileDeleteConfirmHandler: nil guard → loadOwnedTabloForFile → render FileDeleteConfirmFragment
- FileDeleteHandler: nil guard → loadOwnedTabloForFile → S3 Delete (log+continue) → DeleteTabloFile → FileRowGone HTMX / 303 redirect (FILE-05, FILE-06)
- Add FileDeleteConfirmFragment templ component mirroring TaskDeleteConfirmFragment pattern (T-05-03-05)
2026-05-15 12:34:07 +02:00
Arthur Belleville
98a5a02b93
test(05-03): add RED test scaffold for file download + delete handlers
- Expand stubbedFileStorer with deletedKey tracking and deleteErr injection field
- Implement TestFileDownload (FILE-04): 302 redirect to presigned URL
- Implement TestFileDownload_NonOwner: non-owner gets 404
- Implement TestFileDelete (FILE-05): HTMX delete, S3+DB both deleted
- Implement TestFileDelete_S3Failure: S3 error does not abort DB delete, 200 returned
- Implement TestFileOwnership (FILE-06): non-owner gets 404 on all three routes
2026-05-15 12:32:49 +02:00
Arthur Belleville
072eda1028
docs(phase-05): update tracking after wave 2 2026-05-15 12:30:36 +02:00
Arthur Belleville
99719e6e96
docs(05-02): complete file upload + tab navigation plan SUMMARY
- Tasks: 2 + RED scaffold committed
- Commits: cc0d6cf (RED), f50836f (GREEN handlers), a12c5ab (templates + router + wiring)
- All acceptance criteria met; go build + go test ./... pass
- Known stubs: FileDownloadHandler/DeleteHandler/DeleteConfirmHandler (Plan 03)
2026-05-15 12:29:42 +02:00
Arthur Belleville
a12c5abea6
feat(05-02): 3-tab layout + files templates + router + main.go S3 wiring
- tablos.templ: TabloDetailPage gains files+activeTab params, 3-tab nav with hx-push-url
- tablos.templ: TabloOverviewTabFragment + TasksTabFragment (wraps KanbanBoard) added
- files.templ: FilesTabFragment, FileUploadForm (hx-encoding=multipart/form-data),
  FileListRow, FileListEmpty, FileRowGone, UploadErrorFragment
- files_helpers.go: formatBytes() converts int64 bytes to human-readable string
- router.go: fileDeps FilesDeps param added; TabloTasksTabHandler + file routes wired
- handlers_tablos.go: both TabloDetailPage call sites updated (nil, 'overview')
- main.go: S3_ENDPOINT/S3_BUCKET/S3_REGION env vars read; files.NewStore constructed;
  fileDeps wired; nil filesStore allowed when S3 env unset (503 from handlers)
- All test routers updated to pass FilesDeps{} in new param position
2026-05-15 12:28:33 +02:00
Arthur Belleville
f50836fa31
feat(05-02): implement FilesDeps + FileUploadHandler + TabloFilesTabHandler + TabloTasksTabHandler
- FilesDeps struct with Queries, Files FileStorer, MaxUploadMB
- loadOwnedTabloForFile helper (mirrors loadOwnedTabloForTask)
- TabloFilesTabHandler: nil guard first, loadOwnedTablo, list files, HTMX/full-page dispatch
- TabloTasksTabHandler: loadOwnedTablo, list tasks, HTMX/full-page dispatch
- FileUploadHandler: nil guard, MaxBytesReader before ParseMultipartForm, S3 key files/{uuid}, InsertTabloFile, list + redirect
- FileDownloadHandler/FileDeleteConfirmHandler/FileDeleteHandler: 501 stubs for Plan 03
- Security: D-04 S3 key isolation, T-05-02-02 size guard, T-05-02-04 ownership
2026-05-15 12:28:07 +02:00
Arthur Belleville
cc0d6cfd4e
test(05-02): add RED test scaffold for file upload and tab handlers
- TestFileUpload: POST /tablos/{id}/files → 303 redirect + DB row + S3 key check
- TestFileUploadTooLarge: oversized file → 422 + 'too large' message
- TestFilesList: GET /tablos/{id}/files lists pre-inserted file with filename + size
- TestFilesTab: HTMX fragment vs full-page rendering
- stubbedFileStorer records uploadedKey for assertion
- TestFileDownload/Delete/Ownership remain t.Skip (Plan 03)
2026-05-15 12:24:40 +02:00
Arthur Belleville
74af9d7052
docs(phase-05): update tracking after wave 1 2026-05-15 12:21:26 +02:00
Arthur Belleville
848dca9263
docs(05-01): complete files foundation plan SUMMARY
- Wave 0: aws-sdk-go-v2 modules, 0005_files migration, sqlc queries, files.Store, RED test scaffold, MinIO in compose
- go build ./... passes; all 6 TestFile* stubs SKIP; TestStoreImplementsFileStorer PASS
2026-05-15 12:20:05 +02:00
Arthur Belleville
3327a4286d
test(05-01): add RED test scaffold for FILE-01..06 and MinIO in compose.yaml
- Create handlers_files_test.go: six TestFile* stubs (all t.Skip), stubbedFileStorer no-op implementing files.FileStorer
- Create store_test.go: compile-time interface assertion, TestNewStore_SkipIfNoEndpoint skips when S3_ENDPOINT unset
- Update compose.yaml: add minio (port 9000/9001) and minio-init services; minio-init uses restart: no (Pitfall 7); add minio_data volume
2026-05-15 12:19:23 +02:00
Arthur Belleville
e0d72747e0
feat(05-01): add aws-sdk-go-v2 modules, 0005_files migration, sqlc queries, and files.Store
- Add four aws-sdk-go-v2 modules: core, config, credentials, service/s3
- Write 0005_files.sql migration (tablo_files table with ON DELETE CASCADE)
- Write internal/db/queries/files.sql with InsertTabloFile, ListFilesByTablo, GetTabloFileByID, DeleteTabloFile
- Implement internal/files/store.go: FileStorer interface, Store struct, NewStore (UsePathStyle for MinIO), Upload (sniff+stream+bytecount), Delete, PresignDownload
- sqlc generate produces files.sql.go + TabloFile model (gitignored, regeneratable)
2026-05-15 12:18:16 +02:00
Arthur Belleville
5ce8b70f69
docs(05): create phase plan
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 12:14:48 +02:00
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
Arthur Belleville
261c85ae73
docs(05): research phase files domain 2026-05-15 11:46:09 +02:00
Arthur Belleville
740d367dc4
docs(state): record phase 5 context session 2026-05-15 10:58:24 +02:00
Arthur Belleville
1c02de475e
docs(05): capture phase context 2026-05-15 10:58:19 +02:00
Arthur Belleville
9e6ab6d5aa
docs(04): mark review findings fixed
All 6 in-scope findings (CR-01, CR-02, WR-01, WR-02, WR-03, WR-04)
have been applied and verified. Updated status from issues_found to fixed.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:19:54 +02:00
Arthur Belleville
61e6e778e0
fix(04-WR-04): guard against int32 overflow in TaskCreateHandler position arithmetic
maxPos + 100 could silently overflow to a negative value when maxPos
approached MaxInt32. Added a maxAllowedPosition guard that returns a
validation error before the InsertTask call if the column position space
is exhausted.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:19:48 +02:00
Arthur Belleville
f6ab318f4e
fix(04-WR-03): trim whitespace from description in TaskUpdateHandler
A description of spaces-only was being stored as a valid non-null DB value
because the empty-string check ran before trimming. Now consistent with how
other nullable text fields are handled.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:19:30 +02:00
Arthur Belleville
3dafba72cc
fix(04-WR-02): set HX-Retarget/HX-Reswap on 422 path in TaskCreateHandler
Without these headers, HTMX used the form's own hx-target="#column-{status}"
+ hx-swap="beforeend", appending the error form into the task column and
destroying all visible task cards. The error form now lands back in the
add-task slot where it belongs.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:19:10 +02:00
Arthur Belleville
3d32f2d92f
fix(04-WR-01): check UpdateTask errors in TaskReorderHandler instead of discarding
Both the single-task branch and the main loop were using _, _ = to
discard UpdateTask errors. Now both log the error and return 500 so
the client is never shown a false success when DB writes fail.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:18:53 +02:00
Arthur Belleville
392b5321be
fix(04-CR-02): replace fmt.Fprintf in TaskDeleteHandler with TaskCardGone templ component
The raw fmt.Fprintf bypassed templ's auto-escaping pipeline and was
inconsistent with every other handler. Added TaskCardGone(taskID uuid.UUID)
to tasks.templ and updated TaskDeleteHandler to use it. Ran just generate.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:18:34 +02:00
Arthur Belleville
e97f4988bd
fix(04-CR-01): add r.ParseForm() to TaskCreateHandler and TaskUpdateHandler
Both handlers were missing the mandatory ParseForm call before reading
PostFormValue. This caused gorilla/csrf (which reads the body for CSRF
token validation) to consume the body, leaving PostFormValue to return
empty strings. TaskReorderHandler was used as the correct reference.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:17:49 +02:00
Arthur Belleville
142ca547ea
docs(04): add code review report 2026-05-15 10:10:41 +02:00
Arthur Belleville
879f7b1161
docs(phase-04): complete phase execution — tasks kanban
7/7 TASK requirements verified. Human browser verification done in
plan 04-04 checkpoint (badge count + DnD bugs fixed before approval).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:08:02 +02:00
Arthur Belleville
06bbb49a7f
docs(04-04): human checkpoint approved — phase 4 complete
All 7 TASK requirements verified in browser. Two bugs found and fixed
during verification (badge count, drag-and-drop draggable selector).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:04:21 +02:00
Arthur Belleville
131c9fd6b3
fix(04): draggable:.task-card-zone — move wrapper not inner card
Sortable.js draggable must match direct children of .sortable-column.
Using .task-card (grandchild) caused Sortable to detach it from its
.task-card-zone wrapper, breaking HTMX OOB swap targets and making
drag appear to do nothing. Changed to .task-card-zone so the full
wrapper moves, keeping id= attributes intact for HTMX round-trips.

Also removed redundant form.dispatchEvent() before htmx.trigger()
which could cause a double submit on reorder.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:01:34 +02:00
Arthur Belleville
68f2ccdea3
fix(04): badge count + DnD init — use DOMContentLoaded/htmx:afterSettle
Replace htmx.onLoad (requires htmx at parse time) with native
document.addEventListener('DOMContentLoaded') + 'htmx:afterSettle'
so Sortable.js is guaranteed loaded before init runs.

Add task-count-badge-{status} wrapper IDs and updateBadges() that
recounts .task-card elements on every HTMX settle so badge counts
stay in sync after create, delete, and reorder operations.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 09:56:28 +02:00
Arthur Belleville
8ac0a51969
docs(04-03): complete plan 03 — inline edit + reorder fully implemented
- 04-03-SUMMARY.md created with full execution details
- STATE.md updated: decisions, metrics, notes for plan 03
- ROADMAP.md updated with plan progress (3/4 summaries for phase 4)
2026-05-15 09:40:35 +02:00
Arthur Belleville
f6deb8709b
feat(04-03): remove t.Skip from TestTaskOrderPersists — all 9 TestTask* tests active
- Remove t.Skip("handlers_tasks not yet implemented") from TestTaskOrderPersists
- Full test suite green: go test ./... exits 0, no FAIL lines
- All 9 TestTask* tests active (skip on missing TEST_DATABASE_URL per existing pattern)
2026-05-15 09:38:53 +02:00
Arthur Belleville
5f87d7e0ea
feat(04-03): implement TaskReorderHandler + remove t.Skip from reorder tests
- TaskReorderHandler: POST /tablos/{id}/tasks/reorder updates status+position
- Fetches existing task via GetTaskByID before UpdateTask (T-04-08 mass assignment guard)
- Supports both array form (task_id[]/task_col[]) and single-value form (task_id/status/position)
- Invalid UUIDs silently skipped (D-05); tasks from other tablos skipped (T-04-10)
- Returns updated KanbanBoard outerHTML for HTMX swap
- Remove t.Skip from TestTaskReorderCrossColumn and TestTaskReorderSameColumn
2026-05-15 09:38:28 +02:00
Arthur Belleville
2b299e21f4
feat(04-03): implement TaskEditHandler, TaskUpdateHandler, TaskEditFragment
- TaskEditHandler: GET /tablos/{id}/tasks/{task_id}/edit returns TaskEditFragment pre-filled with existing title+description
- TaskUpdateHandler: POST validates title (required, max 255), updates title+description preserving status+position (T-04-12)
- TaskEditFragment: outer .task-card-zone wrapper with outerHTML round-trip, discard restores via /show
- Sortable.js htmx.onLoad init script added to KanbanBoard (Pitfall 2 protection)
- TaskEditFragment added to tasks.templ; remove t.Skip from TestTaskUpdate
2026-05-15 09:37:46 +02:00
Arthur Belleville
5158eb09af
docs(04-02): complete kanban vertical slice plan — handlers, templates, test activation
- Add 04-02-SUMMARY.md with full decisions and deviation documentation
- Update STATE.md: Plans 01-02 done, add decisions, metrics, notes
- Update ROADMAP.md phase 4 progress (2/4 plans complete)
2026-05-15 09:35:03 +02:00
Arthur Belleville
92ebb5f5fe
feat(04-02): activate task integration tests (RED stubs to GREEN-ready)
- Remove t.Skip from TestTasksKanbanRenders, TestTaskCreate, TestTaskCreateValidation, TestTaskDelete, TestTaskOwnership
- Fix column header strings: 'To do'/'In progress'/'In review' to match TaskColumnLabels
- Add kanban-board id assertion and non-owner 404 check to TestTasksKanbanRenders
- TestTaskUpdate, TestTaskReorder*, TestTaskOrderPersists remain SKIP for Plan 03
2026-05-15 09:33:19 +02:00
Arthur Belleville
889164b437
feat(04-02): KanbanBoard, TaskCard, TaskDeleteConfirmFragment templates
- Add tasks.templ with KanbanBoard, KanbanColumn, TaskCard, TaskCreateFormFragment, TaskDeleteConfirmFragment, AddTaskTrigger, TaskCardOOB
- Add TaskColumns/TaskColumnLabels to tasks_forms.go (moved from web package to break import cycle)
- Update TabloDetailPage signature to accept tasks []sqlc.Task; embed KanbanBoard below tablo header
- Update handlers_tablos.go TabloDetailHandler to fetch tasks via ListTasksByTablo
- Update layout.templ: add sortable.min.js script tag, update footer to Phase 4 · Tasks
2026-05-15 09:32:06 +02:00
Arthur Belleville
181ae79369
feat(04-02): TasksDeps, task handlers, router task routes
- Add handlers_tasks.go: TasksDeps, TaskNewFormHandler, TaskCancelNewHandler, TaskCreateHandler, TaskShowHandler, TaskDeleteConfirmHandler, TaskDeleteHandler, plus stub Edit/Update/Reorder handlers
- Add task routes to router.go (static before parametric per Pitfall 1)
- Add TasksDeps param to NewRouter; update main.go and all test callers
- Move TaskColumns/TaskColumnLabels to templates package to avoid import cycle
2026-05-15 09:31:59 +02:00
Arthur Belleville
2be4cb6bc9
docs(04-01): complete Wave 0 foundation plan summary and state updates
- 04-01-SUMMARY.md: documents migration, sqlc, RED scaffold, Sortable.js, soft-danger CSS
- STATE.md: Phase 4 in-progress, 3 new decisions recorded, metrics row added
- ROADMAP.md: Phase 4 progress updated (1/4 plans)
- REQUIREMENTS.md: TASK-01..07 marked complete
2026-05-15 09:26:31 +02:00
Arthur Belleville
55fb32f1e1
chore(04-01): Sortable.js bootstrap and soft-danger button CSS
- justfile: add sortable_version := "1.15.7" variable
- justfile: bootstrap downloads sortable.min.js from jsDelivr
- justfile: clean removes static/sortable.min.js
- button.css: add .ui-button-soft-danger-md rule with hover and focus-visible states
- static/sortable.min.js: downloaded at 1.15.7 (45 kB)
2026-05-15 09:24:44 +02:00
Arthur Belleville
8b9543db6f
test(04-01): add RED test scaffold and task form structs
- handlers_tasks_test.go: 9 TestTask* functions (TASK-01..07 + IDOR) all skip
- TasksDeps stub struct declared in test file for Plan 02 wiring
- tasks_forms.go: TaskCreateForm, TaskCreateErrors, TaskUpdateForm, TaskUpdateErrors structs
- go build ./... passes; go test -run TestTask exits 0 with all 9 SKIP
2026-05-15 09:24:05 +02:00
Arthur Belleville
c9c826247a
feat(04-01): add tasks migration and sqlc query source
- CREATE TYPE task_status ENUM (todo, in_progress, in_review, done)
- CREATE TABLE tasks with tablo_id FK, position, status columns
- DROP order: table before type in Down migration (Pitfall 3)
- sqlc queries: ListTasksByTablo, InsertTask, GetTaskByID, UpdateTask, DeleteTask, MaxPositionByTabloAndStatus
- migration applied cleanly, sqlc generate produces TaskStatus type and Task struct
2026-05-15 09:22:18 +02:00
Arthur Belleville
2c6c3bf87e
docs(04): create phase plan — 4 plans for Tasks (Kanban)
Wave 1: migration 0004_tasks + sqlc + RED test scaffold + Sortable.js + CSS
Wave 2: board render + task create + delete (TASK-01, TASK-02, TASK-06)
Wave 3: inline edit + drag reorder/move (TASK-03, TASK-04, TASK-05, TASK-07)
Wave 4: human-verify checkpoint

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 09:19:42 +02:00
Arthur Belleville
7f58588f5a
docs(04): create phase 4 tasks-kanban plan (4 plans, 3 waves)
Wave 1 (Plan 01): DB migration, sqlc queries, RED test scaffold, Sortable.js bootstrap, soft-danger CSS.
Wave 2 (Plan 02): Kanban board render + task create + task delete vertical slice (TASK-01, TASK-02, TASK-06).
Wave 3 (Plan 03): Inline task edit + Sortable.js drag reorder/move (TASK-03, TASK-04, TASK-05, TASK-07).
Wave 4 (Plan 04): Human-verify checkpoint — full browser verification of all 7 TASK requirements.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 09:16:17 +02:00
Arthur Belleville
cfd30eb277
docs(04): UI design contract 2026-05-15 09:05:54 +02:00
Arthur Belleville
cdcb335fec
docs(04): UI design contract for Tasks (Kanban) phase
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 09:01:17 +02:00
Arthur Belleville
02cf49ac31
docs(04): add validation strategy 2026-05-15 08:52:20 +02:00
Arthur Belleville
1c7b9d632c
docs(04): research phase tasks-kanban domain 2026-05-15 08:51:06 +02:00
Arthur Belleville
338e7e6e92
docs(state): record phase 4 context session 2026-05-15 08:40:41 +02:00