xtablo-source/.planning/phases/05-files/05-02-SUMMARY.md
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

7.8 KiB


phase: 05-files plan: "02" subsystem: files-upload-tabs tags: [htmx, s3, multipart, tab-navigation, templ, router] dependency_graph: requires: - 05-01 (FileStorer interface, sqlc TabloFile model, files.sql.go queries) provides: - backend/internal/web/handlers_files.go (FilesDeps, TabloFilesTabHandler, FileUploadHandler, TabloTasksTabHandler) - backend/templates/files.templ (FilesTabFragment, FileUploadForm, FileListRow, FileRowGone, UploadErrorFragment) - backend/templates/files_helpers.go (formatBytes) - backend/templates/tablos.templ (TabloDetailPage 6-arg, TasksTabFragment, TabloOverviewTabFragment) - backend/internal/web/router.go (fileDeps parameter, file + tasks-tab routes) - backend/cmd/web/main.go (S3 env vars, files.NewStore, fileDeps wired) affects: - backend/internal/web/handlers_tablos.go (TabloDetailPage call sites updated) - backend/internal/web/handlers_auth_test.go (NewRouter signature updated) - backend/internal/web/handlers_tablos_test.go (NewRouter signature updated) - backend/internal/web/handlers_tasks_test.go (NewRouter signature updated) - backend/internal/web/handlers_test.go (NewRouter signature updated) - backend/internal/web/csrf_test.go (NewRouter signature updated) tech_stack: added: [] patterns: - multipart upload via http.MaxBytesReader + ParseMultipartForm (Pitfall 2 guard) - S3 key isolation: "files/{tablo_id}/{uuid}" — filename never reaches S3 key (D-04) - nil FileStorer guard as FIRST statement in TabloFilesTabHandler + FileUploadHandler (T-05-02-06) - hx-push-url on all tab links for browser URL sync (D-08) - HTMX fragment vs full-page dispatch on HX-Request header key_files: created: - backend/internal/web/handlers_files.go - backend/templates/files.templ - backend/templates/files_helpers.go modified: - backend/internal/web/handlers_files_test.go (activated from stub scaffold) - backend/templates/tablos.templ (TabloDetailPage restructured, fragment components added) - backend/internal/web/router.go (fileDeps param, new routes) - backend/internal/web/handlers_tablos.go (call sites updated) - backend/cmd/web/main.go (S3 env + fileDeps wiring) - backend/internal/web/handlers_auth_test.go - backend/internal/web/handlers_tablos_test.go - backend/internal/web/handlers_tasks_test.go - backend/internal/web/handlers_test.go - backend/internal/web/csrf_test.go decisions:

  • "FileStorer type alias in handlers_files.go (= files.FileStorer) — avoids import of files package in test files while keeping interface stable"
  • "itoa helper inlined in handlers_files.go — avoids strconv import in web package for single use"
  • "formatBytes in templates/files_helpers.go — non-templ Go file in templates package; formatBytes called directly from templ files"
  • "TabloTasksTabHandler takes FilesDeps (not TasksDeps) — tab wiring is a Phase 5 concern; FilesDeps has same Queries pointer; avoids dual-deps threading"
  • "nil filesStore allowed in main.go when S3_ENDPOINT unset — dev/test mode without MinIO; handlers return 503 via nil guard"
  • "UploadErrorFragment re-renders full FilesTabFragment with error in FileUploadForm — simpler than OOB error injection; form + list always consistent" metrics: duration: ~25min completed: "2026-05-15" tasks: 2 files: 14

Phase 05 Plan 02: File Upload + Tab Navigation Summary

Vertical slice 1 for the files feature. A user can now visit /tablos/{id}/files, see an upload form and any existing file list, upload a file that gets stored in S3 with a DB row, and navigate the Overview/Tasks/Files tab bar with URL-sync via hx-push-url. The tab restructuring covers D-07/D-08 and unlocks the files workflow.

Tasks Completed

# Name Commit Files
RED File handler test scaffold cc0d6cf handlers_files_test.go
1 FilesDeps + FileUploadHandler + TabloFilesTabHandler + TabloTasksTabHandler f50836f handlers_files.go
2 3-tab layout + files templates + router + main.go S3 wiring a12c5ab tablos.templ, files.templ, files_helpers.go, router.go, handlers_tablos.go, main.go, 5 test files

Verification Results

  • go build ./... exits 0
  • go test ./... exits 0 — all packages pass; TestFile* SKIP (integration tests without DB — expected)
  • backend/internal/web/handlers_files.go contains type FilesDeps struct, func TabloFilesTabHandler(, func FileUploadHandler(, func TabloTasksTabHandler(
  • FileUploadHandler first statement is deps.Files == nil nil guard returning 503
  • TabloFilesTabHandler first statement is deps.Files == nil nil guard returning 503
  • FileUploadHandler contains http.MaxBytesReader before ParseMultipartForm
  • FileUploadHandler contains "files/" string for S3 key (D-04)
  • FileUploadHandler contains http.MaxBytesError for size violation detection
  • backend/templates/tablos.templ contains activeTab string in TabloDetailPage, hx-push-url, tab-content, TasksTabFragment
  • backend/templates/files.templ contains FilesTabFragment, FileUploadForm, hx-encoding, FileRowGone
  • backend/internal/web/router.go contains fileDeps FilesDeps in NewRouter, TabloFilesTabHandler, TabloTasksTabHandler
  • backend/cmd/web/main.go contains files.NewStore and fileDeps := web.FilesDeps
  • Both TabloDetailPage call sites in handlers_tablos.go end with nil, "overview"

Deviations from Plan

Auto-fixed Issues

None — plan executed exactly as written with one minor implementation adaptation:

1. [Rule 2 - Missing critical functionality] itoa helper in handlers_files.go

  • Found during: Task 1 implementation
  • Issue: formatMBError needed integer-to-string conversion; importing strconv only for this single use would add a dependency unnecessarily
  • Fix: Added private itoa(n int) string helper in handlers_files.go; eliminates strconv import
  • Files modified: backend/internal/web/handlers_files.go
  • Commit: f50836f

2. [Rule 3 - Blocking issue] All test routers needed FilesDeps parameter

  • Found during: Task 2 — after NewRouter signature change, 5 test files failed to build
  • Fix: Updated all 5 test routers (handlers_auth_test.go, handlers_tablos_test.go, handlers_tasks_test.go, handlers_test.go, csrf_test.go) to pass FilesDeps{Queries: q} or FilesDeps{} in the new position
  • Files modified: 5 test files
  • Commit: a12c5ab

Known Stubs

File Description Resolved by
backend/internal/web/handlers_files.go FileDownloadHandler returns 501 Plan 03
backend/internal/web/handlers_files.go FileDeleteConfirmHandler returns 501 Plan 03
backend/internal/web/handlers_files.go FileDeleteHandler returns 501 Plan 03
backend/internal/web/handlers_files_test.go TestFileDownload t.Skip — Plan 03 Plan 03
backend/internal/web/handlers_files_test.go TestFileDelete t.Skip — Plan 03 Plan 03
backend/internal/web/handlers_files_test.go TestFileOwnership t.Skip — Plan 03 Plan 03

These stubs are intentional. The stub handlers return 501 and are referenced in FileListRow download/delete links — they will be replaced in Plan 03 (file download + delete).

Threat Surface Scan

All new surface is covered by the plan's threat model:

  • POST /tablos/{id}/files (T-05-02-01..06) — covered
  • GET /tablos/{id}/files (T-05-02-04, T-05-02-06) — covered
  • Stub routes for download/delete return 501 with no data exposure

No new unplanned trust boundaries introduced.

Self-Check: PASSED

Files verified:

  • FOUND: backend/internal/web/handlers_files.go
  • FOUND: backend/templates/files.templ
  • FOUND: backend/templates/files_helpers.go

Commits verified:

  • FOUND: cc0d6cf (test(05-02): add RED test scaffold...)
  • FOUND: f50836f (feat(05-02): implement FilesDeps...)
  • FOUND: a12c5ab (feat(05-02): 3-tab layout...)